373 lines
23 KiB
Markdown
Raw 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: 03-dialog-feedback
plan: 03
type: execute
wave: 3
depends_on:
- "03-01"
- "03-02"
files_modified:
- docs/修改记录.md
autonomous: true
requirements:
- CRED-FE-04
- CRED-FE-05
must_haves:
truths:
- "docs/修改记录.md 顶部存在 [2026-05-08] Phase 3 条目(在 Phase 2 条目之上)"
- "条目按 CLAUDE.md L72-82 格式包含「文件路径 / 修改类型 / 修改内容 / 修改原因」四段"
- "条目「修改内容」段明确列出 3 个改动文件app/layout.tsx + components/ai-model/credential-slot-dialog.tsx + app/ai-model/page.tsx"
- "条目「修改原因」段显式说明 access_token 强制输入的权衡 + 候选下一周期 milestone识别脱敏掩码保留旧值"
- "条目「跨项目联动」段写「无 — Phase 3 是前端 UI 收尾access_token 强制输入语义为 Phase 1+2 已建立的前后端互引commit 46d72b8的延续'留空保留旧值' 语义需后端识别脱敏掩码格式 + 保留旧值,已记入候选下一周期 milestone不属于 v1.0 范畴)」"
- "条目「服务端联动」字段同上「跨项目联动」"
- "Plan 级双重验证tsc 整体反向断言 + grep 13 条 specifics 全表 + lockfile diff 0 行)全部通过"
artifacts:
- path: "docs/修改记录.md"
provides: "Phase 3 修改记录条目(在 Phase 2 [2026-05-08] 条目上方)"
contains: "[2026-05-08] Phase 3"
key_links:
- from: "docs/修改记录.md Phase 3 条目"
to: "Phase 2 [2026-05-08] 条目"
via: "在其上方追加CLAUDE.md「最新在最前」规则"
pattern: "Phase 3.*\\n.*Phase 2"
---
<objective>
Phase 3 收尾 plan
1.`docs/修改记录.md` 顶部追加 [2026-05-08] Phase 3 条目(在 Phase 2 条目上方),按 CLAUDE.md L72-82 格式 + Phase 2 条目作为同期模板
2. 跑 plan 级双重验证tsc 反向断言 + 13 条 grep specifics 全表 + lockfile diff 0 行 + lint 跳过沿用 Phase 1+2 判定
**Purpose**CLAUDE.md L70-94 强制每次代码改动同会话追加修改记录(自动执行),且 Phase 3 引入了一个**业务语义权衡**access_token 强制输入 vs 「留空保留旧值」),必须显式记入「修改原因」便于未来开后端 patch milestone 时反查。
**Output**`docs/修改记录.md` 顶部 +1 个完整条目plan 级整体验证报告。
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-dialog-feedback/03-CONTEXT.md
@.planning/phases/03-dialog-feedback/03-01-SUMMARY.md
@.planning/phases/03-dialog-feedback/03-02-SUMMARY.md
@CLAUDE.md
@docs/修改记录.md
</context>
<tasks>
<task type="auto">
<name>Task 1docs/修改记录.md 顶部追加 Phase 3 条目</name>
<files>docs/修改记录.md</files>
<read_first>
必读 `docs/修改记录.md` 头部:
- L1-22「修改格式说明」段4 字段格式:文件路径 / 修改类型 / 修改内容 / 修改原因)
- L24-26「修改历史」标题 + 「<!-- 新的修改记录添加在此处下方,最新的在最前面 -->」标记
- L28-58上一条 [2026-05-08] Phase 2 条目(**作为格式模板**,特别注意「跨项目联动」与「服务端联动」两段写法)
确认插入位置L26标记注释之后、L28Phase 2 条目)之前。
</read_first>
<action>
**逐字插入以下条目**到 `docs/修改记录.md` 的 L26 注释 `<!-- 新的修改记录添加在此处下方,最新的在最前面 -->` 之后、L28 `### [2026-05-08] Phase 2前端...` 之前。
**完整条目(直接复制粘贴)**
```markdown
### [2026-05-08] Phase 3前端凭据槽位编辑对话框 + 提交反馈
配套服务端 Phase本 phase **不**触达服务端;与服务端 v1.0 Phase 2「管理端读写接口」commit `46d72b8` 既有契约保持兼容GET 脱敏掩码 + PUT 全字段覆写语义不变)
覆盖前端需求CRED-FE-04、CRED-FE-05
- **文件路径**
- `app/layout.tsx`(修改)
- `components/ai-model/credential-slot-dialog.tsx`(新增)
- `app/ai-model/page.tsx`(修改)
- **修改类型**: 修改 + 新增(前端 UI 收尾;纯前端,无新依赖、不动 lockfile、不触达服务端
- **修改内容**:
- `app/layout.tsx`(修复仓库 pre-existing 死代码 bug
- 顶部新增 `import { Toaster } from "@/components/ui/sonner"`
- `<body>{children}</body>``{children}` 之后追加 `<Toaster />`
- 修复仓库内 9 处 `toast(...)` 调用全部静默的 dead-code 状态(`components/ui/sonner.tsx` 早就存在 Toaster 包装但**从未在 RootLayout 挂载**
- `components/ai-model/credential-slot-dialog.tsx`**新建** ~150 行):
- 顶部 `"use client"` 指令;具名导出 `CredentialSlotDialog`
- 文件命名 **kebab-case**(与仓库 9 个现有业务对话框 `user-form-dialog.tsx` / `add-song-dialog.tsx` 等对齐)
- 表单技术栈React Hook Form + Zod + shadcn Form wrapper1:1 模板自 `components/users/user-form-dialog.tsx`
- Zod schema`appId` + `accessToken``min(1)`access_token **强制输入**,见「修改原因」段权衡说明)
- 受控接口:`{ open: boolean; onOpenChange: (open: boolean) => void }`
- 打开时 `useEffect``getCredentialSlot()` 拉取 → `form.reset({ appId: data.appId, accessToken: "" })` —— **accessToken 永远默认空串**,绝不回填脱敏掩码
- `<Input placeholder={slot?.accessTokenMasked} />` —— 仅作视觉提示
- `<FormDescription>每次保存都需要重新输入 Access Token不会显示原值避免回写脱敏掩码</FormDescription>`
- `updatedAt``new Date(slot.updatedAt).toLocaleString("zh-CN")` 中文只读显示
- 提交成功:`toast.success("凭据槽位已更新", { description: "配置已生效" })` + `handleOpenChange(false)`
- 提交失败:`toast.error("保存失败", { description: handleApiError(e) })` + 对话框保持打开 + 表单值不丢
- 关闭时 `form.reset({ appId: "", accessToken: "" })` + `setSlot(null)` —— 避免下次打开残留上次输入
- useEffect cleanup `cancelled` flag 防止快速开关导致的 race condition
- **关键 import 决策**(避免仓库内同名 dead code
- `import { toast } from "sonner"` —— **不**走 `@/hooks/use-toast`Radix Toast 实现,与 Sonner 不通信)
- `import { handleApiError } from "@/lib/api/error-handler"` —— **不**走 barrel `@/lib/api`barrel 内有同名重复定义)
- `app/ai-model/page.tsx`
- 删除 L9-15 Dialog 系列命名导入整段(`Dialog / DialogContent / DialogDescription / DialogHeader / DialogTitle`)—— 占位 Dialog 删除后 page 不再直接使用 Dialog primitive
- 在 lucide-react import 之后新增 `import { CredentialSlotDialog } from "@/components/ai-model/credential-slot-dialog"`
- 删除 L473-485 占位 Dialog`<DialogTitle>通用凭据槽位</DialogTitle>` + `<DialogDescription>对话框真实内容由 Phase 3 落地</DialogDescription>`
- 替换为 `<CredentialSlotDialog open={isCredentialDialogOpen} onOpenChange={setIsCredentialDialogOpen} />`
- 保留 L1 `"use client"` / L20-25 mounted state + isCredentialDialogOpen state + useEffect 守卫 / L35-43「凭据槽位」Button 入口(含 `mounted && hasPermission("credential-slot")` 守卫)
- Tabs / TabsContent / Card 等其余内容L18-471逐字不动
- **修改原因**:
- 收尾 Milestone v1.0「通用凭据槽位前端集成」:让授权运营能查看脱敏的当前凭据、安全提交新值,且成功 / 失败两条路径都有清晰的中文 toast 反馈
- 修复 `app/layout.tsx` 的 pre-existing dead code仓库 `components/ui/sonner.tsx` 早已存在 Sonner Toaster 包装但**从未挂载到 RootLayout**,导致仓库内 9 处既有 `toast(...)` 调用全部静默;本 phase 顺手修复(否则 Phase 3 反馈不可见)
- 拆出独立组件 `credential-slot-dialog.tsx` 而非把表单内联进 page.tsx(a) 与仓库 9 个现有业务对话框抽离风格一致;(b) 让 page.tsx 保持简洁,不掺业务表单状态;(c) 关闭对话框时组件级 form.reset 隔离干净
- **业务语义权衡(重要 — 候选下一周期 milestone 锚点)**:本 phase Zod schema 把 `accessToken: z.string().min(1)` —— **强制每次重输** access_token**不实现** ROADMAP success criteria #2 中提到的「留空保留旧值」语义。原因:
- 后端 PUT 是全字段覆写语义qy_lty 后端 v1.0 Phase 2 已锁定 commit `46d72b8`
- 后端 GET 返回的 access_token 字段是脱敏掩码(末 4 位明文 + 前缀 `*`),前端永远拿不到真值
- 「留空保留旧值」需后端配合识别 PUT body 中 access_token 的脱敏掩码格式并保留旧值(后端逻辑:`if access_token == mask_token(current.access_token): preserve old`
- 该后端识别逻辑不在 Milestone v1.0 范畴,**已记入候选下一周期 milestone**(参见 PROJECT.md / REQUIREMENTS.md 候选清单 + STATE.md 风险段)
- 当前实现退化为「每次保存都要重输 access_token」—— UX 略差但语义正确(永远不会回写脱敏掩码导致后端清空真实凭据)
- 沿用 Phase 1 已建立的「`accessTokenMasked` vs `accessToken` 类型层屏障」(前者是脱敏字符串、后者是明文)—— TS 编译期会拦截「把脱敏字符串赋给 accessToken 字段」的 bug 路径
- Sonner`components/ui/sonner.tsx`+ `lib/api/error-handler.ts:handleApiError` 是 CONTEXT D-Toast / D-错误处理 锁定的双依赖;不引入新依赖、不混合 Radix Toast hook、不走 barrel 同名重复定义,避免仓库内 dead code 串扰
- **跨项目联动**: 无 — Phase 3 是前端 UI 收尾access_token 强制输入语义为 Phase 1+2 已建立的前后端互引commit `46d72b8`)的延续;'留空保留旧值' 语义需后端识别脱敏掩码格式 + 保留旧值,已记入候选下一周期 milestone不属于 v1.0 范畴)
- **服务端联动**: 同上「跨项目联动」字段;后端 commit `46d72b8` 已建立互引闭环,本 phase 无需再次互引;未来若启动「识别脱敏掩码保留旧值」的后端 patch milestone届时双端各写新一轮互引条目
```
**严格约束**
- 插入位置:在 L26 注释之后、L28 Phase 2 条目之前CLAUDE.md L72「最新在最前面」规则
- 不动 L1-22 的「修改格式说明」段
- 不修改 L28 之后已有的任何条目Phase 2 / Phase 1 / [2026-05-07] 锁定契约)
- 「修改原因」段必须包含「access_token 强制输入」「留空保留旧值」「候选下一周期 milestone」三个关键短语方便未来 grep 反查
- 「跨项目联动」字段值**逐字使用** CONTEXT.md / 上下文锁定的中文文本(注意标点、引号、破折号 `—`
- 不引入跨项目互引条目(本 phase 不触达后端)
</action>
<verify>
<automated>
cd C:\Users\admin\Desktop\Lila-Server\qy-lty-admin
# A 段:条目存在性 + 标题正确
Select-String -Path 'docs/修改记录.md' -Pattern '### \[2026-05-08\] Phase 3'
# 期望1 行命中
# B 段插入位置正确Phase 3 出现在 Phase 2 之上)
$content = Get-Content 'docs/修改记录.md' -Raw
$phase3Pos = $content.IndexOf('### [2026-05-08] Phase 3')
$phase2Pos = $content.IndexOf('### [2026-05-08] Phase 2')
if ($phase3Pos -ge 0 -and $phase2Pos -ge 0 -and $phase3Pos -lt $phase2Pos) { Write-Host 'OK: Phase 3 在 Phase 2 上方' } else { Write-Host 'FAIL' }
# 期望:'OK: Phase 3 在 Phase 2 上方'
# C 段4 段格式齐全CLAUDE.md L72-82
Select-String -Path 'docs/修改记录.md' -Pattern '^- \*\*文件路径\*\*' | Where-Object { $_.LineNumber -lt 60 }
# 期望:在 Phase 3 段内(前 60 行≥1 行命中
Select-String -Path 'docs/修改记录.md' -Pattern '^- \*\*修改类型\*\*' | Where-Object { $_.LineNumber -lt 60 }
# 期望≥1 行命中
Select-String -Path 'docs/修改记录.md' -Pattern '^- \*\*修改内容\*\*' | Where-Object { $_.LineNumber -lt 60 }
# 期望≥1 行命中
Select-String -Path 'docs/修改记录.md' -Pattern '^- \*\*修改原因\*\*' | Where-Object { $_.LineNumber -lt 60 }
# 期望≥1 行命中
# D 段3 个改动文件全列
Select-String -Path 'docs/修改记录.md' -Pattern '`app/layout\.tsx`'
Select-String -Path 'docs/修改记录.md' -Pattern '`components/ai-model/credential-slot-dialog\.tsx`'
Select-String -Path 'docs/修改记录.md' -Pattern '`app/ai-model/page\.tsx`.*修改' | Where-Object { $_.LineNumber -lt 80 }
# E 段:业务权衡关键短语
Select-String -Path 'docs/修改记录.md' -Pattern '强制每次重输|强制输入'
# 期望≥1 行
Select-String -Path 'docs/修改记录.md' -Pattern '留空保留旧值'
# 期望≥1 行
Select-String -Path 'docs/修改记录.md' -Pattern '候选下一周期 milestone|候选下一周期'
# 期望≥1 行
Select-String -Path 'docs/修改记录.md' -Pattern '识别脱敏掩码'
# 期望≥1 行
# F 段:「跨项目联动」+「服务端联动」字段都在
Select-String -Path 'docs/修改记录.md' -Pattern '\*\*跨项目联动\*\*' | Where-Object { $_.LineNumber -lt 60 }
Select-String -Path 'docs/修改记录.md' -Pattern '\*\*服务端联动\*\*' | Where-Object { $_.LineNumber -lt 60 }
# G 段CRED-FE-04 + CRED-FE-05 显式列出
Select-String -Path 'docs/修改记录.md' -Pattern 'CRED-FE-04.*CRED-FE-05|CRED-FE-04、CRED-FE-05'
# 期望≥1 行
</automated>
</verify>
<done>
- `docs/修改记录.md` 顶部存在 `### [2026-05-08] Phase 3` 条目
- Phase 3 条目位于 Phase 2 条目上方IndexOf 比较通过)
- 4 段格式(文件路径 / 修改类型 / 修改内容 / 修改原因)全齐
- 3 个改动文件app/layout.tsx / credential-slot-dialog.tsx / app/ai-model/page.tsx全列
- 业务权衡关键短语(强制输入 / 留空保留旧值 / 候选下一周期 milestone / 识别脱敏掩码)全部 ≥1 行命中
- 「跨项目联动」+「服务端联动」字段都在
- CRED-FE-04 + CRED-FE-05 显式列出
</done>
</task>
<task type="auto">
<name>Task 2Plan 级整体双重验证(沿用 Phase 1+2 模式)</name>
<files></files>
<read_first>
回顾 STATE.md L84-85 Plan 02-02 落地说明,本 task 沿用相同验证模式A 段 tsc 反向断言 + B 段 14 条 grep 全表 + C 段 4 个 lockfile diff + D 段 lint 跳过判定)。
</read_first>
<action>
**执行 4 段验证脚本,逐段记录结果到 SUMMARY.md**
**A 段tsc 整体 + 反向断言(必须 0 条指向本 phase 改动文件)**
```powershell
cd C:\Users\admin\Desktop\Lila-Server\qy-lty-admin
# 整体(含 67 条存量错误,与本 phase 无关)
$tscOutput = npx tsc --noEmit 2>&1
$totalErrors = ($tscOutput | Select-String -Pattern '\.tsx?\(\d+,\d+\):' -AllMatches).Matches.Count
Write-Host "tsc 整体错误总数: $totalErrors(应在 60-70 之间,与 Phase 1+2 持平)"
# 反向断言:本 phase 3 个改动文件应 0 条命中
$tscOutput | Select-String -Pattern 'app/layout\.tsx|app\\layout\.tsx|components/ai-model/credential-slot-dialog\.tsx|components\\ai-model\\credential-slot-dialog\.tsx|app/ai-model/page\.tsx|app\\ai-model\\page\.tsx'
# 期望:无任何输出
```
**B 段13 条 grep specifics 全表CONTEXT.md L253-268 表)**
```powershell
# 编号沿用 CONTEXT.md L253-268 表
$file = 'components/ai-model/credential-slot-dialog.tsx'
# spec #1 文件存在 + 导出
Select-String -Path $file -Pattern 'export function CredentialSlotDialog'
# spec #2 RHF + Zod
Select-String -Path $file -Pattern 'useForm'
Select-String -Path $file -Pattern 'zodResolver'
Select-String -Path $file -Pattern 'z\.object'
# spec #3 useEffect 调 getCredentialSlot
Select-String -Path $file -Pattern 'useEffect'
Select-String -Path $file -Pattern 'getCredentialSlot'
# spec #4 反向accessToken 默认空串、不是 accessTokenMasked
Select-String -Path $file -Pattern 'defaultValues.*accessTokenMasked'
# 期望0 行
Select-String -Path $file -Pattern "accessToken: \`"\`""
# 期望≥1 行("accessToken": ""
# spec #5 placeholder 用 accessTokenMasked
Select-String -Path $file -Pattern 'placeholder.*accessTokenMasked'
# spec #6 提示文字
Select-String -Path $file -Pattern '每次保存都需要重新输入'
# spec #7 updatedAt 只读显示
Select-String -Path $file -Pattern 'slot\.updatedAt'
# spec #8 submit 调 updateCredentialSlot
Select-String -Path $file -Pattern 'updateCredentialSlot'
# spec #9 toast.success 中文
Select-String -Path $file -Pattern 'toast\.success.*凭据槽位已更新'
# spec #10 handleApiError
Select-String -Path $file -Pattern 'handleApiError'
# spec #11 page.tsx 占位删除 + 替换
Select-String -Path 'app/ai-model/page.tsx' -Pattern 'import \{ CredentialSlotDialog \} from "@/components/ai-model/credential-slot-dialog"'
Select-String -Path 'app/ai-model/page.tsx' -Pattern '<CredentialSlotDialog'
Select-String -Path 'app/ai-model/page.tsx' -Pattern '对话框真实内容由 Phase 3 落地'
# 期望0 行(已删干净)
# spec #12 tsc 过滤A 段已覆盖)
# spec #13 修改记录条目Task 1 已覆盖)
# Layout Toaster 挂载Plan 03-01 落地)
Select-String -Path 'app/layout.tsx' -Pattern 'from "@/components/ui/sonner"'
Select-String -Path 'app/layout.tsx' -Pattern '<Toaster\s*/>'
# 反向(防回归):不走 Radix Toast hook + 不走 barrel handleApiError
Select-String -Path $file -Pattern 'from\s+"@/hooks/use-toast"'
# 期望0 行
Select-String -Path $file -Pattern '^"use client"'
# 期望1 行(首行)
```
**C 段4 个 manifest+lockfile 在工作区 + HEAD~3 比较均 0 行 diff**
```powershell
# 工作区 diff应已被 git add 且 commit 后查 HEAD
git diff --stat HEAD -- package.json yarn.lock package-lock.json pnpm-lock.yaml
# 期望0 行
# 跨 Phase 3 三个 plan 的累计 diffHEAD~3 = 03-01 之前)
# 注意:父仓库 Lila-Server 视角,路径需含子目录前缀
git diff --stat HEAD~3 HEAD -- 'qy-lty-admin/package.json' 'qy-lty-admin/yarn.lock' 'qy-lty-admin/package-lock.json' 'qy-lty-admin/pnpm-lock.yaml'
# 期望0 行Phase 3 全程不引入新依赖)
```
**D 段lint 跳过判定(沿用 Phase 1+2**
仓库 `package.json``"lint": "next lint"` 在执行时若无 `.eslintrc*` / `eslint-config-next` 会触发交互式 prompt非自动通过本 phase 沿用 Phase 1 + Phase 2 已建立的判定(参见 STATE.md L83-85
- 不主动跑 `npm run lint`
- 在 SUMMARY.md「lint 状态」段写:「无 .eslintrc* / eslint-config-next → next lint 进入 interactive prompt 不可自动判定 → 沿用 Phase 1+2 判定不阻塞ESLint bootstrap 留待候选 #3 milestone」
**记录验证报告**:把 A/B/C/D 四段全部输出 + 解读写到 `.planning/phases/03-dialog-feedback/03-03-SUMMARY.md`「Plan 级双重验证」段。
</action>
<verify>
<automated>
cd C:\Users\admin\Desktop\Lila-Server\qy-lty-admin
# 整 phase 工作区在 commit 后净状态:本 task 不改任何代码 / lockfile仅查询验证
# 验证 SUMMARY.md 已生成
Test-Path '.planning/phases/03-dialog-feedback/03-03-SUMMARY.md'
# 期望True
# SUMMARY 内含 4 段标题
Select-String -Path '.planning/phases/03-dialog-feedback/03-03-SUMMARY.md' -Pattern 'A 段|B 段|C 段|D 段'
# 期望4 段都命中
</automated>
</verify>
<done>
- A 段tsc 整体错误数 60-70 之间(沿用 67 条存量水位) + 反向断言对 3 个改动文件layout.tsx / credential-slot-dialog.tsx / page.tsx输出 0 行
- B 段13 条 grep specifics 全表跑过;正向 ≥1 行命中、反向 0 行命中全部满足Layout Toaster 挂载验证通过
- C 段:工作区 + HEAD~3 比较均 0 行 lockfile diff
- D 段lint 跳过判定文字化记入 SUMMARY
- SUMMARY.md 已生成并包含 A/B/C/D 段完整报告
</done>
</task>
</tasks>
<verification>
**Phase 3 整体收尾验证**(本 plan 已涵盖,此处汇总 Milestone v1.0 收尾态):
1. **修改记录闭环**CLAUDE.md L70-94 强制规则满足 —— Phase 1 / Phase 2 / Phase 3 三条条目按时间倒序排列在 docs/修改记录.md 顶部
2. **5 条 ROADMAP success criteria 对应**ROADMAP.md L58-63
- #1 打开自动 GET + appId 明文 + accessToken 掩码 placeholder + updatedAt 只读 ✓ Plan 03-02 Task 1
- #2 RHF + Zod 校验 + 不回写脱敏掩码(强制输入语义;权衡说明已写入修改记录「修改原因」)✓ Plan 03-02 Task 1 + Plan 03-03 Task 1
- #3 提交成功 toast.success + 自动关闭 + 重新打开自动 reload ✓ Plan 03-02 Task 1
- #4 提交失败 handleApiError 映射 + toast.error + 对话框保持 + 表单不丢 ✓ Plan 03-02 Task 1
- #5 端到端串联(依赖后端 Phase 2commit 46d72b8 已落地)—— 程序化验证通过;浏览器 E2E 推迟(无 E2E 框架CONTEXT 已声明)
3. **不引入新依赖**4 个 lockfile 在 HEAD~3..HEAD 范围 0 行 diff
4. **不破坏 Phase 1+2**Phase 1 落地的 `lib/api/credential-slot.ts` + `lib/api/index.ts` 未改 / Phase 2 落地的 `lib/permissions.ts` + `app/ai-model/page.tsx` Button 入口与 mounted 守卫未改
**Milestone v1.0 收尾态**:本 plan 完成后Milestone v1.0「通用凭据槽位前端集成」全部 5 条前端需求CRED-FE-01~05100% 交付;后端 v1.0CRED-01~06已于 commit 46d72b8 收尾。Milestone 进度 100%3/3 phase
</verification>
<success_criteria>
- [ ] `docs/修改记录.md` 顶部含 [2026-05-08] Phase 3 条目(位于 Phase 2 之上)
- [ ] 条目 4 字段格式齐全 + 3 改动文件全列 + 4 个关键权衡短语全命中
- [ ] 「跨项目联动」+「服务端联动」字段含 CONTEXT 锁定文本
- [ ] CRED-FE-04 + CRED-FE-05 显式列出
- [ ] A 段 tsc 反向断言 0 行layout.tsx / credential-slot-dialog.tsx / page.tsx
- [ ] B 段 13 条 grep specifics 全表通过
- [ ] C 段 lockfile 工作区 + HEAD~3 双比较均 0 行 diff
- [ ] D 段 lint 跳过判定文字化记入 SUMMARY
- [ ] `.planning/phases/03-dialog-feedback/03-03-SUMMARY.md` 已生成并含 4 段完整报告
- [ ] Milestone v1.0 5 条 success criteria 全部确认通过(#1-#4 完整 / #5 程序化验证通过 + 浏览器 E2E 已声明推迟)
</success_criteria>
<output>
完成后创建 `.planning/phases/03-dialog-feedback/03-03-SUMMARY.md`,按 `$HOME/.claude/get-shit-done/templates/summary.md` 格式记录:
- 改动文件清单1 个docs/修改记录.md
- 修改记录条目预览(含 4 段 + 跨项目联动 + 服务端联动)
- 「Plan 级双重验证」段A/B/C/D 四段输出 + 解读
- 「Milestone v1.0 收尾确认」段5 条 success criteria 状态表
- 「下一步」段:
- Milestone v1.0 已 100% 交付,可执行 `/gsd-retrospective` 总结本 milestone
- 候选下一周期 milestone已记入清单
1. 后端「识别脱敏掩码保留旧值」patch解锁 ROADMAP success criteria #2 完整语义)
2. PERM-06 后端独立校验闭环
3. ESLint bootstrap候选 #3
4. 其他 brownfield 候选优先级
</output>