- 复盘:编排设计、逐轮决策链、35 项修复全量清单、关键技术点、踩坑与复用流程 - 真机指南:闭环模型(注入/截图/dumpLayout/hilog 三通道)、命令手册、坐标库、 安全访问选择器等 7 条踩坑、成本红线、标准测试脚本骨架
23 KiB
五轮多 Agent 评审迭代 · 复盘与追溯
日期:2026-08-30 | 执行:ZCode 主控 + PM/开发/测试三类子 agent | 产出:35 项修复(App 24 文件 +2134/-409,后端 3 文件 +65) 关联提交:主仓
0b5d151、子仓meida_backend@00ef56b(均已本地提交,未 push) 过程产物:.scratch/multiagent/(CONTEXT.md、R1R5 五轮 PM/测试报告、checks/checks.mjs、shots-r1r5 截图档案、FINAL-REPORT.md)
本文目标:完整还原这次多 agent 迭代的机制、每一轮的决策链、所有修复的技术细节与踩坑,供后续追溯与复用。真机自动化测试单独成篇:见《真机闭环自动化测试-实践指南.md》。
1. 任务与总体设计
1.1 原始需求
用户原话要点:了解项目(端侧模型/后端/PDF 报告/试衣间/发型设计);开三个 agent——PM 视角 review、开发 agent 按反馈开发并提建议、测试 agent 尽量自动化测试;多 agent 迭代 5 轮;最后用户验收。
1.2 多 agent 编排模型
主控(ZCode 会话)
├─ 基线验证(构建门禁 / 后端单测 / 设备探测)—— 不交给子 agent,自己先跑
├─ 写 .scratch/multiagent/CONTEXT.md(共享上下文,所有子 agent 必读)
├─ Round N:PM agent(只读)→ 开发 agent(改码+门禁)→ 测试 agent(门禁+静态+真机)
│ ↑ 每轮把上一轮测试发现(Nx' 问题)+ 遗留池喂给下一轮 PM
└─ 终局:测试发现的 P0 回归由主控亲手热修(不再开一轮,控制成本)
关键设计决策:
- 共享上下文文件而不是长 prompt。CONTEXT.md 固化项目结构、验证命令(全部主控亲测过可用)、硬约束(禁 commit/push、不跑花钱的 smoke、不开集成测试、最小 diff、代码约定)。每轮 agent prompt 只写"本轮主题 + 读哪些文件 + 输出到哪",避免 prompt 膨胀也避免 agent 复述项目背景浪费上下文。
- 角色硬隔离。PM agent 只许读源码、唯一可写的是自己的报告文件;开发 agent 不许碰后端(除非该轮显式授权一处,如 R3 的 hint 改口、R4 的模板 meta);测试 agent 可修
.scratch下的检查脚本(测试基建)但不能改产品代码。 - 每轮有主题,避免转圈。R1 全局走查 → R2 AI 功能域 → R3 PDF 链路+一致性 → R4 结果可靠性+遗留池裁决 → R5 终局修复+内测验收清单。R3 起 PM 的职责从"找新问题"过渡到"裁决遗留池 + 定修复方案"。
- 报告落盘编号体系。问题编号 #1~#28 全局连续;测试新发现用 N1、N2'… 前缀;文件名固定
R{n}-{pm|test}.md。这让第 5 轮 PM 能产出完整的 40 项遗留池终审表。 - 门禁前移。开发 agent 的任务书里就写死"BUILD SUCCESSFUL 是硬门禁,失败必须修到过",测试 agent 复跑做独立复核——两道验证互相独立。
1.3 基线(迭代前主控亲测)
- App 构建门禁:
cd app && cmd //c .build-gate.bat→ BUILD SUCCESSFUL(~1 分钟,增量更快) - 后端单测(必须 WSL,Windows 侧 uv 管不了 WSL 建的 venv;必须
--extra dev才有 pytest):wsl -e bash -lc "cd /mnt/d/meida/meida_backend && uv run --extra dev pytest tests/unit --no-cov -q"→ 574 passed 基线 - 真机:
hdc list targets→88Z9K26525009283(华为真机,有 PIN 锁)
2. 逐轮还原(决策链与证据)
Round 1:全局走查(16:10–16:44)
PM 输入:核心旅程走查(登录→首页→报告→PDF;试衣间/发型/我的/对话),6 维度(流程完整性/三态/文案/配额/隐私合规/一致性)。 PM 输出(R1-pm.md):
- P0 ×3:#1 报告唯一入口在 AppStorage(重启即丢,后端
GET /api/v1/reports现成未接);#2 doRegenerate 丢弃响应新 report_id(消耗 30 天 1 次的配额却永远看旧报告);#3 人脸照片无单独知情同意(违反合规方案 CR-01/FR-ACC-08) - P1 ×3:#4 失败重试复用同 request_key(幂等重放原样失败→死循环);#5 消耗配额前无提示(违反"禁止静默扣减");#6 技术黑话 8 处
- P2 ×6:#7 会话过期无出路 ~ #12 对话无快捷问题
开发(10 文件,新增 ReportListTab.ets / FacePhotoConsent.ets):
- #1:报告 Tab 从"建设中"占位换成真列表(组件级复用,不算新路由页,绕开"无底部 Tab 锁死用户"的约定冲突)
- #2+#4:采纳新 report_id;三处(create/regen/export)FAILED 后清幂等键换新键
- #3+#5:同意弹窗(会话级)+ 四个消耗点事前确认(复用现有 quota 接口,额度 0 置灰+恢复时间)
- 计划外重大发现:后端任务状态是小写(
succeeded/failed),App 全按大写比较——轮询完成检测恒假、"导出 PDF"按钮从未真正出现过。真机注释里"轮询完成不自动跳转"就是它的症状。统一 toUpperCase() 归一修复。
测试(R1-test.md):门禁绿;建 checks.mjs 静态检查 17(跨页 import 禁令/路由注册/大小写/幂等键清理/同意门控/黑话/前后端路由比对);真机被 PIN 锁阻塞(只完成装包+启动);#1#5 验收 ✅;新发现 N1~N4。放行。
Round 2:AI 功能域(16:46–17:18)
PM 输出(R2-pm.md,#13~#20,无 P0):#13 对话单轮失忆(M4-b 范围,只登记);#14 缺「AI 生成」标识(CR-05 法定,2025-09-01 已施行);#15 发送失败丢输入无重试未带幂等键;#16 试衣生成期锁屏即丢结果;#17 结果只活在签名 URL;#18 发型页列表态/保存误报;#19 调试页处置;#20 同意弹窗承诺无工程背书。 开发(4 文件):
- ChatPage 四合一:AI 标识常驻(列表与输入区之间固定层)、4 个快捷问题 chips、额度 0 置灰+reset_at 恢复时间、失败回填+失败气泡重试+request_key(失败保留/重试复用/成功换新)、500 字上限
- TryonPage:
setWindowKeepScreenOn(true)(结束/取消/销毁恢复)、onBackPress 挽留确认、取消出口(Phase A 直连无服务端任务,文案如实说"不涉及额度扣减")、genSeq 防迟到响应串扰;TryonApi 加 cancelChangeCloth - TaskPollingPanel:连续 3 次网络错误=「放弃轮询」不再上抛 onFailed(宿主幂等键不清,额度未退),面板内给"稍后在报告列表查看"引导;仅后端终态 FAILED 走失败流程 测试:12 项静态检查 11 PASS(#6 黑话范围外单列);真机仍 PIN 锁;6 项验收全 ✅;新发现 N5(UTC 时区差 8h,波及 5 处)/N6(压缩期取消不彻底)/N7(额度耗尽不同步)。放行。
Round 3:PDF 链路收尾(17:20–19:40,测试一度被中断后重跑)
PM 输出(R3-pm.md,#21~#26 + 遗留池首轮裁决):
- P1 ×3:#21 PDF 下载前不删旧文件(OHOS download 不能覆盖已存在——同报告第二次导出/重试必失败,且发生在额度已扣之后);#22 loadReport 只清 regen 不清 export 幂等键(导出→重生成→再导出=IDEMPOTENCY_CONFLICT 死路,且后端 hint 说"任务中心"——App 不存在的页面);#23 PDF 失败态无出路+loadDocument 挂载时序风险+FR-RPT-12 分享长图(V1 P0)反而不做却先做了 V2 的 PDF(范围倒挂登记)
- 裁决:本轮修 #6 清尾/#7/N5/N2/#10 最小集;R4 修 #17/#18/N7/N6/N4/#24/#25;不修项给理由 开发(17 文件 + 新增 Theme.ets;后端唯一授权改动 errors.py 两处 hint):
- #21 下载前 accessSync→unlinkSync;#22 loadReport 清两个幂等键;#23 PdfView 常驻挂载+遮罩式加载/失败态+重新加载按钮+分享另存文案拆分
- #7:ApiTypes 加
isAuthError()(40100~40105),四处错误态加「重新登录」出路 - N5:formatIsoTime 改
new Date(s)本地时区解析(原手工截串);连带 ProfileContent 7 处时间归正 - #6 十处逐条替换(含 PDF 文件名内嵌报告 ID 改「形象报告 vN」);N2 报告 Tab onChange+onPageShow 双触发刷新;#10 主色收敛
core/Theme.ets(15 文件替换两套主色)+返回控件统一+Tab3 文案 测试(R3-test.md,重跑后完成):真机解锁,PDF 全链首次完整走通——登录→报告 Tab→导出确认→轮询(无黑话)→服务端渲染 350KB→下载→PdfView ready→另存→分享取消静默;二次导出同报告(旧文件在场)成功——#21 unlink 修复实证(旧代码必 2300037);19/19 静态检查;#21/#22/#23/…验收 ✅;新发现 N1'~N7'(PDF 元数据缺 AI 声明等)。放行。
Round 4:结果可靠性(19:42–21:35)
PM 输出(R4-pm.md):10 项修复清单(#17 试衣转存为首位,含"下载前 unlink"同款坑提醒;#18 与 #17 统一方案;N2'-① PDF meta;N1' 提示自清;#24 修正 R3 的"6 卡"为 4 卡真实消耗集——tryon/hairstyle 直连不经后端配额,上卡会恒显满额假象;#25 登录页内测口径;N7(正确码 40301,R3 写的 42910 是短信频控);#19 debug 守卫——发现门禁产物恒 debug,包裹≠隐藏,但 T18-REPORT 未产出前不能彻底删;#10 尾差)+ 40 项遗留池终审表(已修 22/本轮 10/R5 修 3/登记不修 5)。新登记 #27(结果图无水印=Phase B 放量红线)、#28(T18-REPORT 未产出)。 开发(8 文件 + 后端模板/单测):
- #17:生成成功即下载
filesDir/results/tryon_last.jpg(unlink 重入);展示/保存走本地副本(fileUri.getUriFromPath);aboutToAppear 回看「上次结果」卡;保存三态(photoAccessHelper 返回空数组=取消/异常=失败,d.ts:3570 判别);转存失败降级 URL+提示;N6 genSeq 复查加在压缩后 changeCloth 前 - #18:发型列表三态(listLoaded 复位支持重试)、保存三态、结果落 hair_last.jpg;ImageCodec 抽 writeBase64File 助手,新增 base64ToPersistFile(filesDir,自动建目录),cacheDir 版保留作兜底
- N2'-①:report.html.j2 加 author/description/generator meta(用现成 report_id/version/date_text 零新数据;不伪造 27 位编号——后端 compliance/service.py 明示 fail-close"half-way is worse");+2 单测(WeasyPrint 把 Info 字典放压缩对象流,测试用 zlib 解压后断言
/Author等标记,不新增 pypdf 依赖) - #24/#25/N7/#19/#10尾差 按清单落地;BuildProfile 踩坑:虚拟路径 import 不解析,改直引
build/default/generated/.../BuildProfile(clean 构建亦可用,任务顺序有保证,但 IDE 升级有脆性——已记录) 测试(R4-test.md,两段完成):前任 agent 真机实操 58 张截图(含相机拍照选图、飞行模式、杀 App、登出重登、PDF 另存)后上下文超限挂掉;接任 agent 不重复操作、只分析证据+跑门禁:三门禁绿、#18 列表三态/#24/#25/#19/N2'-① 真机或单测实证;但发现 R4N1 [P0]:真机上 photoUris 已送达 App 而 humanUri 永不更新(await 链静默死亡)、cameraPicker resultUri 恒空——试衣/发型照片链真机全断(存量缺陷,此前轮次照片链从未在真机走通过所以没暴露)。有条件放行。
Round 5:终局(21:40–23:10)
PM 输出(R5-pm.md):
- R4N1 根因排序(排除法):三个会进 catch 的假设被"从未出现失败 statusText"排除;H1(最可能)=
describe()内await getImageInfo()挂起不回调(ImageSource 懒加载+真机媒体路径,官方 FAQ 给过 getImageInfoSync 替代方案);H2 = picker Promise 提前 resolve 空/续体丢失(拍照路径"resolve 但 resultUri 空"实证此形态)。事实修正:报告页照片是"即将开放"预告没有接选图代码,修复面收窄为两页 - 最小改动方案:收口 ImageCodec 单文件——A 裁 describe(width/height 全仓零消费,纯风险零收益);B select/pick 套 Promise.race 超时;C createImageSource(uri) URI 直建;拍照降级预案预授权
- R5 清单 4 项:R4N1 / N4 同意持久化+撤回 / N5' f2 换用 / statusText 1 行;内测发布验收清单(A12+B16+C8+D5 条) 开发(两任 agent:第一任被用户熄屏打断留下半成品,第二任核对发现四项其实已全做,补 checks 断言 25~28 + 跑门禁):
- ImageCodec:A/B/C 全落地 + MEIDA_IMG 打点 11 处;FaceConsentStore 镜像 AuthStore 的 preferences 先例(
{granted, version:'v1', grantedAt},init hydrate 校验版本,revoke 清空);ProfileContent 授权管理行+撤回确认框;ReportResultPage/ReportListTab 换 f2 LoadingView/ErrorView(保留 #7 重新登录出路);TryonPage 两处 statusText 清空 测试(R5-test.md):三门禁绿、契约零越界;A1~A12 真机:选图+拍照送达修通(gallery: select end, 1 uri(s)带真实 URI、camera: pick end, resultUri length=63)、N4 全链(杀 App 不弹/撤回后重现)、N5' 断网闭环、0 崩溃;换装/发型真实生成各 1 次止步压缩失败未触网(服务零消耗);发现 R5 回归:R5N2 [P0]image.createImageSource('file://media/…')真机返回 undefined→toJpegBuffer 抛 TypeError→生成链全断(修复 C 引入);R5N1 [P1] TryonPage@Builder PhotoSlot按值传参不响应 @State→预览永不显示(发型页直引写法正常)。结论:未达内测门槛,差两个小修复。
终局热修(主控亲手,23:10–23:40)
- R5N2:ImageCodec 加
openSource()——先image.createImageSource(uri),真机返回空则fs.openSync(uri, READ_ONLY)→createImageSource(file.fd)(华为官方文档模式);closeSource()在 finally 里 release+关 fd(成功/失败都回收) - R5N1:
@Builder PhotoSlot($$: PhotoSlotArgs)(显式 interface,ArkTS 严格模式不许裸对象字面量作类型),调用点改对象字面量传参——按引用、@State 变化即刷新 - 踩坑复现:第一次构建失败(arkts-no-obj-literals-as-types),补 interface 后绿;install -r 不杀进程——验证时发现跑的还是旧代码(ps 看启动时间),
aa force-stop+ 重启后才是新包 - 真机实证:60s 超时兜底可感知(两次触发「读取照片失败」+hilog)、cameraPicker resolve 正常(退出=空 URI 取消语义)、N4 跨 force-stop 不重弹、0 崩溃;合成点击进不了"安全访问"选择器(设备限制,详见真机指南文档)——预览刷新与压缩链留一分钟人工验收
- 静态检查项 26 更新为 openSource 双路口径后 28/28 PASS;提交
0b5d151(主仓)+00ef56b(子仓)
3. 修复全量清单(35 项速查)
| # | 问题 | 轮次 | 修复要点 | 验证 |
|---|---|---|---|---|
| 1 | 报告重启即丢 | R1 | ReportListTab 接 GET /reports 替换占位 Tab | 真机 ✅ |
| 2 | 重新生成显示旧报告 | R1 | 采纳响应新 report_id | 静态 ✅ |
| 3 | 人脸照片无知情同意 | R1→R5 | 弹窗→preferences 持久化+版本+撤回 | 真机 ✅ |
| 4 | 失败重试幂等死循环 | R1 | FAILED 清 requestKey 换新键(三处) | 静态 ✅ |
| 5 | 静默扣配额 | R1 | 四消耗点确认框(代价+剩余+置灰+恢复时间) | 真机 ✅ |
| 6 | 技术黑话 10 处 | R3 | 逐处替换文案表 | 真机抽证 ✅ |
| 7 | 会话过期无出路 | R3 | isAuthError+四处「重新登录」 | 静态 ✅ |
| 10 | 主色两套/返回三套 | R3 | Theme.ets 唯一来源,15 文件替换 | 静态 ✅ |
| 12 | 对话无快捷问题 | R2 | 4 chips 对应需求文档四类意图 | 真机 ✅ |
| 14 | 缺 AI 生成标识 | R2 | 列表与输入区之间固定层常驻 | 真机 ✅ |
| 15 | 发送失败丢输入 | R2 | 回填+重试+request_key | 静态+契约 ✅ |
| 16 | 生成期锁屏丢结果 | R2 | keepScreenOn+挽留+取消+genSeq | 静态 ✅ |
| 17 | 试衣结果只活在 URL | R4 | filesDir/results 转存+回看卡+离线保存 | 静态 ✅/真机待人工 |
| 18 | 发型页三态缺失 | R4 | 列表三态+保存三态+结果转存 | 真机(列表/错误态)✅ |
| 21 | PDF 二次下载必败 | R3 | 下载前 unlink | 真机实证 ✅ |
| 22 | 导出幂等冲突死路 | R3 | loadReport 清双幂等键+后端 hint 改口 | 静态 ✅ |
| 23 | PDF 失败态无出路 | R3 | 常驻挂载+重试+文案拆分 | 真机 ✅ |
| 24 | 配额卡信息架构 | R4 | 4 张真实消耗卡+名称对齐 | 真机 ✅ |
| 25 | 登录页临时性呈现 | R4 | 内测徽章+账号标识话术+断网指引 | 真机 ✅ |
| N1 | 轮询网络误判失败 | R2 | 放弃≠终态,不清幂等键 | 静态 ✅ |
| N2 | 报告列表不刷新 | R3 | Tab 切换+页面重现双触发 | 真机 ✅ |
| N3/N7 | 额度 0 体验 | R2/R4 | 置灰+恢复时间;40301 同步 | 真机 ✅ |
| N4 | 同意不持久化 | R5 | preferences+撤回入口 | 真机 ✅ |
| N5 | UTC 时区差 8h | R3 | new Date 本地解析,7 处归正 | 真机实证 ✅ |
| N6 | 压缩期取消不彻底 | R4 | genSeq 复查移到 changeCloth 前 | 静态 ✅ |
| N2'-① | PDF 缺 AI 元数据 | R4 | 模板 meta+2 单测(zlib 解压断言) | 单测 ✅ |
| N1' | 保存提示常驻 | R4 | 3s 自清(白名单防误清) | 真机 ✅ |
| N5' | f2 组件零采用 | R5 | 两页换用,保留重新登录出路 | 真机(断网)✅ |
| #19 | 调试页暴露 | R4 | BUILD_MODE_NAME 守卫+「内部调试」文案 | 真机 ✅ |
| R4N1 | 真机照片链断裂 | R5 | 裁 describe+超时兜底+URI 直建 | 真机(送达)✅ |
| R5N1 | @Builder 不刷新 | 热修 | $$ 对象接口按引用传参 | 构建+机制 ✅/待人工点选 |
| R5N2 | createImageSource 空 | 热修 | openSource 双路(URI→fd 回退) | 构建+机制 ✅/待人工点选 |
| 状态大小写 | 导出按钮从未可达 | R1 | toUpperCase 归一(多处) | 真机(PDF 链)✅ |
(#8/#9/#11/#13/#20/#27/#28 等=登记不修或后置,理由见 R4-pm.md 遗留池终审表与 FINAL-REPORT.md 第五节。)
4. 关键技术知识点(学习重点)
- 幂等键生命周期(request_key):发起前生成→失败保留→重试复用→成功作废;页面加载新实体(loadReport)时要清掉旧实体的键,否则跨版本操作撞 IDEMPOTENCY_CONFLICT。前后端契约:
ChatRequest.request_key描述即"相同键不会重复扣配额"。 - ArkTS
@Builder传参语义:原始类型按值传参不响应 @State;要响应式必须$$+ 显式 interface 对象字面量(且 ArkTS 严格模式禁止裸对象字面量作类型声明——会报 arkts-no-obj-literals-as-types)。 - image kit 双口径:
createImageSource(uri)对file://media/…在部分真机 ROM 返回空(不是抛错!);官方稳妥路径是fs.openSync(uri, READ_ONLY)+createImageSource(fd),fd 需调用方在 release 后关闭。getImageInfo()在媒体 URI 上有整类"挂起不回调"风险(官方 FAQ 承认),能不用就不用、必须用就加超时。 - OHOS request.download 不能覆盖已存在文件(错误码 2300037)——下载前必须 accessSync→unlinkSync。
install -r不杀进程:验证新包前必须aa force-stop+ 重启,否则测的还是旧代码(用ps -ef的启动时间戳甄别)。- 系统 picker 安全访问模式:合成点击(uitest)可能完全无效;picker 空结果收场后可能残留不可见蒙层吞输入,
aa force-stop com.huawei.hmos.photos恢复。详见真机指南。 - 后端纪律:Prompt 固定前缀不许含易变内容(缓存单价差 30 倍);DeepSeek 峰谷双价成本落库不重算;compliance fail-close(宁可不做标识也不伪造编号)。
- WeasyPrint PDF 元数据在压缩对象流里,测试断言要先 zlib 解压再 grep,裸字节 grep 不到。
5. 过程踩坑与复盘(下次怎么做得更好)
踩坑
| 坑 | 后果 | 对策 |
|---|---|---|
| 测试 agent 真机实操 58 张截图+几十个 dump 全进上下文 | agent 上下文超限挂掉("Invalid string length") | 接任 agent 只分析精选 16 张关键帧;dump json 只 grep 不整读;视觉结论必须与 dump 文本/源码交叉验证(视觉模型对中文文案有幻觉) |
| R3 测试执行中被外部打断 | 报告没写、checks.mjs 半成品 | 重派时明确"接续半成品:核对→补完→产出报告",半成品价值不浪费 |
| 真机 PIN 锁连续三轮挡住交互 | 只能装包+启动+hilog | 第四轮用户解锁后一次清偿三轮欠账;环境依赖要早发现早向用户求助 |
| 主控热修后直接装机验证 | 测到旧进程行为,一度误判修复无效 | 流程固化:install → force-stop → start → 验证(已写进真机指南) |
| agent 汇报"全做了"与实际半成品 | — | 第二任开发 agent 先 git diff 核对半成品清单再动手,避免重复或漏做 |
Git Bash 路径转义吃掉 /data/local/tmp |
hdc file recv 找不到文件 | export MSYS_NO_PATHCONV=1(详见真机指南) |
做得好的
- 门禁双保险(开发自跑+测试复跑)+ 静态检查资产滚动累积(7→12→19→24→28 项,每轮修复都固化成断言,防回归)
- PM 裁决制:R3 起不再堆新问题,而是对遗留池逐条给"修/不修/排哪轮"+理由,5 轮收敛而不是发散
- 开发 agent 被授权质疑 PM:如 R4 把 R3 的"配额 6 卡"修正为 4 卡(假象比缺卡更糟)、R5 PM 修正"报告页照片受影响"的事实错误——角色对抗出真知
- 红线写在任务书里:换装/发型真实生成各 ≤1 次、对话 ≤2 条、不跑花钱 smoke——全程服务成本近零
- 编号体系全局连续,第 5 轮能产出完整 40 项终审表,最终报告有据可查
复用这套流程
CONTEXT.md模板:项目结构/已验证命令/硬约束/角色权限——换项目只需改这一份- 每轮 prompt 三段式:必读上下文(文件路径)→ 本轮主题与任务(含验收标准出处)→ 输出格式与落盘文件名
- 测试 agent 任务书必带:门禁复跑命令原文、静态检查脚本路径、"新问题喂下一轮 PM"的交接语义
- agent 挂掉的恢复策略:不重做,派"接任者"核对半成品→补完→出报告
6. 遗留事项(放量前)
- 一分钟人工验收(热修两项):试衣间选图看预览即时显示;点「开始试穿」看生成进入
- 评审会落 owner:#11 认同/修正+预约留资、#13 对话多轮上下文(M4-b)、FR-RPT-12 分享长图、FR-ACC-04 手机号登录、#20 即删背书、#27 结果图水印、#28 T18-REPORT
- 环境债:BuildProfile 直引生成物路径偏脆(IDE 升级风险);picker 空结果蒙层(ROM 级,已记规避法)
- WSL 后端/worker/PG/Redis 仍在运行(供真机联调,停法见 FINAL-REPORT.md 第七节)