deerflow2/.planning/phases/06-/06-05-PLAN.md

179 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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"
---
<objective>
关闭 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 回归。
</objective>
<execution_context>
@/home/mt/.codex/get-shit-done/workflows/execute-plan.md
@/home/mt/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
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 (...) => {});
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: 对齐引用展示合同与上限 10</name>
<files>frontend/src/components/workspace/input-box.tsx</files>
<read_first>
- .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
</read_first>
<action>
按 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 组件),替换当前自定义 `<div>` 候选层,不允许继续保留自定义浮层作为最终实现。把 `MAX_REFERENCES_PER_MESSAGE`、所有用户可见文案和任何与上限绑定的辅助文案统一恢复为 10扩展候选/已选引用的展示模型,明确渲染“文件名 + 类型 + 路径尾段”,其中“类型”必须是用户可见的独立维度,而不是仅靠 `ref_source` 或文件扩展隐含表达。若需要为 E2E 提供稳定定位,优先补充明确的 `data-testid`、`aria-label` 或可预测文案,避免依赖模糊文本匹配;不要重新引入 `Tag` 组件、不要把去歧义信息回退成纯路径尾段,也不要用新的自定义 `<div>` 候选层绕过 D-09。
</action>
<acceptance_criteria>
- `input-box.tsx` 中引用上限常量和提示文案全部为 10没有残留“6 个”。
- `@` 候选面板恢复为 `DropdownMenu` / `DropdownMenuContent` / `DropdownMenuItem` 渲染链路,不再使用自定义 `<div>` 候选层,满足 D-09。
- dropdown 候选与 inline preview 都能在同名场景表达“文件名 + 类型 + 路径尾段”,满足 ATREF-02。
- 提供给 E2E 使用的可定位语义是唯一且稳定的,不依赖 strict text locator 猜测。
</acceptance_criteria>
<verify>
<automated>cd frontend && rg -n "DropdownMenu(Content|Item)?|from ['\\\"]@/components/ui/dropdown-menu['\\\"]" src/components/workspace/input-box.tsx</automated>
<automated>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</automated>
<automated>cd frontend && pnpm -s test:e2e --grep "DF-INPUT-007|DF-INPUT-009"</automated>
</verify>
<done>`input-box.tsx` 明确恢复为基于 `DropdownMenu*` 的候选面板实现ATREF-02 的代码合同、可见文案和回归断言前提全部回到 requirement=10且“类型”展示缺口被消除。</done>
</task>
<task type="auto">
<name>Task 2: 移除永久 skip 并稳定化 DF-INPUT-008/009 回归</name>
<files>frontend/tests/e2e/input-and-compose.spec.ts, frontend/tests/e2e/support/chat-helpers.ts</files>
<read_first>
- .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
</read_first>
<action>
直接关闭 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 行为。
</action>
<acceptance_criteria>
- DF-INPUT-008 不再包含永久 skip且能验证 stale toast + 消息继续发送。
- DF-INPUT-009 明确断言“最多 10 个”,第 11 次添加失败,并且不再触发 strict locator 多命中。
- 对 thread/env 的依赖被集中到 helper 或 route stub测试结果不再依赖偶然的线程候选数量。
</acceptance_criteria>
<verify>
<automated>cd frontend && pnpm -s test:e2e --grep "DF-INPUT-008|DF-INPUT-009"</automated>
</verify>
<done>ATREF-04 的 E2E 回归护栏稳定可运行verification 中关于永久 skip 和 strict locator 的两条缺口都能被关闭。</done>
</task>
</tasks>
<threat_model>
## 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 模式多命中导致的回归失真。 |
</threat_model>
<verification>
- `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"`
</verification>
<success_criteria>
- ATREF-02实现、用户文案、E2E 断言全部统一到“上限 10 + 文件名/类型/路径尾段”。
- ATREF-04DF-INPUT-008 不再永久 skipDF-INPUT-009 不再因 strict locator 多命中失败。
- 新计划只追加 gap-closure 修复,不回退 `06-04-SUMMARY.md` 已记录的 artifact materialization 与软失败主链路。
</success_criteria>
<output>
After completion, create `.planning/phases/06-/06-05-SUMMARY.md`
</output>