Record real Playwright ledgers, mark 20 audits and 9 remediations done, and leave management-implementation-plan as plan-only.
5.4 KiB
title, status, created, updated, severity
| title | status | created | updated | severity |
|---|---|---|---|---|
| 项目 reviewer 未同步到受控文档职责 | done | 2026-09-13 | 2026-09-22 | critical |
项目 reviewer 未同步到受控文档职责
W3 UI-rerun 关闭(2026-09-22):
status=done。真实 Playwright 账本 PASS:ui-acceptance-results.json(main UI-00..16)+ui-acceptance-results-post-restart.json(UI-15)。任务 tip 已合入本地dev;详见 short-term 与 测试报告。
现象
第17轮真实 UI(schema 30,dev@639969f):UI-00 至 UI-12 PASS。UI-13a 精确职责断言首次暴露产品缺陷:
- UI-01 页面建项目把 USER_B 的职责下拉设为
reviewer - B 登录打开
doc_proj6145_intake权限矩阵时,selectvalue/ badgedata-doc-role实际为viewer - 失败:
reviewer B intake duty select must be reviewer: expected_duty:reviewer;got:viewer
现场文档头仍写「编制人: 编制工程师 / 审核人: 审核负责人 / 批准人: 批准负责人」。
根因分析
只读查本轮 project 6145(不暴露账号密码/token):
| 层 | 证据 | 后果 |
|---|---|---|
| 立项 UI | CreateProjectModal 把 B 标成 reviewer;默认还勾选 seed id=3 李主管 / id=4 王总监 |
用户意图是 B=审核人 |
| 成员持久化 | toApiProjectRole 禁止 author/reviewer/approver,一律映射 member;payload 无 document duty |
B=171 落成 project_role=member |
project_members |
170 owner(A)、171 member(B)、3/4 member |
库表无法区分 B 是 reviewer |
| 任务生成 | bizdev.service:ReviewerID==0 → 写死 2 +「审核负责人」;ApproverID==0 → 写死 3 |
与真实成员无关 |
document_records |
intake / prelim_design 均为 author_id=170(恰为创建者 UserID)、reviewer_id=2、approver_id=3 |
第二份文档继承了同一套假职责 |
| 列表 API | 原样投影 reviewer_id=2 |
前端拿到权威但错误的 id |
resolveCurrentDocRole |
受控文档按 id 匹配,fail-closed 为 viewer | B=171 ≠ 2 → viewer;不是前端提权,是服务端职责丢失 |
document_duty_assignments 只挂官方工作流实例任务,聊天 TRIGGER_TASK 生成账本不走该表。
严重度
critical
项目创建时明确指定的审核人在后续每份受控文档上变成查阅者;写路径虽因 fail-closed 未把 B 提成编制人,但审核职责与权限矩阵验收全部失真。
关联整改
- Remediation: ../remediation/2026-09-13-document-revision-reviewer-duty-sync.md
- Short-term: ../ops/short-term/2026-09-13-document-revision-reviewer-duty-sync.md
- 分支:
fix/document-revision-reviewer-duty-sync
证据
- 失败归档(只读,不改不删):
.doc/test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui13a-reviewer-duty-sync-failure/ phase-state.json:controlledDocId=doc_proj6145_intake,项目UI验收-超声微创系统-mu0bvnp8- Playwright:
expected_duty:reviewer;got:viewer
独立审查收紧(同一分支,暂不合入)
独立审查指出首轮 Heal「已是任意在任成员则保留」会让批准人串位:6145 类场景 approver=3(李主管也是成员)而 assigned=4 时不应保留 3。项目 document_duty 才是权威。
立项后成员增改/恢复若只发 project_role,document_duty 会静默失效。更新成员时若省略 project_role 且带 document_duty,默认 member 会把 owner 降级。
生成/列表路径若吞掉自愈错误,会带着 reviewer_id=2 假成功。
最后一轮缺口(已在同分支修复)
- 移出成员只改
exited不同步文档;前端移出不刷新矩阵 - 空 assigned 若保留非成员占位,退出审核人会留在文档上
- reviewer→viewer 时人仍 active,generic heal 会把槽位留下,必须按更新前 duty 显式撤销
CreateOrGetDocument错误被_吞掉会假成功
原子清槽约束(已在同分支修复)
职责撤销/转移不得在成员 upsert 已提交后再用服务层前后快照做非原子清理。权威路径是同一 DB 事务:pg_advisory_xact_lock → 读旧 duty / 冲突 holder → 成员更新 → 精确清 reviewer_* / approver_*。随后 heal 填新 holder。
Heal 与成员写竞态(已在同分支修复)
HealProjectDocuments 若无项目锁、用 pool 先读后写,会出现:heal 读到 reviewer A → 成员事务持锁把 A 清槽并提交为 B → 旧 heal 把 A 写回。现已与成员写共用 pg_advisory_xact_lock(project_id),解析与 document_records 更新在同一事务。
开放问题
- 本轮不重跑真实 UI-13a;不得标 E2E 通过或任务
done - 6145 剩余风险:迁移后 owner 会补
author;B(171)当时只落成member且document_duty=''。服务端不能臆造 reviewer。须再次经成员 API 写入document_duty=reviewer后,自愈才会把文档从占位2对齐到 B。 - 隔离 PG 集成须用临时库 +
TEST_DATABASE_URL,见scripts/run-isolated-duty-sync-pg-tests.sh;禁止默认连agent_system_dev