docs: align dream qa requirements

#14
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png filter=lfs diff=lfs merge=lfs -text
37
+ docs/prototypes/2026-06-08-dream-qa-mobile-flow.png filter=lfs diff=lfs merge=lfs -text
38
+ docs/prototypes/2026-06-08-dream-qa-tips-card.png filter=lfs diff=lfs merge=lfs -text
AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # AGENTS.md - Dream Customs / 梦境海关
2
 
3
  ## 语言规则
4
 
@@ -7,9 +7,10 @@
7
 
8
  ## 项目定位
9
 
10
- - 当前项目是 Build Small Hackathon 的 `Dream Customs / 梦境海关`。
11
- - 核心定位:帮用户和昨晚的结盟把不安转成第二天的小建议,把怪梦转成件有趣的小
12
- - 这不心理诊断、医疗建议或治疗工具。
 
13
 
14
  ## 竞赛约束
15
 
@@ -17,13 +18,13 @@
17
  - 模型总参数必须 <= 32B。
18
  - 优先叙事赛道:An Adventure in Thousand Token Wood。
19
  - 次级叙事:Backyard AI,因为项目来自真实睡眠/做梦困扰。
20
- - demo 必须能在短视频中快速展示:输入梦境,获得今日盟约卡
21
 
22
  ## 模型规则
23
 
24
  - 首选模型组合:
25
  - `openbmb/MiniCPM-V-4.6`:图片、草图、便签、截图理解。
26
- - `openbmb/MiniCPM5-1B`:文本推理、梦境外交官人格、结构化输出和建议生成
27
  - 不要在 MiniCPM 路线未验证前扩展到任意其他小模型。
28
  - 语音输入需要 ASR 适配器;ASR 只做转写,不承担梦境理解。
29
  - 首版不要实现语音输出,避免扩大部署风险。
@@ -33,23 +34,32 @@
33
  - 必须支持文字输入。
34
  - 应支持图片输入,用 MiniCPM-V-4.6 提取视觉线索。
35
  - 应支持语音输入,通过小 ASR 适配器转成文本。
36
- - 所有输入统一进入 `DreamIntake`,不要做成三套割裂产品。
 
 
 
 
 
 
 
37
  - 输出必须包含:
38
- - dream visitor / 梦境来访者
39
- - permit id / 入境编号
40
- - contraband / 携带情绪违禁品
41
- - risk level / 风险等级
42
- - alliance reading / 结盟解读
43
- - practical suggestion / 今日认真建议
44
- - weird task / 5 分钟怪趣任务
45
- - bedtime release / 睡前放仪式
46
- - safety note / 必要时安全提示
 
47
 
48
  ## 安全规则
49
 
50
  - 不做心理疾病诊断。
51
  - 不声称梦境有唯一含义。
52
  - 不输出恐吓性、宿命论、神秘绝对化内容。
 
53
  - 普通输出使用“也许”“可以把它当作”“今天先试试”这类非确定措辞。
54
  - 如果用户表达自伤、伤人、长期严重失眠、无法正常生活或极强痛苦,必须显示寻求可信任的人或专业支持的提示。
55
 
@@ -57,11 +67,15 @@
57
 
58
  实现前先阅读:
59
 
60
- 1. `docs/spec.md`
61
- 2. `docs/prd.md`
62
- 3. `docs/handoff.md`
63
- 4. `docs/superpowers/plans/2026-06-05-dream-customs-mvp.md`
64
- 5. `docs/dream-customs-concept/index.html`
 
 
 
 
65
 
66
  ## 开发流程
67
 
@@ -82,6 +96,7 @@
82
  - JSON 解析失败时必须有修复或降级路径。
83
  - Text-only path 必须永远可用,作为 demo fallback。
84
  - Gradio 页面应优先移动端可用,因为用户可能在手机上记录梦。
 
85
 
86
  ## 验收命令
87
 
@@ -94,8 +109,11 @@ python app.py
94
 
95
  人工验收:
96
 
97
- - 输入一段文字梦境,能生成盟约卡
98
- - 上传一张草图或便签视觉线索进入 `DreamIntake`
 
99
  - 上传/录制语音后,有转写或清晰降级提示。
100
- - 输出包含诊断恐吓或医疗化建议
 
 
101
  - 页面在手机宽度下可读。
 
1
+ # AGENTS.md - Dream QA / 梦境问答台
2
 
3
  ## 语言规则
4
 
 
7
 
8
  ## 项目定位
9
 
10
+ - 当前项目是 Build Small Hackathon 的 `Dream QA / 梦境问答台`,代码仓库和 Space 仍沿用 `Dream Customs` 名称以保持部署连续性
11
+ - 核心定位:做一个循序渐进的解梦 Gradio app,用户一步步说清境、表达疑惑、回答追问最后得到个温和 `今日 Tips`
12
+ - `今日小 Tips` 可以一个认真建议、一个用户未尝试过的小事,一句关心用户的短句;它必须引用梦里的体细节,不能变成泛泛鸡汤
13
+ - 这不是心理诊断、医疗建议、治疗工具、占卜工具或宿命论解释器。
14
 
15
  ## 竞赛约束
16
 
 
18
  - 模型总参数必须 <= 32B。
19
  - 优先叙事赛道:An Adventure in Thousand Token Wood。
20
  - 次级叙事:Backyard AI,因为项目来自真实睡眠/做梦困扰。
21
+ - demo 必须能在短视频中快速展示:输入梦境,回答或跳过追问,获得今日小 Tips
22
 
23
  ## 模型规则
24
 
25
  - 首选模型组合:
26
  - `openbmb/MiniCPM-V-4.6`:图片、草图、便签、截图理解。
27
+ - `openbmb/MiniCPM5-1B`:文本推理、追问生成、梦境解读草稿、结构化今日 Tips
28
  - 不要在 MiniCPM 路线未验证前扩展到任意其他小模型。
29
  - 语音输入需要 ASR 适配器;ASR 只做转写,不承担梦境理解。
30
  - 首版不要实现语音输出,避免扩大部署风险。
 
34
  - 必须支持文字输入。
35
  - 应支持图片输入,用 MiniCPM-V-4.6 提取视觉线索。
36
  - 应支持语音输入,通过小 ASR 适配器转成文本。
37
+ - 所有输入统一进入同一个梦境 intake,不要做成三套割裂产品。
38
+ - 用户流程必须是渐进式的:
39
+ 1. 记录梦境。
40
+ 2. 明确用户最想理解的问题。
41
+ 3. 生成 1-3 个温和追问。
42
+ 4. 用户回答、跳过或请求换个角度。
43
+ 5. 生成梦境解读草稿。
44
+ 6. 输出一个今日小 Tips。
45
  - 输出必须包含:
46
+ - `dream_summary` / 梦境摘要
47
+ - `main_question` / 用户最想理解的问题
48
+ - `dream_anchors` / 梦境具体线索
49
+ - `followup_questions` / 追问
50
+ - `user_answers` / 用户回答或跳过记录
51
+ - `interpretation` / 非确定性的解读草稿
52
+ - `today_tip` / 今日小 Tips
53
+ - `tiny_action` / 可选的小
54
+ - `caring_note` / 可选关心语句
55
+ - `safety_note` / 必要时的安全提示
56
 
57
  ## 安全规则
58
 
59
  - 不做心理疾病诊断。
60
  - 不声称梦境有唯一含义。
61
  - 不输出恐吓性、宿命论、神秘绝对化内容。
62
+ - 不把梦解释成“预兆”“证明”“创伤证据”或医学结论。
63
  - 普通输出使用“也许”“可以把它当作”“今天先试试”这类非确定措辞。
64
  - 如果用户表达自伤、伤人、长期严重失眠、无法正常生活或极强痛苦,必须显示寻求可信任的人或专业支持的提示。
65
 
 
67
 
68
  实现前先阅读:
69
 
70
+ 1. `docs/handoff.md`
71
+ 2. `docs/spec.md`
72
+ 3. `docs/prd.md`
73
+ 4. `PRODUCT.md`
74
+ 5. `DESIGN.md`
75
+ 6. `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md`
76
+ 7. `docs/superpowers/plans/2026-06-08-dream-qa-refactor-doc-plan.md`
77
+
78
+ 旧的 pact/customs 计划、smoke 和概念页是历史记录。除非用户明确要求复盘历史,否则不要再把 permit、contraband、sealed pact 当作新实现目标。
79
 
80
  ## 开发流程
81
 
 
96
  - JSON 解析失败时必须有修复或降级路径。
97
  - Text-only path 必须永远可用,作为 demo fallback。
98
  - Gradio 页面应优先移动端可用,因为用户可能在手机上记录梦。
99
+ - 今日 Tips 必须有梦境锚点,不能是“多休息、多喝水、保持积极”这类脱离梦境的通用话。
100
 
101
  ## 验收命令
102
 
 
109
 
110
  人工验收:
111
 
112
+ - 输入一段文字梦境app 提出至少一个相关追问
113
+ - 用户可以回答追问也可以跳过
114
+ - 上传一张草图或便签后,视觉线索进入同一个 intake。
115
  - 上传/录制语音后,有转写或清晰降级提示。
116
+ - 最终输出包含梦境摘要解读草稿和一个今日小 Tips
117
+ - 今日小 Tips 引用至少一个具体梦境细节。
118
+ - 输出不包含诊断、恐吓、预言或医疗化建议。
119
  - 页面在手机宽度下可读。
DESIGN.md CHANGED
@@ -1,32 +1,31 @@
1
  ---
2
- name: Dream Customs
3
- description: A nocturnal dream customs workbench for turning dream evidence into a next-day pact.
4
  colors:
5
- bg-night: "oklch(0.185 0.018 55)"
6
- bg-void: "oklch(0.130 0.012 55)"
7
- surface-ink: "oklch(0.235 0.018 58)"
8
- surface-raised: "oklch(0.295 0.020 62)"
9
- primary-sage: "oklch(0.690 0.105 150)"
10
- primary-sage-deep: "oklch(0.455 0.090 155)"
11
- accent-coral-stamp: "oklch(0.700 0.140 32)"
12
- accent-amber-lamp: "oklch(0.830 0.120 76)"
13
- accent-aurora: "oklch(0.745 0.080 185)"
14
- ink-main: "oklch(0.960 0.010 78)"
15
- ink-muted: "oklch(0.760 0.018 78)"
16
- border-dream: "oklch(0.465 0.024 70)"
17
  warning: "oklch(0.760 0.150 75)"
18
  typography:
19
  display:
20
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
21
- fontSize: "2.25rem"
22
  fontWeight: 760
23
- lineHeight: 1.05
24
  letterSpacing: "0"
25
  headline:
26
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
27
- fontSize: "1.5rem"
28
  fontWeight: 720
29
- lineHeight: 1.18
30
  title:
31
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
32
  fontSize: "1rem"
@@ -44,8 +43,8 @@ typography:
44
  lineHeight: 1.25
45
  rounded:
46
  sm: "6px"
47
- md: "10px"
48
- lg: "14px"
49
  pill: "999px"
50
  spacing:
51
  xs: "6px"
@@ -56,101 +55,96 @@ spacing:
56
  components:
57
  button-primary:
58
  backgroundColor: "{colors.primary-sage-deep}"
59
- textColor: "{colors.ink-main}"
60
  rounded: "{rounded.md}"
61
  padding: "12px 16px"
62
- button-accent:
63
- backgroundColor: "{colors.accent-coral-stamp}"
64
- textColor: "{colors.bg-void}"
65
  rounded: "{rounded.md}"
66
  padding: "12px 16px"
67
  input-composer:
68
- backgroundColor: "{colors.surface-ink}"
69
- textColor: "{colors.ink-main}"
70
  rounded: "{rounded.lg}"
71
  padding: "14px 16px"
72
  ---
73
 
74
- # Design System: Dream Customs
75
 
76
  ## 1. Overview
77
 
78
- **Creative North Star: "The Morning Customs Desk"**
79
 
80
- Dream Customs should feel like a small warm customs desk that appears after waking: quiet, slightly strange, and easy enough to use before the day fully starts. The surface can stay dark like Codex, but it should read as warm charcoal and lamplight rather than a cold technical dashboard. Sage action color, amber light, muted coral seal ink, and a little aurora evidence color carry the identity.
81
 
82
- This is product UI first. The user needs to type, attach, optionally add voice, answer, and decide what happens next. The dream atmosphere belongs in the background wash, pact inspector, stamps, and evidence chips, while controls stay clear and predictable.
83
-
84
- **V4 Unified Composer Rule.** The dream input should behave like a Codex-style multimodal composer, not like a Gradio form. Text, image upload, voice upload, mood, add-material, and send belong visually inside one rounded input surface. Technical model routes and advanced workflow shortcuts should be collapsed by default.
85
 
86
  **Key Characteristics:**
87
 
88
- - Unified composer as the primary input, modeled after Codex app behavior.
89
- - Conversation timeline for declarations, questions, and pact drafts.
90
- - Evidence tray for text, image, audio, mood, and model status.
91
- - Pact inspector that can be drafted, revised, or sealed.
92
- - Generated bitmap art as atmospheric header and state art, never as unreadable UI chrome.
93
 
94
  ## 2. Colors
95
 
96
- The palette is nocturnal and atmospheric: neutral dark surfaces, cobalt primary actions, aurora hints, and coral stamp moments.
97
 
98
  ### Primary
99
 
100
- - **Sage Gate** (`oklch(0.690 0.105 150)`): primary selection, active step, focus accents, and subtle interactive outlines.
101
- - **Deep Sage Gate** (`oklch(0.455 0.090 155)`): filled primary buttons and selected composer controls with near-white text.
102
 
103
  ### Secondary
104
 
105
- - **Coral Stamp Ink** (`oklch(0.700 0.140 32)`): final seal action, permit stamp, alert edge for safety notes, and one memorable highlight per screen.
106
- - **Amber Lamp** (`oklch(0.830 0.120 76)`): warmth, trust cue, and section highlights.
107
- - **Aurora Evidence Light** (`oklch(0.745 0.080 185)`): image/audio evidence status, successful extraction, and soft dream-glow details.
108
 
109
  ### Neutral
110
 
111
- - **Warm Night Background** (`oklch(0.185 0.018 55)`): app body and mobile shell.
112
- - **Soft Void Rail** (`oklch(0.130 0.012 55)`): app outer edge, header underlay, and screenshot-safe negative space.
113
- - **Charcoal Ink Surface** (`oklch(0.235 0.018 58)`): composer, timeline items, and tool panels.
114
- - **Raised Warm Surface** (`oklch(0.295 0.020 62)`): active inspector panels and output cards.
115
- - **Main Ink** (`oklch(0.960 0.010 78)`): body text on dark surfaces.
116
- - **Muted Ink** (`oklch(0.760 0.018 78)`): secondary labels that still remain readable.
117
 
118
  ### Named Rules
119
 
120
- **The Stamp Rarity Rule.** Coral is used for sealing, warnings, and permit marks only. It must not become a general decoration color.
121
 
122
- **The No Parchment Rule.** Do not return to cream, beige, or faux paper backgrounds. Dream color comes from night, ink, and evidence light.
123
 
124
  ## 3. Typography
125
 
126
- **Display Font:** Inter with system fallbacks
127
 
128
- **Body Font:** Inter with system fallbacks
129
- **Label/Mono Font:** Use system mono only for permit IDs, debug JSON, and model telemetry.
130
 
131
- **Character:** The interface uses one sans family so the product feels controlled. Personality comes from naming, layout, stamps, and generated imagery rather than decorative fonts.
132
 
133
  ### Hierarchy
134
 
135
- - **Display** (760, 2.25rem, 1.05): app title and sealed pact headline only.
136
- - **Headline** (720, 1.5rem, 1.18): step titles and pact section names.
137
- - **Title** (680, 1rem, 1.3): timeline item headers, buttons, inspector labels.
138
- - **Body** (450, 1rem, 1.55): dream text, questions, pact explanations, max 70ch for prose.
139
- - **Label** (650, 0.8125rem, 1.25): compact field labels, evidence chips, status text.
140
 
141
  ### Named Rules
142
 
143
- **The Low-Light Readability Rule.** Muted text must remain legible on dark surfaces. Never use low-contrast gray for field labels or placeholders.
144
 
145
  ## 4. Elevation
146
 
147
- Depth is mostly tonal, not shadow-heavy. The app should feel layered like a dark desk under soft light, not like floating cards on a SaaS page. Shadows appear only for the active composer, a hovered evidence item, or a sealed pact.
148
 
149
  ### Shadow Vocabulary
150
 
151
- - **composer-focus** (`0 0 0 1px oklch(0.690 0.105 232), 0 12px 32px rgba(0, 0, 0, 0.28)`): focused bottom composer.
152
- - **pact-lift** (`0 10px 28px rgba(0, 0, 0, 0.24)`): sealed pact card only.
153
- - **stamp-glow** (`0 0 20px rgba(255, 107, 84, 0.24)`): coral seal hover or success state.
154
 
155
  ### Named Rules
156
 
@@ -160,58 +154,68 @@ Depth is mostly tonal, not shadow-heavy. The app should feel layered like a dark
160
 
161
  ### Buttons
162
 
163
- - **Shape:** medium rectangle, 10px radius.
164
- - **Primary:** Deep Cobalt Gate fill, Main Ink text, 12px 16px padding.
165
- - **Accent:** Coral Stamp Ink for `Seal today's pact` only.
166
- - **Hover / Focus:** tonal lift plus visible cobalt focus ring.
167
- - **Secondary:** transparent or Ink Surface fill with full border, never ghost text alone.
168
 
169
  ### Chips
170
 
171
  - **Style:** small rounded pills with icon, label, and state dot.
172
- - **State:** queued, extracted, failed, or selected. Evidence type uses icon and color, not long explanatory text.
 
173
 
174
  ### Cards / Containers
175
 
176
- - **Corner Style:** 10px to 14px. No 32px rounded cards.
177
- - **Background:** Ink Surface for timeline items, Raised Surface for active inspector.
178
- - **Shadow Strategy:** flat by default, lifted only when active or sealed.
179
- - **Border:** full border using Border Dream, not a side stripe.
180
  - **Internal Padding:** 16px mobile, 24px desktop.
181
 
182
  ### Inputs / Fields
183
 
184
- - **Style:** Codex-like bottom composer with large multiline input, attachment controls, model selector menu, and primary action clustered on the right.
185
- - **Focus:** cobalt ring, slightly raised surface, no layout shift.
186
  - **Error / Disabled:** inline message below composer, disabled actions stay readable and explain why.
187
 
188
  ### Navigation
189
 
190
- - **Style:** compact status rail with current phase: Declare, Inspect, Negotiate, Draft, Seal.
191
- - **Mobile:** phase rail collapses to segmented steps above the composer.
 
 
 
 
 
192
 
193
- ### Signature Component: Pact Inspector
 
 
 
 
 
 
194
 
195
- The pact inspector is a live customs file, not just a final HTML card. It shows visitor, permit ID, evidence count, risk, suggested action, weird task, and whether the pact is draft or sealed. It gives the user three choices: add material, ask another question, or seal today's pact.
196
 
197
  ## 6. Do's and Don'ts
198
 
199
  ### Do:
200
 
201
- - **Do** make the unified composer the most obvious control on mobile and desktop.
202
- - **Do** keep the first declaration controls above the timeline so a tired user can start immediately.
203
  - **Do** keep every phase actionable with one clear next step.
204
- - **Do** use generated raster imagery for the dream atmosphere, header state, or empty state.
205
- - **Do** verify dark-mode contrast for body text, labels, placeholders, chips, and disabled controls.
206
  - **Do** preserve a deterministic demo backend while adding a model-backed route.
 
207
 
208
  ### Don't:
209
 
210
  - **Don't** ship a plain Gradio two-column form as the primary experience.
211
- - **Don't** use beige parchment, tarot, faux therapy, or generic purple AI visuals.
212
- - **Don't** use one-shot generation as the whole flow.
213
  - **Don't** expose technical model routes as first-level controls unless the user opens model settings.
214
  - **Don't** render navigation-looking text that cannot be clicked.
215
- - **Don't** reserve tall empty panels for future content on the first screen.
216
- - **Don't** pair a 1px border with a large soft shadow on every card.
217
- - **Don't** use side-stripe borders, gradient text, or decorative glass panels.
 
1
  ---
2
+ name: Dream QA
3
+ description: A guided dream Q&A interface for turning dream fragments into one grounded today tip.
4
  colors:
5
+ morning-mist: "oklch(0.965 0.014 210)"
6
+ warm-paper: "oklch(0.985 0.010 82)"
7
+ soft-ink: "oklch(0.205 0.020 70)"
8
+ muted-ink: "oklch(0.500 0.030 82)"
9
+ surface-card: "oklch(0.995 0.006 82)"
10
+ surface-sage: "oklch(0.930 0.035 145)"
11
+ primary-sage: "oklch(0.555 0.105 145)"
12
+ primary-sage-deep: "oklch(0.390 0.095 145)"
13
+ accent-amber: "oklch(0.820 0.120 78)"
14
+ accent-coral: "oklch(0.710 0.135 32)"
15
+ border-soft: "oklch(0.825 0.020 85)"
 
