docs: align dream qa requirements
#14
by ADJCJH - opened
- .gitattributes +3 -0
- AGENTS.md +42 -24
- DESIGN.md +94 -90
- PRODUCT.md +13 -10
- README.md +34 -15
- design-qa.md +17 -14
- docs/design/2026-06-05-uiux-v2-brief.md +2 -0
- docs/goal-prompts/2026-06-08-model-led-pact-v2.md +23 -0
- docs/handoff.md +80 -61
- docs/prd.md +63 -57
- docs/prototypes/2026-06-08-dream-qa-desktop-workbench.png +3 -0
- docs/prototypes/2026-06-08-dream-qa-mobile-flow.png +3 -0
- docs/prototypes/2026-06-08-dream-qa-tips-card.png +3 -0
- docs/spec.md +110 -76
- docs/superpowers/plans/2026-06-08-dream-qa-refactor-doc-plan.md +202 -0
- docs/superpowers/plans/2026-06-08-dream-qa-refactor.md +195 -0
- docs/superpowers/plans/2026-06-08-model-led-pact-v2.md +1357 -0
.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
|
| 2 |
|
| 3 |
## 语言规则
|
| 4 |
|
|
@@ -7,9 +7,10 @@
|
|
| 7 |
|
| 8 |
## 项目定位
|
| 9 |
|
| 10 |
-
- 当前项目是 Build Small Hackathon 的 `Dream
|
| 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 |
-
- 所有输入统一进入
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
- 输出必须包含:
|
| 38 |
-
-
|
| 39 |
-
-
|
| 40 |
-
-
|
| 41 |
-
-
|
| 42 |
-
-
|
| 43 |
-
-
|
| 44 |
-
-
|
| 45 |
-
-
|
| 46 |
-
-
|
|
|
|
| 47 |
|
| 48 |
## 安全规则
|
| 49 |
|
| 50 |
- 不做心理疾病诊断。
|
| 51 |
- 不声称梦境有唯一含义。
|
| 52 |
- 不输出恐吓性、宿命论、神秘绝对化内容。
|
|
|
|
| 53 |
- 普通输出使用“也许”“可以把它当作”“今天先试试”这类非确定措辞。
|
| 54 |
- 如果用户表达自伤、伤人、长期严重失眠、无法正常生活或极强痛苦,必须显示寻求可信任的人或专业支持的提示。
|
| 55 |
|
|
@@ -57,11 +67,15 @@
|
|
| 57 |
|
| 58 |
实现前先阅读:
|
| 59 |
|
| 60 |
-
1. `docs/
|
| 61 |
-
2. `docs/
|
| 62 |
-
3. `docs/
|
| 63 |
-
4. `
|
| 64 |
-
5. `
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
-
|
|
|
|
| 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
|
| 3 |
-
description: A
|
| 4 |
colors:
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
accent-
|
| 14 |
-
|
| 15 |
-
|
| 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: "
|
| 22 |
fontWeight: 760
|
| 23 |
-
lineHeight: 1.
|
| 24 |
letterSpacing: "0"
|
| 25 |
headline:
|
| 26 |
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
|
| 27 |
-
fontSize: "1.
|
| 28 |
fontWeight: 720
|
| 29 |
-
lineHeight: 1.
|
| 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: "
|
| 48 |
-
lg: "
|
| 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.
|
| 60 |
rounded: "{rounded.md}"
|
| 61 |
padding: "12px 16px"
|
| 62 |
-
button-
|
| 63 |
-
backgroundColor: "{colors.
|
| 64 |
-
textColor: "{colors.
|
| 65 |
rounded: "{rounded.md}"
|
| 66 |
padding: "12px 16px"
|
| 67 |
input-composer:
|
| 68 |
-
backgroundColor: "{colors.surface-
|
| 69 |
-
textColor: "{colors.
|
| 70 |
rounded: "{rounded.lg}"
|
| 71 |
padding: "14px 16px"
|
| 72 |
---
|
| 73 |
|
| 74 |
-
# Design System: Dream
|
| 75 |
|
| 76 |
## 1. Overview
|
| 77 |
|
| 78 |
-
**Creative North Star: "The Morning
|
| 79 |
|
| 80 |
-
Dream
|
| 81 |
|
| 82 |
-
|
| 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 |
-
-
|
| 89 |
-
- Conversation timeline for
|
| 90 |
-
-
|
| 91 |
-
-
|
| 92 |
-
-
|
| 93 |
|
| 94 |
## 2. Colors
|
| 95 |
|
| 96 |
-
The palette is
|
| 97 |
|
| 98 |
### Primary
|
| 99 |
|
| 100 |
-
- **Sage
|
| 101 |
-
- **Deep Sage
|
| 102 |
|
| 103 |
### Secondary
|
| 104 |
|
| 105 |
-
- **
|
| 106 |
-
- **
|
| 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 |
-
- **
|
| 112 |
-
- **
|
| 113 |
-
- **
|
| 114 |
-
- **
|
| 115 |
-
- **
|
| 116 |
-
- **Muted Ink** (`oklch(0.760 0.018 78)`): secondary labels that still remain readable.
|
| 117 |
|
| 118 |
### Named Rules
|
| 119 |
|
| 120 |
-
**The
|
| 121 |
|
| 122 |
-
**The No
|
| 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 |
-
**
|
| 132 |
|
| 133 |
### Hierarchy
|
| 134 |
|
| 135 |
-
- **Display** (760,
|
| 136 |
-
- **Headline** (720, 1.
|
| 137 |
-
- **Title** (680, 1rem, 1.3): timeline item headers, buttons,
|
| 138 |
-
- **Body** (450, 1rem, 1.55): dream text, questions,
|
| 139 |
-
- **Label** (650, 0.8125rem, 1.25):
|
| 140 |
|
| 141 |
### Named Rules
|
| 142 |
|
| 143 |
-
**The
|
| 144 |
|
| 145 |
## 4. Elevation
|
| 146 |
|
| 147 |
-
Depth is
|
| 148 |
|
| 149 |
### Shadow Vocabulary
|
| 150 |
|
| 151 |
-
- **composer-focus** (`0 0 0 1px
|
| 152 |
-
- **
|
| 153 |
-
- **
|
| 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:**
|
| 164 |
-
- **Primary:** Deep
|
| 165 |
-
- **
|
| 166 |
-
- **
|
| 167 |
-
- **
|
| 168 |
|
| 169 |
### Chips
|
| 170 |
|
| 171 |
- **Style:** small rounded pills with icon, label, and state dot.
|
| 172 |
-
- **
|
|
|
|
| 173 |
|
| 174 |
### Cards / Containers
|
| 175 |
|
| 176 |
-
- **Corner Style:**
|
| 177 |
-
- **Background:**
|
| 178 |
-
- **Shadow Strategy:** flat by default, lifted only when active or
|
| 179 |
-
- **Border:** full
|
| 180 |
- **Internal Padding:** 16px mobile, 24px desktop.
|
| 181 |
|
| 182 |
### Inputs / Fields
|
| 183 |
|
| 184 |
-
- **Style:**
|
| 185 |
-
- **Focus:**
|
| 186 |
- **Error / Disabled:** inline message below composer, disabled actions stay readable and explain why.
|
| 187 |
|
| 188 |
### Navigation
|
| 189 |
|
| 190 |
-
- **Style:**
|
| 191 |
-
- **Mobile:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
-
The
|
| 196 |
|
| 197 |
## 6. Do's and Don'ts
|
| 198 |
|
| 199 |
### Do:
|
| 200 |
|
| 201 |
-
- **Do** make the
|
| 202 |
-
- **Do**
|
| 203 |
- **Do** keep every phase actionable with one clear next step.
|
| 204 |
-
- **Do**
|
| 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
|
| 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**
|
| 216 |
-
- **Don't**
|
| 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
|
| 10 |
|
| 11 |
## Product Purpose
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
-
Success means the user receives something more useful than a one-shot interpretation: a
|
| 16 |
|
| 17 |
## Brand Personality
|
| 18 |
|
| 19 |
Gentle, strange, lucid.
|
| 20 |
|
| 21 |
-
The voice is
|
| 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
|
|
|
|
| 28 |
- Do not bury the main action below many controls.
|
| 29 |
-
- Do not
|
|
|
|
| 30 |
|
| 31 |
## Design Principles
|
| 32 |
|
| 33 |
-
1. The AI is a
|
| 34 |
-
2. The user keeps agency:
|
| 35 |
3. The interface should be dream-colored but product-trustworthy.
|
| 36 |
-
4. Every output should lead to
|
| 37 |
-
5. The
|
|
|
|
| 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:
|
| 5 |
-
colorTo:
|
| 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:
|
| 13 |
models:
|
| 14 |
- openbmb/MiniCPM5-1B
|
| 15 |
- openbmb/MiniCPM-V-4.6
|
|
@@ -20,22 +20,45 @@ tags:
|
|
| 20 |
- dream-journal
|
| 21 |
---
|
| 22 |
|
| 23 |
-
# Dream
|
| 24 |
|
| 25 |
-
A Build Small Hackathon Gradio app that helps users
|
|
|
|
|
|
|
| 26 |
|
| 27 |
## Concept
|
| 28 |
|
| 29 |
-
Dream
|
|
|
|
|
|
|
| 30 |
|
| 31 |
## Models
|
| 32 |
|
| 33 |
- `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
|
| 34 |
-
- `openbmb/MiniCPM5-1B` for dream
|
| 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
|
| 93 |
PY
|
| 94 |
```
|
| 95 |
|
|
@@ -116,12 +139,8 @@ python -m pytest -q
|
|
| 116 |
|
| 117 |
## Deployment Smoke Status
|
| 118 |
|
| 119 |
-
|
| 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
|
|
|
|
| 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
|
| 2 |
|
| 3 |
-
Date: 2026-06-
|
| 4 |
|
| 5 |
-
Reference:
|
| 6 |
|
| 7 |
## Checks
|
| 8 |
|
| 9 |
-
- First screen
|
| 10 |
-
-
|
| 11 |
-
-
|
| 12 |
-
-
|
| 13 |
-
-
|
| 14 |
-
-
|
| 15 |
-
-
|
| 16 |
-
-
|
|
|
|
|
|
|
| 17 |
|
| 18 |
## Notes
|
| 19 |
|
| 20 |
-
-
|
| 21 |
-
-
|
|
|
|
| 22 |
- The UI keeps safety copy non-diagnostic.
|
| 23 |
|
| 24 |
-
|
|
|
|
| 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
|
| 2 |
|
| 3 |
-
Last updated: 2026-06-
|
| 4 |
|
| 5 |
-
## Current
|
| 6 |
|
| 7 |
-
The project
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
| 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 |
-
|
| 18 |
|
| 19 |
-
|
| 20 |
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
|
| 24 |
-
- `docs/dream-customs-concept/assets/multimodal-intake.svg`
|
| 25 |
-
- `docs/dream-customs-concept/assets/alliance-card.svg`
|
| 26 |
|
| 27 |
-
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
## Product Decision
|
| 38 |
|
| 39 |
-
Build
|
| 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,
|
| 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 `
|
| 71 |
-
5. MiniCPM5-1B
|
| 72 |
-
6.
|
| 73 |
-
7.
|
| 74 |
-
8.
|
|
|
|
|
|
|
| 75 |
|
| 76 |
## Required Output Fields
|
| 77 |
|
| 78 |
-
- `
|
| 79 |
-
- `
|
| 80 |
-
- `
|
| 81 |
-
- `
|
| 82 |
-
- `
|
| 83 |
-
- `
|
| 84 |
-
- `
|
| 85 |
-
- `
|
|
|
|
| 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
|
| 93 |
|
| 94 |
-
The
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
## Implementation Priorities
|
| 118 |
|
| 119 |
-
1.
|
| 120 |
-
2.
|
| 121 |
-
3.
|
| 122 |
-
4.
|
| 123 |
-
5.
|
| 124 |
-
6.
|
|
|
|
| 125 |
|
| 126 |
## Repository Status
|
| 127 |
|
| 128 |
-
This directory is
|
| 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
|
| 135 |
|
| 136 |
## Modal Backend Route
|
| 137 |
|
| 138 |
-
The intended production-quality route
|
| 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
|
| 150 |
|
| 151 |
## Open Questions For User
|
| 152 |
|
| 153 |
-
These do not block
|
| 154 |
|
| 155 |
-
- Should the
|
| 156 |
-
-
|
| 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
|
| 2 |
|
| 3 |
-
Last updated: 2026-06-
|
| 4 |
|
| 5 |
## 1. Executive Summary
|
| 6 |
|
| 7 |
### Problem Statement
|
| 8 |
|
| 9 |
-
Users who wake up from
|
| 10 |
|
| 11 |
### Proposed Solution
|
| 12 |
|
| 13 |
-
Dream
|
|
|
|
|
|
|
| 14 |
|
| 15 |
### Success Criteria
|
| 16 |
|
| 17 |
-
- A user can complete the core
|
| 18 |
-
- At least
|
| 19 |
-
-
|
| 20 |
-
-
|
|
|
|
|
|
|
| 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
|
| 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
|
| 42 |
|
| 43 |
-
As a vivid dreamer, I want to type my dream so that
|
| 44 |
|
| 45 |
Acceptance criteria:
|
| 46 |
|
| 47 |
- User can submit text-only input.
|
| 48 |
-
- App creates a valid
|
| 49 |
-
-
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
#### Story 2: Image Dream
|
| 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
|
| 59 |
-
- Visual clues are included in the final
|
| 60 |
- If image extraction fails, text-only flow still works.
|
| 61 |
|
| 62 |
-
#### Story 3: Voice Dream
|
| 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
|
| 71 |
- If ASR fails, user receives a concise fallback message and can type manually.
|
| 72 |
|
| 73 |
-
#### Story 4:
|
| 74 |
|
| 75 |
-
As a user, I want the AI to ask me a few questions so that the final
|
| 76 |
|
| 77 |
Acceptance criteria:
|
| 78 |
|
| 79 |
-
- App generates
|
| 80 |
-
- User can answer all, answer some, or
|
| 81 |
-
- Skip path still generates a final
|
|
|
|
| 82 |
|
| 83 |
-
#### Story 5: Today
|
| 84 |
|
| 85 |
-
As a user, I want a
|
| 86 |
|
| 87 |
Acceptance criteria:
|
| 88 |
|
| 89 |
-
-
|
| 90 |
-
-
|
| 91 |
-
-
|
| 92 |
-
-
|
| 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,
|
| 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
|
| 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
|
|
|
|
| 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 |
-
-
|
| 138 |
-
-
|
| 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 |
-
-
|
|
|
|
| 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 |
-
->
|
| 158 |
-
-> MiniCPM5-1B
|
| 159 |
-
->
|
| 160 |
-
->
|
| 161 |
-
->
|
|
|
|
|
|
|
| 162 |
```
|
| 163 |
|
| 164 |
### Core Components
|
| 165 |
|
| 166 |
-
- `app.py`: Gradio
|
| 167 |
-
- `dream_customs/schema.py`:
|
| 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
|
| 173 |
-
- `
|
|
|
|
| 174 |
|
| 175 |
### Integration Points
|
| 176 |
|
| 177 |
- Hugging Face Space runtime.
|
| 178 |
-
- Hugging Face model downloads or
|
| 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
|
| 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
|
| 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 |
-
-
|
| 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
|
docs/prototypes/2026-06-08-dream-qa-mobile-flow.png
ADDED
|
Git LFS Details
|
docs/prototypes/2026-06-08-dream-qa-tips-card.png
ADDED
|
Git LFS Details
|
docs/spec.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
# Dream
|
| 2 |
|
| 3 |
-
Last updated: 2026-06-
|
| 4 |
|
| 5 |
## 1. One-Line Concept
|
| 6 |
|
| 7 |
-
梦境
|
| 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
|
| 16 |
-
- Secondary story: Backyard AI, because the concept comes from a real sleep problem: frequent vivid dreams and
|
| 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
|
| 22 |
|
| 23 |
## 4. Product Principles
|
| 24 |
|
| 25 |
1. Do not diagnose.
|
| 26 |
2. Do not frighten the user.
|
| 27 |
-
3.
|
| 28 |
-
4.
|
| 29 |
-
5.
|
| 30 |
-
6.
|
|
|
|
| 31 |
|
| 32 |
## 5. User Inputs
|
| 33 |
|
| 34 |
-
All inputs are normalized into one
|
| 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 91 |
-
- Do not provide psychological
|
| 92 |
|
| 93 |
### MiniCPM5-1B
|
| 94 |
|
| 95 |
-
Role: dream
|
| 96 |
|
| 97 |
Responsibilities:
|
| 98 |
|
| 99 |
- Merge dream text, voice transcript, and visual clues.
|
| 100 |
-
-
|
| 101 |
-
-
|
| 102 |
-
-
|
|
|
|
|
|
|
| 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 `
|
| 112 |
-
6. MiniCPM5-1B
|
| 113 |
-
7.
|
| 114 |
-
8.
|
| 115 |
-
9.
|
|
|
|
|
|
|
| 116 |
|
| 117 |
## 9. Output Contract
|
| 118 |
|
| 119 |
-
The final
|
| 120 |
|
| 121 |
-
- Dream
|
| 122 |
-
-
|
| 123 |
-
-
|
| 124 |
-
-
|
| 125 |
-
-
|
| 126 |
-
-
|
| 127 |
-
- One 5-minute weird task.
|
| 128 |
-
- One bedtime release phrase.
|
| 129 |
- Safety note if the input indicates severe distress.
|
| 130 |
|
| 131 |
-
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 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:
|
| 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 |
-
- "
|
| 161 |
|
| 162 |
-
### Screen 2:
|
| 163 |
|
| 164 |
Content:
|
| 165 |
|
| 166 |
-
-
|
| 167 |
-
-
|
| 168 |
-
-
|
|
|
|
| 169 |
|
| 170 |
-
### Screen 3:
|
| 171 |
|
| 172 |
Content:
|
| 173 |
|
| 174 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
- Copy text button.
|
| 176 |
-
-
|
| 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
|
| 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
|
| 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 |
-
-
|
| 201 |
-
-
|
|
|
|
| 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
|
| 218 |
|
| 219 |
2. Which track should the submission emphasize?
|
| 220 |
- Recommended answer: Thousand Token Wood first, Backyard AI second.
|
| 221 |
|
| 222 |
-
3.
|
| 223 |
-
- Recommended answer:
|
| 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.
|