agent-exec transport 793bf9727d04cae50d0b270d9064534b/21372630236f

This commit is contained in:
agent-exec-transport
2026-10-22 05:50:45 +00:00
parent fa08be4145
commit a61eaac9ae
@@ -0,0 +1,90 @@
# 增量 C 前端独立评审(W10 审计工作台 + 依据/候选确认路径)
- 评审对象:`feature/rdm-two-trees-followup-w10` @ `fa08be4`(基线 `ec1ced3`,dev 无本地改动)
- 范围:`src/components/RdmAuditWorkspace.tsx(+test)`、`Conversation.tsx`、`ConversationWork.tsx`、`Workbench.tsx`、`src/api.ts`、`src/styles.css`、`src/companion-conversation.css`、`e2e/rdm-audit.playwright.ts`
- 增量:`+1048 / -7`,9 文件(`git diff --stat ec1ced3 fa08be4`)
- 评审性质:只读评审。代码零改动;本文件是唯一写入。契约核对基线:RDM 侧 `Agent-System-RDM-wt-feature-rdm-two-trees-followup-c-w8-w10` @ `c4c6df7`(本地检出的 W8b/W5 命令面);计划基线:`modules/documents` `rdm-two-trees-stage5-implementation-test-plan-r1.md` §W10
- 环境限制:本仓为独立检出,组合仓 `../ui`(`@ui` 源码别名)缺失、子模块未初始化 ⇒ 依赖 `@ui` 的 22 个测试文件与 `tsc -b` 无法在本工作区运行(见 §验证)。`npm ci --offline` 可用,故守护测试与 knip 实跑。
## 1. 逐项结论
| # | 检查项 | 结论 | 依据 |
|---|---|---|---|
| 1 | 设计门禁(令牌/动效/折叠) | **FAIL(部分)** | 裸色值、私有 keyframes、行内字号、`transition`、`--fs-ui-*` 全部通过守护测试(7/7 PASS);但两处折叠未用 `@ui Collapsible`(MAJOR-3) |
| 2 | DTO 边界(只消费服务端已裁剪 DTO、无新增全量拉取) | **PASS** | 页面只渲染 `display/recorded/disclosure`;服务端 `redactOperationBodies`(`inspection_visibility.go:70-88`)确实把无权正文的 `summary.display` 换成「受限」并清空 `object.current`,前端未越权补正文;列表单页 `limit:30`,证据/影响分析均为按需单条读取,无全量拉取 |
| 3 | 异步纪律(单一 owner、seq 丢迟到响应) | **PASS(有 1 处冗余镜像,见 MINOR-4)** | `createOperationsFeed` 每请求取 `++seq`,非最新 seq 的成功与失败一并丢弃,`dispose()` 亦作废在途;续页回传首页 `cutoffSeq`;其余面板沿用既有 `startLatestFetch` 单 owner 模式 |
| 4 | 四态可读 + 游标失效显式 | **PASS** | 加载/空/错误(denied·cursor·query·capability·auth 分流)/旧记录「当时未记录」/正文受限/部分覆盖/影响截断均有独立文案;错误分类正则与服务端真实串一致(`权限拒绝:…` 出自 `perms_api.go:234`;`操作记录游标已失效…` 出自 `operation_projection.go:42`),非臆测匹配 |
| 5 | 既有行为兼容(树/卡片/对话/导出预览;右栏收起的确认路径) | **FAIL** | 树/卡片/对话/导出预览无回归(页签按名定位,`审计记录` 与既有 `操作记录` 不冲突,`.rdm-audit-card` 计数断言互不干扰);但右栏收起后的「对话确认路径」在最需要它的工作卡场景下是空操作(MAJOR-1) |
| 6 | e2e 扩展质量(skip 如实) | **PASS(附 MINOR-6)** | `test.skip(!SVC_REAL_RDM_BIN, …)` 与注释如实说明「缺真实 RDM 二进制则整组跳过、不伪造」;但当前 CI 与 Dev `scripts/e2e.sh` 均不注入该变量 ⇒ 4 条新用例在任何现有执行入口下都永不运行(零证据) |
| 7 | 知识多挂载为 TODO 未实现、未虚报 | **PASS(附 MINOR-7)** | `RdmAuditWorkspace.tsx:591` 仅留 TODO,未见任何实现或「已完成」表述;TODO 给出的理由经核实为真:W8b 分支 RDM 侧 `grep '"mounts"'` 零命中,`rdm.inspect.tree/object` 确实无挂载字段(域层已有 `MountsOf`,仅未出 DTO) |
## 2. BLOCKING
无。
## 3. MAJOR
### MAJOR-1 右栏收起后的「对话确认路径」在目标场景下是空操作(承诺与实现不符)
- 位置:`src/components/Conversation.tsx:571-572, 734-740`;关联 `src/components/ConversationWork.tsx:65, 91`;承诺文案 `src/components/Workbench.tsx:82`
- 理由:新提示条仅做 DOM 定位滚动:
`body.querySelector('[data-card-id="…"]') ?? body.querySelector('[data-conversation-work]')`。
但显示条件恰是 `!workPaneOwnsCards`(右栏收起或非工作页签),此时:
1. `ConversationWork` 命中 `if (!detailHere) return null`(`ConversationWork.tsx:91`)——`[data-conversation-work]` 整个子树不存在;
2. 工作卡在对话正文里只经 `CardAnchor` 出现,而 `Conversation.tsx:56` 对带 `card.work` 的卡显式传 `card={undefined}`,UI 侧 `data-card-id={card?.id}`(`modules/ui/src/CardAnchor.tsx:25`)因此不落属性。
结果:W10 重点场景(候选工作卡待确认)点「N 项待你确认 · 在对话中处理 ↓」→ `target` 为 `null` → `target?.scrollIntoView` 静默无动作,而收起按钮的 title 却承诺「N 项待确认会在对话中继续处理」。
附带:`pendingHere`(含 `work.decision.state === 'open'`)与 `pendingCardId`(仅 `status === 'action_required'`,`Conversation.tsx:572`)口径不同,decision-open 卡即使能定位也算不出 `id`。
- 修复建议:改走仓内既有的唯一定位路径 `store.openInbox(pendingCardId)`(`src/store.ts:1625-1633`:展开右栏、切工作页签、`flashLocate('.right-panel','data-card-id',…)`),或在 `!workPaneOwnsCards` 时把候选/决议控件渲染进对话正文;同时让 `pendingCardId` 与 `pendingConfirmations` 用同一谓词。
### MAJOR-2 新增「审计记录」页签无能力门禁,缺命令面的构建上恒为错误态
- 位置:`src/components/RdmAuditWorkspace.tsx:52, 125-137`
- 理由:页签无条件挂载,而仓内既有约定是「能力缺失即不暴露」(`src/store.ts:1462-1475` 依 `api.listCommands()` 刷新能力位;`src/workspaces.ts:52` `rdmAuditReady`)。当前就绪门 `RDM_AUDIT_COMMANDS` 只含 7 个 `rdm.inspect.*`(`workspaces.ts:23-31`),不含 W8b/W5 的 `rdm.audit.operations`、`rdm.trace.coverage`、`rdm.trace.pending-recheck`、`rdm.compliance.impact.*`。因此在已合入的 RDM 上审计工作区入口照常可见,而新页签必然落到 `classifyOperationsError → capability`(「当前服务未提供审计能力」),与 `e2e/plain-base.playwright.ts:38-51` 记录的「零暴露、不进入即 400」原则相悖。
- 修复建议:在 `workspaces.ts` 增加 `rdmAuditOperationsReady(cmds)`(上述 5 条命令)并在页签列表按能力过滤;覆盖面板与影响分析入口同样受此门控;若坚持常显,则须在 PR 描述中登记为已知降级并由 RDM 发布节奏兜底。
### MAJOR-3 两处折叠未使用 `@ui Collapsible`(违反 DESIGN.md §9.2)
- 位置:`src/components/RdmAuditWorkspace.tsx:1200`(`EvidenceLink`)、`:1266`(`ImpactAnalysisBlock`)
- 理由:两者用 `<button aria-expanded>` + 条件挂载自建展开,未消费全项目唯一折叠原语;DESIGN.md §9.2「一切折叠区…用它:外壳高度动画 + 内容 fade 两层分离、300ms 延迟卸载、展开态记忆」。守护测试只拦 `<details>`,故测试全绿但违宪——本次评审门禁「折叠只用 @ui Collapsible」据此判 FAIL。
- 修复建议:改为 `<Collapsible id={…} trigger={…}>`(这两处都有稳定的 id 语义:证据 `${pid}/${kind}/${id}`、影响分析 `${pid}/${analysisId}`),可顺带获得展开态记忆与卸载动画。若以「同文件 `:408`、`RegCitation.tsx:157` 已有先例」为由豁免,请在 PR 中显式登记豁免理由,而非默认放行。
## 4. MINOR
| # | 位置 | 问题 | 建议 |
|---|---|---|---|
| MINOR-1 | `src/components/ConversationWork.tsx:22-24` | `candidateActions` 未排除已有决议表单的卡:@ui `WorkCardBody` 对 `work.decision` 开放态渲染 `DecisionForm`,若该卡 `actions` 同时含 confirm/reject,则同一决议出现两个入口,且新按钮只提交 `{resultVersion}`(不含 `selectedIds`/`freeText`),存在「未选选项即提交」风险 | 首行加 `if (card.work?.decision?.state === 'open') return []`,保持一个动作一个入口 |
| MINOR-2 | `src/components/Workbench.tsx:38-41` | `pendingConfirmations` 与 @ui `pendingCount`(`modules/ui/src/InboxList.tsx:103-106`)口径不一致:新函数未排除 `status === 'resolved'`,「resolved + decision open」的卡会被计入输入区提示与收起提示,却不计入收件箱/顶栏计数 ⇒ 同屏两个数字打架 | 直接复用 `pendingCount` 语义,或在注释中说明差异并统一文案 |
| MINOR-3 | `src/components/RdmAuditWorkspace.tsx:1303` | 覆盖面板用 `Promise.all([rdmTraceCoverage, rdmTracePendingRecheck])`:任一命令缺失(较旧构建只有 coverage)即整面板降级,正式/候选覆盖数字一并消失 | 改 `Promise.allSettled`,coverage 可用即渲染,pending 不可用时只在待复核一栏给说明 |
| MINOR-4 | `src/components/RdmAuditWorkspace.tsx:1051, 1060` | `dict` 是 `state.dictionary` 的第二份异步状态镜像;切项目时旧项目词典保留到新页落地,可能把「新项目不存在的筛选值」静默带上,且 `<select>` 无匹配 option 时显示为空白 | 随 `pid` 变化清空 `dict`,或词典未就绪前禁用两个 select |
| MINOR-5 | `src/components/RdmAuditWorkspace.tsx:1090` | `.rdm-audit-ops` 在任何 CSS 中都无规则(本次只补了 `.rdm-audit-wrap`、`.rdm-audit-op .rdm-audit-opgroup-head/.rdm-audit-op-pending`、`.rdm-audit-op-object`),列表容器无布局约束 | 补一条容器规则或复用既有类名,避免长对象名/窄屏下出现不可预期的块级堆叠 |
| MINOR-6 | `e2e/rdm-audit.playwright.ts:150` | 整组 skip 条件 `SVC_REAL_RDM_BIN` 在 Dev `scripts/e2e.sh` 与本仓 CI 中均无注入(`grep SVC_REAL_RDM_BIN scripts/e2e.sh` 零命中;CI 只跑 `npm test` + `npm run build`)⇒ 4 条新用例(含游标翻页、游标失效重查、无授权三态)当前零执行证据 | 组合验证时由 e2e.sh 导出该变量(它已在 `$OUT/agent-system-rdm` 构建出二进制,`e2e.sh:88`),或在 PR 中把「e2e 未运行」显式登记为 NOT_RUN 缺口 |
| MINOR-7 | `src/components/RdmAuditWorkspace.tsx:591` | 知识多挂载降级只以代码 TODO 记录;计划 §W10 把「知识多挂载」列入实施范围(`rdm-two-trees-stage5-implementation-test-plan-r1.md:154`),缺登记的降级记录,容易在跨仓验收时被当作已交付 | 在 Dev 任务卡/证据条目登记该验收项为「待 RDM DTO 增补」,并回指此 TODO;代码内无需再改 |
## 5. 已核实的正面结论(附证据)
1. **DTO 边界真实成立**:`internal/rdmapp/inspection_visibility.go:70-88` 在无权时把 `change.summary.display` 置为「受限」、`disclosure='restricted'`、`object.current=nil`;前端 `OperationRow`(`:1125-1170`)只渲染服务端 `display/recorded`,`refsText` 只拼 `kind.label+id`,未渲染任何前端臆造的正文或补全的「当前」名称。
2. **契约字段与 RDM 实际 DTO 对齐**:`OperationRecordDTO`/`OperationPage`/`ImpactAnalysis`/`TraceCoverageResult`/`TracePendingEdge` 的 JSON tag 与 `src/api.ts:73-175` 的类型逐字段核对一致(含 `cutoffSeq/nextCursor/total`、`ObjectRef.revision` 为字符串、`CGap{rule,node,reason}`)。
3. **错误分类与服务端真实串匹配**:`权限拒绝:`(`perms_api.go:234`)→ `denied`;`操作记录游标已失效`(`operation_projection.go:42`)/`操作记录游标不合法`(`:454`)→ `cursor` 且隐藏翻页按钮并给「从第一页重新查询」。
4. **异步纪律的测试覆盖到位**:`createOperationsFeed` 的 seq、dispose、续页 `cutoffSeq`、迟到失败响应均有断言(`RdmAuditWorkspace.test.tsx:145-215`),且实现与断言一致(`request()` 成功/失败双路都比对 `mine === seq`)。
5. **未引入 knip 基线压力**:`node scripts/check-knip.mjs` → `knip unused exports 58 <= 58`;逐项核对 knip JSON,本次改动的 5 个文件未新增 unused exports/types。
6. **既有回归面检查通过**:既有 `操作记录` 页签(TimelineTab)与新 `审计记录` 页签按 aria 名定位互不冲突;既有 e2e 的 `.rdm-audit-card` 计数断言作用于概览页签,与新覆盖卡片不同时渲染;导出预览入口(`exportNeedsDraft`/`DocumentRenderWorkspace`)未改动。
## 6. 验证命令与结果(本工作区实跑)
| 命令 | 结果 |
|---|---|
| `git diff --stat ec1ced3 fa08be4` | 9 files, +1048/-7(评审对象确认) |
| `npm ci --offline --no-audit --no-fund` | 176 packages(仅本地缓存,未联网) |
| `npx vitest run test/design-guard.test.ts` | **PASS** — Test Files 1 passed, Tests 7 passed(① 门禁实测) |
| `npx vitest run` | 43 files passed / 22 files failed;398 tests passed, 1 failed。22 个失败文件与该 1 个失败全部为 `Error: Cannot find package '@ui'`(组合仓 `../ui` 未检出),无其他错误类别 |
| `npx vitest run src/components/RdmAuditWorkspace.test.tsx` | **NOT_RUN** — 同一 `@ui` 解析失败(新增单测在本工作区无法执行) |
| `npx tsc -b` | **NOT_RUN** — 全部为 `TS2307 Cannot find module '@ui'` 及其引发的隐式 any,无新增类型错误信号 |
| `node scripts/check-knip.mjs` | **PASS** — `knip unused exports 58 <= 58` |
| `npm run e2e` / `npx playwright test` | **NOT_RUN** — 无 `PLAYWRIGHT_BASE_URL` 且无浏览器/后端组合环境;`SVC_REAL_RDM_BIN` 未注入时新组按声明跳过 |
| 浅/深主题、窄屏、长对象名视觉检查 | **NOT_RUN** — 需真实组合浏览器;静态侧只能确认新增样式全部消费令牌 |
## 7. 合入建议
1. **合入门禁**:MAJOR-1、MAJOR-2 建议在合入前修复(前者是承诺失效,后者是能力面 fail-closed 破口);MAJOR-3 可修或登记豁免。
2. **证据缺口**:本单元的 vitest/tsc/e2e 在组合检出(`../ui` 就绪 + auth-on 真实 RDM)下必须补跑;在补跑前,本评审对新增单测与 e2e 的结论仅为静态核对,不得记为「测试通过」。
3. **范围声明**:知识多挂载(MINOR-7)按计划 §W10 属未完成项,验收矩阵中应保持「待 RDM DTO 增补」状态,不得因代码内 TODO 而计为已交付。