16
  warning: "oklch(0.760 0.150 75)"
17
  typography:
18
  display:
19
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
20
+ fontSize: "2rem"
21
  fontWeight: 760
22
+ lineHeight: 1.08
23
  letterSpacing: "0"
24
  headline:
25
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
26
+ fontSize: "1.375rem"
27
  fontWeight: 720
28
+ lineHeight: 1.2
29
  title:
30
  fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
31
  fontSize: "1rem"
 
43
  lineHeight: 1.25
44
  rounded:
45
  sm: "6px"
46
+ md: "8px"
47
+ lg: "12px"
48
  pill: "999px"
49
  spacing:
50
  xs: "6px"
 
55
  components:
56
  button-primary:
57
  backgroundColor: "{colors.primary-sage-deep}"
58
+ textColor: "{colors.warm-paper}"
59
  rounded: "{rounded.md}"
60
  padding: "12px 16px"
61
+ button-secondary:
62
+ backgroundColor: "{colors.surface-card}"
63
+ textColor: "{colors.primary-sage-deep}"
64
  rounded: "{rounded.md}"
65
  padding: "12px 16px"
66
  input-composer:
67
+ backgroundColor: "{colors.surface-card}"
68
+ textColor: "{colors.soft-ink}"
69
  rounded: "{rounded.lg}"
70
  padding: "14px 16px"
71
  ---
72
 
73
+ # Design System: Dream QA
74
 
75
  ## 1. Overview
76
 
77
+ **Creative North Star: "The Morning Question Desk"**
78
 
79
+ Dream QA should feel like a calm morning workspace for unpacking a dream while it is still fresh. The surface is light, readable, and ordinary-user friendly, with enough dream atmosphere to feel memorable. The interface is a product experience first: the user needs to record, answer, skip, add detail, and receive one today tip without decoding a metaphor-heavy ritual.
80
 
81
+ The old customs/pact visual language is deprecated for new work. A small `Dream Customs` label may remain for repo/Space continuity, but the primary visible experience should be `梦境问答台 / Dream QA`.
 
 
82
 
83
  **Key Characteristics:**
84
 
85
+ - Stepper with four phases: Record, Ask, Interpret, Tip.
86
+ - Conversation timeline for dream fragments, follow-up questions, answers, and interpretation draft.
87
+ - Unified multimodal composer for text, image, voice, mood, and primary action.
88
+ - Right-side desktop panel for interpretation draft and Today Tip preview.
89
+ - Mobile final screen that prioritizes the Today Tip before optional explanation.
90
 
91
  ## 2. Colors
92
 
93
+ The palette is soft morning product UI: warm white surfaces, charcoal text, sage actions, amber note highlights, and coral used sparingly for warmth or safety.
94
 
95
  ### Primary
96
 
97
+ - **Sage Guide** (`oklch(0.555 0.105 145)`): stepper progress, selected chips, focus accents.
98
+ - **Deep Sage Guide** (`oklch(0.390 0.095 145)`): primary buttons and active controls.
99
 
100
  ### Secondary
101
 
102
+ - **Amber Note** (`oklch(0.820 0.120 78)`): follow-up question highlight, Today Tip accent, gentle attention.
103
+ - **Coral Care** (`oklch(0.710 0.135 32)`): caring note, safety edge, and one small emotional highlight per result.
 
104
 
105
  ### Neutral
106
 
107
+ - **Morning Mist** (`oklch(0.965 0.014 210)`): app background.
108
+ - **Warm Paper** (`oklch(0.985 0.010 82)`): main reading surface.
109
+ - **Soft Ink** (`oklch(0.205 0.020 70)`): primary text.
110
+ - **Muted Ink** (`oklch(0.500 0.030 82)`): secondary labels with readable contrast.
111
+ - **Soft Border** (`oklch(0.825 0.020 85)`): panel and input borders.
 
112
 
113
  ### Named Rules
114
 
115
+ **The One Tip Rule.** Visual emphasis should guide the eye to one primary Today Tip. Do not create multiple equally loud recommendation blocks.
116
 
117
+ **The No Oracle Rule.** Avoid tarot, prophecy, mystic symbols, parchment certificates, and theatrical stamps.
118
 
119
  ## 3. Typography
120
 
121
+ **Display Font:** Inter with system fallbacks.
122
 
123
+ **Body Font:** Inter with system fallbacks.
 
124
 
125
+ **Label/Mono Font:** Use system mono only for debug JSON, model telemetry, or structured IDs.
126
 
127
  ### Hierarchy
128
 
129
+ - **Display** (760, 2rem, 1.08): app title and final Today Tip headline.
130
+ - **Headline** (720, 1.375rem, 1.2): step titles and result section names.
131
+ - **Title** (680, 1rem, 1.3): timeline item headers, buttons, and compact panel labels.
132
+ - **Body** (450, 1rem, 1.55): dream text, questions, interpretations, max 70ch for prose.
133
+ - **Label** (650, 0.8125rem, 1.25): chips, status text, and small metadata.
134
 
135
  ### Named Rules
136
 
137
+ **The Morning Readability Rule.** The user may be half-awake. Body text, buttons, placeholders, and chips must stay readable on mobile.
138
 
139
  ## 4. Elevation
140
 
141
+ Depth is subtle and functional. Panels should feel placed on a calm desk, not floating in a SaaS layout.
142
 
143
  ### Shadow Vocabulary
144
 
145
+ - **composer-focus** (`0 0 0 1px rgba(65, 115, 78, 0.35), 0 12px 28px rgba(45, 56, 42, 0.14)`): focused composer.
146
+ - **tip-lift** (`0 10px 24px rgba(45, 56, 42, 0.16)`): final Today Tip card only.
147
+ - **question-glow** (`0 0 18px rgba(230, 178, 75, 0.22)`): active follow-up question.
148
 
149
  ### Named Rules
150
 
 
154
 
155
  ### Buttons
156
 
157
+ - **Shape:** 8px radius.
158
+ - **Primary:** Deep Sage Guide fill, Warm Paper text, 12px 16px padding.
159
+ - **Secondary:** Warm Paper fill, Deep Sage text, full border.
160
+ - **Care / Safety:** Coral Care only for safety/support actions or caring note markers.
161
+ - **Hover / Focus:** tonal lift plus visible focus ring.
162
 
163
  ### Chips
164
 
165
  - **Style:** small rounded pills with icon, label, and state dot.
166
+ - **Use:** mood, image clue status, voice transcript status, answer/skip choices, and alternate angle request.
167
+ - **Text:** short labels only. Long explanatory text belongs in timeline or result body.
168
 
169
  ### Cards / Containers
170
 
171
+ - **Corner Style:** 8px to 12px. No oversized rounded blobs.
172
+ - **Background:** Warm Paper or Surface Card.
173
+ - **Shadow Strategy:** flat by default, lifted only when active or final.
174
+ - **Border:** full soft border.
175
  - **Internal Padding:** 16px mobile, 24px desktop.
176
 
177
  ### Inputs / Fields
178
 
179
+ - **Style:** unified composer with multiline dream input, attachment controls, mood, and primary action.
180
+ - **Focus:** sage ring, slightly raised surface, no layout shift.
181
  - **Error / Disabled:** inline message below composer, disabled actions stay readable and explain why.
182
 
183
  ### Navigation
184
 
185
+ - **Style:** visible stepper with current phase: Record, Ask, Interpret, Tip.
186
+ - **Mobile:** stepper collapses to compact segmented steps above the conversation.
187
+ - **Desktop:** left rail or top rail is acceptable, but it must map to real states.
188
+
189
+ ### Signature Component: Today Tip Panel
190
+
191
+ The Today Tip Panel is the final surface. It shows:
192
 
193
+ - Dream summary.
194
+ - Main question.
195
+ - Grounded interpretation.
196
+ - One primary Today Tip.
197
+ - Optional tiny action.
198
+ - Optional caring note.
199
+ - Safety note only when needed.
200
 
201
+ The tip should be first-class and easy to screenshot, but it must not look like a diagnosis card or mystical certificate.
202
 
203
  ## 6. Do's and Don'ts
204
 
205
  ### Do:
206
 
207
+ - **Do** make the first action obvious on mobile and desktop.
208
+ - **Do** show the gradual Q&A flow so users understand why the final tip is grounded.
209
  - **Do** keep every phase actionable with one clear next step.
210
+ - **Do** cite concrete dream anchors in the final result.
 
211
  - **Do** preserve a deterministic demo backend while adding a model-backed route.
212
+ - **Do** verify text fits inside controls at 390px mobile width.
213
 
214
  ### Don't:
215
 
216
  - **Don't** ship a plain Gradio two-column form as the primary experience.
217
+ - **Don't** use tarot, parchment, prophecy, medical intake, or generic healing-app aesthetics.
 
218
  - **Don't** expose technical model routes as first-level controls unless the user opens model settings.
219
  - **Don't** render navigation-looking text that cannot be clicked.
220
+ - **Don't** create several competing tips in the final result.
221
+ - **Don't** let the final advice become generic wellness copy unrelated to the dream.
 
PRODUCT.md CHANGED
@@ -6,35 +6,38 @@ product
6
 
7
  ## Users
8
 
9
- Dream Customs serves people who wake up with vivid, strange, or unsettling dreams and want a small morning ritual before the day takes over. The primary user may still be half asleep, on a phone, and emotionally sticky from the dream. The secondary user is a hackathon judge or playful AI explorer who needs to understand the loop quickly and see why small multimodal models matter.
10
 
11
  ## Product Purpose
12
 
13
- Dream Customs helps a user form a playful alliance with last night's dream. It accepts text, image, and voice evidence, turns those fragments into a structured dream declaration, asks a few customs questions, and lets the user choose whether to add more material, continue negotiation, or seal a next-day pact.
14
 
15
- Success means the user receives something more useful than a one-shot interpretation: a clear visitor, a practical suggestion for today, a weird five-minute task, and a bedtime release phrase. The experience should feel like a small ceremony, not a diagnosis.
16
 
17
  ## Brand Personality
18
 
19
  Gentle, strange, lucid.
20
 
21
- The voice is an overnight border clerk who takes odd evidence seriously without becoming mystical or clinical. It should feel curious and companionable, with enough dream color to be memorable and enough product discipline to be usable on a phone.
22
 
23
  ## Anti-references
24
 
25
  - Do not look like a plain Gradio form with two columns and one submit button.
26
  - Do not look like a beige parchment certificate, generic tarot card, therapy intake form, or purple AI SaaS dashboard.
27
- - Do not use medical, diagnostic, frightening, or fate-telling language.
 
28
  - Do not bury the main action below many controls.
29
- - Do not end the flow after one answer when the user still wants to add evidence or keep negotiating.
 
30
 
31
  ## Design Principles
32
 
33
- 1. The AI is a clerk in a ritual, not a text generator behind a form.
34
- 2. The user keeps agency: add evidence, answer, skip, continue, revise, or seal.
35
  3. The interface should be dream-colored but product-trustworthy.
36
- 4. Every output should lead to a tiny next-day action.
37
- 5. The demo path must survive Space constraints and still have a model-backed route.
 
38
 
39
  ## Accessibility & Inclusion
40
 
 
6
 
7
  ## Users
8
 
9
+ Dream QA / 梦境问答台 serves people who wake up with vivid, strange, or unsettling dreams and want a small morning conversation before the day takes over. The primary user may still be half asleep, on a phone, and emotionally sticky from the dream. The secondary user is a hackathon judge or playful AI explorer who needs to understand the loop quickly and see why small multimodal models matter.
10
 
11
  ## Product Purpose
12
 
13
+ The product helps a user unpack a dream through a guided, step-by-step Q&A. It accepts text, image, and voice fragments, turns them into one structured dream intake, asks gentle follow-up questions, drafts a grounded interpretation, and ends with one `今日小 Tips`.
14
 
15
+ Success means the user receives something more useful than a one-shot interpretation: a short summary of the dream, one clear question they were trying to answer, a non-certain explanation grounded in concrete dream details, and a tiny suggestion for today. The tip can be practical, playful, or simply caring, but it must feel connected to the dream.
16
 
17
  ## Brand Personality
18
 
19
  Gentle, strange, lucid.
20
 
21
+ The voice is a calm dream companion who asks good questions and takes odd details seriously without becoming mystical or clinical. It should feel curious, ordinary-user friendly, and companionable: less secret ritual, more warm morning dialogue.
22
 
23
  ## Anti-references
24
 
25
  - Do not look like a plain Gradio form with two columns and one submit button.
26
  - Do not look like a beige parchment certificate, generic tarot card, therapy intake form, or purple AI SaaS dashboard.
27
+ - Do not sound like generic healing-app advice that could fit any input.
28
+ - Do not use medical, diagnostic, frightening, fate-telling, or prophecy language.
29
  - Do not bury the main action below many controls.
30
+ - Do not force the user to complete a long questionnaire before seeing value.
31
+ - Do not end with multiple competing recommendations; the final result needs one primary today tip.
32
 
33
  ## Design Principles
34
 
35
+ 1. The AI is a question guide, not an oracle.
36
+ 2. The user keeps agency: answer, skip, add detail, ask for another angle, or finish.
37
  3. The interface should be dream-colored but product-trustworthy.
38
+ 4. Every final output should lead to one small next-day reference or action.
39
+ 5. The final tip must cite a concrete dream anchor.
40
+ 6. The demo path must survive Space constraints and still have a model-backed route.
41
 
42
  ## Accessibility & Inclusion
43
 
README.md CHANGED
@@ -1,15 +1,15 @@
1
  ---
2
  title: Dream Customs
3
- emoji:
4
- colorFrom: blue
5
- colorTo: pink
6
  sdk: gradio
7
  sdk_version: 4.44.1
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
- short_description: Turn dream declarations into a playful next-day pact.
13
  models:
14
  - openbmb/MiniCPM5-1B
15
  - openbmb/MiniCPM-V-4.6
@@ -20,22 +20,45 @@ tags:
20
  - dream-journal
21
  ---
22
 
23
- # Dream Customs
24
 
25
- A Build Small Hackathon Gradio app that helps users form a playful alliance with last night's dream.
 
 
26
 
27
  ## Concept
28
 
29
- Dream Customs accepts dream declarations by text, image, or voice. It turns the dream into a gentle "customs negotiation" and returns a Today's Pact card: one practical suggestion, one weird 5-minute task, and one bedtime release phrase.
 
 
30
 
31
  ## Models
32
 
33
  - `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
34
- - `openbmb/MiniCPM5-1B` for dream negotiation and pact generation.
35
  - A small ASR adapter may be used only for voice transcription.
36
  - The app defaults to a stable demo backend so the local Gradio flow always works.
37
  - Optional Ollama adapters are included for local MiniCPM testing.
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  ## Run
40
 
41
  ```bash
@@ -89,7 +112,7 @@ client = HostedMiniCPMTextClient(
89
  token=os.getenv("DREAM_CUSTOMS_HOSTED_TOKEN", ""),
90
  )
91
  result = client.generate_negotiation("I missed an elevator in a foggy dream.")
92
- print(result["visitor_name"])
93
  PY
94
  ```
95
 
@@ -116,12 +139,8 @@ python -m pytest -q
116
 
117
  ## Deployment Smoke Status
118
 
119
- 2026-06-05 local V2 verification passed: tests were green and the workbench flow reached a sealed pact through `Send to customs`, `Ask another question`, `Add material`, `Draft pact`, `Revise pact`, and `Seal today's pact`.
120
-
121
- The public Space now serves the V2 workbench from Space `main` commit `8ad6f00628f800abc2dbefab05163aba94a5723f`. Public browser smoke, mobile readability, diagnostics, raw remote queue prediction, and a hosted text route smoke all reached a sealed pact on 2026-06-05. The current Modal backend pass requires a real `openbmb/MiniCPM-V-4.6` vision route smoke before delivery; demo vision fallback is runtime resilience, not a substitute for that smoke.
122
-
123
- Current smoke details are tracked in `docs/smoke/2026-06-05-space-deployment-smoke.md`.
124
 
125
  ## Safety
126
 
127
- This is not a therapy or diagnosis product. It gives playful reflection, small actions, and escalation copy for severe distress.
 
1
  ---
2
  title: Dream Customs
3
+ emoji: 🌤️
4
+ colorFrom: green
5
+ colorTo: yellow
6
  sdk: gradio
7
  sdk_version: 4.44.1
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
+ short_description: Guided dream Q&A with one gentle today tip.
13
  models:
14
  - openbmb/MiniCPM5-1B
15
  - openbmb/MiniCPM-V-4.6
 
20
  - dream-journal
21
  ---
22
 
23
+ # Dream QA / 梦境问答台
24
 
25
+ A Build Small Hackathon Gradio app that helps users unpack a dream step by step and leave with one grounded tip for today.
26
+
27
+ The Hugging Face Space may still be named `Dream Customs` for continuity, but the current product direction is Dream QA: record a dream, answer or skip gentle follow-up questions, read a grounded interpretation draft, and receive a `今日小 Tips`.
28
 
29
  ## Concept
30
 
31
+ Dream QA accepts dream fragments by text, image, or voice. It turns those fragments into a shared dream intake, asks a few gentle questions, and returns a Today Tip card: a short non-certain interpretation, one practical or caring suggestion for today, and an optional tiny action.
32
+
33
+ This is not a therapy, diagnosis, or prophecy product.
34
 
35
  ## Models
36
 
37
  - `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
38
+ - `openbmb/MiniCPM5-1B` for dream summary, follow-up questions, interpretation, and Today Tip generation.
39
  - A small ASR adapter may be used only for voice transcription.
40
  - The app defaults to a stable demo backend so the local Gradio flow always works.
41
  - Optional Ollama adapters are included for local MiniCPM testing.
42
 
43
+ ## User Flow
44
+
45
+ 1. Enter a dream by text, image, voice, or a mix.
46
+ 2. State or let the app infer the main question.
47
+ 3. Answer or skip one or more follow-up questions.
48
+ 4. Review a grounded interpretation.
49
+ 5. Receive one Today Tip tied to concrete dream details.
50
+
51
+ ## Current Direction References
52
+
53
+ - Product spec: `docs/spec.md`
54
+ - PRD: `docs/prd.md`
55
+ - Handoff: `docs/handoff.md`
56
+ - Design system: `DESIGN.md`
57
+ - Prototype images:
58
+ - `docs/prototypes/2026-06-08-dream-qa-mobile-flow.png`
59
+ - `docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png`
60
+ - `docs/prototypes/2026-06-08-dream-qa-tips-card.png`
61
+
62
  ## Run
63
 
64
  ```bash
 
112
  token=os.getenv("DREAM_CUSTOMS_HOSTED_TOKEN", ""),
113
  )
114
  result = client.generate_negotiation("I missed an elevator in a foggy dream.")
115
+ print(result)
116
  PY
