diff --git a/.compound-engineering/config.local.example.yaml b/.compound-engineering/config.local.example.yaml new file mode 100644 index 0000000..b5cea2c --- /dev/null +++ b/.compound-engineering/config.local.example.yaml @@ -0,0 +1,49 @@ +# Compound Engineering -- local config +# Copy to .compound-engineering/config.local.yaml in your project root. +# All settings are optional. Invalid values fall through to defaults. + +# --- Work delegation (Codex) --- + +# work_delegate: codex # codex | false (default: false) +# work_delegate_consent: true # true | false (default: false) +# work_delegate_sandbox: yolo # yolo | full-auto (default: yolo) +# work_delegate_decision: auto # auto | ask (default: auto) +# work_delegate_model: gpt-5.4 # any valid codex model (omit to use ~/.codex/config.toml default) +# work_delegate_effort: high # minimal | low | medium | high | xhigh (omit to use ~/.codex/config.toml default) + +# --- Product pulse --- +# Settings written by /ce-product-pulse first-run interview. Re-run the skill with +# argument `setup` or `reconfigure` to edit interactively. + +# pulse_product_name: "Spiral" # used in report titles (no default) +# pulse_lookback_default: 24h # 1h | 24h | 7d | 30d (default: 24h) +# pulse_primary_event: "session_started" # the event that means "user showed up" +# pulse_value_event: "task_completed" # the event that means "user got value" +# pulse_completion_events: "onboarded,first_purchase" # comma-separated, 0-3 events +# pulse_quality_scoring: false # true | false (default: false; AI products only) +# pulse_quality_dimension: "answer accuracy" # dimension scored 1-5 when pulse_quality_scoring is true +# pulse_analytics_source: posthog # posthog | mixpanel | custom (no default) +# pulse_tracing_source: sentry # sentry | datadog | custom (no default) +# pulse_payments_source: stripe # stripe | custom (no default) +# pulse_db_enabled: false # true | false (default: false; read-only DB if true) +# pulse_metric_sources: "retention_d7=posthog,nps=delighted" # strategy-metric -> source overrides; comma-separated 'metric=source' pairs; unlisted metrics fall back to pulse_analytics_source +# pulse_pending_metrics: "retention_d7,nps" # comma-separated strategy metrics awaiting instrumentation; render as 'no data' +# pulse_excluded_metrics: "north_star" # comma-separated strategy metrics intentionally not in pulse + +# --- Output format --- +# Per-skill output format default. Selects the exclusive format the artifact +# is written in: `md` produces a markdown file, `html` produces a single +# self-contained HTML file. The two are mutually exclusive -- there is no +# sibling artifact. See DESIGN.md or your agent instructions to influence +# HTML styling. CLI arguments override these defaults; pipeline contexts +# (e.g., LFG, disable-model-invocation) always force `md` regardless. + +# plan_output: html # md | html (default: md) +# brainstorm_output: html # md | html (default: md) +# ideate_output: md # md | html (default: html -- ideation docs are human-facing, so HTML is the default; set md to opt out) + +# --- ce-promote --- +# Written automatically when you decline the Spiral setup offer in /ce-promote. +# Suppresses that one-time setup nudge in this project. Remove the key to re-enable. + +# ce_promote_spiral_optout: true # true | (absent) (default: absent -- offer once) diff --git a/.gitignore b/.gitignore index fae5625..b534f51 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,7 @@ data/ # Knowledge graph tooling (local-only, generated index) .understand-anything/ + +# Local temp files +tmp_*.html +/delete_old_cluster.sh diff --git a/docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md b/docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md new file mode 100644 index 0000000..88ce759 --- /dev/null +++ b/docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md @@ -0,0 +1,78 @@ +--- +date: 2026-07-02 +topic: private-board-restrictions-and-scheme-b-bubbles +--- + +## Summary + +收尾私董会(@board)模块的 UI 细节与单会话状态约束。三处改动:(1) 限制一个对话只能存在一个私董会,新建私董会必须从新会话发起;(2) 将 `BoardBannerCard`(私董会开始卡片)从带边框 / 紫条 / 进度条 / 专家 chip 的重样式简化为单行标题 + 副标题;(3) 给所有 `AssistantText` 渲染添加方案 B 风格的浅灰圆角矩形气泡,与方案 B 截图保持一致。 + +## Problem Frame + +私董会功能自 2026-06 上线以来已能正确触发多轮讨论,但 UI 上仍残留三处粗糙点: + +1. **单会话多私董会无约束**。`ChatInput.vue:75` 的"私董会"按钮 `@click` 直接 `showBoardModal = true`,对当前会话是否已经存在私董会无任何判断。后端在 `board_started` 事件中没有按会话做去重,连续两次 `SendMessage("@board:...")` 会在同一会话里创建第二个私董会,叠加在第一个未结束的私董会之上,`boardState.experts` 被覆盖、轮次错乱、`StickyModeHeader` 头像数与实际不符。 +2. **`BoardBannerCard` 样式过重**。`BoardBannerCard.vue:55-137` 使用了 `background / border / border-radius / box-shadow` 四件套 + 4px 紫条 + 进度条 + 专家 chip pill,与方案 B 整体"克制、不重样式"的取向冲突。方案 B 截图(参考 `docs/.../2026-06-18-chat-area-vi-redesign-requirements.md` 中的方案 B 示意)的"开始"标题区域是单行文本,不带装饰。 +3. **方案 B 气泡未落地**。方案 B 截图中的"专家发言"区域是**有**浅灰圆角矩形气泡包裹内容(不是无气泡),与 ChatGPT / Notion AI 风格一致。当前 `AssistantText.vue:1-30` 的内容区 `.assistant-text` 没有背景 / 边框 / 圆角,气泡效果完全缺失。 + +## Key Decisions + +- **私董会限制的"已存在"判断以 `boardState.status` 为准**:`status === 'discussing' | 'concluding'` 时禁止在当前会话再次发起;`status === 'completed' | 'dissolved' | null` 时允许(已完成 / 已解散的旧私董会不阻塞新私董会)。判断点放在 `ChatInput.vue` "私董会"按钮 `@click` 处(最自然的 UX 拦截点),不放在 `BoardMeetingModal` 内部(避免用户填表后才发现不能发起)。 +- **私董会限制的反馈方式是 a-modal 弹窗 + 快捷新建按钮**。点击"私董会"按钮时若检测到已有私董会,弹出 `a-modal`,标题"当前会话已存在私董会",副文"请新建会话来创建新的私董会",按钮"我知道了" + "新建会话"。"新建会话"按钮直接调用 `chatStore.createConversation()` 并 `chatStore.selectConversation(newId)`,让用户立即在新会话里继续操作。 +- **BoardBannerCard 简化为单行标题 + 副标题**。完全去掉 `BankOutlined` 图标、专家 chip 列表、4px 紫条、进度条、卡片背景 / 边框 / 圆角 / 阴影。最终输出形如: + + ``` + 私董会 — 利用 agent 实现私董会的功能,应该用什么功能来打动客户 + 轮次:第 1 / 5 轮 + ``` + + 标题字号 = `var(--font-base)` 加粗 + 主题文本;副标题字号 = `var(--font-xs)` + `var(--text-tertiary)`。`StickyModeHeader` 顶部的紫色"私董会"徽章 + 主题 + 专家头像组保持不变,承担需要"重样式"的展示职责。 +- **方案 B 浅灰气泡应用到所有 assistant 消息**。具体范围:`role === 'assistant'` 的所有 `MessageShell` 内的内容(普通 chat、@team 阶段、@board 发言、Debate、Plan exec、Tool result 等)都加同款浅灰圆角矩形气泡;`role === 'user'` 的用户消息气泡保留现有 `UserBubble.vue` 的右对齐独立样式,不加 AssistantText 风格的浅灰块。气泡使用 token 颜色(`var(--bg-secondary)` 或 `var(--bg-elevated)` 系)+ 圆角 `var(--radius-md)` + 内边距 `var(--space-3) var(--space-4)`,确保与方案 B 截图视觉一致;颜色与边框用 CSS 变量绑定,**禁止硬编码** `#f3f4f6` / `#fbfbfa` / `#ededec` 等值。 +- **气泡内的代码块、表格、行内代码样式保持不变**。`AssistantText.vue:257-401` 的 `pre / hljs / code / table` 样式已经在 dark-on-light 配色上做了适配(`--code-bg` / `--code-fg` / `--code-keyword` 等 token),气泡背景换浅灰后这些 token 自动适配,不需要单独再改。 +- **不触碰 StickyModeHeader 顶部条**。顶部条的紫色边框、徽章、4 个专家头像等不属于本次改动范围。 + +## Requirements + +### 单会话私董会限制 + +- R1. `ChatInput.vue` 的"私董会"按钮 `@click` 处理函数在打开 `BoardMeetingModal` 前,先检查 `chatStore.boardState.value`:若非 null 且 `status === 'discussing' | 'concluding'`,触发 a-modal 弹窗,**不**打开 `BoardMeetingModal`。 +- R2. a-modal 弹窗内容:标题"当前会话已存在私董会",副文案"请新建会话来创建新的私董会",按钮"我知道了"(关闭弹窗)+ "新建会话"(主操作)。"新建会话"按钮点击后:(a) 关闭弹窗;(b) 调用 `chatStore.createConversation()`;(c) `await chatStore.selectConversation(newId)`;(d) 不自动打开 `BoardMeetingModal`,由用户在新会话中再次点击"私董会"按钮继续。 +- R3. 若 `boardState.value === null` 或 `status === 'completed' | 'dissolved'`,保持当前行为:`showBoardModal = true` 直接打开 `BoardMeetingModal`,不弹提示。 +- R4. 判定不依赖后端 / `is_board` 标记 / `conv.is_board`——以前端 `chatStore.boardState.value` 实时状态为权威源,避免 reload 后误判。 + +### 私董会开始卡片简化 + +- R5. `BoardBannerCard.vue` 重构为单行标题 + 副标题:保留 `topic / maxRounds / currentRound` props(向后兼容调用方),不再使用 `experts` props(删除 prop)。 +- R6. 模板输出仅两行:第一行 `私董会 — {topic}`(无 BankOutlined、无边框、无背景、无圆角、无 4px 紫条);第二行 `轮次:第 {currentRound} / {maxRounds} 轮`(小灰字)。 +- R7. `