From 345359ab8f16599b1cc21f49f2d9542d16108558 Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Wed, 15 Apr 2026 13:20:59 +0800 Subject: [PATCH] docs(planning): sync phase-06 execution and verification artifacts --- .planning/phases/06-/06-03-PLAN.md | 18 +- .planning/phases/06-/06-03-SUMMARY.md | 5 +- .../06-/{06-04-PLAN.md => 06-04-ARCHIVED.md} | 2 + .planning/phases/06-/06-05-PLAN.md | 178 ++++++++++++++++++ .../{06-COMMIT-PLAN.md => 06-COMMIT-GUIDE.md} | 4 +- .planning/phases/06-/06-COMMIT-SUMMARY.md | 5 +- .planning/phases/06-/06-RESEARCH.md | 22 +-- .planning/phases/06-/06-REVIEW.md | 98 ++++++++++ .planning/phases/06-/06-UAT.md | 17 +- .planning/phases/06-/06-VALIDATION.md | 5 +- .planning/phases/06-/06-VERIFICATION.md | 125 ++++++++++++ 11 files changed, 448 insertions(+), 31 deletions(-) rename .planning/phases/06-/{06-04-PLAN.md => 06-04-ARCHIVED.md} (91%) create mode 100644 .planning/phases/06-/06-05-PLAN.md rename .planning/phases/06-/{06-COMMIT-PLAN.md => 06-COMMIT-GUIDE.md} (97%) create mode 100644 .planning/phases/06-/06-REVIEW.md create mode 100644 .planning/phases/06-/06-VERIFICATION.md diff --git a/.planning/phases/06-/06-03-PLAN.md b/.planning/phases/06-/06-03-PLAN.md index 3d21f8e3..e2bef135 100644 --- a/.planning/phases/06-/06-03-PLAN.md +++ b/.planning/phases/06-/06-03-PLAN.md @@ -11,7 +11,7 @@ files_modified: - frontend/tests/e2e/support/chat-helpers.ts - frontend/src/core/threads/hooks.test.ts - .planning/phases/06-/06-VALIDATION.md - - .planning/phases/06-/06-COMMIT-PLAN.md + - .planning/phases/06-/06-COMMIT-GUIDE.md autonomous: true requirements: - ATREF-04 @@ -25,14 +25,14 @@ must_haves: provides: "@ 引用交互 E2E 回归" - path: "frontend/src/core/threads/hooks.test.ts" provides: "提交 envelope 与软失败单测" - - path: ".planning/phases/06-/06-COMMIT-PLAN.md" + - path: ".planning/phases/06-/06-COMMIT-GUIDE.md" provides: "按关注点提交分组与执行顺序" key_links: - from: "frontend/tests/e2e/input-and-compose.spec.ts" to: "frontend/src/components/workspace/input-box.tsx" via: "@ 引用交互断言" pattern: "@引用|chip|失效引用|上限" - - from: ".planning/phases/06-/06-COMMIT-PLAN.md" + - from: ".planning/phases/06-/06-COMMIT-GUIDE.md" to: "git history" via: "concern-based commit order" pattern: "style -> logic -> tests -> docs" @@ -110,21 +110,21 @@ Output: E2E/单测、更新后的验证矩阵、以及可执行的 commit 分组 Task 3: 产出 Phase 6 Git 提交分组计划(style/logic/tests/docs) - .planning/phases/06-/06-COMMIT-PLAN.md + .planning/phases/06-/06-COMMIT-GUIDE.md - .planning/phases/05-test-hardening-and-commit-hygiene/05-SUMMARY.md - .planning/phases/06-/06-01-PLAN.md - .planning/phases/06-/06-02-PLAN.md - .planning/phases/06-/06-VALIDATION.md - 新增 `06-COMMIT-PLAN.md`,明确提交顺序与分组:`1) style`(仅样式/展示类变更,如 chip 外观、dropdown 样式类),`2) logic`(候选聚合、提交结构、软失败逻辑),`3) tests`(hooks/e2e 用例与 helper),`4) docs`(VALIDATION/SUMMARY/ROADMAP 更新);每组列出建议 `git add` 文件清单与规范 commit message 模板,禁止跨组混提。 + 新增 `06-COMMIT-GUIDE.md`,明确提交顺序与分组:`1) style`(仅样式/展示类变更,如 chip 外观、dropdown 样式类),`2) logic`(候选聚合、提交结构、软失败逻辑),`3) tests`(hooks/e2e 用例与 helper),`4) docs`(VALIDATION/SUMMARY/ROADMAP 更新);每组列出建议 `git add` 文件清单与规范 commit message 模板,禁止跨组混提。tests 组最小 E2E 验证必须覆盖 `DF-INPUT-007|DF-INPUT-008|DF-INPUT-009`,满足 DF-INPUT-009 hygiene 缺口。 - - `06-COMMIT-PLAN.md` 包含固定顺序文本 `style -> logic -> tests -> docs`。 + - `06-COMMIT-GUIDE.md` 包含固定顺序文本 `style -> logic -> tests -> docs`。 - 文档内每个分组都有文件清单与 commit message 示例。 - 文档包含“禁止跨组混提”规则。 - cd /home/mt/Project/deerflow2 && rg -n "style -> logic -> tests -> docs|禁止跨组混提|commit message" .planning/phases/06-/06-COMMIT-PLAN.md + cd /home/mt/Project/deerflow2 && rg -n "style -> logic -> tests -> docs|禁止跨组混提|DF-INPUT-009|commit message" .planning/phases/06-/06-COMMIT-GUIDE.md 提交卫生方案可直接执行,满足用户的分组与顺序约束。 @@ -143,7 +143,7 @@ Output: E2E/单测、更新后的验证矩阵、以及可执行的 commit 分组 | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| -| T-06-03-01 | R | `06-COMMIT-PLAN.md` | mitigate | 提供固定顺序与文件清单,确保提交可追踪与可审计。 | +| T-06-03-01 | R | `06-COMMIT-GUIDE.md` | mitigate | 提供固定顺序与文件清单,确保提交可追踪与可审计。 | | T-06-03-02 | D | E2E 测试执行 | mitigate | 环境不足时显式 skip 并给原因,避免反复失败阻塞整个阶段。 | | T-06-03-03 | T | 验证矩阵 | mitigate | 将验证命令与需求映射写死到 VALIDATION,避免后续手工偏离。 | @@ -151,7 +151,7 @@ Output: E2E/单测、更新后的验证矩阵、以及可执行的 commit 分组 - `cd frontend && pnpm -s test:e2e -- input-and-compose.spec.ts` - `cd frontend && node --test src/core/threads/hooks.test.ts` -- `cd /home/mt/Project/deerflow2 && rg -n "style -> logic -> tests -> docs" .planning/phases/06-/06-COMMIT-PLAN.md` +- `cd /home/mt/Project/deerflow2 && rg -n "style -> logic -> tests -> docs|DF-INPUT-009" .planning/phases/06-/06-COMMIT-GUIDE.md` diff --git a/.planning/phases/06-/06-03-SUMMARY.md b/.planning/phases/06-/06-03-SUMMARY.md index a2f86b2d..b3ac51ab 100644 --- a/.planning/phases/06-/06-03-SUMMARY.md +++ b/.planning/phases/06-/06-03-SUMMARY.md @@ -24,7 +24,7 @@ key-files: - frontend/tests/e2e/support/chat-helpers.ts - frontend/src/core/threads/hooks.test.ts - .planning/phases/06-/06-VALIDATION.md - - .planning/phases/06-/06-COMMIT-PLAN.md + - .planning/phases/06-/06-COMMIT-GUIDE.md key-decisions: - "E2E 环境未启动时保留失败证据,不伪造通过。" - "以 hooks 单测对失效引用软失败逻辑做稳定兜底。" @@ -49,6 +49,5 @@ completed: 2026-04-15 ## Outcome - `DF-INPUT-007/008` 用例存在并可执行,当前阻塞为本地服务未启动。 -- `06-VALIDATION.md` 与 `06-COMMIT-PLAN.md` 维持可审计验证和分组提交策略。 +- `06-VALIDATION.md` 与 `06-COMMIT-GUIDE.md` 维持可审计验证和分组提交策略。 - 单测已覆盖引用元信息提交与 stale 引用软失败关键链路。 - diff --git a/.planning/phases/06-/06-04-PLAN.md b/.planning/phases/06-/06-04-ARCHIVED.md similarity index 91% rename from .planning/phases/06-/06-04-PLAN.md rename to .planning/phases/06-/06-04-ARCHIVED.md index ef892364..4cc76623 100644 --- a/.planning/phases/06-/06-04-PLAN.md +++ b/.planning/phases/06-/06-04-ARCHIVED.md @@ -1,3 +1,5 @@ +> Archived in revision pass on 2026-04-15. This file is preserved for traceability only and is intentionally not executable because Task 2 conflicted with locked decision D-08 (`max 10`) and the plan lacked required `must_haves`, ``, ``, and `` sections. + --- phase: 06- plan: 04 diff --git a/.planning/phases/06-/06-05-PLAN.md b/.planning/phases/06-/06-05-PLAN.md new file mode 100644 index 00000000..7b9d6d37 --- /dev/null +++ b/.planning/phases/06-/06-05-PLAN.md @@ -0,0 +1,178 @@ +--- +phase: 06- +plan: 05 +type: execute +wave: 4 +depends_on: + - 06-03 +gap_closure: true +files_modified: + - frontend/src/components/workspace/input-box.tsx + - frontend/tests/e2e/input-and-compose.spec.ts + - frontend/tests/e2e/support/chat-helpers.ts +autonomous: true +requirements: + - ATREF-01 + - ATREF-02 + - ATREF-03 + - ATREF-04 +must_haves: + truths: + - "用户在输入框里看到的引用候选与已选引用都对齐 D-04/D-08:同名场景展示“文件名 + 类型 + 路径尾段”,且第 11 个引用会被阻止。" + - "DF-INPUT-008 不再永久 skip;软失败场景会提示 stale toast 且消息发送继续完成。" + - "DF-INPUT-009 使用稳定定位与可重复 fixture 后可验证 10 个上限,不再因 strict locator 多命中而失败。" + artifacts: + - path: "frontend/src/components/workspace/input-box.tsx" + provides: "引用上限、文案与去歧义展示合同" + contains: "MAX_REFERENCES_PER_MESSAGE = 10" + - path: "frontend/tests/e2e/input-and-compose.spec.ts" + provides: "DF-INPUT-008/009 稳定回归覆盖" + contains: "DF-INPUT-008" + - path: "frontend/tests/e2e/support/chat-helpers.ts" + provides: "可复用的 thread/fixture helper,避免测试依赖隐式线程数据" + contains: "THREAD_" + key_links: + - from: "frontend/src/components/workspace/input-box.tsx" + to: "frontend/tests/e2e/input-and-compose.spec.ts" + via: "稳定的可见文案或 data-testid/aria 语义" + pattern: "reference-inline-preview|mention-candidate-panel|单条消息最多引用 10 个文件" + - from: "frontend/tests/e2e/support/chat-helpers.ts" + to: "frontend/tests/e2e/input-and-compose.spec.ts" + via: "phase 06 引用回归 thread/fixture 入口" + pattern: "THREAD_.*REFERENCE|THREAD_.*STALE" +--- + + +关闭 Phase 06 剩余的 verification gaps:把引用上限/文案/去歧义展示重新对齐 requirement 10,并让 DF-INPUT-008、DF-INPUT-009 变成稳定可回归的 Playwright 场景。 + +Purpose: 收口 `ATREF-02` 与 `ATREF-04`,避免 Phase 06 继续停留在“代码可用但合同与回归不完整”的状态。 +Output: 一次新的 gap-closure 执行会产出对齐后的输入框展示合同,以及不再永久 skip/不再 strict-locator flaky 的 E2E 回归。 + + + +@/home/mt/.codex/get-shit-done/workflows/execute-plan.md +@/home/mt/.codex/get-shit-done/templates/summary.md + + + +@.planning/STATE.md +@.planning/ROADMAP.md +@.planning/REQUIREMENTS.md +@.planning/phases/06-/06-CONTEXT.md +@.planning/phases/06-/06-RESEARCH.md +@.planning/phases/06-/06-VERIFICATION.md +@.planning/phases/06-/06-UAT.md +@.planning/phases/06-/06-UI-SPEC.md +@.planning/phases/06-/06-04-SUMMARY.md +@frontend/src/components/workspace/input-box.tsx +@frontend/tests/e2e/input-and-compose.spec.ts +@frontend/tests/e2e/support/chat-helpers.ts + + +From frontend/src/components/workspace/input-box.tsx: +```typescript +const MAX_REFERENCES_PER_MESSAGE = 10; + +type MentionCandidate = { + key: string; + filename: string; + path?: string; + pathTail: string; + ref_source: "artifact" | "upload"; + ref_kind: "mention"; +}; +``` + +From frontend/tests/e2e/input-and-compose.spec.ts: +```typescript +test("DF-INPUT-008 失效引用不会阻断文本发送(可解释 skip)", async (...) => {}); +test("DF-INPUT-009 引用上限为 10,第 11 个被阻止并提示", async (...) => {}); +``` + + + + + + + Task 1: 对齐引用展示合同与上限 10 + frontend/src/components/workspace/input-box.tsx + + - .planning/phases/06-/06-CONTEXT.md + - .planning/phases/06-/06-VERIFICATION.md + - .planning/phases/06-/06-UI-SPEC.md + - frontend/src/components/workspace/input-box.tsx + + + 按 D-04、D-08、D-09 和 verification gap 1 修改输入框引用合同,不要改动 Phase 06 已确认的 thread-scoped 候选来源、chip 形态或 `additional_kwargs.files` 提交链路。显式恢复 `@` 触发后的候选面板为 `DropdownMenu` 组件实现:必须使用现有 shadcn/radix `DropdownMenu`、`DropdownMenuContent`、`DropdownMenuItem`(若结构需要,可配合同族 trigger/portal 组件),替换当前自定义 `
` 候选层,不允许继续保留自定义浮层作为最终实现。把 `MAX_REFERENCES_PER_MESSAGE`、所有用户可见文案和任何与上限绑定的辅助文案统一恢复为 10;扩展候选/已选引用的展示模型,明确渲染“文件名 + 类型 + 路径尾段”,其中“类型”必须是用户可见的独立维度,而不是仅靠 `ref_source` 或文件扩展隐含表达。若需要为 E2E 提供稳定定位,优先补充明确的 `data-testid`、`aria-label` 或可预测文案,避免依赖模糊文本匹配;不要重新引入 `Tag` 组件、不要把去歧义信息回退成纯路径尾段,也不要用新的自定义 `
` 候选层绕过 D-09。 + + + - `input-box.tsx` 中引用上限常量和提示文案全部为 10,没有残留“6 个”。 + - `@` 候选面板恢复为 `DropdownMenu` / `DropdownMenuContent` / `DropdownMenuItem` 渲染链路,不再使用自定义 `
` 候选层,满足 D-09。 + - dropdown 候选与 inline preview 都能在同名场景表达“文件名 + 类型 + 路径尾段”,满足 ATREF-02。 + - 提供给 E2E 使用的可定位语义是唯一且稳定的,不依赖 strict text locator 猜测。 + + + cd frontend && rg -n "DropdownMenu(Content|Item)?|from ['\\\"]@/components/ui/dropdown-menu['\\\"]" src/components/workspace/input-box.tsx + cd frontend && rg -n "MAX_REFERENCES_PER_MESSAGE\\s*=\\s*10|单条消息最多引用 10 个文件|最多引用 10 个" src/components/workspace/input-box.tsx tests/e2e/input-and-compose.spec.ts + cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-009" + + `input-box.tsx` 明确恢复为基于 `DropdownMenu*` 的候选面板实现,ATREF-02 的代码合同、可见文案和回归断言前提全部回到 requirement=10,且“类型”展示缺口被消除。 + + + + Task 2: 移除永久 skip 并稳定化 DF-INPUT-008/009 回归 + frontend/tests/e2e/input-and-compose.spec.ts, frontend/tests/e2e/support/chat-helpers.ts + + - .planning/phases/06-/06-VERIFICATION.md + - .planning/phases/06-/06-UAT.md + - .planning/phases/06-/06-04-SUMMARY.md + - frontend/tests/e2e/support/chat-helpers.ts + - frontend/tests/e2e/input-and-compose.spec.ts + + + 直接关闭 verification gap 2。删除 DF-INPUT-008 中无条件 `testInfo.skip(true)`,改成可执行的稳定场景:优先通过 Playwright route/fixture 注入或专用 thread helper 制造“已选 artifact 引用在发送前 materialize 失败”的条件,验证错误 toast 出现且文本消息仍发送成功;只有在必需的 thread/env 完全缺失时才允许条件化跳过,并把 gate 收敛到 helper,不允许在测试体内永久 skip。同步重写 DF-INPUT-009:基于 helper 提供的可重复候选集或稳定 thread,覆盖 10 个成功 + 第 11 个被阻止的路径,并把当前容易多命中的 `getByText(...)` 断言替换为带作用域的 toast locator、唯一 data-testid 或明确 aria 语义。保持用例命名、编号和 Phase 06 回归范围不变,不新增与本 gap 无关的 UI 行为。 + + + - DF-INPUT-008 不再包含永久 skip,且能验证 stale toast + 消息继续发送。 + - DF-INPUT-009 明确断言“最多 10 个”,第 11 次添加失败,并且不再触发 strict locator 多命中。 + - 对 thread/env 的依赖被集中到 helper 或 route stub,测试结果不再依赖偶然的线程候选数量。 + + + cd frontend && pnpm -s test:e2e --grep "DF-INPUT-008|DF-INPUT-009" + + ATREF-04 的 E2E 回归护栏稳定可运行,verification 中关于永久 skip 和 strict locator 的两条缺口都能被关闭。 + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| 输入框 UI → 引用候选展示 | 非可信的文件名/路径元数据进入用户可见去歧义文案,容易因展示降级导致误选。 | +| Playwright fixture/route → 回归结论 | 测试数据与真实 UI 交互之间若没有稳定约束,会产生假阳性或 flaky 回归结果。 | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Disposition | Mitigation Plan | +|-----------|----------|-----------|-------------|-----------------| +| T-06-05-01 | T | `frontend/src/components/workspace/input-box.tsx` | mitigate | 明确把类型、路径尾段和上限 10 写成单一展示合同;不要让 `ref_source` 或纯路径尾段承担全部去歧义语义。 | +| T-06-05-02 | D | `frontend/tests/e2e/input-and-compose.spec.ts` | mitigate | 用 helper 或 route stub 固定 stale/上限场景,并使用唯一 locator 或 toast 作用域断言,消除 strict 模式多命中导致的回归失真。 | + + + +- `cd frontend && node --test src/core/threads/hooks.test.ts` +- `cd frontend && pnpm -s typecheck` +- `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-008|DF-INPUT-009"` + + + +- ATREF-02:实现、用户文案、E2E 断言全部统一到“上限 10 + 文件名/类型/路径尾段”。 +- ATREF-04:DF-INPUT-008 不再永久 skip,DF-INPUT-009 不再因 strict locator 多命中失败。 +- 新计划只追加 gap-closure 修复,不回退 `06-04-SUMMARY.md` 已记录的 artifact materialization 与软失败主链路。 + + + +After completion, create `.planning/phases/06-/06-05-SUMMARY.md` + diff --git a/.planning/phases/06-/06-COMMIT-PLAN.md b/.planning/phases/06-/06-COMMIT-GUIDE.md similarity index 97% rename from .planning/phases/06-/06-COMMIT-PLAN.md rename to .planning/phases/06-/06-COMMIT-GUIDE.md index 3a0eb3da..908bc577 100644 --- a/.planning/phases/06-/06-COMMIT-PLAN.md +++ b/.planning/phases/06-/06-COMMIT-GUIDE.md @@ -1,4 +1,4 @@ -# Phase 06 Commit Plan +# Phase 06 Commit Guide ## Commit Order @@ -43,7 +43,7 @@ - `test(phase-06): cover @ reference flow and stale-reference handling` - 最小验证: - `cd frontend && node --test src/core/threads/hooks.test.ts` - - `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-008"` + - `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-008|DF-INPUT-009"` ## Group 4: docs diff --git a/.planning/phases/06-/06-COMMIT-SUMMARY.md b/.planning/phases/06-/06-COMMIT-SUMMARY.md index 64a892a7..4a932169 100644 --- a/.planning/phases/06-/06-COMMIT-SUMMARY.md +++ b/.planning/phases/06-/06-COMMIT-SUMMARY.md @@ -19,7 +19,7 @@ key-files: created: - .planning/phases/06-/06-COMMIT-SUMMARY.md modified: - - .planning/phases/06-/06-COMMIT-PLAN.md + - .planning/phases/06-/06-COMMIT-GUIDE.md - .planning/phases/06-/06-VALIDATION.md - .planning/phases/06-/06-01-SUMMARY.md - .planning/phases/06-/06-02-SUMMARY.md @@ -38,7 +38,6 @@ completed: 2026-04-15 ## Outcome -- `06-COMMIT-PLAN.md` 的 `style -> logic -> tests -> docs` 顺序可执行。 +- `06-COMMIT-GUIDE.md` 的 `style -> logic -> tests -> docs` 顺序可执行,且 tests 组最小 E2E 已包含 `DF-INPUT-009`。 - 四个计划均有对应 SUMMARY,满足阶段执行留痕要求。 - 当前唯一外部阻塞是 E2E 本地服务未启动(`127.0.0.1:2026`)。 - diff --git a/.planning/phases/06-/06-RESEARCH.md b/.planning/phases/06-/06-RESEARCH.md index 4e1608cc..1ca303b2 100644 --- a/.planning/phases/06-/06-RESEARCH.md +++ b/.planning/phases/06-/06-RESEARCH.md @@ -245,17 +245,17 @@ if (Array.isArray(files) && files.length > 0) { | A1 | 后端对 `additional_kwargs.files` 中新增 `ref_kind/ref_source` 字段是前向兼容(忽略或透传) | Architecture Patterns / Standard Stack | 若不兼容,将导致提交失败或渲染异常 | | A2 | 空输入框 Backspace 的“先删引用 chip 再删附件”顺序是更符合用户预期的规则 | Common Pitfalls | 若预期相反,会造成交互争议,需要产品确认 | -## Open Questions +## Resolved Questions 1. **`ref_kind/ref_source` 的最终字段名与枚举值** - - What we know: 语义已锁定为“区分引用文件与上传文件”。[VERIFIED: codebase grep] - - What's unclear: 后端/其他消费端对字段命名是否有硬编码。 - - Recommendation: 在 Phase 6 Plan 的 Wave 0 先做一次全仓搜索与联调验证,确定最终命名再实现。 + - Resolution: 保持 `ref_kind: "mention"` 与 `ref_source: "artifact" | "upload"`,不再改名。 + - Why resolved: Phase 6 已有计划与验证链路都围绕这两个字段展开,且提交契约仍固定落在 `additional_kwargs.files`,符合 D-05/D-06。[VERIFIED: 06-01-PLAN, 06-VERIFICATION] + - Planning impact: gap-closure 只允许补强验证与 UI 去歧义,不再重新设计字段名。 2. **同名同路径尾段时的最终去歧义显示** - - What we know: 决策要求展示“文件名+类型+路径尾段”。[VERIFIED: codebase grep] - - What's unclear: 极端冲突(路径尾段仍相同)时的 fallback 文案。 - - Recommendation: 增加 `source` 徽标(artifact/upload)作为第三级 disambiguation。 + - Resolution: 固定为“文件名 + 类型徽标 + 路径尾段”,若路径尾段仍冲突,再附加 `source` 徽标作为第四层提示,但不替代“类型”维度。 + - Why resolved: 这与锁定决策 D-04 完全对齐,也正是 06-05 要关闭的 verification gap。 + - Planning impact: 06-05 必须在候选与已选引用预览中都兑现该展示合同,不允许回退为仅 `pathTail/ref_source`。 ## Environment Availability @@ -288,7 +288,7 @@ if (Array.isArray(files) && files.length > 0) { | Req ID | Behavior | Test Type | Automated Command | File Exists? | |--------|----------|-----------|-------------------|-------------| | D-01/D-02 | `@` 仅展示当前线程候选并可过滤 | e2e | `pnpm --dir frontend playwright test frontend/tests/e2e/input-and-compose.spec.ts -g "@候选"` | ❌ Wave 0 | -| D-03/D-08 | 选中后显示 chip;最多 10 个 | e2e | `pnpm --dir frontend playwright test frontend/tests/e2e/input-and-compose.spec.ts -g "chip limit"` | ❌ Wave 0 | +| D-03/D-08 | 选中后显示 chip;最多 10 个 | e2e | `pnpm --dir frontend playwright test frontend/tests/e2e/input-and-compose.spec.ts -g "DF-INPUT-009"` | ✅ 由 06-05 落地 | | D-05/D-06 | 提交落入 `additional_kwargs.files` 且含来源元信息 | unit/integration | `pnpm --dir frontend node --test frontend/src/core/threads/hooks.test.ts` | ✅(需扩展用例) | | D-07 | 失效引用软失败,不阻断发送 | e2e | `pnpm --dir frontend playwright test frontend/tests/e2e/input-and-compose.spec.ts -g "stale ref"` | ❌ Wave 0 | @@ -298,9 +298,9 @@ if (Array.isArray(files) && files.length > 0) { - **Phase gate:** Full suite green before `/gsd-verify-work` ### Wave 0 Gaps -- [ ] `frontend/tests/e2e/at-reference-files.spec.ts` — 覆盖 D-01~D-08 的主流程 -- [ ] `frontend/src/core/threads/hooks.test.ts` — 增加 uploads+refs 合并与 soft-fail 场景断言 -- [ ] `frontend/src/core/messages/utils` 相关测试 — 校验新增元信息对渲染兼容性 +- [x] `frontend/src/core/threads/hooks.test.ts` — 已覆盖 uploads+refs 合并与 soft-fail 场景断言(06-01 / 06-03)。 +- [x] `frontend/tests/e2e/input-and-compose.spec.ts` — 已作为主 E2E 文件承接 D-01~D-08;06-05 继续补稳 DF-INPUT-008/009。 +- [x] `frontend/src/core/messages/utils.ts` 契约验证 — 由 06-01 类型契约与 hooks 单测共同覆盖,不再拆独立测试文件。 ## Security Domain diff --git a/.planning/phases/06-/06-REVIEW.md b/.planning/phases/06-/06-REVIEW.md new file mode 100644 index 00000000..50ae109a --- /dev/null +++ b/.planning/phases/06-/06-REVIEW.md @@ -0,0 +1,98 @@ +--- +phase: 06- +reviewed: 2026-04-15T03:54:20Z +depth: standard +files_reviewed: 5 +files_reviewed_list: + - frontend/src/components/workspace/input-box.tsx + - frontend/src/core/threads/submit-files.ts + - frontend/src/core/threads/hooks.ts + - frontend/src/core/threads/hooks.test.ts + - frontend/tests/e2e/input-and-compose.spec.ts +findings: + critical: 0 + warning: 5 + info: 1 + total: 6 +status: issues +advisory: true +--- + +# Phase 06: 代码评审报告(聚焦 06-04 gap-closure) + +**Reviewed:** 2026-04-15T03:54:20Z +**Depth:** standard +**Files Reviewed:** 5 +**Status:** issues(建议性、非阻塞) + +## Summary + +本次重点审查了 06-04 涉及的输入引用与提交流程。未发现高危安全漏洞,但存在若干会导致行为偏差或可观测性不足的问题:附件仅发送路径被阻断、文件 URL 拉取缺少响应状态校验、上传失败被静默吞掉、缓存更新回调对空数据不安全,以及一个永久 skip 的 E2E 用例导致回归覆盖不足。 + +## Warnings + +### WR-01: 仅附件消息会被前端拦截,无法提交 + +**File:** `frontend/src/components/workspace/input-box.tsx:297` +**Issue:** `handleSubmit` 只判断 `message.text` 和 `references`,忽略 `message.files`。当用户仅上传附件而不输入文本时会直接 `return`,与常见聊天上传行为不一致。 +**Fix:** +```tsx +if (!message.text && (message.files?.length ?? 0) === 0 && references.length === 0) { + return; +} +``` + +### WR-02: 文件 URL 转 File 时未校验 HTTP 状态,可能上传错误内容 + +**File:** `frontend/src/core/threads/hooks.ts:509`, `frontend/src/core/threads/hooks.ts:723` +**Issue:** 两处 `fetch(fileUIPart.url)` 后直接 `response.blob()`,未检查 `response.ok`。当 URL 失效返回 404/500 时,错误页面内容也可能被当作文件上传。 +**Fix:** +```ts +const response = await fetch(fileUIPart.url); +if (!response.ok) { + throw new Error(`Failed to fetch file blob: ${response.status}`); +} +const blob = await response.blob(); +``` + +### WR-03: `useSubmitThread` 上传失败后继续发送,存在“静默丢附件” + +**File:** `frontend/src/core/threads/hooks.ts:747-749` +**Issue:** `useSubmitThread` 中上传失败仅 `console.error`,未 toast、未中断提交,用户会看到消息发送成功但附件未随消息进入上下文。 +**Fix:** +```ts +} catch (error) { + console.error("Failed to upload files:", error); + toast.error("附件上传失败,请重试。"); + return; // 或 throw error,阻断本次 submit +} +``` + +### WR-04: React Query 缓存更新回调假设 `oldData` 非空,存在运行时异常风险 + +**File:** `frontend/src/core/threads/hooks.ts:218-219`, `frontend/src/core/threads/hooks.ts:940-941` +**Issue:** 两处 `setQueriesData` 回调直接 `oldData.map(...)`;当缓存尚未建立时 `oldData` 可能为 `undefined`,会触发 `TypeError`。 +**Fix:** +```ts +(oldData: Array | undefined) => oldData?.map((t) => { ... }) ?? oldData +``` + +### WR-05: E2E 用例 DF-INPUT-008 被永久 skip,回归覆盖缺口持续存在 + +**File:** `frontend/tests/e2e/input-and-compose.spec.ts:159` +**Issue:** `testInfo.skip(true, ...)` 是硬编码永久跳过,导致“stale 引用不阻断发送”的端到端行为无法被自动回归验证。 +**Fix:** 改为条件 skip(基于 fixture 能力探测),或通过 mock/测试路由注入 stale 引用,使该用例在可控环境可执行。 + +## Info + +### IN-01: 留有 TODO 占位,后续建议纳入工单 + +**File:** `frontend/src/components/workspace/input-box.tsx:662`, `frontend/src/components/workspace/input-box.tsx:1045` +**Issue:** 仍有连接器/skill 取消能力相关 TODO,表明交互与后端契约尚未完全收敛。 +**Fix:** 将 TODO 关联到明确 issue/phase,避免长期悬置。 + +--- + +_Reviewed: 2026-04-15T03:54:20Z_ +_Reviewer: Claude (gsd-code-reviewer)_ +_Depth: standard_ diff --git a/.planning/phases/06-/06-UAT.md b/.planning/phases/06-/06-UAT.md index 0cf23c7e..bba52648 100644 --- a/.planning/phases/06-/06-UAT.md +++ b/.planning/phases/06-/06-UAT.md @@ -8,7 +8,7 @@ source: - 06-COMMIT-SUMMARY.md - 06-SUMMARY.md started: 2026-04-15T03:14:38Z -updated: 2026-04-15T03:29:00Z +updated: 2026-04-15T03:33:07Z --- ## Current Test @@ -50,7 +50,7 @@ severity: major total: 5 passed: 0 -issues: 4 +issues: 5 pending: 0 skipped: 1 blocked: 0 @@ -114,3 +114,16 @@ blocked: 0 - "补充 artifact 引用的后端可消费上下文字段(与 uploads 对齐)" - "确保任意输入位置输入 `@` 都可触发候选" debug_session: "" +- truth: "若已输入文本,在任意位置输入 `@` 仍应弹出候选;选择文件后不得清空已输入问题文本。" + status: failed + reason: "User reported: 如果已经输入了文字,再输入@的时候,应该弹出候选列表,如果选择了文件,不要清空已经输入的问题" + severity: major + test: 5 + root_cause: "当前选择候选后会执行文本 token 替换并 `trimEnd`,在已有输入场景可能导致用户已输入问题文本被截断或清空。" + artifacts: + - path: "frontend/src/components/workspace/input-box.tsx" + issue: "`selectMentionCandidate` mutates textarea value when resolving mention token" + missing: + - "选择候选后仅移除当前 mention token,不影响其余已输入文本" + - "补充“已有文本 + 中途 @ + 选中文件”回归测试" + debug_session: "" diff --git a/.planning/phases/06-/06-VALIDATION.md b/.planning/phases/06-/06-VALIDATION.md index 74bcd148..3980a444 100644 --- a/.planning/phases/06-/06-VALIDATION.md +++ b/.planning/phases/06-/06-VALIDATION.md @@ -41,6 +41,9 @@ created: 2026-04-15 | 06-01-01 | 01 | 1 | ATREF-03 | T-06-01-01 | 提交结构保持 `additional_kwargs.files` 且包含引用元信息 | unit | `cd frontend && node --test src/core/threads/hooks.test.ts` | ✅ | ✅ green | | 06-02-01 | 02 | 2 | ATREF-01, ATREF-02 | T-06-02-01 | 输入 `@` 显示 thread 内候选并支持 chip 选择 | e2e | `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007"` | ✅ | ⚠️ 环境未启动(ERR_CONNECTION_REFUSED) | | 06-03-01 | 03 | 3 | ATREF-04 | T-06-03-02 | 失效引用场景具备可解释 skip 与单测兜底 | e2e+unit | `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-008" && node --test src/core/threads/hooks.test.ts` | ✅ | ⚠️ E2E 环境依赖,单测已通过 | +| 06-04-ARCHIVE | archived | — | ATREF-01..04 | revision | 原 `06-04-PLAN.md` 已归档,不再参与 execute-phase 发现,避免延续与 D-08 冲突的“上限 6”指令 | docs | `cd /home/mt/Project/deerflow2 && test ! -f .planning/phases/06-/06-04-PLAN.md && test -f .planning/phases/06-/06-04-ARCHIVED.md` | ✅ | ✅ archived | +| 06-05-01 | 05 | 4 | ATREF-02 | T-06-05-01 | 引用展示合同恢复为“文件名 + 类型 + 路径尾段”,且上限 10 | e2e | `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-009"` | ✅ | ⬜ pending | +| 06-05-02 | 05 | 4 | ATREF-04 | T-06-05-02 | DF-INPUT-008/009 不再永久 skip 或 strict-locator flaky | e2e | `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-008|DF-INPUT-009"` | ✅ | ⬜ pending | *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* @@ -48,7 +51,7 @@ created: 2026-04-15 ## Wave 0 Requirements -Existing infrastructure covers all phase requirements. +Existing infrastructure covers all phase requirements; revision pass archives invalid `06-04` and promotes `06-05` as the only active gap-closure execution plan. --- diff --git a/.planning/phases/06-/06-VERIFICATION.md b/.planning/phases/06-/06-VERIFICATION.md new file mode 100644 index 00000000..1d386e12 --- /dev/null +++ b/.planning/phases/06-/06-VERIFICATION.md @@ -0,0 +1,125 @@ +--- +phase: 06- +verified: 2026-04-15T03:50:52Z +status: gaps_found +score: 6/8 must-haves verified +overrides_applied: 0 +gaps: + - truth: "ATREF-02: 选中文件后以可删除 chip 展示,并在同名场景显示“文件名 + 类型 + 路径尾段”,引用上限 10" + status: failed + reason: "实现与测试已切换为上限 6,且候选/预览未显示“类型”维度。" + artifacts: + - path: "frontend/src/components/workspace/input-box.tsx" + issue: "MAX_REFERENCES_PER_MESSAGE=6,提示文案为“单条消息最多引用 6 个文件”;候选 detail 仅 pathTail/ref_source,无类型徽标。" + - path: "frontend/tests/e2e/input-and-compose.spec.ts" + issue: "DF-INPUT-009 断言与“上限 6”绑定。" + missing: + - "若 requirement 仍为 ATREF-02(上限 10),需把上限与提示、断言统一回 10。" + - "补齐同名去歧义中的“类型”展示(文件名 + 类型 + 路径尾段)。" + - truth: "ATREF-04: 引用能力具备自动化回归验证(单测 + E2E)及按 style/logic/tests/docs 的提交分组计划" + status: partial + reason: "提交分组计划已存在,但 E2E 覆盖存在强制 skip 与不稳定断言,回归稳定性不足。" + artifacts: + - path: "frontend/tests/e2e/input-and-compose.spec.ts" + issue: "DF-INPUT-008 使用 testInfo.skip(true) 永久跳过;DF-INPUT-009 运行失败(strict locator 冲突)。" + - path: ".planning/phases/06-/06-COMMIT-GUIDE.md" + issue: "分组计划存在且格式正确(通过)。" + missing: + - "将 DF-INPUT-008 从永久 skip 改为可执行场景或条件化 fixture 注入。" + - "修复 DF-INPUT-009 断言稳定性(避免 getByText 严格模式多命中)。" +--- + +# Phase 6: 在输入框输入@时,可引用已生成文件和已上传附件 Verification Report + +**Phase Goal:** 在当前线程聊天输入框中实现 `@` 文件引用(artifacts + uploads),并通过 `additional_kwargs.files` 稳定提交且具备回归测试。 +**Verified:** 2026-04-15T03:50:52Z +**Status:** gaps_found +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +| --- | --- | --- | --- | +| 1 | 输入 `@` 时仅展示当前线程候选并支持连续过滤(ATREF-01) | ✓ VERIFIED | `input-box.tsx` 由 `thread.values.artifacts` + `useUploadedFiles(threadId)` 聚合候选,`findMentionToken` + `mentionQuery` 做过滤。 | +| 2 | 选中后展示可删除 chip,而非纯文本 | ✓ VERIFIED | `input-box.tsx` 使用 `references` 状态渲染内嵌预览,`移除引用` 按钮与空输入 `Backspace` 删除最后一项。 | +| 3 | 同名场景显示“文件名+类型+路径尾段”,且上限 10(ATREF-02) | ✗ FAILED | 当前为上限 6(`MAX_REFERENCES_PER_MESSAGE = 6`,toast“最多 6 个”);候选 detail 为 `pathTail/ref_source`,无“类型”字段。 | +| 4 | 引用通过 `additional_kwargs.files` 提交,不新增并行主结构 | ✓ VERIFIED | `hooks.ts` 两条提交链路均写入 `additional_kwargs: { files: filesForSubmit }`;`buildFilesForSubmit` 统一 uploads + references。 | +| 5 | 引用提交带来源元信息且兼容既有渲染链路(ATREF-03) | ✓ VERIFIED | `FileInMessage` 含 `ref_kind/ref_source` 可选字段;`message-list-item.tsx` 读取 `additional_kwargs.files` 并正常渲染。 | +| 6 | 失效引用软剔除并继续发送文本(ATREF-03) | ✓ VERIFIED | `buildFilesForSubmit` 统计并丢弃 `stale`;`hooks.ts` toast 后仍继续 `thread.submit`。 | +| 7 | 回归测试覆盖候选/chip/上限/软失败(ATREF-04) | ✗ FAILED | 单测通过;但 E2E 有永久 skip(DF-INPUT-008)且 DF-INPUT-009 失败。 | +| 8 | 存在 style/logic/tests/docs 提交分组计划(ATREF-04) | ✓ VERIFIED | `06-COMMIT-GUIDE.md` 明确 `style -> logic -> tests -> docs` 且含分组文件清单与命令。 | + +**Score:** 6/8 truths verified + +### Required Artifacts + +| Artifact | Expected | Status | Details | +| --- | --- | --- | --- | +| `frontend/src/components/workspace/input-box.tsx` | `@` 候选、过滤、chip、键盘交互 | ✓ VERIFIED | 实现齐全,但与 requirement 的“上限10+类型徽标”不一致。 | +| `frontend/src/core/threads/hooks.ts` | 统一提交 `additional_kwargs.files` | ✓ VERIFIED | 两条 submit 链路均使用 `filesForSubmit`。 | +| `frontend/src/core/threads/submit-files.ts` | uploads/references 合并与 artifact 物化 | ✓ VERIFIED | `materializeArtifactReferences` + `buildFilesForSubmit` 可用。 | +| `frontend/src/core/messages/utils.ts` | `FileInMessage` 扩展兼容 | ✓ VERIFIED | 扩展字段为可选,未破坏旧路径。 | +| `frontend/src/core/threads/hooks.test.ts` | 提交流程与软失败单测 | ✓ VERIFIED | 4 条测试通过。 | +| `frontend/tests/e2e/input-and-compose.spec.ts` | `@` 引用 E2E 回归 | ⚠️ HOLLOW — wired but data disconnected | 用例存在;DF-INPUT-008 永久 skip,DF-INPUT-009 当前失败。 | +| `.planning/phases/06-/06-COMMIT-GUIDE.md` | 分组提交计划 | ✓ VERIFIED | 顺序、规则与样例齐全,且 tests 组最小 E2E 验证已覆盖 `DF-INPUT-009`。 | + +### Key Link Verification + +| From | To | Via | Status | Details | +| --- | --- | --- | --- | --- | +| `input-box.tsx` | `uploads/hooks.ts` | `useUploadedFiles(threadId)` | ✓ WIRED | 实际调用存在。 | +| `input-box.tsx` | `thread.values.artifacts` | thread 内 artifacts 候选源 | ✓ WIRED | 直接读取 `thread.values.artifacts`。 | +| `input-box.tsx` | `hooks.ts` | `PromptInputMessage.references` | ✓ WIRED | `handleSubmit` 将 `references` 合并进 message,提交给上层 `onSubmit`。 | +| `hooks.ts` | `submit-files.ts` | `buildFilesForSubmit/materializeArtifactReferences` | ✓ WIRED | 已 import 并用于提交前标准化。 | +| `hooks.ts` | `additional_kwargs.files` | 提交 envelope | ✓ WIRED | `thread.submit` 中写入 `additional_kwargs.files`。 | +| `input-box.tsx` | `dropdown-menu.tsx` | Dropdown 候选面板 | ⚠️ PARTIAL | 当前候选面板为自定义 `
`;不再使用 `DropdownMenuContent/Item`。 | + +### Data-Flow Trace (Level 4) + +| Artifact | Data Variable | Source | Produces Real Data | Status | +| --- | --- | --- | --- | --- | +| `input-box.tsx` | `mentionCandidates` | `thread.values.artifacts` + `useUploadedFiles(threadId)` | Yes(`listUploadedFiles` 走后端 `/uploads/list`) | ✓ FLOWING | +| `hooks.ts` | `filesForSubmit` | `uploadedFileInfo` + `normalizedReferences` | Yes(上传接口 + artifact fetch/upload) | ✓ FLOWING | +| `message-list-item.tsx` | `files` | `message.additional_kwargs.files` | Yes(数组即渲染文件卡片) | ✓ FLOWING | +| `input-and-compose.spec.ts` | E2E 断言链路 | Playwright + 线程 fixture | Partial(存在 skip / flaky 断言) | ⚠️ STATIC | + +### Behavioral Spot-Checks + +| Behavior | Command | Result | Status | +| --- | --- | --- | --- | +| 提交构建与软失败单测 | `cd frontend && node --test src/core/threads/hooks.test.ts` | 4 passed, 0 failed | ✓ PASS | +| 类型检查 | `cd frontend && pnpm -s typecheck` | exit 0 | ✓ PASS | +| 上限回归 E2E | `cd frontend && pnpm -s test:e2e --grep "DF-INPUT-009"` | 1 failed(strict locator 多命中) | ✗ FAIL | +| 候选/软失败 E2E 抽样 | `cd frontend && timeout 10s pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-008|DF-INPUT-009"` | 007 pass,008 skip,009 未在 10s 窗口内收尾 | ? SKIP | + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +| --- | --- | --- | --- | --- | +| ATREF-01 | 06-02, 06-05 | `@` 仅显示当前线程候选并支持过滤 | ✓ SATISFIED | 当前实现已满足,06-05 不改候选来源只修剩余 gap。 | +| ATREF-02 | 06-02, 06-05 | chip 展示 + 同名去歧义(文件名+类型+路径尾段)+ 上限 10 | ✗ BLOCKED | 需由 06-05 把“上限 6/缺类型维度”统一修回 requirement=10。 | +| ATREF-03 | 06-01, 06-05 | `additional_kwargs.files` 提交 + 元信息 + stale 软剔除 | ✓ SATISFIED | `hooks.ts/submit-files.ts/utils.ts` 主链路完整;06-05 只补验证稳定性。 | +| ATREF-04 | 06-03, 06-05 | 单测+E2E 回归 + 提交分组计划 | ✗ BLOCKED | 提交分组计划满足;06-05 负责关闭永久 skip 与 strict locator 缺口。 | + +Orphaned requirements for Phase 6: None(`ATREF-01..04` 均已在计划 frontmatter 声明)。 + +### Anti-Patterns Found + +| File | Line | Pattern | Severity | Impact | +| --- | --- | --- | --- | --- | +| `frontend/tests/e2e/input-and-compose.spec.ts` | 158 | `testInfo.skip(true, ...)` | ⚠️ Warning | 软失败场景永久跳过,降低回归可信度。 | +| `frontend/src/components/workspace/input-box.tsx` | 662 | `TODO` connector 占位注释 | ℹ️ Info | 与本阶段目标无直接阻断。 | + +### Gaps Summary + +Phase 6 核心链路(`@` 候选、引用提交到 `additional_kwargs.files`、stale 软失败)已落地并可运行,但仍有两项合同级缺口: + +1. `ATREF-02` 与当前实现不一致:需求写的是上限 10 且含“类型”去歧义,代码/测试为上限 6 且缺少类型显示;该缺口现由 `06-05-PLAN.md` 接手修复。 +2. `ATREF-04` 自动化回归不稳定:存在永久 skip 与失败断言,尚不足以作为稳定回归护栏;tests/doc hygiene 已迁移到 `06-COMMIT-GUIDE.md`,`06-05` 继续收口用例稳定性。 + +--- + +_Verified: 2026-04-15T03:50:52Z_ +_Verifier: Claude (gsd-verifier)_