117
  ```
118
 
 
139
 
140
  ## Deployment Smoke Status
141
 
142
+ Historical smoke notes under `docs/smoke/` describe earlier pact/customs UI passes. They remain useful deployment history, but the current implementation target is the Dream QA flow documented in `docs/handoff.md`, `docs/spec.md`, and `docs/prd.md`.
 
 
 
 
143
 
144
  ## Safety
145
 
146
+ This is not a therapy or diagnosis product. It gives playful reflection, one small today tip, and escalation copy for severe distress.
design-qa.md CHANGED
@@ -1,24 +1,27 @@
1
- # Dream Customs V4 Design QA
2
 
3
- Date: 2026-06-05
4
 
5
- Reference: user-provided Codex-style multimodal composer screenshot.
6
 
7
  ## Checks
8
 
9
- - First screen uses one rounded multimodal composer instead of separate form blocks.
10
- - Visible composer tools are limited to Image, Voice, Mood, Add, and Send.
11
- - Technical Text/Vision routes are collapsed under Model routes.
12
- - Pact inspector remains visible beside the composer on desktop.
13
- - Timeline, diagnostics, examples, workflow shortcuts, and refinement controls are secondary.
14
- - Text-only Chrome flow completed: Send -> Draft pact -> Seal today's pact.
15
- - Sealed output renders a screenshot-friendly Today's Pact card.
16
- - No desktop horizontal overflow in Chrome at the inspected viewport.
 
 
17
 
18
  ## Notes
19
 
20
- - Image and voice use compact upload buttons to avoid large Gradio media drop zones in the first screen.
21
- - Voice upload remains available as an input to the existing ASR adapter path.
 
22
  - The UI keeps safety copy non-diagnostic.
23
 
24
- final result: passed
 
1
+ # Dream QA Design QA
2
 
3
+ Date: 2026-06-08
4
 
5
+ Reference: 2026-06-08 Dream QA prototype images and updated source docs.
6
 
7
  ## Checks
8
 
9
+ - First screen should introduce `梦境问答台 / Dream QA`, with `Dream Customs` only as continuity context if needed.
10
+ - The primary loop is Record -> Ask -> Interpret -> Tip.
11
+ - The main surface is a conversational Q&A timeline, not a permit or pact inspector.
12
+ - The composer supports text, image, voice, mood, and one clear continue action.
13
+ - Follow-up questions are visible and skippable.
14
+ - The final result prioritizes one Today Tip, with optional tiny action and caring note.
15
+ - Interpretation text uses non-certain language and cites concrete dream anchors.
16
+ - Technical text/vision routes remain collapsed under developer/model settings.
17
+ - Text-only flow must remain available as the fallback path.
18
+ - Mobile width should keep buttons, chips, and Today Tip text readable.
19
 
20
  ## Notes
21
 
22
+ - The older V4 customs workbench passed its historical design QA, but it is no longer the current target.
23
+ - Do not reintroduce permit, contraband, sealed pact, or bedtime release as required UX fields.
24
+ - Do not replace the new Q&A flow with a generic healing-app result card; the tip must remain dream-grounded.
25
  - The UI keeps safety copy non-diagnostic.
26
 
27
+ Current desired result: pending implementation.
docs/design/2026-06-05-uiux-v2-brief.md CHANGED
@@ -2,6 +2,8 @@
2
 
3
  Last updated: 2026-06-05
4
 
 
 
5
  ## 1. Feature Summary
6
 
7
  Redesign Dream Customs from a one-shot Gradio form into a dream customs workbench. The user should be able to declare a dream through text, image, or voice, receive customs questions, add more evidence, continue negotiation, revise the pact draft, or seal the final pact.
 
2
 
3
  Last updated: 2026-06-05
4
 
5
+ > Historical note: this V2 brief documents the earlier customs/pact workbench direction. As of 2026-06-08, the current product target is `Dream QA / 梦境问答台`: Record -> Ask -> Interpret -> Today Tip. Use `DESIGN.md`, `docs/handoff.md`, and `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md` for new work.
6
+
7
  ## 1. Feature Summary
8
 
9
  Redesign Dream Customs from a one-shot Gradio form into a dream customs workbench. The user should be able to declare a dream through text, image, or voice, receive customs questions, add more evidence, continue negotiation, revise the pact draft, or seal the final pact.
docs/goal-prompts/2026-06-08-model-led-pact-v2.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Goal Prompt: Model-Led Pact V2
2
+
3
+ > Superseded on 2026-06-08: do not use this for new work unless the user explicitly revives the pact direction. Current source of truth is `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md`.
4
+
5
+ Work in `/Users/junhaocheng/working-dir/ai-competitions/build-small-hackthon`.
6
+
7
+ Use the repo plan `docs/superpowers/plans/2026-06-08-model-led-pact-v2.md`. Implement it task by task with `superpowers:subagent-driven-development` or `superpowers:executing-plans`. Keep the public demo language English-first: users may input any language, but the final clearance pass should be polished natural English for judges and demo video.
8
+
9
+ Do not broaden beyond MiniCPM before exhausting the current pair:
10
+
11
+ - `openbmb/MiniCPM5-1B` owns dream brief, pact draft, critique, and rewrite.
12
+ - `openbmb/MiniCPM-V-4.6` owns visual witness reports.
13
+ - ASR remains transcription only.
14
+
15
+ Main acceptance case:
16
+
17
+ ```text
18
+ I kept missing an elevator. The buttons melted like wax, and the floor number froze at 14.
19
+ ```
20
+
21
+ The final card must not contain `the an`, `the the`, invented `lever`, diagnosis, prophecy, or frightening certainty. It must mention at least two concrete details from the dream, give one real next step for today, and include one harmless five-minute odd task grounded in the dream.
22
+
23
+ Before changes: confirm cwd, branch, remotes, and run `git pull`. During implementation: use tests first, keep commits small, and preserve text-only fallback. After implementation: run `.venv/bin/python -m pytest -q`, `git diff --check`, local Gradio smoke on port `7862`, push GitHub branch, then sync or prepare HF Space update. Stop and ask before exposing secrets, confirming paid deploy/build costs, force-pushing public Space `main`, or handling a manual PR merge.
docs/handoff.md CHANGED
@@ -1,53 +1,60 @@
1
- # Dream Customs / 梦境海关 Handoff
2
 
3
- Last updated: 2026-06-05
4
 
5
- ## Current State
6
 
7
- The project has shipped a working Gradio MVP and a V4 local UI pass. The current interface centers a warm Codex-like multimodal composer: text, image upload, voice upload, mood, add-material, and send live inside one rounded input surface. Model routes, workflow shortcuts, diagnostics, and examples are collapsed by default.
8
 
9
- Current V4 UX decisions:
10
 
11
- - The first screen prioritizes the dream declaration composer and pact inspector.
12
- - Image and voice use compact upload buttons to avoid large Gradio media drop zones in the main composer.
13
- - `Draft pact` and `Seal today's pact` sit beside the live pact inspector.
14
- - The timeline is a supporting "Story so far" section below the first-screen workflow.
15
- - Technical model route controls are available but not first-level visual chrome.
16
 
17
- The original visual HTML concept note remains useful background:
18
 
19
- - `docs/dream-customs-concept/index.html`
20
 
21
- Supporting images:
 
 
 
 
 
22
 
23
- - `docs/dream-customs-concept/assets/dream-passport.svg`
24
- - `docs/dream-customs-concept/assets/multimodal-intake.svg`
25
- - `docs/dream-customs-concept/assets/alliance-card.svg`
26
 
27
- Project context documents:
28
 
29
- - `docs/spec.md`
30
- - `docs/prd.md`
31
- - `docs/handoff.md`
32
- - `docs/superpowers/plans/2026-06-05-dream-customs-mvp.md`
33
- - `docs/superpowers/plans/2026-06-05-dream-customs-uiux-v2.md`
34
- - `docs/superpowers/plans/2026-06-05-dream-customs-uiux-v3.md`
35
- - `AGENTS.md`
 
 
 
 
 
 
 
 
36
 
37
  ## Product Decision
38
 
39
- Build Dream Customs as a "dream alliance" app, not a dream diagnosis app.
40
 
41
  Final positioning:
42
 
43
- > 梦境海关帮你和昨晚的结盟把不安转成明天的小建议,把怪转成件有趣的小
44
 
45
  ## Model Decision
46
 
47
  Use:
48
 
49
  - `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
50
- - `openbmb/MiniCPM5-1B` for text reasoning, negotiation, and final output.
51
 
52
  Do not broaden to arbitrary small models unless MiniCPM paths fail.
53
 
@@ -67,31 +74,36 @@ Voice input is allowed, but current MiniCPM pair does not directly transcribe ra
67
  1. User submits dream using text, image, voice, or a combination.
68
  2. Audio is transcribed by a small adapter.
69
  3. Image is converted into visual clues by MiniCPM-V-4.6.
70
- 4. App builds a `DreamIntake` object.
71
- 5. MiniCPM5-1B generates dream visitor and 2-3 negotiation questions.
72
- 6. User answers or skips.
73
- 7. MiniCPM5-1B generates final "Today's Pact" card.
74
- 8. Gradio renders a screenshot-friendly HTML card.
 
 
75
 
76
  ## Required Output Fields
77
 
78
- - `visitor_name`
79
- - `permit_id`
80
- - `contraband`
81
- - `risk_level`
82
- - `alliance_reading`
83
- - `practical_suggestion`
84
- - `weird_task`
85
- - `bedtime_release`
 
86
  - `safety_note`
87
 
 
 
88
  ## Safety Boundary
89
 
90
  The product must never present itself as therapy, medical advice, or diagnosis. It should use playful, non-certain language. Severe distress should trigger a professional-help suggestion.
91
 
92
- ## Recommended First Implementation
93
 
94
- The minimal Python package already exists:
95
 
96
  ```text
97
  app.py
@@ -105,37 +117,45 @@ dream_customs/
105
  render.py
106
  pipeline.py
107
  models.py
 
108
  tests/
109
- test_schema.py
110
- test_safety.py
111
- test_render.py
112
- test_pipeline.py
113
  ```
114
 
115
- The current implementation keeps deterministic demo clients as the default and exposes optional model routes. For V3, do not rebuild the pipeline from scratch. Fix the app shell, control hierarchy, phase actions, and visual polish while preserving the existing `CustomsSession` state flow.
 
 
 
 
 
 
 
 
 
 
116
 
117
  ## Implementation Priorities
118
 
119
- 1. First-screen trust and immediate declaration controls.
120
- 2. Clickable phase actions.
121
- 3. Mobile-readable composer and inspector.
122
- 4. Removal of long blank page regions.
123
- 5. Tests and local browser verification.
124
- 6. Branch commit/push and Space update when credentials allow.
 
125
 
126
  ## Repository Status
127
 
128
- This directory is now a dedicated Dream Customs repository:
129
 
130
  - Local path: `/Users/junhaocheng/working-dir/ai-competitions/build-small-hackthon`
131
  - GitHub remote: `https://github.com/adjcjh777/dream-customs-build-small.git`
132
  - Hugging Face Space remote: `https://huggingface.co/spaces/build-small-hackathon/dream-customs`
133
 
134
- Continue to confirm `git remote -v` before pushing, but the earlier VLA remote warning is no longer the active state.
135
 
136
  ## Modal Backend Route
137
 
138
- The intended production-quality route is:
139
 
140
  ```text
141
  Hugging Face Space Gradio UI
@@ -146,13 +166,12 @@ Hugging Face Space Gradio UI
146
 
147
  The Space defaults to `model` for both text and vision backends, so a configured Space calls Modal immediately. The `demo` backend remains available in developer settings as the deterministic fallback path.
148
 
149
- The public Space can run on ZeroGPU after `dream_customs.zerogpu` registers the lightweight `@spaces.GPU` startup probe. Modal credits still pay for the hidden L4 GPU backend; the ZeroGPU probe is only there to make the HF hardware setting valid for this Gradio frontend.
150
 
151
  ## Open Questions For User
152
 
153
- These do not block initial scaffolding:
154
 
155
- - Should the demo language be Chinese-only or bilingual?
156
- - Which Hugging Face Space name should be used?
157
- - Should we reuse any previous `build-small-relics` assets, or keep Dream Customs separate?
158
  - Which ASR adapter is acceptable for the hackathon submission?
 
1
+ # Dream QA / 梦境问答台 Handoff
2
 
3
+ Last updated: 2026-06-08
4
 
5
+ ## Current Product Direction
6
 
7
+ The project is pivoting from a customs/pact-centered ritual into a step-by-step dream Q&A Gradio app.
8
 
9
+ The repo and Space still use the `Dream Customs` name for continuity, but future product work should use the `Dream QA / 梦境问答台` direction:
10
 
11
+ > 输入梦境,回答或跳过几个温和追问,最后得到一个和梦境细节相关的今日小 Tips。
 
 
 
 
12
 
13
+ ## Deprecated Context
14
 
15
+ Earlier implementation and smoke docs mention:
16
 
17
+ - dream visitor
18
+ - customs negotiation
19
+ - permit ID
20
+ - contraband
21
+ - sealed pact
22
+ - bedtime release
23
 
24
+ Those are historical context, not the new target. Do not build new work around permit/contraband/sealed-pact unless the user explicitly reverses this pivot.
 
 
25
 
26
+ ## Current Source Documents
27
 
28
+ Read in this order:
29
+
30
+ 1. `AGENTS.md`
31
+ 2. `docs/spec.md`
32
+ 3. `docs/prd.md`
33
+ 4. `PRODUCT.md`
34
+ 5. `DESIGN.md`
35
+ 6. `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md`
36
+ 7. `docs/superpowers/plans/2026-06-08-dream-qa-refactor-doc-plan.md`
37
+
38
+ Prototype references:
39
+
40
+ - `docs/prototypes/2026-06-08-dream-qa-mobile-flow.png`
41
+ - `docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png`
42
+ - `docs/prototypes/2026-06-08-dream-qa-tips-card.png`
43
 
44
  ## Product Decision
45
 
46
+ Build a guided dream interpretation app, not a diagnosis app, not a fortune-telling app, and not a one-shot pact generator.
47
 
48
  Final positioning:
49
 
50
+ > 梦境问答台帮你一步步整理境疑惑先记录,回答几个温和追问,再得到个今天可以参考的小 Tips
51
 
52
  ## Model Decision
53
 
54
  Use:
55
 
56
  - `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
57
+ - `openbmb/MiniCPM5-1B` for text reasoning, follow-up questions, interpretation, and final Today Tip.
58
 
59
  Do not broaden to arbitrary small models unless MiniCPM paths fail.
60
 
 
74
  1. User submits dream using text, image, voice, or a combination.
75
  2. Audio is transcribed by a small adapter.
76
  3. Image is converted into visual clues by MiniCPM-V-4.6.
77
+ 4. App builds a `DreamQuestionIntake`.
78
+ 5. MiniCPM5-1B creates a short dream summary and identifies the user's likely main question.
79
+ 6. MiniCPM5-1B asks 1-3 gentle follow-up questions.
80
+ 7. User answers, skips, adds detail, or asks for another angle.
81
+ 8. MiniCPM5-1B drafts a grounded interpretation.
82
+ 9. MiniCPM5-1B generates a final `TodayTipCard`.
83
+ 10. Gradio renders a mobile-readable result card and plain text.
84
 
85
  ## Required Output Fields
86
 
87
+ - `dream_summary`
88
+ - `main_question`
89
+ - `dream_anchors`
90
+ - `followup_questions`
91
+ - `user_answers`
92
+ - `interpretation`
93
+ - `today_tip`
94
+ - `tiny_action`
95
+ - `caring_note`
96
  - `safety_note`
97
 
98
+ The final `today_tip` must reference at least one concrete dream anchor. Generic wellness advice is a regression.
99
+
100
  ## Safety Boundary
101
 
102
  The product must never present itself as therapy, medical advice, or diagnosis. It should use playful, non-certain language. Severe distress should trigger a professional-help suggestion.
103
 
104
+ ## Recommended Implementation Scope
105
 
106
+ The existing package should be refactored rather than replaced from scratch:
107
 
108
  ```text
109
  app.py
 
117
  render.py
118
  pipeline.py
119
  models.py
120
+ ui/
121
  tests/
 
 
 
 
122
  ```
123
 
124
+ Likely code files to change:
125
+
126
+ - `dream_customs/schema.py`
127
+ - `dream_customs/prompts.py`
128
+ - `dream_customs/pipeline.py`
129
+ - `dream_customs/render.py`
130
+ - `dream_customs/ui/actions.py`
131
+ - `dream_customs/ui/app.py`
132
+ - `dream_customs/ui/copy.py`
133
+ - `dream_customs/ui/styles.py`
134
+ - tests covering schema, prompts, pipeline, render, UI actions, and safety
135
 
136
  ## Implementation Priorities
137
 
138
+ 1. Preserve text-only demo fallback.
139
+ 2. Replace pact schema with Q&A state and Today Tip output.
140
+ 3. Preserve image and voice as optional evidence in the same intake.
141
+ 4. Make follow-up questions a first-class user action.
142
+ 5. Render one clear final tip, not a multi-field customs certificate.
143
+ 6. Keep mobile readability.
144
+ 7. Run tests and local Gradio smoke before any Space sync.
145
 
146
  ## Repository Status
147
 
148
+ This directory is a dedicated Dream Customs/Dream QA repository:
149
 
150
  - Local path: `/Users/junhaocheng/working-dir/ai-competitions/build-small-hackthon`
151
  - GitHub remote: `https://github.com/adjcjh777/dream-customs-build-small.git`
152
  - Hugging Face Space remote: `https://huggingface.co/spaces/build-small-hackathon/dream-customs`
153
 
154
+ Continue to confirm `git remote -v` before pushing.
155
 
156
  ## Modal Backend Route
157
 
158
+ The intended production-quality route remains:
159
 
160
  ```text
161
  Hugging Face Space Gradio UI
 
166
 
167
  The Space defaults to `model` for both text and vision backends, so a configured Space calls Modal immediately. The `demo` backend remains available in developer settings as the deterministic fallback path.
168
 
169
+ The public Space can run on ZeroGPU after `dream_customs.zerogpu` registers the lightweight `@spaces.GPU` startup probe. Modal credits still pay for the hidden GPU backend; the ZeroGPU probe is only there to make the HF hardware setting valid for this Gradio frontend.
170
 
171
  ## Open Questions For User
172
 
173
+ These do not block document alignment:
174
 
175
+ - Should the public name remain `Dream Customs`, or should visible UI switch fully to `梦境问答台 / Dream QA`?
176
+ - Should final demo language be Chinese-first, English-first, or bilingual?
 
177
  - Which ASR adapter is acceptable for the hackathon submission?
docs/prd.md CHANGED
@@ -1,23 +1,27 @@
1
- # Dream Customs / 梦境海关 PRD
2
 
3
- Last updated: 2026-06-05
4
 
5
  ## 1. Executive Summary
6
 
7
  ### Problem Statement
8
 
9
- Users who wake up from frequent or vivid dreams may feel unsettled, tired, or mentally cluttered. They need a lightweight way to turn dream residue into a gentle next-day action without receiving pseudo-clinical dream diagnosis.
10
 
11
  ### Proposed Solution
12
 
13
- Dream Customs is a Gradio app that accepts voice, image, and text dream declarations, normalizes them into a structured dream intake, and uses MiniCPM models to generate a playful "dream alliance" card with practical advice, a weird 5-minute task, and a bedtime release phrase.
 
 
14
 
15
  ### Success Criteria
16
 
17
- - A user can complete the core flow from input to final card in <= 90 seconds on the hosted demo.
18
- - At least 90% of demo runs produce a parseable final output object matching the required schema.
19
- - At least 5 real users can understand the output without extra explanation and rate it >= 4/5 for delight.
20
- - At least 3 real users report that the "today's pact" feels actionable.
 
 
21
  - The app runs with total model parameters <= 32B and is packaged for Gradio/Hugging Face Space.
22
 
23
  ## 2. User Experience & Functionality
@@ -26,7 +30,7 @@ Dream Customs is a Gradio app that accepts voice, image, and text dream declarat
26
 
27
  1. Primary persona: vivid dreamer.
28
  - Recently sleeps poorly or wakes with strong dream fragments.
29
- - Wants a small morning ritual, not therapy.
30
 
31
  2. Secondary persona: playful AI explorer.
32
  - Wants a strange AI experience worth showing a friend.
@@ -38,28 +42,30 @@ Dream Customs is a Gradio app that accepts voice, image, and text dream declarat
38
 
39
  ### User Stories
40
 
41
- #### Story 1: Text Dream Declaration
42
 
43
- As a vivid dreamer, I want to type my dream so that I can get a next-day pact even if I have no image or audio.
44
 
45
  Acceptance criteria:
46
 
47
  - User can submit text-only input.
48
- - App creates a valid `DreamIntake` object.
49
- - Final card includes dream visitor, practical suggestion, weird task, and bedtime release phrase.
 
 
50
 
51
- #### Story 2: Image Dream Declaration
52
 
53
  As a user who remembers images better than words, I want to upload a sketch or bedside note so that the system can use visual dream clues.
54
 
55
  Acceptance criteria:
56
 
57
  - User can upload one image.
58
- - MiniCPM-V-4.6 extracts at least 3 visual clues when visible content exists.
59
- - Visual clues are included in the final `DreamIntake`.
60
  - If image extraction fails, text-only flow still works.
61
 
62
- #### Story 3: Voice Dream Declaration
63
 
64
  As a half-awake user, I want to speak my dream so that I do not need to type immediately after waking.
65
 
@@ -67,33 +73,35 @@ Acceptance criteria:
67
 
68
  - User can record or upload an audio clip.
69
  - ASR adapter returns a transcript.
70
- - Transcript is visible/editable before final submission or transparently included in the declaration.
71
  - If ASR fails, user receives a concise fallback message and can type manually.
72
 
73
- #### Story 4: Alliance Negotiation
74
 
75
- As a user, I want the AI to ask me a few questions so that the final advice feels connected to my dream.
76
 
77
  Acceptance criteria:
78
 
79
- - App generates 2-3 questions.
80
- - User can answer all, answer some, or skip.
81
- - Skip path still generates a final card.
 
82
 
83
- #### Story 5: Today's Pact Card
84
 
85
- As a user, I want a visually clear output card so that I can screenshot or share the result.
86
 
87
  Acceptance criteria:
88
 
89
- - Card includes all required output fields.
90
- - Card is readable on mobile and desktop.
91
- - Card avoids medical or diagnostic framing.
92
- - Card includes a short safety note only when needed.
93
 
94
  ### Non-Goals
95
 
96
  - Medical diagnosis or therapy.
 
97
  - Sleep-stage tracking.
98
  - Long-term journaling database.
99
  - Account creation.
@@ -106,10 +114,10 @@ Acceptance criteria:
106
  ### Tool Requirements
107
 
108
  - MiniCPM-V-4.6 for image-to-text visual clue extraction.
109
- - MiniCPM5-1B for text reasoning, persona, structured output, and alliance card generation.
110
  - A small ASR adapter for voice-to-text. This adapter is not the core AI experience.
111
  - Gradio for UI.
112
- - Optional HTML/CSS card renderer.
113
 
114
  ### Prompt Requirements
115
 
@@ -119,7 +127,8 @@ Prompts must:
119
  - Keep the tone playful, gentle, and non-clinical.
120
  - Avoid strong claims about dream meaning.
121
  - Include a safety boundary for severe distress.
122
- - Keep final advice small enough to execute in 5-10 minutes.
 
123
 
124
  ### Evaluation Strategy
125
 
@@ -133,9 +142,10 @@ Create an eval set with at least 12 dream inputs:
133
  Measure:
134
 
135
  - Schema validity.
 
136
  - Tone safety.
137
- - Practicality of advice.
138
- - Weird-task delight.
139
  - Ability to recover from missing modality.
140
 
141
  Pass thresholds:
@@ -143,7 +153,8 @@ Pass thresholds:
143
  - Schema validity: >= 90%.
144
  - No diagnostic language in ordinary cases: 100%.
145
  - Safety note appears in distress-edge cases: >= 90%.
146
- - Human delight rating on 5 demo cases: average >= 4/5.
 
147
 
148
  ## 4. Technical Specifications
149
 
@@ -154,28 +165,31 @@ Gradio inputs
154
  -> text input
155
  -> image upload -> MiniCPM-V-4.6 visual clue extractor
156
  -> audio upload/record -> ASR adapter
157
- -> DreamIntake builder
158
- -> MiniCPM5-1B customs officer prompt
159
- -> negotiation questions
160
- -> final MiniCPM5-1B pact prompt
161
- -> HTML card renderer
 
 
162
  ```
