179 lines
9.8 KiB
Markdown
179 lines
9.8 KiB
Markdown
---
|
||
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-04:DF-INPUT-008 不再永久 skip,DF-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>
|