163
 
164
  ### Core Components
165
 
166
- - `app.py`: Gradio UI composition and event wiring.
167
- - `dream_customs/schema.py`: dataclasses or Pydantic models for inputs and outputs.
168
  - `dream_customs/models.py`: model/client loading and inference wrappers.
169
  - `dream_customs/prompts.py`: prompt templates.
170
  - `dream_customs/pipeline.py`: orchestration logic.
171
  - `dream_customs/safety.py`: lightweight safety checks and copy.
172
- - `dream_customs/render.py`: HTML card rendering.
173
- - `tests/`: schema, prompt, safety, and pipeline unit tests with mocked model outputs.
 
174
 
175
  ### Integration Points
176
 
177
  - Hugging Face Space runtime.
178
- - Hugging Face model downloads or inference endpoints.
179
  - Gradio audio, image, and text components.
180
  - Optional model cache in Space environment.
181
 
@@ -195,22 +209,22 @@ Gradio inputs
195
  - Text, image, and voice input.
196
  - Dream intake normalization.
197
  - MiniCPM-V-4.6 image clue extraction.
198
- - MiniCPM5-1B negotiation and final card.
199
  - HTML output card.
200
  - Safety boundary.
201
 
202
  #### v1.1
203
 
204
- - More polished card export.
205
  - Better example gallery.
206
  - Optional bilingual output.
207
  - More robust eval harness.
 
208
 
209
  #### v2.0
210
 
211
  - Voice output through VoxCPM2.
212
  - Local dream history stored only in browser/session.
213
- - Multiple customs officer styles.
214
 
215
  ### Technical Risks
216
 
@@ -218,12 +232,4 @@ Gradio inputs
218
  - MiniCPM5-1B output may fail strict JSON without repair.
219
  - ASR adapter may complicate dependencies.
220
  - Audio input on mobile browsers may behave inconsistently.
221
- - Safety classification may be too weak if only prompt-based.
222
-
223
- ### Mitigations
224
-
225
- - Keep text-only demo path always functional.
226
- - Add JSON repair and schema validation.
227
- - Use examples with fixed sample media for demo recording.
228
- - Make ASR optional and fallback to manual transcript.
229
- - Keep image extraction output concise to reduce latency.
 
1
+ # Dream QA / 梦境问答台 PRD
2
 
3
+ Last updated: 2026-06-08
4
 
5
  ## 1. Executive Summary
6
 
7
  ### Problem Statement
8
 
9
+ Users who wake up from vivid dreams may feel unsettled, curious, or mentally cluttered. They often do not need a definitive "meaning"; they need a short, safe conversation that helps them identify what question the dream left behind and what tiny thing they can do today.
10
 
11
  ### Proposed Solution
12
 
13
+ Dream QA is a Gradio app that accepts voice, image, and text dream fragments, normalizes them into a structured dream intake, asks gentle follow-up questions, drafts a grounded interpretation, and ends with one `今日小 Tips`.
14
+
15
+ The repository and Space may continue to use `Dream Customs` as the deployed project name, but the product experience should no longer be built around permits, contraband, or sealed pact cards.
16
 
17
  ### Success Criteria
18
 
19
+ - A user can complete the core text-only Q&A flow in <= 90 seconds on the hosted demo.
20
+ - At least one follow-up question feels related to the dream in >= 90% of demo runs.
21
+ - Final output references at least one concrete dream detail.
22
+ - Final output gives exactly one primary today tip.
23
+ - Ordinary cases contain no diagnostic, prophetic, or frightening language.
24
+ - Distress-edge cases show a support note.
25
  - The app runs with total model parameters <= 32B and is packaged for Gradio/Hugging Face Space.
26
 
27
  ## 2. User Experience & Functionality
 
30
 
31
  1. Primary persona: vivid dreamer.
32
  - Recently sleeps poorly or wakes with strong dream fragments.
33
+ - Wants a small morning reflection, not therapy or fortune-telling.
34
 
35
  2. Secondary persona: playful AI explorer.
36
  - Wants a strange AI experience worth showing a friend.
 
42
 
43
  ### User Stories
44
 
45
+ #### Story 1: Text Dream Q&A
46
 
47
+ As a vivid dreamer, I want to type my dream so that the app can help me figure out what I am actually wondering about.
48
 
49
  Acceptance criteria:
50
 
51
  - User can submit text-only input.
52
+ - App creates a valid dream intake object.
53
+ - App asks at least one relevant follow-up question.
54
+ - User can answer or skip.
55
+ - Final output includes a grounded today tip.
56
 
57
+ #### Story 2: Image-Assisted Dream Clues
58
 
59
  As a user who remembers images better than words, I want to upload a sketch or bedside note so that the system can use visual dream clues.
60
 
61
  Acceptance criteria:
62
 
63
  - User can upload one image.
64
+ - MiniCPM-V-4.6 extracts visible clues when content exists.
65
+ - Visual clues are included in the dream intake and final interpretation.
66
  - If image extraction fails, text-only flow still works.
67
 
68
+ #### Story 3: Voice Dream Capture
69
 
70
  As a half-awake user, I want to speak my dream so that I do not need to type immediately after waking.
71
 
 
73
 
74
  - User can record or upload an audio clip.
75
  - ASR adapter returns a transcript.
76
+ - Transcript is visible/editable or transparently included in the dream intake.
77
  - If ASR fails, user receives a concise fallback message and can type manually.
78
 
79
+ #### Story 4: Follow-Up Question Flow
80
 
81
+ As a user, I want the AI to ask me a few questions so that the final interpretation feels connected to my dream rather than generic.
82
 
83
  Acceptance criteria:
84
 
85
+ - App generates 1-3 follow-up questions.
86
+ - User can answer all, answer some, skip, or ask for another angle.
87
+ - Skip path still generates a final tip.
88
+ - Questions avoid clinical or diagnostic phrasing.
89
 
90
+ #### Story 5: Today Tip Card
91
 
92
+ As a user, I want a clear final result so that I leave with one small thing for today.
93
 
94
  Acceptance criteria:
95
 
96
+ - Result includes dream summary, main question, interpretation, today tip, optional tiny action, optional caring note, and safety note when needed.
97
+ - Result is readable on mobile and desktop.
98
+ - Result avoids medical, prophetic, or deterministic framing.
99
+ - Today tip references a concrete dream anchor.
100
 
101
  ### Non-Goals
102
 
103
  - Medical diagnosis or therapy.
104
+ - Dream prophecy or symbolic certainty.
105
  - Sleep-stage tracking.
106
  - Long-term journaling database.
107
  - Account creation.
 
114
  ### Tool Requirements
115
 
116
  - MiniCPM-V-4.6 for image-to-text visual clue extraction.
117
+ - MiniCPM5-1B for text reasoning, follow-up questions, interpretation, and Today Tip generation.
118
  - A small ASR adapter for voice-to-text. This adapter is not the core AI experience.
119
  - Gradio for UI.
120
+ - Optional HTML/CSS result renderer.
121
 
122
  ### Prompt Requirements
123
 
 
127
  - Keep the tone playful, gentle, and non-clinical.
128
  - Avoid strong claims about dream meaning.
129
  - Include a safety boundary for severe distress.
130
+ - Keep final advice small enough to execute today.
131
+ - Require at least one dream anchor in the final tip.
132
 
133
  ### Evaluation Strategy
134
 
 
142
  Measure:
143
 
144
  - Schema validity.
145
+ - Follow-up relevance.
146
  - Tone safety.
147
+ - Dream-anchor grounding.
148
+ - Practicality of today tip.
149
  - Ability to recover from missing modality.
150
 
151
  Pass thresholds:
 
153
  - Schema validity: >= 90%.
154
  - No diagnostic language in ordinary cases: 100%.
155
  - Safety note appears in distress-edge cases: >= 90%.
156
+ - At least one concrete dream anchor in final result: >= 90%.
157
+ - Human usefulness/delight rating on 5 demo cases: average >= 4/5.
158
 
159
  ## 4. Technical Specifications
160
 
 
165
  -> text input
166
  -> image upload -> MiniCPM-V-4.6 visual clue extractor
167
  -> audio upload/record -> ASR adapter
168
+ -> DreamQuestionIntake builder
169
+ -> MiniCPM5-1B summary/main-question prompt
170
+ -> follow-up question flow
171
+ -> user answer/skip/another-angle
172
+ -> MiniCPM5-1B interpretation prompt
173
+ -> TodayTipCard prompt
174
+ -> HTML result renderer
175
  ```
176
 
177
  ### Core Components
178
 
179
+ - `app.py`: thin Gradio entrypoint.
180
+ - `dream_customs/schema.py`: Pydantic models for dream intake, Q&A state, and Today Tip output.
181
  - `dream_customs/models.py`: model/client loading and inference wrappers.
182
  - `dream_customs/prompts.py`: prompt templates.
183
  - `dream_customs/pipeline.py`: orchestration logic.
184
  - `dream_customs/safety.py`: lightweight safety checks and copy.
185
+ - `dream_customs/render.py`: HTML result rendering.
186
+ - `dream_customs/ui/`: Gradio UI shell, copy, actions, and styles.
187
+ - `tests/`: schema, prompt, safety, pipeline, render, and UI action unit tests with mocked model outputs.
188
 
189
  ### Integration Points
190
 
191
  - Hugging Face Space runtime.
192
+ - Hugging Face model downloads or private Modal endpoints.
193
  - Gradio audio, image, and text components.
194
  - Optional model cache in Space environment.
195
 
 
209
  - Text, image, and voice input.
210
  - Dream intake normalization.
211
  - MiniCPM-V-4.6 image clue extraction.
212
+ - MiniCPM5-1B follow-up and final Today Tip.
213
  - HTML output card.
214
  - Safety boundary.
215
 
216
  #### v1.1
217
 
 
218
  - Better example gallery.
219
  - Optional bilingual output.
220
  - More robust eval harness.
221
+ - Better result export.
222
 
223
  #### v2.0
224
 
225
  - Voice output through VoxCPM2.
226
  - Local dream history stored only in browser/session.
227
+ - Multiple question-guide styles.
228
 
229
  ### Technical Risks
230
 
 
232
  - MiniCPM5-1B output may fail strict JSON without repair.
233
  - ASR adapter may complicate dependencies.
234
  - Audio input on mobile browsers may behave inconsistently.
235
+ - Final tips may become generic unless prompts and repair logic enforce dream anchors.
 
 
 
 
 
 
 
 
docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png ADDED

Git LFS Details

  • SHA256: 39e04ecb3eef3a66e635553ce82db80aa86c7f1abe6057dec22035f34f1f9e7c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.12 MB
docs/prototypes/2026-06-08-dream-qa-mobile-flow.png ADDED

Git LFS Details

  • SHA256: 5e41fcc9cef046e2d8d31c6760a93278232830f13b6a01c266fcdf8c2b7afa97
  • Pointer size: 132 Bytes
  • Size of remote file: 1.27 MB
docs/prototypes/2026-06-08-dream-qa-tips-card.png ADDED

Git LFS Details

  • SHA256: 788066226e98163a1bdbf47bbd82c93b1ca6d8fef6666b1fc45223b2164e56d5
  • Pointer size: 132 Bytes
  • Size of remote file: 1.36 MB
docs/spec.md CHANGED
@@ -1,10 +1,10 @@
1
- # Dream Customs / 梦境海关 Product Spec
2
 
3
- Last updated: 2026-06-05
4
 
5
  ## 1. One-Line Concept
6
 
7
- 梦境海关帮用户昨晚的梦结盟:用户用语音、图片或文字申报梦境,系统把不安转成第二天小建议把怪梦转成一件 5 分钟能完成有趣
8
 
9
  ## 2. Contest Fit
10
 
@@ -12,26 +12,27 @@ Last updated: 2026-06-05
12
  - Build window: 2026-06-05 to 2026-06-15.
13
  - Deployment target: Gradio app on Hugging Face Space.
14
  - Model constraint: total model parameters must be <= 32B.
15
- - Primary track fit: An Adventure in Thousand Token Wood, because the AI is the load-bearing delight of the experience.
16
- - Secondary story: Backyard AI, because the concept comes from a real sleep problem: frequent vivid dreams and poor sleep.
17
  - Model family constraint: prioritize MiniCPM models before considering any unrelated small model.
18
 
19
  ## 3. Core User Problem
20
 
21
- The user wakes up after vivid or unsettling dreams and wants a quick, non-clinical way to make sense of the dream before the day begins. Existing dream tools often feel either too mystical, too diagnostic, or too generic. This product should feel like a playful ritual that gives emotional order without pretending to be therapy.
22
 
23
  ## 4. Product Principles
24
 
25
  1. Do not diagnose.
26
  2. Do not frighten the user.
27
- 3. Convert dream residue into a small next-day action.
28
- 4. Keep the output weird enough to show a friend.
29
- 5. Make the AI the main experience, not a helper hidden behind a form.
30
- 6. Keep the MVP stable enough for Gradio Space and demo-video recording.
 
31
 
32
  ## 5. User Inputs
33
 
34
- All inputs are normalized into one `DreamIntake` object so the app does not become three separate products.
35
 
36
  ### 5.1 Text Input
37
 
@@ -39,6 +40,7 @@ The user writes:
39
 
40
  - Dream narrative.
41
  - Wake-up mood.
 
42
  - Repeated symbols.
43
  - Any second-day concern or wish.
44
 
@@ -59,7 +61,15 @@ The user speaks the dream immediately after waking.
59
 
60
  Implementation note: MiniCPM-V-4.6 and MiniCPM5-1B are not raw audio ASR models. Voice input needs a small transcription adapter. The adapter only converts audio to text; dream understanding remains MiniCPM-driven.
61
 
62
- ## 6. Dream Intake Schema
 
 
 
 
 
 
 
 
63
 
64
  ```json
65
  {
@@ -67,12 +77,43 @@ Implementation note: MiniCPM-V-4.6 and MiniCPM5-1B are not raw audio ASR models.
67
  "voice_transcript": "我梦见我一直赶不上电梯...",
68
  "visual_clues": ["歪斜电梯门", "蓝色楼道", "融化的数字 14"],
69
  "mood": "焦虑但有点滑稽",
 
70
  "recurring_symbols": ["电梯", "迟到", "融化的按钮"],
71
  "uncertainty": "梦的后半段记不清",
72
  "user_context": "最近晚上常做梦,睡醒后想获得第二天建议"
73
  }
74
  ```
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  ## 7. Model Responsibilities
77
 
78
  ### MiniCPM-V-4.6
@@ -87,19 +128,21 @@ Responsibilities:
87
 
88
  Non-responsibilities:
89
 
90
- - Do not generate the final alliance card.
91
- - Do not provide psychological interpretation.
92
 
93
  ### MiniCPM5-1B
94
 
95
- Role: dream diplomat.
96
 
97
  Responsibilities:
98
 
99
  - Merge dream text, voice transcript, and visual clues.
100
- - Name the dream visitor.
101
- - Ask 2-3 alliance negotiation questions.
102
- - Generate the final alliance card.
 
 
103
  - Keep tone playful, gentle, and non-clinical.
104
 
105
  ## 8. MVP User Flow
@@ -108,45 +151,36 @@ Responsibilities:
108
  2. User submits any combination of text, image, and voice.
109
  3. App transcribes voice if present.
110
  4. App calls MiniCPM-V-4.6 if image is present.
111
- 5. App builds `DreamIntake`.
112
- 6. MiniCPM5-1B generates a dream visitor profile and 2-3 follow-up questions.
113
- 7. User answers questions or clicks "skip / refuse cooperation".
114
- 8. MiniCPM5-1B generates a final alliance card.
115
- 9. App renders the card as a shareable visual block and plain text.
 
 
116
 
117
  ## 9. Output Contract
118
 
119
- The final card must include:
120
 
121
- - Dream visitor name.
122
- - Entry permit number.
123
- - Carried emotional contraband.
124
- - Risk level, phrased playfully.
125
- - Alliance reading: what the dream may be trying to protect.
126
- - One practical next-day suggestion.
127
- - One 5-minute weird task.
128
- - One bedtime release phrase.
129
  - Safety note if the input indicates severe distress.
130
 
131
- Example:
132
 
133
- ```json
134
- {
135
- "visitor_name": "迟到的电梯",
136
- "permit_id": "DC-0605-014",
137
- "contraband": ["未申报的焦虑", "融化的按钮", "一小袋没来得及开始的事"],
138
- "risk_level": "橙色:需要被安置,但不需要被害怕",
139
- "alliance_reading": "这个梦像是在提醒你,今天不要把启动一件事和完成一件事混成同一个压力。",
140
- "practical_suggestion": "提前 10 分钟开始一件最小的任务,只要求打开它。",
141
- "weird_task": "给电梯写一句道歉信:抱歉总让你替我背迟到的锅。",
142
- "bedtime_release": "今日电梯已停靠,未完成事项明日再报关。",
143
- "safety_note": ""
144
- }
145
- ```
146
 
147
  ## 10. UX Structure
148
 
149
- ### Screen 1: Dream Declaration Desk
150
 
151
  Inputs:
152
 
@@ -154,40 +188,51 @@ Inputs:
154
  - Image upload for dream sketch or bedside note.
155
  - Audio input for spoken dream.
156
  - Optional mood selector.
 
157
 
158
  Primary action:
159
 
160
- - "Submit to Customs"
161
 
162
- ### Screen 2: Alliance Negotiation
163
 
164
  Content:
165
 
166
- - Dream visitor name.
167
- - 2-3 questions from the customs officer.
168
- - Buttons: answer, skip, refuse cooperation.
 
169
 
170
- ### Screen 3: Today's Pact Card
171
 
172
  Content:
173
 
174
- - Card-style result.
 
 
 
 
 
 
 
 
 
 
175
  - Copy text button.
176
- - Download/share image target if feasible.
177
- - "Release this dream for tonight" reset action.
178
 
179
  ## 11. Safety Boundaries
180
 
181
  The app must:
182
 
183
  - Avoid diagnoses, medical claims, or certainty about dream meaning.
184
- - Avoid telling users that a dream proves trauma, illness, or danger.
185
  - Use language such as "may be", "could be treated as", and "for today, try".
186
  - If the user mentions self-harm, intent to harm others, severe insomnia, panic, or inability to function, show a supportive escalation note.
187
 
188
  Escalation copy:
189
 
190
- > This dream sounds heavier than a playful customs ritual should handle. If you feel unsafe, unable to sleep for many nights, or worried you may hurt yourself or someone else, please reach out to a trusted person or professional support now.
191
 
192
  ## 12. Recommended MVP Scope
193
 
@@ -197,8 +242,9 @@ Included:
197
  - Text input.
198
  - Image input through MiniCPM-V-4.6.
199
  - Voice input through small ASR adapter.
200
- - MiniCPM5-1B prompt flow.
201
- - Result card in HTML.
 
202
  - Example mode for demo stability.
203
  - Basic safety classifier prompt or rule layer.
204
 
@@ -210,27 +256,15 @@ Excluded from MVP:
210
  - Dream history database.
211
  - Personalized therapy plans.
212
  - Multi-user social feed.
 
213
 
214
  ## 13. Grill Questions And Recommended Answers
215
 
216
  1. Is this a therapy tool?
217
- - Recommended answer: no. It is a playful reflection and next-day ritual tool.
218
 
219
  2. Which track should the submission emphasize?
220
  - Recommended answer: Thousand Token Wood first, Backyard AI second.
221
 
222
- 3. Should voice be core?
223
- - Recommended answer: voice input yes, voice output no for MVP.
224
-
225
- 4. Should the image input be mandatory?
226
- - Recommended answer: no. Image is a strong MiniCPM-V demo path but text alone must work.
227
-
228
- 5. What is the winning demo moment?
229
- - Recommended answer: user speaks a dream, uploads a quick sketch, and receives a strange but useful "Today's Pact" card.
230
-
231
- ## 14. Open Questions
232
-
233
- - Which ASR adapter should be used on Hugging Face Space for the MVP?
234
- - Should the first demo be in Chinese, English, or bilingual?
235
- - Do we want to submit under the same team/repo as the earlier Ordinary Relics MVP, or create a new Space?
236
- - Should the visual card support image download in MVP or only screenshot-friendly HTML?
 
1
+ # Dream QA / 梦境问答台 Product Spec
2
 
3
+ Last updated: 2026-06-08
4
 
5
  ## 1. One-Line Concept
6
 
7
+ 梦境问答台帮用户循序渐进地理解昨晚的梦:用户用文字、图片或语音记录梦境,系统先追问关键细节,再给出一个非诊断、非宿命论解读草稿最后生成一个温和今日 Tips
8
 
9
  ## 2. Contest Fit
10
 
 
12
  - Build window: 2026-06-05 to 2026-06-15.
13
  - Deployment target: Gradio app on Hugging Face Space.
14
  - Model constraint: total model parameters must be <= 32B.
15
+ - Primary track fit: An Adventure in Thousand Token Wood, because the AI-driven conversation is the main experience.
16
+ - Secondary story: Backyard AI, because the concept comes from a real sleep problem: frequent vivid dreams and morning confusion.
17
  - Model family constraint: prioritize MiniCPM models before considering any unrelated small model.
18
 
19
  ## 3. Core User Problem
20
 
21
+ The user wakes up after a vivid or unsettling dream and wants help making sense of it without receiving mystical certainty, therapy language, or generic advice. They may not know the right question yet, so the app should help them gradually clarify what they are curious or worried about.
22
 
23
  ## 4. Product Principles
24
 
25
  1. Do not diagnose.
26
  2. Do not frighten the user.
27
+ 3. Do not claim the dream has one fixed meaning.
28
+ 4. Ask before interpreting.
29
+ 5. Ground every final answer in concrete dream details.
30
+ 6. End with one small today tip, not a pile of advice.
31
+ 7. Keep the MVP stable enough for Gradio Space and demo-video recording.
32
 
33
  ## 5. User Inputs
34
 
35
+ All inputs are normalized into one dream intake object so the app does not become three separate products.
36
 
37
  ### 5.1 Text Input
38
 
 
40
 
41
  - Dream narrative.
42
  - Wake-up mood.
43
+ - What feels confusing or important.
44
  - Repeated symbols.
45
  - Any second-day concern or wish.
46
 
 
61
 
62
  Implementation note: MiniCPM-V-4.6 and MiniCPM5-1B are not raw audio ASR models. Voice input needs a small transcription adapter. The adapter only converts audio to text; dream understanding remains MiniCPM-driven.
63
 
64
+ ## 6. Data Model
65
+
66
+ The refactor should move from `DreamIntake -> PactCard` toward:
67
+
68
+ ```text
69
+ DreamQuestionIntake -> DreamQAState -> TodayTipCard
70
+ ```
71
+
72
+ ### 6.1 DreamQuestionIntake
73
 
74
  ```json
75
  {
 
77
  "voice_transcript": "我梦见我一直赶不上电梯...",
78
  "visual_clues": ["歪斜电梯门", "蓝色楼道", "融化的数字 14"],
79
  "mood": "焦虑但有点滑稽",
80
+ "main_question": "这个梦是不是和我最近拖延有关?",
81
  "recurring_symbols": ["电梯", "迟到", "融化的按钮"],
82
  "uncertainty": "梦的后半段记不清",
83
  "user_context": "最近晚上常做梦,睡醒后想获得第二天建议"
84
  }
85
  ```
86
 
87
+ ### 6.2 DreamQAState
88
+
89
+ ```json
90
+ {
91
+ "dream_summary": "你梦见自己赶不上电梯,楼层按钮像蜡一样融化。",
92
+ "dream_anchors": ["赶不上电梯", "融化的按钮", "焦虑但滑稽"],
93
+ "main_question": "这个梦和最近拖延有关吗?",
94
+ "followup_questions": [
95
+ "梦里最强烈的感受是着急、害怕,还是荒诞?",
96
+ "最近有没有一件事让你觉得还没开始就已经迟到?"
97
+ ],
98
+ "user_answers": ["更像是还没开始就觉得迟到。"],
99
+ "current_step": "interpret"
100
+ }
101
+ ```
102
+
103
+ ### 6.3 TodayTipCard
104
+
105
+ ```json
106
+ {
107
+ "dream_summary": "你梦见赶不上电梯,按钮融化,醒来有点焦虑。",
108
+ "main_question": "它是否和最近拖延有关?",
109
+ "interpretation": "也许这个梦不是在证明你真的迟到了,而是在把“还没开始就担心来不及”的感觉演成了电梯。",
110
+ "today_tip": "今天先选一件小事,只���打开它的第一步,别要求自己马上抵达。",
111
+ "tiny_action": "用 5 分钟写下:如果电梯只停一层,我今天先停在哪一层?",
112
+ "caring_note": "你不需要一醒来就解决所有问题,先把节奏找回来就很好。",
113
+ "safety_note": ""
114
+ }
115
+ ```
116
+
117
  ## 7. Model Responsibilities
118
 
119
  ### MiniCPM-V-4.6
 
128
 
129
  Non-responsibilities:
130
 
131
+ - Do not generate the final TodayTipCard.
132
+ - Do not provide psychological diagnosis.
133
 
134
  ### MiniCPM5-1B
135
 
136
+ Role: dream question guide.
137
 
138
  Responsibilities:
139
 
140
  - Merge dream text, voice transcript, and visual clues.
141
+ - Summarize the dream without overexplaining.
142
+ - Infer or ask the user's main question.
143
+ - Ask 1-3 gentle follow-up questions.
144
+ - Draft a non-certain interpretation grounded in dream anchors.
145
+ - Generate exactly one primary today tip, with optional tiny action and caring note.
146
  - Keep tone playful, gentle, and non-clinical.
147
 
148
  ## 8. MVP User Flow
 
151
  2. User submits any combination of text, image, and voice.
152
  3. App transcribes voice if present.
153
  4. App calls MiniCPM-V-4.6 if image is present.
154
+ 5. App builds `DreamQuestionIntake`.
155
+ 6. MiniCPM5-1B summarizes the dream and identifies the likely `main_question`.
156
+ 7. App asks 1-3 follow-up questions.
157
+ 8. User answers, skips, adds detail, or asks for another angle.
158
+ 9. MiniCPM5-1B drafts a grounded interpretation.
159
+ 10. MiniCPM5-1B generates a final `TodayTipCard`.
160
+ 11. Gradio renders a mobile-readable result block and plain text.
161
 
162
  ## 9. Output Contract
163
 
164
+ The final result must include:
165
 
166
+ - Dream summary.
167
+ - Main question.
168
+ - Grounded interpretation.
169
+ - One primary today tip.
170
+ - Optional tiny action.
171
+ - Optional caring note.
 
 
172
  - Safety note if the input indicates severe distress.
173
 
174
+ The final result must:
175
 
176
+ - Reference at least one concrete dream anchor.
177
+ - Avoid diagnosis, prophecy, or frightening certainty.
178
+ - Avoid generic advice that could fit any dream.
179
+ - Keep the today tip small enough to try today.
 
 
 
 
 
 
 
 
 
180
 
181
  ## 10. UX Structure
182
 
183
+ ### Screen 1: Record
184
 
185
  Inputs:
186
 
 
188
  - Image upload for dream sketch or bedside note.
189
  - Audio input for spoken dream.
190
  - Optional mood selector.
191
+ - Optional field: "你最想理解什么?"
192
 
193
  Primary action:
194
 
195
+ - "继续解梦" / "Continue"
196
 
197
+ ### Screen 2: Ask
198
 
199
  Content:
200
 
201
+ - Short dream summary.
202
+ - Main question guess.
203
+ - 1-3 follow-up questions.
204
+ - Buttons: answer, skip, ask another angle.
205
 
206
+ ### Screen 3: Interpret
207
 
208
  Content:
209
 
210
+ - Dream anchors detected.
211
+ - Draft interpretation using "也许 / 可以把它当作 / 今天先试试" language.
212
+ - Controls: add detail, regenerate angle, finish.
213
+
214
+ ### Screen 4: Today Tip
215
+
216
+ Content:
217
+
218
+ - One primary today tip.
219
+ - Optional tiny action.
220
+ - Optional caring note.
221
  - Copy text button.
222
+ - Reset action.
 
223
 
224
  ## 11. Safety Boundaries
225
 
226
  The app must:
227
 
228
  - Avoid diagnoses, medical claims, or certainty about dream meaning.
229
+ - Avoid telling users that a dream proves trauma, illness, danger, fate, or prophecy.
230
  - Use language such as "may be", "could be treated as", and "for today, try".
231
  - If the user mentions self-harm, intent to harm others, severe insomnia, panic, or inability to function, show a supportive escalation note.
232
 
233
  Escalation copy:
234
 
235
+ > This dream sounds heavier than a playful reflection tool should handle. If you feel unsafe, unable to sleep for many nights, or worried you may hurt yourself or someone else, please reach out to a trusted person or professional support now.
236
 
237
  ## 12. Recommended MVP Scope
238
 
 
242
  - Text input.
243
  - Image input through MiniCPM-V-4.6.
244
  - Voice input through small ASR adapter.
245
+ - Dream intake normalization.
246
+ - Progressive follow-up questions.
247
+ - Today Tip result in HTML.
248
  - Example mode for demo stability.
249
  - Basic safety classifier prompt or rule layer.
250
 
 
256
  - Dream history database.
257
  - Personalized therapy plans.
258
  - Multi-user social feed.
259
+ - Fine-tuning during the hackathon MVP.
260
 
261
  ## 13. Grill Questions And Recommended Answers
262
 
263
  1. Is this a therapy tool?
264
+ - Recommended answer: no. It is a playful reflection and next-day tip tool.
265
 
266
  2. Which track should the submission emphasize?
267
  - Recommended answer: Thousand Token Wood first, Backyard AI second.
268
 
269
+ 3. What should the demo show?
270
+ - Recommended answer: user enters a dream, answers or skips one follow-up question, and receives a grounded today tip.
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/superpowers/plans/2026-06-08-dream-qa-refactor-doc-plan.md ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dream QA Refactor Document Plan
2
+
3
+ Last updated: 2026-06-08
4
+
5
+ ## Goal
6
+
7
+ Refactor Dream Customs from a customs/pact-centered ritual into a step-by-step dream Q&A Gradio app.
8
+
9
+ The new product should help the user gradually answer doubts about a dream, then end with one gentle "today tip". The tip can be a practical suggestion, a tiny thing the user has not tried before, or a short caring sentence. It must stay non-clinical and avoid claiming a single fixed dream meaning.
10
+
11
+ ## Prototype Images
12
+
13
+ Use these generated mockups as discussion references before implementation:
14
+
15
+ - `docs/prototypes/2026-06-08-dream-qa-mobile-flow.png`
16
+ - `docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png`
17
+ - `docs/prototypes/2026-06-08-dream-qa-tips-card.png`
18
+
19
+ ## Product Shift
20
+
21
+ Current docs emphasize:
22
+
23
+ - Dream visitor.
24
+ - Customs negotiation.
25
+ - Permit ID.
26
+ - Contraband.
27
+ - Today's pact card.
28
+ - Seal/revise pact actions.
29
+
30
+ New direction should emphasize:
31
+
32
+ - Dream question intake.
33
+ - One clear uncertainty the user wants to understand.
34
+ - 2-4 progressive follow-up questions.
35
+ - A grounded interpretation draft that references specific dream details.
36
+ - One final "today tip" with optional tiny action.
37
+ - Safety note only when needed.
38
+
39
+ Keep the good parts:
40
+
41
+ - Text-only path must always work.
42
+ - Image and voice can enrich the same intake object.
43
+ - MiniCPM-V-4.6 remains the image clue extractor.
44
+ - MiniCPM5-1B remains the main reasoning and generation model.
45
+ - No diagnosis, prophecy, therapy framing, or fear-based copy.
46
+
47
+ ## Document Modification Plan
48
+
49
+ ### 1. `AGENTS.md`
50
+
51
+ Rewrite the project positioning and product boundary sections.
52
+
53
+ Change from:
54
+
55
+ - "和昨晚的梦结盟"
56
+ - "获得今日盟约卡"
57
+ - required output fields such as `permit_id`, `contraband`, `risk_level`, `bedtime_release`
58
+
59
+ Change to:
60
+
61
+ - "循序渐进帮用户理解梦境疑惑"
62
+ - final output is `today_tip` / `今日小 Tips`
63
+ - required output fields:
64
+ - `dream_summary`
65
+ - `main_question`
66
+ - `followup_questions`
67
+ - `user_answers`
68
+ - `interpretation`
69
+ - `today_tip`
70
+ - `tiny_action`
71
+ - `caring_note`
72
+ - `safety_note`
73
+
74
+ Also update manual acceptance:
75
+
76
+ - Submit a dream.
77
+ - App asks at least one useful follow-up question.
78
+ - User can answer or skip.
79
+ - Final result references concrete dream details and gives one today tip.
80
+ - Output avoids diagnosis, prophecy, and medicalized advice.
81
+
82
+ ### 2. `docs/spec.md`
83
+
84
+ Rewrite the product spec around a `DreamQuestionIntake -> DreamQAState -> TodayTipCard` flow.
85
+
86
+ Needed edits:
87
+
88
+ - Replace pact/card schema with Q&A schema.
89
+ - Replace "MVP User Flow" with progressive steps:
90
+ 1. User records dream.
91
+ 2. User picks or writes the doubt they want answered.
92
+ 3. Model asks a gentle follow-up.
93
+ 4. User answers, skips, or asks for a different angle.
94
+ 5. Model drafts an interpretation grounded in dream details.
95
+ 6. Model produces one today tip.
96
+ - Keep MiniCPM and multimodal constraints.
97
+ - Add a rule that final advice must cite at least one dream anchor.
98
+
99
+ ### 3. `docs/prd.md`
100
+
101
+ Update user stories and success criteria.
102
+
103
+ New user stories:
104
+
105
+ - Text dream Q&A.
106
+ - Image-assisted dream clue extraction.
107
+ - Voice dream capture.
108
+ - Follow-up question flow.
109
+ - Final Today Tip card.
110
+
111
+ New success criteria:
112
+
113
+ - User can complete a text-only Q&A in <= 90 seconds.
114
+ - At least one follow-up question feels related to the dream.
115
+ - Final tip references concrete dream details.
116
+ - Ordinary cases never include diagnostic or frightening language.
117
+ - Distress cases show support guidance.
118
+
119
+ ### 4. `PRODUCT.md`
120
+
121
+ Rewrite product purpose and anti-references.
122
+
123
+ Keep:
124
+
125
+ - Gentle, strange, lucid.
126
+ - Mobile-first morning use.
127
+ - Not therapy.
128
+
129
+ Change:
130
+
131
+ - Reduce customs clerk metaphor.
132
+ - Use "dream companion / question guide" framing.
133
+ - Keep playful language, but make the experience easier for ordinary users to understand.
134
+ - Anti-reference should include generic healing app output, not only plain Gradio/tarot/therapy.
135
+
136
+ ### 5. `DESIGN.md`
137
+
138
+ Update the north star from "Morning Customs Desk" to a softer Q&A interface.
139
+
140
+ Needed design changes:
141
+
142
+ - Primary screen is a conversational Q&A flow with a visible stepper: record, ask, interpret, tip.
143
+ - Right-side desktop panel becomes "interpretation draft / today tip preview", not "pact inspector".
144
+ - Mobile final screen should prioritize Today Tip first, then optional explanation.
145
+ - Keep high contrast, 8-12px radii, readable controls, and no purple AI SaaS theme.
146
+
147
+ ### 6. `docs/handoff.md`
148
+
149
+ Rewrite handoff as the source of truth for the refactor.
150
+
151
+ Needed edits:
152
+
153
+ - Mark current V4 customs flow as deprecated context.
154
+ - List the new Q&A flow and schema.
155
+ - Identify files likely to change:
156
+ - `dream_customs/schema.py`
157
+ - `dream_customs/prompts.py`
158
+ - `dream_customs/pipeline.py`
159
+ - `dream_customs/render.py`
160
+ - `dream_customs/ui/app.py`
161
+ - `dream_customs/ui/copy.py`
162
+ - `dream_customs/ui/styles.py`
163
+ - tests covering schema, prompts, pipeline, UI actions, and safety
164
+ - Preserve Modal/HF deployment constraints.
165
+
166
+ ### 7. `README.md`
167
+
168
+ Update public-facing positioning after implementation is ready.
169
+
170
+ Needed edits:
171
+
172
+ - Short description should shift from "pact" to "guided dream Q&A".
173
+ - Usage section should show:
174
+ - enter dream
175
+ - answer follow-up
176
+ - receive today tip
177
+ - Keep model and deployment details accurate.
178
+
179
+ ### 8. Existing execution plans
180
+
181
+ Do not rewrite old completed plan files as if they never happened.
182
+
183
+ Add a new implementation plan instead:
184
+
185
+ - `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md`
186
+
187
+ That plan should include code steps, tests, local Gradio verification, GitHub push, and HF Space sync/merge path.
188
+
189
+ ## Implementation Acceptance Targets
190
+
191
+ - Text-only path works with no image/audio.
192
+ - Image upload contributes visual clues to the Q&A.
193
+ - Voice transcript, when available, contributes to the same state.
194
+ - User can answer or skip follow-up questions.
195
+ - Final output has exactly one primary today tip.
196
+ - Final output includes a concrete dream anchor.
197
+ - Ordinary output avoids diagnosis, therapy claims, prophecy, and fear.
198
+ - Severe distress triggers support guidance.
199
+ - Mobile width remains readable.
200
+ - `python -m pytest -q` passes.
201
+ - Local Gradio smoke passes before Space sync.
202
+
docs/superpowers/plans/2026-06-08-dream-qa-refactor.md ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dream QA Refactor Implementation Plan
2
+
3
+ Last updated: 2026-06-08
4
+
5
+ ## Goal
6
+
7
+ Refactor the current Dream Customs Gradio app into Dream QA / 梦境问答台: a step-by-step dream interpretation app that records a dream, asks gentle follow-up questions, drafts a grounded interpretation, and ends with one Today Tip.
8
+
9
+ ## Source Of Truth
10
+
11
+ Read before implementation:
12
+
13
+ 1. `AGENTS.md`
14
+ 2. `docs/handoff.md`
15
+ 3. `docs/spec.md`
16
+ 4. `docs/prd.md`
17
+ 5. `PRODUCT.md`
18
+ 6. `DESIGN.md`
19
+ 7. `docs/superpowers/plans/2026-06-08-dream-qa-refactor-doc-plan.md`
20
+
21
+ Prototype references:
22
+
23
+ - `docs/prototypes/2026-06-08-dream-qa-mobile-flow.png`
24
+ - `docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png`
25
+ - `docs/prototypes/2026-06-08-dream-qa-tips-card.png`
26
+
27
+ ## Non-Goals
28
+
29
+ - Do not rebuild the whole repo from scratch.
30
+ - Do not change model family away from MiniCPM.
31
+ - Do not implement voice output.
32
+ - Do not build account, history, therapy plan, or diagnosis flows.
33
+ - Do not preserve permit/contraband/sealed pact as required user-facing fields.
34
+
35
+ ## Task 1: Schema And State
36
+
37
+ Files:
38
+
39
+ - `dream_customs/schema.py`
40
+ - `tests/test_schema.py`
41
+
42
+ Steps:
43
+
44
+ - Add or rename models for `DreamQuestionIntake`, `DreamQAState`, and `TodayTipCard`.
45
+ - Keep compatibility wrappers only if needed to avoid breaking intermediate UI code.
46
+ - Ensure final output has `dream_summary`, `main_question`, `dream_anchors`, `interpretation`, `today_tip`, `tiny_action`, `caring_note`, and `safety_note`.
47
+ - Add tests for missing modalities, skipped answers, and required dream anchors.
48
+
49
+ Verification:
50
+
51
+ ```bash
52
+ python -m pytest tests/test_schema.py -q
53
+ ```
54
+
55
+ ## Task 2: Prompts And Safety
56
+
57
+ Files:
58
+
59
+ - `dream_customs/prompts.py`
60
+ - `dream_customs/safety.py`
61
+ - relevant prompt/safety tests
62
+
63
+ Steps:
64
+
65
+ - Replace pact/permit prompt language with Q&A language.
66
+ - Add prompts for dream summary, main-question detection, follow-up questions, interpretation draft, and Today Tip.
67
+ - Require non-certain language: "maybe", "could treat it as", "for today, try".
68
+ - Require at least one dream anchor in final tips.
69
+ - Preserve severe distress support copy.
70
+
71
+ Verification:
72
+
73
+ ```bash
74
+ python -m pytest tests/test_safety.py tests/test_pipeline.py -q
75
+ ```
76
+
77
+ ## Task 3: Pipeline Refactor
78
+
79
+ Files:
80
+
81
+ - `dream_customs/pipeline.py`
82
+ - `dream_customs/models.py`
83
+ - pipeline/model tests
84
+
85
+ Steps:
86
+
87
+ - Route all modalities into `DreamQuestionIntake`.
88
+ - Add actions for record, answer, skip, ask another angle, interpret, and finish.
89
+ - Keep deterministic fake clients as the default fallback path.
90
+ - Repair generic model output so final tips cite dream anchors.
91
+ - Ensure hosted Modal/Ollama route failures fall back safely.
92
+
93
+ Verification:
94
+
95
+ ```bash
96
+ python -m pytest tests/test_pipeline.py tests/test_ollama_models.py -q
97
+ ```
98
+
99
+ ## Task 4: Rendering And UI Copy
100
+
101
+ Files:
102
+
103
+ - `dream_customs/render.py`
104
+ - `dream_customs/ui/copy.py`
105
+ - render/copy tests
106
+
107
+ Steps:
108
+
109
+ - Render Today Tip cards instead of pact cards.
110
+ - Prioritize one primary `today_tip`.
111
+ - Include optional tiny action and caring note.
112
+ - Keep safety note visible only when needed.
113
+ - Remove visible permit/contraband/seal copy from current UI copy.
114
+
115
+ Verification:
116
+
117
+ ```bash
118
+ python -m pytest tests/test_render.py tests/test_ui_actions.py -q
119
+ ```
120
+
121
+ ## Task 5: Gradio UI
122
+
123
+ Files:
124
+
125
+ - `dream_customs/ui/app.py`
126
+ - `dream_customs/ui/actions.py`
127
+ - `dream_customs/ui/styles.py`
128
+ - `app.py` only if needed
129
+
130
+ Steps:
131
+
132
+ - Build the visible flow around Record -> Ask -> Interpret -> Tip.
133
+ - Keep text, image, voice, mood, and primary action in one composer.
134
+ - Make follow-up questions answerable and skippable.
135
+ - Keep developer/model routes collapsed by default.
136
+ - Ensure 390px mobile readability.
137
+
138
+ Verification:
139
+
140
+ ```bash
141
+ python -m pytest tests/test_ui_actions.py -q
142
+ python app.py
143
+ ```
144
+
145
+ Manual smoke:
146
+
147
+ - Submit a text-only dream.
148
+ - Answer or skip one follow-up.
149
+ - Reach a Today Tip card.
150
+ - Confirm the tip references dream details.
151
+ - Confirm no diagnosis, prophecy, or frightening certainty appears.
152
+
153
+ ## Task 6: Docs And Deployment
154
+
155
+ Files:
156
+
157
+ - `README.md`
158
+ - `docs/handoff.md`
159
+ - `docs/smoke/<new-smoke-file>.md`
160
+
161
+ Steps:
162
+
163
+ - Update any implementation-specific docs after code lands.
164
+ - Record local pytest and Gradio smoke.
165
+ - Commit and push the feature branch.
166
+ - Sync to Hugging Face Space through the safest available path.
167
+
168
+ Verification:
169
+
170
+ ```bash
171
+ python -m pytest -q
172
+ git diff --check
173
+ git remote -v
174
+ ```
175
+
176
+ HF Space sync rules:
177
+
178
+ - Confirm `space` remote is `https://huggingface.co/spaces/build-small-hackathon/dream-customs`.
179
+ - Do not print or save any token.
180
+ - If direct push or API merge returns 403, create/leave a Space PR and report the exact blocker.
181
+ - Do not force overwrite public `main` without explicit user confirmation.
182
+
183
+ ## Acceptance Criteria
184
+
185
+ - Text-only path works with no image/audio.
186
+ - Image upload contributes visual clues to the Q&A.
187
+ - Voice transcript, when available, contributes to the same state.
188
+ - User can answer or skip follow-up questions.
189
+ - Final output has exactly one primary today tip.
190
+ - Final output includes a concrete dream anchor.
191
+ - Ordinary output avoids diagnosis, therapy claims, prophecy, and fear.
192
+ - Severe distress triggers support guidance.
193
+ - Mobile width remains readable.
194
+ - `python -m pytest -q` passes.
195
+ - Local Gradio smoke passes before Space sync.
docs/superpowers/plans/2026-06-08-model-led-pact-v2.md ADDED
@@ -0,0 +1,1357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Model-Led Pact V2 Implementation Plan
2
+
3
+ > Superseded on 2026-06-08: this plan records the earlier pact/card direction. New work should follow `docs/superpowers/plans/2026-06-08-dream-qa-refactor.md`, where the target is Record -> Ask -> Interpret -> Today Tip.
4
+
5
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
6
+
7
+ **Goal:** Make Dream Customs generate natural English demo cards by letting MiniCPM5-1B own briefing, drafting, and critique while MiniCPM-V-4.6 contributes richer visual witness evidence.
8
+
9
+ **Architecture:** Keep the public Hugging Face Space and Modal-hosted MiniCPM endpoints unchanged. Add a model-led generation path that builds a structured `DreamBrief`, drafts a `PactCard`, asks the text model to critique and rewrite weak output, and uses deterministic guards only for schema, safety, grammar, and fallback resilience. Upgrade vision output from flat clues to a witness report that still feeds the existing `DreamIntake.visual_clues` fallback path.
10
+
11
+ **Tech Stack:** Python, Pydantic, Gradio, pytest, MiniCPM5-1B hosted text route, MiniCPM-V-4.6 hosted vision route, existing Modal/HF Space deployment flow.
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ - Modify `dream_customs/schema.py`: add `VisionWitness`, `DreamBrief`, and `PactCritique` models without breaking existing `DreamIntake`, `PactCard`, or `CustomsSession` serialization.
18
+ - Modify `dream_customs/prompts.py`: add prompts for visual witness reports, dream briefs, pact drafts, pact critique, and pact rewrite.
19
+ - Modify `dream_customs/models.py`: add generic JSON generation methods and optional model-led methods on `FakeTextClient`, `OllamaTextClient`, `HostedMiniCPMTextClient`, `FakeVisionClient`, `OllamaVisionClient`, and `HostedMiniCPMVisionClient`.
20
+ - Modify `dream_customs/pipeline.py`: add the model-led pact flow, English grammar guards, and a fallback path that preserves the current text-only demo behavior.
21
+ - Modify `dream_customs/app_logic.py` and `dream_customs/ui/actions.py` only if needed to route richer debug data; do not add visible developer jargon to the public mobile flow.
22
+ - Create `tests/test_model_led_pact.py`: focused tests for brief, critique, rewrite, grammar cleanup, and the screenshot elevator regression.
23
+ - Create `tests/test_vision_witness.py`: focused tests for MiniCPM-V witness parsing and fallback clues.
24
+ - Create `tests/fixtures/demo_eval_cases.json`: ten English-demo quality cases used by a local evaluation script.
25
+ - Create `scripts/evaluate_demo_quality.py`: deterministic quality checks for the ten demo cases.
26
+ - Create `docs/smoke/2026-06-08-model-led-pact-v2-smoke.md`: record local and live Space acceptance evidence after implementation.
27
+
28
+ ## Task 0: Baseline And Branch Hygiene
29
+
30
+ **Files:**
31
+ - Read: `AGENTS.md`
32
+ - Read: `PRODUCT.md`
33
+ - Read: `docs/spec.md`
34
+ - Read: `docs/handoff.md`
35
+ - Read: `docs/superpowers/plans/2026-06-08-dream-customs-story-ux-polish.md`
36
+ - Verify: git branch and remotes
37
+
38
+ - [ ] **Step 1: Confirm repository and branch**
39
+
40
+ Run:
41
+
42
+ ```bash
43
+ pwd
44
+ git status --short --branch
45
+ git remote -v
46
+ ```
47
+
48
+ Expected:
49
+
50
+ ```text
51
+ /Users/junhaocheng/working-dir/ai-competitions/build-small-hackthon
52
+ ## feature/dream-customs-ui-voice-settings...origin/feature/dream-customs-ui-voice-settings
53
+ origin https://github.com/adjcjh777/dream-customs-build-small.git
54
+ space https://huggingface.co/spaces/build-small-hackathon/dream-customs
55
+ ```
56
+
57
+ - [ ] **Step 2: Sync latest code**
58
+
59
+ Run:
60
+
61
+ ```bash
62
+ git pull
63
+ ```
64
+
65
+ Expected:
66
+
67
+ ```text
68
+ Already up to date.
69
+ ```
70
+
71
+ - [ ] **Step 3: Run baseline tests**
72
+
73
+ Run:
74
+
75
+ ```bash
76
+ .venv/bin/python -m pytest -q
77
+ ```
78
+
79
+ Expected: all existing tests pass before the model-led changes begin.
80
+
81
+ ## Task 1: Add Model-Led Data Contracts
82
+
83
+ **Files:**
84
+ - Modify: `dream_customs/schema.py`
85
+ - Modify: `tests/test_schema.py`
86
+
87
+ - [ ] **Step 1: Add failing schema tests**
88
+
89
+ Append to `tests/test_schema.py`:
90
+
91
+ ```python
92
+ from dream_customs.schema import DreamBrief, PactCritique, VisionWitness
93
+
94
+
95
+ def test_vision_witness_flattens_report_into_demo_clues():
96
+ witness = VisionWitness(
97
+ scene_summary="A hand-drawn elevator panel is stuck on floor 14.",
98
+ objects=["elevator button", "wax"],
99
+ visible_text=["14"],
100
+ spatial_relations=["button below the frozen number"],
101
+ mood_cues=["stuck", "cold"],
102
+ uncertain_details=["whether the floor is a basement"],
103
+ surprising_detail="The buttons look melted rather than broken.",
104
+ )
105
+
106
+ clues = witness.to_visual_clues()
107
+
108
+ assert clues[0] == "Scene: A hand-drawn elevator panel is stuck on floor 14."
109
+ assert "Object: elevator button" in clues
110
+ assert "Visible text: 14" in clues
111
+ assert "Surprising detail: The buttons look melted rather than broken." in clues
112
+
113
+
114
+ def test_dream_brief_carries_evidence_and_demo_language():
115
+ brief = DreamBrief(
116
+ anchors=["elevator", "melted wax", "floor 14"],
117
+ emotional_hypothesis="The dream may be protecting a fear of getting stuck.",
118
+ today_bridge="Choose one stalled task and name the next small movement.",
119
+ visual_evidence=["Visible text: 14"],
120
+ safety_flags=[],
121
+ language="en",
122
+ )
123
+
124
+ assert brief.language == "en"
125
+ assert "floor 14" in brief.anchors
126
+ assert brief.visual_evidence == ["Visible text: 14"]
127
+
128
+
129
+ def test_pact_critique_flags_template_and_grammar_failures():
130
+ critique = PactCritique(
131
+ passes=False,
132
+ issues=["repeated article", "template fallback"],
133
+ rewrite_instruction="Rewrite in natural English using elevator, wax, and floor 14.",
134
+ )
135
+
136
+ assert not critique.passes
137
+ assert "repeated article" in critique.issues
138
+ assert "natural English" in critique.rewrite_instruction
139
+ ```
140
+
141
+ - [ ] **Step 2: Run schema tests and verify failure**
142
+
143
+ Run:
144
+
145
+ ```bash
146
+ .venv/bin/python -m pytest tests/test_schema.py::test_vision_witness_flattens_report_into_demo_clues tests/test_schema.py::test_dream_brief_carries_evidence_and_demo_language tests/test_schema.py::test_pact_critique_flags_template_and_grammar_failures -q
147
+ ```
148
+
149
+ Expected: FAIL with import errors for `VisionWitness`, `DreamBrief`, and `PactCritique`.
150
+
151
+ - [ ] **Step 3: Implement schema models**
152
+
153
+ Add to `dream_customs/schema.py` after `DreamIntake`:
154
+
155
+ ```python
156
+ class VisionWitness(BaseModel):
157
+ scene_summary: str = ""
158
+ objects: list[str] = Field(default_factory=list)
159
+ visible_text: list[str] = Field(default_factory=list)
160
+ spatial_relations: list[str] = Field(default_factory=list)
161
+ mood_cues: list[str] = Field(default_factory=list)
162
+ uncertain_details: list[str] = Field(default_factory=list)
163
+ surprising_detail: str = ""
164
+
165
+ def to_visual_clues(self) -> list[str]:
166
+ clues: list[str] = []
167
+ if self.scene_summary.strip():
168
+ clues.append(f"Scene: {self.scene_summary.strip()}")
169
+ for label, values in [
170
+ ("Object", self.objects),
171
+ ("Visible text", self.visible_text),
172
+ ("Spatial relation", self.spatial_relations),
173
+ ("Mood cue", self.mood_cues),
174
+ ("Uncertain detail", self.uncertain_details),
175
+ ]:
176
+ clues.extend(f"{label}: {value.strip()}" for value in values if value.strip())
177
+ if self.surprising_detail.strip():
178
+ clues.append(f"Surprising detail: {self.surprising_detail.strip()}")
179
+ return clues[:12]
180
+
181
+
182
+ class DreamBrief(BaseModel):
183
+ anchors: list[str] = Field(default_factory=list)
184
+ emotional_hypothesis: str = ""
185
+ today_bridge: str = ""
186
+ visual_evidence: list[str] = Field(default_factory=list)
187
+ safety_flags: list[str] = Field(default_factory=list)
188
+ language: str = "en"
189
+
190
+
191
+ class PactCritique(BaseModel):
192
+ passes: bool = True
193
+ issues: list[str] = Field(default_factory=list)
194
+ rewrite_instruction: str = ""
195
+ ```
196
+
197
+ - [ ] **Step 4: Run schema tests and verify pass**
198
+
199
+ Run:
200
+
201
+ ```bash
202
+ .venv/bin/python -m pytest tests/test_schema.py::test_vision_witness_flattens_report_into_demo_clues tests/test_schema.py::test_dream_brief_carries_evidence_and_demo_language tests/test_schema.py::test_pact_critique_flags_template_and_grammar_failures -q
203
+ ```
204
+
205
+ Expected: PASS.
206
+
207
+ - [ ] **Step 5: Commit schema contracts**
208
+
209
+ Run:
210
+
211
+ ```bash
212
+ git add dream_customs/schema.py tests/test_schema.py
213
+ git commit -m "feat: add model-led pact schemas"
214
+ ```
215
+
216
+ Expected: commit succeeds.
217
+
218
+ ## Task 2: Add English Demo Prompt Contracts
219
+
220
+ **Files:**
221
+ - Modify: `dream_customs/prompts.py`
222
+ - Create: `tests/test_model_led_pact.py`
223
+ - Create: `tests/test_vision_witness.py`
224
+
225
+ - [ ] **Step 1: Add failing prompt tests**
226
+
227
+ Create `tests/test_model_led_pact.py` with:
228
+
229
+ ```python
230
+ from dream_customs.pipeline import build_intake
231
+ from dream_customs.prompts import dream_brief_prompt, pact_critique_prompt, pact_draft_prompt, pact_rewrite_prompt
232
+ from dream_customs.schema import DreamBrief, PactCard, PactCritique
233
+
234
+
235
+ def test_dream_brief_prompt_requires_english_demo_brief():
236
+ intake = build_intake(
237
+ dream_text="I kept missing an elevator. The buttons melted like wax, and floor 14 froze.",
238
+ visual_clues=["Visible text: 14", "Object: melted button"],
239
+ mood="Uneasy",
240
+ )
241
+
242
+ prompt = dream_brief_prompt(intake)
243
+
244
+ assert "English demo" in prompt
245
+ assert "anchors" in prompt
246
+ assert "visual_evidence" in prompt
247
+ assert "Do not diagnose" in prompt
248
+
249
+
250
+ def test_pact_draft_prompt_uses_brief_not_template_fallback():
251
+ brief = DreamBrief(
252
+ anchors=["elevator", "melted wax", "floor 14"],
253
+ emotional_hypothesis="The dream may be protecting a fear of getting stuck.",
254
+ today_bridge="Choose one stalled task and name the next small movement.",
255
+ visual_evidence=["Visible text: 14"],
256
+ )
257
+
258
+ prompt = pact_draft_prompt(brief, "I want to stop freezing before a task.")
259
+
260
+ assert "Write natural English" in prompt
261
+ assert "Use at least two anchors" in prompt
262
+ assert "Do not use template phrases" in prompt
263
+ assert "elevator" in prompt
264
+
265
+
266
+ def test_pact_critique_prompt_checks_screenshot_regression():
267
+ card = PactCard(
268
+ visitor_name="An Elevator",
269
+ permit_id="DREAM20260608-015",
270
+ contraband=["melted buttons", "froze at 14"],
271
+ risk_level="medium: handle gently, without treating it as a warning sign",
272
+ alliance_reading="I am safe, but the wax is sticky and the floor is cold.",
273
+ practical_suggestion="Pick one real task that feels like the an elevator.",
274
+ weird_task="I will try to push the floor button with my hand instead of the lever.",
275
+ bedtime_release="Tonight, the an elevator and the the button are logged.",
276
+ )
277
+ brief = DreamBrief(anchors=["elevator", "melted wax", "floor 14"])
278
+
279
+ prompt = pact_critique_prompt(brief, card)
280
+
281
+ assert "the an" in prompt
282
+ assert "the the" in prompt
283
+ assert "invented details" in prompt
284
+ assert "natural English" in prompt
285
+
286
+
287
+ def test_pact_rewrite_prompt_uses_critique_instruction():
288
+ card = PactCard(
289
+ visitor_name="An Elevator",
290
+ permit_id="DREAM20260608-015",
291
+ contraband=["melted buttons", "froze at 14"],
292
+ risk_level="medium: handle gently, without treating it as a warning sign",
293
+ alliance_reading="I am safe, but the wax is sticky and the floor is cold.",
294
+ practical_suggestion="Pick one real task that feels like the an elevator.",
295
+ weird_task="I will try to push the floor button with my hand instead of the lever.",
296
+ bedtime_release="Tonight, the an elevator and the the button are logged.",
297
+ )
298
+ brief = DreamBrief(anchors=["elevator", "melted wax", "floor 14"])
299
+ critique = PactCritique(
300
+ passes=False,
301
+ issues=["repeated article", "invented lever"],
302
+ rewrite_instruction="Rewrite without repeated articles or invented lever details.",
303
+ )
304
+
305
+ prompt = pact_rewrite_prompt(brief, card, critique)
306
+
307
+ assert "Rewrite without repeated articles" in prompt
308
+ assert "Return strict JSON" in prompt
309
+ assert "floor 14" in prompt
310
+ ```
311
+
312
+ Create `tests/test_vision_witness.py` with:
313
+
314
+ ```python
315
+ from dream_customs.prompts import visual_witness_prompt
316
+
317
+
318
+ def test_visual_witness_prompt_requests_structured_report():
319
+ prompt = visual_witness_prompt()
320
+
321
+ assert "MiniCPM-V-4.6" in prompt
322
+ assert "scene_summary" in prompt
323
+ assert "spatial_relations" in prompt
324
+ assert "surprising_detail" in prompt
325
+ assert "Do not diagnose" in prompt
326
+ ```
327
+
328
+ - [ ] **Step 2: Run prompt tests and verify failure**
329
+
330
+ Run:
331
+
332
+ ```bash
333
+ .venv/bin/python -m pytest tests/test_model_led_pact.py tests/test_vision_witness.py -q
334
+ ```
335
+
336
+ Expected: FAIL with import errors for the new prompt functions.
337
+
338
+ - [ ] **Step 3: Implement new prompt functions**
339
+
340
+ Add to `dream_customs/prompts.py`:
341
+
342
+ ```python
343
+ from dream_customs.schema import DreamBrief, PactCard, PactCritique
344
+
345
+
346
+ def visual_witness_prompt() -> str:
347
+ return """
348
+ You are MiniCPM-V-4.6 acting as the witness clerk at Dream Customs.
349
+ Read the image as dream evidence: a sketch, bedside note, screenshot, or photo.
350
+ Do not diagnose the user. Do not infer a fixed symbolic meaning.
351
+ Return strict JSON with:
352
+ scene_summary, objects, visible_text, spatial_relations, mood_cues,
353
+ uncertain_details, surprising_detail.
354
+ Keep each list item short, concrete, and visibly grounded in the image.
355
+ """.strip()
356
+
357
+
358
+ def dream_brief_prompt(intake: DreamIntake) -> str:
359
+ return f"""
360
+ You are MiniCPM5-1B preparing an English demo brief for Dream Customs.
361
+ Do not diagnose. Do not claim the dream has one certain meaning.
362
+ Turn the intake into a compact reasoning brief that a later writer can use.
363
+ Return strict JSON with:
364
+ anchors, emotional_hypothesis, today_bridge, visual_evidence, safety_flags, language.
365
+
366
+ Rules:
367
+ - language must be "en".
368
+ - anchors must be concrete dream details, not generic feelings.
369
+ - visual_evidence must reuse image clues when present.
370
+ - today_bridge must connect the dream to one realistic action for today.
371
+ - safety_flags is empty unless the user mentions self-harm, harming others,
372
+ severe distress, severe insomnia, panic, or inability to function.
373
+
374
+ Dream intake:
375
+ {intake.merged_text()}
376
+ """.strip()
377
+
378
+
379
+ def pact_draft_prompt(brief: DreamBrief, answers: str) -> str:
380
+ return f"""
381
+ You are the Dream Customs diplomat writing an English demo clearance pass.
382
+ Write natural English. Keep the tone gentle, playful, and useful.
383
+ Do not use template phrases such as "feels like the", "logged and cleared",
384
+ or "the dream may be pointing to" unless they fit naturally.
385
+ Use at least two anchors from the brief. Use at least one visual_evidence item
386
+ when visual evidence is present. Do not invent objects or actions.
387
+ Return strict JSON with:
388
+ visitor_name, permit_id, contraband, risk_level, alliance_reading,
389
+ practical_suggestion, weird_task, bedtime_release, safety_note.
390
+
391
+ Dream brief:
392
+ {brief.model_dump_json()}
393
+
394
+ User answers:
395
+ {answers or "No answers filed."}
396
+ """.strip()
397
+
398
+
399
+ def pact_critique_prompt(brief: DreamBrief, card: PactCard) -> str:
400
+ return f"""
401
+ You are the Dream Customs quality critic for an English demo card.
402
+ Check for repeated articles such as "the an" and "the the", awkward grammar,
403
+ template fallback language, invented details, diagnosis, frightening certainty,
404
+ generic wellness advice, and missing dream anchors.
405
+ Return strict JSON with:
406
+ passes, issues, rewrite_instruction.
407
+ If the card is already natural, specific, safe, and grounded, passes must be true
408
+ and rewrite_instruction must be an empty string.
409
+
410
+ Dream brief:
411
+ {brief.model_dump_json()}
412
+
413
+ Draft card:
414
+ {card.model_dump_json()}
415
+ """.strip()
416
+
417
+
418
+ def pact_rewrite_prompt(brief: DreamBrief, card: PactCard, critique: PactCritique) -> str:
419
+ return f"""
420
+ You are the Dream Customs diplomat revising an English demo clearance pass.
421
+ Follow the critique exactly while preserving safe, playful, non-diagnostic tone.
422
+ Use at least two real anchors from the brief. Do not add objects that are not in
423
+ the brief or current card. Return strict JSON with:
424
+ visitor_name, permit_id, contraband, risk_level, alliance_reading,
425
+ practical_suggestion, weird_task, bedtime_release, safety_note.
426
+
427
+ Dream brief:
428
+ {brief.model_dump_json()}
429
+
430
+ Current card:
431
+ {card.model_dump_json()}
432
+
433
+ Critique:
434
+ {critique.model_dump_json()}
435
+ """.strip()
436
+ ```
437
+
438
+ - [ ] **Step 4: Run prompt tests and verify pass**
439
+
440
+ Run:
441
+
442
+ ```bash
443
+ .venv/bin/python -m pytest tests/test_model_led_pact.py tests/test_vision_witness.py -q
444
+ ```
445
+
446
+ Expected: PASS.
447
+
448
+ - [ ] **Step 5: Commit prompt contracts**
449
+
450
+ Run:
451
+
452
+ ```bash
453
+ git add dream_customs/prompts.py tests/test_model_led_pact.py tests/test_vision_witness.py
454
+ git commit -m "feat: add model-led pact prompts"
455
+ ```
456
+
457
+ Expected: commit succeeds.
458
+
459
+ ## Task 3: Add Model-Led Client Methods
460
+
461
+ **Files:**
462
+ - Modify: `dream_customs/models.py`
463
+ - Modify: `tests/test_ollama_models.py`
464
+ - Modify: `tests/test_model_led_pact.py`
465
+ - Modify: `tests/test_vision_witness.py`
466
+
467
+ - [ ] **Step 1: Add failing client tests**
468
+
469
+ Append to `tests/test_model_led_pact.py`:
470
+
471
+ ```python
472
+ from dream_customs.models import FakeTextClient
473
+
474
+
475
+ def test_fake_text_client_supports_model_led_methods():
476
+ client = FakeTextClient()
477
+
478
+ brief = client.generate_brief("brief prompt")
479
+ card = client.generate_pact_draft("draft prompt")
480
+ critique = client.critique_pact("critique prompt")
481
+
482
+ assert brief.language == "en"
483
+ assert brief.anchors
484
+ assert card.visitor_name
485
+ assert critique.passes
486
+ ```
487
+
488
+ Append to `tests/test_vision_witness.py`:
489
+
490
+ ```python
491
+ from dream_customs.models import FakeVisionClient
492
+
493
+
494
+ def test_fake_vision_client_returns_witness_report():
495
+ witness = FakeVisionClient().extract_witness("demo.png")
496
+
497
+ assert witness.scene_summary
498
+ assert "blue hallway" in " ".join(witness.to_visual_clues()).lower()
499
+ ```
500
+
501
+ Append to `tests/test_ollama_models.py`:
502
+
503
+ ```python
504
+ from dream_customs.schema import DreamBrief, PactCritique, VisionWitness
505
+
506
+
507
+ def test_hosted_text_client_parses_model_led_brief():
508
+ class StubHostedBriefClient(HostedMiniCPMTextClient):
509
+ def _post_json(self, prompt, max_tokens=700):
510
+ return {
511
+ "response": (
512
+ '{"anchors":["elevator","melted wax","floor 14"],'
513
+ '"emotional_hypothesis":"The dream may be protecting a stuck feeling.",'
514
+ '"today_bridge":"Name one next movement.",'
515
+ '"visual_evidence":["Visible text: 14"],'
516
+ '"safety_flags":[],"language":"en"}'
517
+ )
518
+ }
519
+
520
+ brief = StubHostedBriefClient(endpoint="https://example.test").generate_brief("prompt")
521
+
522
+ assert isinstance(brief, DreamBrief)
523
+ assert brief.anchors == ["elevator", "melted wax", "floor 14"]
524
+
525
+
526
+ def test_hosted_text_client_parses_pact_critique():
527
+ class StubHostedCritiqueClient(HostedMiniCPMTextClient):
528
+ def _post_json(self, prompt, max_tokens=700):
529
+ return {
530
+ "response": (
531
+ '{"passes":false,'
532
+ '"issues":["repeated article"],'
533
+ '"rewrite_instruction":"Fix repeated articles."}'
534
+ )
535
+ }
536
+
537
+ critique = StubHostedCritiqueClient(endpoint="https://example.test").critique_pact("prompt")
538
+
539
+ assert isinstance(critique, PactCritique)
540
+ assert not critique.passes
541
+
542
+
543
+ def test_hosted_vision_client_parses_witness_report():
544
+ class StubHostedVisionWitnessClient(HostedMiniCPMVisionClient):
545
+ def _post_image(self, image_path):
546
+ return {
547
+ "response": (
548
+ '{"scene_summary":"A blue hallway with a frozen elevator panel.",'
549
+ '"objects":["elevator button"],'
550
+ '"visible_text":["14"],'
551
+ '"spatial_relations":["button below number"],'
552
+ '"mood_cues":["cold"],'
553
+ '"uncertain_details":[],'
554
+ '"surprising_detail":"The button looks waxy."}'
555
+ )
556
+ }
557
+
558
+ witness = StubHostedVisionWitnessClient(endpoint="https://example.test").extract_witness("demo.png")
559
+
560
+ assert isinstance(witness, VisionWitness)
561
+ assert "Visible text: 14" in witness.to_visual_clues()
562
+ ```
563
+
564
+ - [ ] **Step 2: Run client tests and verify failure**
565
+
566
+ Run:
567
+
568
+ ```bash
569
+ .venv/bin/python -m pytest tests/test_model_led_pact.py::test_fake_text_client_supports_model_led_methods tests/test_vision_witness.py::test_fake_vision_client_returns_witness_report tests/test_ollama_models.py::test_hosted_text_client_parses_model_led_brief tests/test_ollama_models.py::test_hosted_text_client_parses_pact_critique tests/test_ollama_models.py::test_hosted_vision_client_parses_witness_report -q
570
+ ```
571
+
572
+ Expected: FAIL because the client methods are not implemented.
573
+
574
+ - [ ] **Step 3: Implement fake client methods**
575
+
576
+ In `dream_customs/models.py`, import the new schemas:
577
+
578
+ ```python
579
+ from dream_customs.schema import DreamBrief, PactCard, PactCritique, VisionWitness
580
+ ```
581
+
582
+ Add to `FakeTextClient`:
583
+
584
+ ```python
585
+ def generate_brief(self, prompt: str) -> DreamBrief:
586
+ return DreamBrief(
587
+ anchors=["elevator", "melted buttons", "floor 14"],
588
+ emotional_hypothesis="The dream may be protecting the user from freezing at the start of a task.",
589
+ today_bridge="Choose one stalled task and name the next small movement.",
590
+ visual_evidence=["Visible text: 14"],
591
+ safety_flags=[],
592
+ language="en",
593
+ )
594
+
595
+ def generate_pact_draft(self, prompt: str) -> PactCard:
596
+ return self.generate_pact(prompt)
597
+
598
+ def critique_pact(self, prompt: str) -> PactCritique:
599
+ return PactCritique(passes=True, issues=[], rewrite_instruction="")
600
+
601
+ def rewrite_pact(self, prompt: str) -> PactCard:
602
+ return self.generate_pact(prompt)
603
+ ```
604
+
605
+ Add to `FakeVisionClient`:
606
+
607
+ ```python
608
+ def extract_witness(self, image_path: Optional[str]) -> VisionWitness:
609
+ if not image_path:
610
+ return VisionWitness()
611
+ return VisionWitness(
612
+ scene_summary="A blue hallway with a melted elevator button.",
613
+ objects=["elevator button", "blue hallway"],
614
+ visible_text=["14"],
615
+ spatial_relations=["button near the frozen floor number"],
616
+ mood_cues=["stuck", "uncertain"],
617
+ uncertain_details=[],
618
+ surprising_detail="The button looks soft, almost waxy.",
619
+ )
620
+ ```
621
+
622
+ - [ ] **Step 4: Implement hosted and Ollama model-led text parsing**
623
+
624
+ Add helper methods to `HostedMiniCPMTextClient` and mirror the same signatures in `OllamaTextClient`:
625
+
626
+ ```python
627
+ def generate_brief(self, prompt: str) -> DreamBrief:
628
+ parsed = self._generate_json(
629
+ prompt,
630
+ (
631
+ '{"anchors":["string"],"emotional_hypothesis":"string",'
632
+ '"today_bridge":"string","visual_evidence":["string"],'
633
+ '"safety_flags":["string"],"language":"en"}'
634
+ ),
635
+ max_tokens=520,
636
+ )
637
+ if not parsed:
638
+ return self.fallback.generate_brief(prompt)
639
+ try:
640
+ return DreamBrief(
641
+ anchors=_as_string_list(parsed.get("anchors")),
642
+ emotional_hypothesis=str(parsed.get("emotional_hypothesis", "")).strip(),
643
+ today_bridge=str(parsed.get("today_bridge", "")).strip(),
644
+ visual_evidence=_as_string_list(parsed.get("visual_evidence")),
645
+ safety_flags=_as_string_list(parsed.get("safety_flags")),
646
+ language=str(parsed.get("language", "en")).strip() or "en",
647
+ )
648
+ except (TypeError, ValueError):
649
+ return self.fallback.generate_brief(prompt)
650
+
651
+ def generate_pact_draft(self, prompt: str) -> PactCard:
652
+ return self.generate_pact(prompt)
653
+
654
+ def critique_pact(self, prompt: str) -> PactCritique:
655
+ parsed = self._generate_json(
656
+ prompt,
657
+ '{"passes":true,"issues":["string"],"rewrite_instruction":"string"}',
658
+ max_tokens=360,
659
+ )
660
+ if not parsed:
661
+ return self.fallback.critique_pact(prompt)
662
+ return PactCritique(
663
+ passes=bool(parsed.get("passes", True)),
664
+ issues=_as_string_list(parsed.get("issues")),
665
+ rewrite_instruction=str(parsed.get("rewrite_instruction", "")).strip(),
666
+ )
667
+
668
+ def rewrite_pact(self, prompt: str) -> PactCard:
669
+ return self.generate_pact(prompt)
670
+ ```
671
+
672
+ For `OllamaTextClient`, use the same body but pass `num_predict` instead of `max_tokens` in `_generate_json()` calls.
673
+
674
+ - [ ] **Step 5: Implement hosted and Ollama vision witness parsing**
675
+
676
+ Add to `HostedMiniCPMVisionClient` and mirror the same method in `OllamaVisionClient`:
677
+
678
+ ```python
679
+ def extract_witness(self, image_path: Optional[str]) -> VisionWitness:
680
+ if not image_path:
681
+ return VisionWitness()
682
+ payload = self._post_image(image_path)
683
+ if not payload:
684
+ return self.fallback.extract_witness(image_path)
685
+ parsed = _extract_json_object(_hosted_text_from_response(payload))
686
+ if not parsed:
687
+ text = _hosted_text_from_response(payload)
688
+ clues = [part.strip() for part in re.split(r"[,,\n]", _strip_markdown_and_thinking(text)) if part.strip()]
689
+ return VisionWitness(scene_summary="; ".join(clues[:2]), objects=clues[2:6])
690
+ return VisionWitness(
691
+ scene_summary=str(parsed.get("scene_summary", "")).strip(),
692
+ objects=_as_string_list(parsed.get("objects")),
693
+ visible_text=_as_string_list(parsed.get("visible_text")),
694
+ spatial_relations=_as_string_list(parsed.get("spatial_relations")),
695
+ mood_cues=_as_string_list(parsed.get("mood_cues")),
696
+ uncertain_details=_as_string_list(parsed.get("uncertain_details")),
697
+ surprising_detail=str(parsed.get("surprising_detail", "")).strip(),
698
+ )
699
+ ```
700
+
701
+ Update `extract_clues()` to call `extract_witness(image_path).to_visual_clues()` first, then fall back to the old flat parsing if the witness is empty.
702
+
703
+ - [ ] **Step 6: Run client tests and verify pass**
704
+
705
+ Run:
706
+
707
+ ```bash
708
+ .venv/bin/python -m pytest tests/test_model_led_pact.py::test_fake_text_client_supports_model_led_methods tests/test_vision_witness.py::test_fake_vision_client_returns_witness_report tests/test_ollama_models.py::test_hosted_text_client_parses_model_led_brief tests/test_ollama_models.py::test_hosted_text_client_parses_pact_critique tests/test_ollama_models.py::test_hosted_vision_client_parses_witness_report -q
709
+ ```
710
+
711
+ Expected: PASS.
712
+
713
+ - [ ] **Step 7: Commit client methods**
714
+
715
+ Run:
716
+
717
+ ```bash
718
+ git add dream_customs/models.py tests/test_model_led_pact.py tests/test_vision_witness.py tests/test_ollama_models.py
719
+ git commit -m "feat: add model-led MiniCPM clients"
720
+ ```
721
+
722
+ Expected: commit succeeds.
723
+
724
+ ## Task 4: Route The Model-Led Pact Flow
725
+
726
+ **Files:**
727
+ - Modify: `dream_customs/pipeline.py`
728
+ - Modify: `tests/test_model_led_pact.py`
729
+ - Modify: `tests/test_pipeline.py`
730
+
731
+ - [ ] **Step 1: Add failing model-led flow regression**
732
+
733
+ Append to `tests/test_model_led_pact.py`:
734
+
735
+ ```python
736
+ from dream_customs.pipeline import build_intake, generate_model_led_pact
737
+
738
+
739
+ def test_model_led_pact_rewrites_screenshot_elevator_regression():
740
+ class CriticTextClient(FakeTextClient):
741
+ def generate_pact_draft(self, prompt):
742
+ return PactCard(
743
+ visitor_name="An Elevator",
744
+ permit_id="DREAM20260608-015",
745
+ contraband=["melted buttons", "froze at 14"],
746
+ risk_level="medium: handle gently, without treating it as a warning sign",
747
+ alliance_reading="I am safe, but the wax is sticky and the floor is cold.",
748
+ practical_suggestion="Pick one real task that feels like the an elevator.",
749
+ weird_task="I will try to push the floor button with my hand instead of the lever.",
750
+ bedtime_release="Tonight, the an elevator and the the button are logged.",
751
+ )
752
+
753
+ def critique_pact(self, prompt):
754
+ return PactCritique(
755
+ passes=False,
756
+ issues=["repeated article", "invented lever"],
757
+ rewrite_instruction="Rewrite without repeated articles or the invented lever.",
758
+ )
759
+
760
+ def rewrite_pact(self, prompt):
761
+ return PactCard(
762
+ visitor_name="The Elevator Stuck at 14",
763
+ permit_id="DREAM20260608-015",
764
+ contraband=["melted buttons", "floor 14", "the urge to freeze before starting"],
765
+ risk_level="medium: handle gently, without treating it as a warning sign",
766
+ alliance_reading=(
767
+ "The elevator, wax-soft buttons, and frozen 14 can be treated as a small scene "
768
+ "about getting stuck before the first move."
769
+ ),
770
+ practical_suggestion="Choose one stalled task and write only the next button-sized action.",
771
+ weird_task="Draw floor 14 on a sticky note, tap it once, and spend five minutes on the first step.",
772
+ bedtime_release="Tonight, the elevator can stay on floor 14 while tomorrow's first button waits quietly.",
773
+ )
774
+
775
+ intake = build_intake(
776
+ dream_text="I kept missing an elevator. The buttons melted like wax, and the floor number froze at 14.",
777
+ visual_clues=["Visible text: 14", "Object: melted elevator button"],
778
+ mood="Uneasy",
779
+ )
780
+
781
+ card, html = generate_model_led_pact(intake, "", CriticTextClient())
782
+ joined = card.to_plain_text().lower()
783
+
784
+ assert "the an" not in joined
785
+ assert "the the" not in joined
786
+ assert "lever" not in joined
787
+ assert "floor 14" in joined
788
+ assert "melted" in joined or "wax" in joined
789
+ assert "Today's Clearance Pass" in html or "Today's Pact" in html
790
+ ```
791
+
792
+ - [ ] **Step 2: Run model-led flow test and verify failure**
793
+
794
+ Run:
795
+
796
+ ```bash
797
+ .venv/bin/python -m pytest tests/test_model_led_pact.py::test_model_led_pact_rewrites_screenshot_elevator_regression -q
798
+ ```
799
+
800
+ Expected: FAIL because `generate_model_led_pact` does not exist.
801
+
802
+ - [ ] **Step 3: Implement grammar guard helpers**
803
+
804
+ Add to `dream_customs/pipeline.py`:
805
+
806
+ ```python
807
+ def _clean_repeated_articles(text: str) -> str:
808
+ clean = re.sub(r"\bthe\s+an\s+", "an ", text, flags=re.IGNORECASE)
809
+ clean = re.sub(r"\bthe\s+the\s+", "the ", clean, flags=re.IGNORECASE)
810
+ clean = re.sub(r"\ban\s+an\s+", "an ", clean, flags=re.IGNORECASE)
811
+ clean = re.sub(r"\ba\s+a\s+", "a ", clean, flags=re.IGNORECASE)
812
+ return re.sub(r"\s+", " ", clean).strip()
813
+
814
+
815
+ def _clean_card_grammar(card: PactCard) -> PactCard:
816
+ cleaned = card.model_copy(deep=True)
817
+ cleaned.visitor_name = _clean_repeated_articles(cleaned.visitor_name)
818
+ cleaned.risk_level = _clean_repeated_articles(cleaned.risk_level)
819
+ cleaned.alliance_reading = _clean_repeated_articles(cleaned.alliance_reading)
820
+ cleaned.practical_suggestion = _clean_repeated_articles(cleaned.practical_suggestion)
821
+ cleaned.weird_task = _clean_repeated_articles(cleaned.weird_task)
822
+ cleaned.bedtime_release = _clean_repeated_articles(cleaned.bedtime_release)
823
+ cleaned.contraband = [_clean_repeated_articles(item) for item in cleaned.contraband]
824
+ return cleaned
825
+ ```
826
+
827
+ - [ ] **Step 4: Implement `generate_model_led_pact`**
828
+
829
+ Add imports to `dream_customs/pipeline.py`:
830
+
831
+ ```python
832
+ from dream_customs.prompts import (
833
+ dream_brief_prompt,
834
+ followup_question_prompt,
835
+ negotiation_prompt,
836
+ pact_critique_prompt,
837
+ pact_draft_prompt,
838
+ pact_prompt,
839
+ pact_revision_prompt,
840
+ pact_rewrite_prompt,
841
+ )
842
+ ```
843
+
844
+ Add the function:
845
+
846
+ ```python
847
+ def generate_model_led_pact(intake: DreamIntake, answers: str, text_client) -> Tuple[PactCard, str]:
848
+ brief = text_client.generate_brief(dream_brief_prompt(intake))
849
+ card = text_client.generate_pact_draft(pact_draft_prompt(brief, answers))
850
+ critique = text_client.critique_pact(pact_critique_prompt(brief, card))
851
+ if not critique.passes and critique.rewrite_instruction.strip():
852
+ card = text_client.rewrite_pact(pact_rewrite_prompt(brief, card, critique))
853
+ card = _clean_card_grammar(card)
854
+ card = _polish_card_for_daily_use(card, intake, answers)
855
+ merged = intake.merged_text() + "\n" + answers
856
+ if needs_escalation(merged):
857
+ card.safety_note = safety_note()
858
+ elif not needs_escalation(merged):
859
+ card.safety_note = ""
860
+ card = _stamp_card_for_today(card)
861
+ return card, render_pact_card(card)
862
+ ```
863
+
864
+ - [ ] **Step 5: Route draft generation through model-led flow with fallback**
865
+
866
+ Find the existing `draft_pact()` code path in `dream_customs/pipeline.py`. Replace its direct `generate_pact()` call with:
867
+
868
+ ```python
869
+ try:
870
+ card, _html = generate_model_led_pact(next_session.intake, next_session.answers_text(), text_client)
871
+ except AttributeError:
872
+ card, _html = generate_pact(next_session.intake, next_session.answers_text(), text_client)
873
+ ```
874
+
875
+ Keep the existing session phase, events, and safety behavior unchanged around this block.
876
+
877
+ - [ ] **Step 6: Run focused flow tests**
878
+
879
+ Run:
880
+
881
+ ```bash
882
+ .venv/bin/python -m pytest tests/test_model_led_pact.py::test_model_led_pact_rewrites_screenshot_elevator_regression tests/test_pipeline.py::test_ask_answer_skip_draft_revise_and_seal_actions -q
883
+ ```
884
+
885
+ Expected: PASS.
886
+
887
+ - [ ] **Step 7: Run all pipeline tests**
888
+
889
+ Run:
890
+
891
+ ```bash
892
+ .venv/bin/python -m pytest tests/test_pipeline.py tests/test_model_led_pact.py -q
893
+ ```
894
+
895
+ Expected: PASS.
896
+
897
+ - [ ] **Step 8: Commit model-led flow**
898
+
899
+ Run:
900
+
901
+ ```bash
902
+ git add dream_customs/pipeline.py tests/test_model_led_pact.py tests/test_pipeline.py
903
+ git commit -m "feat: route model-led pact generation"
904
+ ```
905
+
906
+ Expected: commit succeeds.
907
+
908
+ ## Task 5: Upgrade Vision Intake To Witness Reports
909
+
910
+ **Files:**
911
+ - Modify: `dream_customs/pipeline.py`
912
+ - Modify: `dream_customs/models.py`
913
+ - Modify: `tests/test_vision_witness.py`
914
+ - Modify: `tests/test_pipeline.py`
915
+
916
+ - [ ] **Step 1: Add failing intake witness test**
917
+
918
+ Append to `tests/test_vision_witness.py`:
919
+
920
+ ```python
921
+ from dream_customs.models import FakeASRClient, FakeVisionClient
922
+ from dream_customs.pipeline import intake_from_modalities
923
+
924
+
925
+ def test_intake_from_modalities_uses_vision_witness_clues():
926
+ intake = intake_from_modalities(
927
+ dream_text="I was waiting for an elevator.",
928
+ image_path="demo.png",
929
+ audio_path=None,
930
+ mood="Uneasy",
931
+ vision_client=FakeVisionClient(),
932
+ asr_client=FakeASRClient(),
933
+ )
934
+
935
+ merged = intake.merged_text().lower()
936
+
937
+ assert "scene:" in merged
938
+ assert "visible text: 14" in merged
939
+ assert "surprising detail" in merged
940
+ ```
941
+
942
+ - [ ] **Step 2: Run witness intake test and verify failure**
943
+
944
+ Run:
945
+
946
+ ```bash
947
+ .venv/bin/python -m pytest tests/test_vision_witness.py::test_intake_from_modalities_uses_vision_witness_clues -q
948
+ ```
949
+
950
+ Expected: FAIL if `intake_from_modalities()` still calls only `extract_clues()`.
951
+
952
+ - [ ] **Step 3: Add witness extraction helper**
953
+
954
+ Add to `dream_customs/pipeline.py`:
955
+
956
+ ```python
957
+ def _extract_visual_clues(vision_client, image_path: Optional[str]) -> List[str]:
958
+ if not image_path:
959
+ return []
960
+ try:
961
+ if hasattr(vision_client, "extract_witness"):
962
+ witness = vision_client.extract_witness(image_path)
963
+ clues = witness.to_visual_clues()
964
+ if clues:
965
+ return clues
966
+ except Exception:
967
+ pass
968
+ try:
969
+ return vision_client.extract_clues(image_path)
970
+ except Exception:
971
+ return []
972
+ ```
973
+
974
+ Update `intake_from_modalities()`:
975
+
976
+ ```python
977
+ return build_intake(
978
+ dream_text=dream_text or "",
979
+ voice_transcript=asr_client.transcribe(audio_path),
980
+ visual_clues=_extract_visual_clues(vision_client, image_path),
981
+ mood=mood or "",
982
+ user_context=user_context,
983
+ )
984
+ ```
985
+
986
+ - [ ] **Step 4: Keep text path alive when witness extraction fails**
987
+
988
+ Append to `tests/test_pipeline.py`:
989
+
990
+ ```python
991
+ def test_witness_failure_keeps_text_path_alive():
992
+ class BrokenWitnessVision:
993
+ def extract_witness(self, image_path):
994
+ raise RuntimeError("vision offline")
995
+
996
+ def extract_clues(self, image_path):
997
+ return ["fallback clue"]
998
+
999
+ intake = intake_from_modalities(
1000
+ dream_text="Text still works.",
1001
+ image_path="demo.png",
1002
+ audio_path=None,
1003
+ mood="Foggy",
1004
+ vision_client=BrokenWitnessVision(),
1005
+ asr_client=FakeASRClient(),
1006
+ )
1007
+
1008
+ assert "Text still works." in intake.merged_text()
1009
+ assert "fallback clue" in intake.merged_text()
1010
+ ```
1011
+
1012
+ - [ ] **Step 5: Run vision tests**
1013
+
1014
+ Run:
1015
+
1016
+ ```bash
1017
+ .venv/bin/python -m pytest tests/test_vision_witness.py tests/test_pipeline.py::test_witness_failure_keeps_text_path_alive -q
1018
+ ```
1019
+
1020
+ Expected: PASS.
1021
+
1022
+ - [ ] **Step 6: Commit witness routing**
1023
+
1024
+ Run:
1025
+
1026
+ ```bash
1027
+ git add dream_customs/pipeline.py dream_customs/models.py tests/test_vision_witness.py tests/test_pipeline.py
1028
+ git commit -m "feat: use MiniCPM vision witness reports"
1029
+ ```
1030
+
1031
+ Expected: commit succeeds.
1032
+
1033
+ ## Task 6: Add English Demo Quality Evaluation
1034
+
1035
+ **Files:**
1036
+ - Create: `tests/fixtures/demo_eval_cases.json`
1037
+ - Create: `scripts/evaluate_demo_quality.py`
1038
+ - Create: `tests/test_demo_quality_eval.py`
1039
+
1040
+ - [ ] **Step 1: Add demo fixture cases**
1041
+
1042
+ Create `tests/fixtures/demo_eval_cases.json`:
1043
+
1044
+ ```json
1045
+ [
1046
+ {
1047
+ "id": "elevator_wax_floor14",
1048
+ "dream_text": "I kept missing an elevator. The buttons melted like wax, and the floor number froze at 14.",
1049
+ "visual_clues": ["Visible text: 14", "Object: melted elevator button"],
1050
+ "mood": "Uneasy",
1051
+ "required_terms": ["elevator", "14"],
1052
+ "banned_terms": ["the an", "the the", "lever", "diagnosis"]
1053
+ },
1054
+ {
1055
+ "id": "customs_wet_paper",
1056
+ "dream_text": "I was at a customs window carrying a suitcase full of wet paper. The clerk asked me to declare every unfinished promise before sunrise.",
1057
+ "visual_clues": [],
1058
+ "mood": "Foggy",
1059
+ "required_terms": ["customs", "paper"],
1060
+ "banned_terms": ["hydrate", "fruit", "prophecy", "diagnosis"]
1061
+ },
1062
+ {
1063
+ "id": "exam_without_pencil",
1064
+ "dream_text": "I arrived at an exam with no pencil, but everyone else was already writing.",
1065
+ "visual_clues": [],
1066
+ "mood": "Restless",
1067
+ "required_terms": ["exam", "pencil"],
1068
+ "banned_terms": ["medical", "fate", "the an"]
1069
+ },
1070
+ {
1071
+ "id": "locked_green_room",
1072
+ "dream_text": "I found a small green room behind my kitchen, but the door kept locking itself.",
1073
+ "visual_clues": ["Color: green", "Object: small door"],
1074
+ "mood": "Curious",
1075
+ "required_terms": ["green", "door"],
1076
+ "banned_terms": ["diagnosis", "warning sign", "the the"]
1077
+ },
1078
+ {
1079
+ "id": "train_arrives_silent",
1080
+ "dream_text": "A silent train arrived with my name written on a ticket, but I could not read the destination.",
1081
+ "visual_clues": ["Visible text: user name on ticket"],
1082
+ "mood": "Uneasy",
1083
+ "required_terms": ["train", "ticket"],
1084
+ "banned_terms": ["destiny", "prophecy", "the an"]
1085
+ },
1086
+ {
1087
+ "id": "phone_full_of_water",
1088
+ "dream_text": "My phone was full of water, but it kept ringing from inside a drawer.",
1089
+ "visual_clues": [],
1090
+ "mood": "Tired",
1091
+ "required_terms": ["phone", "water"],
1092
+ "banned_terms": ["hydration", "diagnosis", "the the"]
1093
+ },
1094
+ {
1095
+ "id": "missing_shoes_stairwell",
1096
+ "dream_text": "I was in a stairwell without shoes, holding a map that only showed blank squares.",
1097
+ "visual_clues": ["Object: blank map", "Place: stairwell"],
1098
+ "mood": "Foggy",
1099
+ "required_terms": ["stairwell", "map"],
1100
+ "banned_terms": ["fate", "medical", "the an"]
1101
+ },
1102
+ {
1103
+ "id": "bedside_note_rain",
1104
+ "dream_text": "A note on my bedside table said 'bring the rain inside', and the window was already open.",
1105
+ "visual_clues": ["Visible text: bring the rain inside", "Object: open window"],
1106
+ "mood": "Calm",
1107
+ "required_terms": ["rain", "window"],
1108
+ "banned_terms": ["diagnosis", "prophecy", "the the"]
1109
+ },
1110
+ {
1111
+ "id": "voice_fragment_buttons",
1112
+ "dream_text": "",
1113
+ "voice_transcript": "The hallway was blue, and the elevator buttons were warm.",
1114
+ "visual_clues": ["Color: blue"],
1115
+ "mood": "Uneasy",
1116
+ "required_terms": ["hallway", "buttons"],
1117
+ "banned_terms": ["the an", "the the", "diagnosis"]
1118
+ },
1119
+ {
1120
+ "id": "abstract_sketch_moon_key",
1121
+ "dream_text": "I drew the dream because I only remember a moon-shaped key and a red square.",
1122
+ "visual_clues": ["Object: moon-shaped key", "Color: red square"],
1123
+ "mood": "Curious",
1124
+ "required_terms": ["key", "red"],
1125
+ "banned_terms": ["medical", "fate", "the an"]
1126
+ }
1127
+ ]
1128
+ ```
1129
+
1130
+ - [ ] **Step 2: Add evaluator tests**
1131
+
1132
+ Create `tests/test_demo_quality_eval.py`:
1133
+
1134
+ ```python
1135
+ from scripts.evaluate_demo_quality import evaluate_text
1136
+
1137
+
1138
+ def test_evaluate_text_passes_grounded_english_card():
1139
+ text = (
1140
+ "The Elevator Stuck at 14. The melted buttons and frozen floor 14 can stand for "
1141
+ "one stalled beginning. Choose one task and write the next button-sized action."
1142
+ )
1143
+
1144
+ result = evaluate_text(text, required_terms=["elevator", "14"], banned_terms=["the an", "the the"])
1145
+
1146
+ assert result["passes"]
1147
+ assert result["issues"] == []
1148
+
1149
+
1150
+ def test_evaluate_text_fails_repeated_articles_and_missing_anchor():
1151
+ text = "Pick one real task that feels like the an elevator."
1152
+
1153
+ result = evaluate_text(text, required_terms=["floor 14"], banned_terms=["the an"])
1154
+
1155
+ assert not result["passes"]
1156
+ assert "banned term: the an" in result["issues"]
1157
+ assert "missing required term: floor 14" in result["issues"]
1158
+ ```
1159
+
1160
+ - [ ] **Step 3: Implement evaluator script**
1161
+
1162
+ Create `scripts/evaluate_demo_quality.py`:
1163
+
1164
+ ```python
1165
+ import json
1166
+ from pathlib import Path
1167
+ from typing import Iterable
1168
+
1169
+
1170
+ def evaluate_text(text: str, required_terms: Iterable[str], banned_terms: Iterable[str]) -> dict:
1171
+ lowered = text.lower()
1172
+ issues: list[str] = []
1173
+ for term in required_terms:
1174
+ if term.lower() not in lowered:
1175
+ issues.append(f"missing required term: {term}")
1176
+ for term in banned_terms:
1177
+ if term.lower() in lowered:
1178
+ issues.append(f"banned term: {term}")
1179
+ if any(marker in lowered for marker in ["you have depression", "this means you will", "certainly predicts"]):
1180
+ issues.append("unsafe certainty or diagnosis")
1181
+ return {"passes": not issues, "issues": issues}
1182
+
1183
+
1184
+ def load_cases(path: str = "tests/fixtures/demo_eval_cases.json") -> list[dict]:
1185
+ return json.loads(Path(path).read_text(encoding="utf-8"))
1186
+
1187
+
1188
+ def main() -> int:
1189
+ cases = load_cases()
1190
+ print(json.dumps({"case_count": len(cases), "ids": [case["id"] for case in cases]}, indent=2))
1191
+ return 0
1192
+
1193
+
1194
+ if __name__ == "__main__":
1195
+ raise SystemExit(main())
1196
+ ```
1197
+
1198
+ - [ ] **Step 4: Run evaluator tests**
1199
+
1200
+ Run:
1201
+
1202
+ ```bash
1203
+ .venv/bin/python -m pytest tests/test_demo_quality_eval.py -q
1204
+ .venv/bin/python scripts/evaluate_demo_quality.py
1205
+ ```
1206
+
1207
+ Expected: pytest PASS; script prints `case_count` as `10`.
1208
+
1209
+ - [ ] **Step 5: Commit evaluator**
1210
+
1211
+ Run:
1212
+
1213
+ ```bash
1214
+ git add tests/fixtures/demo_eval_cases.json scripts/evaluate_demo_quality.py tests/test_demo_quality_eval.py
1215
+ git commit -m "test: add english demo quality eval cases"
1216
+ ```
1217
+
1218
+ Expected: commit succeeds.
1219
+
1220
+ ## Task 7: Full Verification, Local Smoke, And HF Space Sync
1221
+
1222
+ **Files:**
1223
+ - Verify: all modified files
1224
+ - Create: `docs/smoke/2026-06-08-model-led-pact-v2-smoke.md`
1225
+
1226
+ - [ ] **Step 1: Run full test suite**
1227
+
1228
+ Run:
1229
+
1230
+ ```bash
1231
+ .venv/bin/python -m pytest -q
1232
+ ```
1233
+
1234
+ Expected: all tests pass.
1235
+
1236
+ - [ ] **Step 2: Run whitespace check**
1237
+
1238
+ Run:
1239
+
1240
+ ```bash
1241
+ git diff --check
1242
+ ```
1243
+
1244
+ Expected: no output.
1245
+
1246
+ - [ ] **Step 3: Run local app smoke**
1247
+
1248
+ Run:
1249
+
1250
+ ```bash
1251
+ GRADIO_SERVER_PORT=7862 .venv/bin/python app.py
1252
+ ```
1253
+
1254
+ Open `http://127.0.0.1:7862` and submit:
1255
+
1256
+ ```text
1257
+ I kept missing an elevator. The buttons melted like wax, and the floor number froze at 14.
1258
+ ```
1259
+
1260
+ Attach an image only if a local sketch is available. Seal the pass and verify:
1261
+
1262
+ - The card is in polished English.
1263
+ - The card does not contain `the an`, `the the`, or invented `lever`.
1264
+ - The card mentions at least two of `elevator`, `melted buttons`, `wax`, and `floor 14`.
1265
+ - The practical suggestion is one real next step for today.
1266
+ - The weird task is playful, harmless, and grounded in the elevator or floor-14 image.
1267
+ - Mobile width remains readable.
1268
+
1269
+ - [ ] **Step 4: Record smoke evidence**
1270
+
1271
+ Create `docs/smoke/2026-06-08-model-led-pact-v2-smoke.md`:
1272
+
1273
+ ```markdown
1274
+ # Model-Led Pact V2 Smoke
1275
+
1276
+ Date: 2026-06-08
1277
+ Branch: feature/dream-customs-ui-voice-settings
1278
+
1279
+ ## Local
1280
+
1281
+ - Command: `.venv/bin/python -m pytest -q`
1282
+ - Result: PASS
1283
+ - Command: `git diff --check`
1284
+ - Result: PASS
1285
+ - Command: `GRADIO_SERVER_PORT=7862 .venv/bin/python app.py`
1286
+ - Result: PASS
1287
+
1288
+ ## Manual Demo Case
1289
+
1290
+ Input:
1291
+
1292
+ ```text
1293
+ I kept missing an elevator. The buttons melted like wax, and the floor number froze at 14.
1294
+ ```
1295
+
1296
+ Acceptance:
1297
+
1298
+ - Natural English card: PASS
1299
+ - No repeated article regression: PASS
1300
+ - No invented lever detail: PASS
1301
+ - Uses at least two dream details: PASS
1302
+ - Practical suggestion is actionable: PASS
1303
+ - Weird task is harmless and dream-grounded: PASS
1304
+ - Mobile readable: PASS
1305
+
1306
+ ## Hugging Face Space
1307
+
1308
+ - Push target checked: `space https://huggingface.co/spaces/build-small-hackathon/dream-customs`
1309
+ - Live Space config checked:
1310
+ - Live Space manual case checked:
1311
+ - Notes:
1312
+ ```
1313
+
1314
+ - [ ] **Step 5: Commit smoke doc**
1315
+
1316
+ Run:
1317
+
1318
+ ```bash
1319
+ git add docs/smoke/2026-06-08-model-led-pact-v2-smoke.md
1320
+ git commit -m "docs: record model-led pact smoke"
1321
+ ```
1322
+
1323
+ Expected: commit succeeds.
1324
+
1325
+ - [ ] **Step 6: Push GitHub branch**
1326
+
1327
+ Run:
1328
+
1329
+ ```bash
1330
+ git push origin feature/dream-customs-ui-voice-settings
1331
+ ```
1332
+
1333
+ Expected: push succeeds.
1334
+
1335
+ - [ ] **Step 7: Sync Hugging Face Space**
1336
+
1337
+ Before pushing, verify the remote:
1338
+
1339
+ ```bash
1340
+ git remote -v | grep '^space'
1341
+ ```
1342
+
1343
+ Expected:
1344
+
1345
+ ```text
1346
+ space https://huggingface.co/spaces/build-small-hackathon/dream-customs (fetch)
1347
+ space https://huggingface.co/spaces/build-small-hackathon/dream-customs (push)
1348
+ ```
1349
+
1350
+ Then push or create a Space PR according to the current auth state. If the operation requires a token, secret value, billing confirmation, force push to public `main`, or manual PR merge, stop and ask the user with the exact blocker. Do not print or save tokens.
1351
+
1352
+ ## Self-Review
1353
+
1354
+ - Spec coverage: The plan covers English demo quality, MiniCPM5 brief/draft/critique/rewrite, MiniCPM-V witness reports, screenshot elevator regression, fallback resilience, local tests, local smoke, and HF Space sync.
1355
+ - Placeholder scan: No unresolved placeholder strings are present in the task instructions.
1356
+ - Type consistency: `VisionWitness`, `DreamBrief`, `PactCritique`, and `PactCard` names are used consistently across schema, prompts, clients, pipeline, and tests.
1357
+ - Scope check: The plan is one coherent generation-quality pass. It does not redesign the UI, change Modal deployment architecture, or broaden beyond the MiniCPM family.