From a0196142a39338527df0c49545708abc4446f063 Mon Sep 17 00:00:00 2001 From: weichen Date: Sun, 13 Sep 2026 22:44:40 +0000 Subject: [PATCH] docs(ops): record heal lock serialization against duty transfer Note that HealProjectDocuments now shares pg_advisory_xact_lock(project_id) with member writes so an old reviewer cannot be written back. Co-authored-by: Cursor --- ...2026-09-13-document-revision-reviewer-duty-sync.md | 4 ++++ .doc/ops/CURRENT.md | 2 +- ...2026-09-13-document-revision-reviewer-duty-sync.md | 1 + ...2026-09-13-document-revision-reviewer-duty-sync.md | 11 ++++++----- ...2026-09-13-document-revision-reviewer-duty-sync.md | 3 ++- ...2026-09-13-document-revision-reviewer-duty-sync.md | 2 +- .doc/status/FEATURE-MATRIX.md | 2 +- 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.doc/audit/2026-09-13-document-revision-reviewer-duty-sync.md b/.doc/audit/2026-09-13-document-revision-reviewer-duty-sync.md index 3b5cad4d..9ac648e1 100644 --- a/.doc/audit/2026-09-13-document-revision-reviewer-duty-sync.md +++ b/.doc/audit/2026-09-13-document-revision-reviewer-duty-sync.md @@ -71,6 +71,10 @@ severity: critical 职责撤销/转移不得在成员 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` diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index f292d812..0759aa31 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -5,7 +5,7 @@ ## 焦点 -1. **DOCREV-RT**: [项目 reviewer 同步到受控文档职责](short-term/2026-09-13-document-revision-reviewer-duty-sync.md)(`fix/document-revision-reviewer-duty-sync`;原子清槽已提交,暂不合入;不声称 E2E 通过) +1. **DOCREV-RT**: [项目 reviewer 同步到受控文档职责](short-term/2026-09-13-document-revision-reviewer-duty-sync.md)(`fix/document-revision-reviewer-duty-sync`;heal 同事务锁已提交,暂不合入;不声称 E2E 通过) 2. **DOCREV-RT**: [质量修复子工作区契约](short-term/2026-09-13-document-revision-quality-repair-workspace-contract.md)(已合入 `dev@339e871`;仍 `in_progress`,待占槽重跑 UI-03) 3. **DOCREV-RT**: [编辑弹窗 hydration 竞态](short-term/2026-09-13-document-revision-editor-hydration-race.md)(已合入 `dev@8b1a531`;仍 `in_progress`,待占槽重跑 UI-06) diff --git a/.doc/ops/checklists/2026-09-13-document-revision-reviewer-duty-sync.md b/.doc/ops/checklists/2026-09-13-document-revision-reviewer-duty-sync.md index bd5d6980..5ccaebf5 100644 --- a/.doc/ops/checklists/2026-09-13-document-revision-reviewer-duty-sync.md +++ b/.doc/ops/checklists/2026-09-13-document-revision-reviewer-duty-sync.md @@ -15,6 +15,7 @@ related_task: "2026-09-13-document-revision-reviewer-duty-sync.md" - [x] 显式 revoke:reviewer→viewer、职责转移、退出、幂等;author 不进撤销集 - [x] `CreateOrGetDocument` 失败任务不成功;fault test - [x] 成员 upsert/退出:advisory lock、读旧/冲突 holder、成员更新、`document_records` 精确清槽同一事务;提交后 heal;失败 `ERR_DUTY_SYNC` +- [x] `HealProjectDocuments` 与成员写同锁键/顺序;解析与文档更新同事务;转移后旧 holder 不复活;第二次 heal 幂等 - [x] `gofmt` / `go vet` / `go test -short` / 相关 `-race` / `npm test` / `build` / 隔离 PG - [ ] 真实 UI-13a(本轮不做) diff --git a/.doc/ops/handoffs/2026-09-13-document-revision-reviewer-duty-sync.md b/.doc/ops/handoffs/2026-09-13-document-revision-reviewer-duty-sync.md index 1046f84e..b2af5cbe 100644 --- a/.doc/ops/handoffs/2026-09-13-document-revision-reviewer-duty-sync.md +++ b/.doc/ops/handoffs/2026-09-13-document-revision-reviewer-duty-sync.md @@ -1,5 +1,5 @@ --- -title: "reviewer 职责同步:原子清槽后交接" +title: "reviewer 职责同步:heal 同事务锁后交接" created: 2026-09-13 from: Cursor-Auto to: next-agent @@ -18,9 +18,10 @@ status: open - Heal 以 assigned 为权威;空 assigned 只清失效审核/批准 holder - 成员增改/移出:同一事务内 advisory lock + 读旧/冲突 holder + 成员写 + 精确清 reviewer/approver 槽;提交后服务层 heal 新 holder +- `HealProjectDocuments` 与成员写共用 `pg_advisory_xact_lock(project_id)`;解析与文档读写同事务;错误回滚 +- 确定性并发 PG:成员写持锁未提交时 heal 进入 `pg_locks` 等待;提交后旧 reviewer 不复活;第二次 heal 幂等 - 同步失败仍显式 `ERR_DUTY_SYNC`;author 不因普通成员变更清空 - 账本创建失败任务失败 -- 隔离临时库 PG:同事务 commit/rollback、upsert 后未 heal 槽已空、转移后 heal 上新 holder、退出清槽、并发无双 reviewer ## 未完成 @@ -42,9 +43,9 @@ status: open | 路径 | 说明 | |------|------| -| `internal/docduty/revoke.go` | `PlanSlotRevocations` + `ApplySlotRevocations`(调用方事务) | -| `internal/authz/authorizer.go` | upsert / setStatus 同事务 lock+读+写+清槽 | -| `internal/server/project_members_handlers.go` | 提交后只 heal,失败 `ERR_DUTY_SYNC` | +| `internal/docduty/revoke.go` | `AcquireProjectDutyLock`(与成员写同键) | +| `internal/docduty/store.go` | heal lock + 解析 + 回填同事务 | +| `internal/authz/authorizer.go` | upsert / setStatus 走同一 lock helper | | `scripts/run-isolated-duty-sync-pg-tests.sh` | 隔离 PG 入口 | ## 如何验证 diff --git a/.doc/ops/short-term/2026-09-13-document-revision-reviewer-duty-sync.md b/.doc/ops/short-term/2026-09-13-document-revision-reviewer-duty-sync.md index 45449c18..a67eb18b 100644 --- a/.doc/ops/short-term/2026-09-13-document-revision-reviewer-duty-sync.md +++ b/.doc/ops/short-term/2026-09-13-document-revision-reviewer-duty-sync.md @@ -38,6 +38,7 @@ mutex: document-revision-reviewer-duty-sync 5. [x] 独立审查收紧(暂不合入):Heal 以 assigned 为权威;成员变更带 duty 并同步文档;缺 role 不降 owner;写权精确断言;迁移多 owner 确定回填;并发 upsert PG 用例;自愈失败不假成功 6. [x] 最后一轮:移出成员 sync + 前端刷新;空 assigned 只清失效 holder;显式 revoke 再 heal;账本创建失败不成功 7. [x] 补充审查:lock + 读旧/冲突 holder + 成员写 + 精确清槽同事务;提交后 heal;失败仍 `ERR_DUTY_SYNC` +8. [x] Heal 与成员写共用 `pg_advisory_xact_lock(project_id)`;解析与文档读写同事务;确定性并发 PG 证明旧 reviewer 不复活 ## 验收标准 @@ -59,7 +60,7 @@ mutex: document-revision-reviewer-duty-sync - 基线:`dev@639969f` - 证据(只读):`.doc/test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui13a-reviewer-duty-sync-failure/` - 验证(原子清槽轮):`gofmt`/`go vet` PASS;`go test -short ./...` PASS;相关 `-race` PASS;`npm test`/`build` PASS;隔离 PG PASS -- 原子性:`UpsertProjectMemberSpec` / `SetProjectMemberStatus` 在同一事务内 lock→读 holder→写成员→清 reviewer/approver 槽;服务层只 heal,不再用 upsert 前提交后的快照做非原子清理 +- 原子性:成员写与 `HealProjectDocuments` 均先 `pg_advisory_xact_lock(project_id)`,再在同一事务内读成员/文档并更新;旧 heal 不能在转移提交后把 reviewer A 写回 - PG 集成已写 heal/空 assigned/多 owner 回填/并发 upsert;`-short` 或无 `TEST_DATABASE_URL` 时跳过 - 隔离跑法:`scripts/run-isolated-duty-sync-pg-tests.sh`(`TEST_DATABASE_URL` 或 `PG_ADMIN_URL` 建临时库)。拒绝 `agent_system_dev` - 本轮已在已运行的 context Postgres 上 `CREATE DATABASE agent_duty_sync_itest_*`,串行跑通后 `DROP`;未 `env-service`、未动共享库数据 diff --git a/.doc/remediation/2026-09-13-document-revision-reviewer-duty-sync.md b/.doc/remediation/2026-09-13-document-revision-reviewer-duty-sync.md index a593c6f8..4918af77 100644 --- a/.doc/remediation/2026-09-13-document-revision-reviewer-duty-sync.md +++ b/.doc/remediation/2026-09-13-document-revision-reviewer-duty-sync.md @@ -30,7 +30,7 @@ updated: 2026-09-13 1. 访问角色仍是 `owner/member/viewer`;文档职责另存 `document_duty`(`author`/`reviewer`/`approver`/空),同一项目同一职责仅一名在任成员。 2. `Resolve`:author=指定编制人或创建者;reviewer/approver=指定职责,否则仅当请求 id 是在任成员才采用;**禁止**默认 2/3。 3. `Heal`:项目 `document_duty` 为权威。assigned 非零则对齐(错 ID——即使对方也是在任成员——或同 ID 旧名称);assigned 为空才保留当前值,避免臆造/清空。禁止「任意 active member 就放过」。 -4. 列表/生成走自愈;**失败必须返回可诊断错误**,生成路径不得假成功;`CreateOrGetDocument` 失败不得报告成功。成员增改/移出:advisory lock、读取旧/冲突 holder、成员更新、`document_records` 精确清 reviewer/approver 槽必须在同一事务;提交后服务层 heal 新 holder;失败仍 `ERR_DUTY_SYNC`。author 不因普通成员变更清空。 +4. 列表/生成走自愈;**失败必须返回可诊断错误**,生成路径不得假成功;`CreateOrGetDocument` 失败不得报告成功。成员增改/移出:advisory lock、读取旧/冲突 holder、成员更新、`document_records` 精确清 reviewer/approver 槽必须在同一事务;提交后服务层 heal 新 holder;失败仍 `ERR_DUTY_SYNC`。author 不因普通成员变更清空。`HealProjectDocuments` 必须与成员写共用同一 `pg_advisory_xact_lock(project_id)`,并在该事务内完成解析与文档更新,避免旧 heal 把已转移的 reviewer 写回。 5. 前端立项与立项后增改/恢复都发 `document_dutyFromMemberRole`。更新成员省略 `project_role` 时保留现有访问角色,禁止默认 `member` 把 owner 降级。 6. 迁移 owner 回填:创建者优先,否则最低 `user_id`,单行 UPDATE,避免部分唯一索引冲突。 diff --git a/.doc/status/FEATURE-MATRIX.md b/.doc/status/FEATURE-MATRIX.md index 5e393539..7746b482 100644 --- a/.doc/status/FEATURE-MATRIX.md +++ b/.doc/status/FEATURE-MATRIX.md @@ -25,7 +25,7 @@ | 前端工作台 | 三栏 + 块文档 + SSE | partial | [test workbench](../test-reports/2026-09-08-three-column-workbench-and-block-document-test-report.md);[现场专项审计](../audit/2026-09-13-conversation-intent-runtime-state.md) | 错误卡片/任务进度/文档 busy 状态刷新恢复与人工抽检待完成 | `web` | | PI Runner / 配置链 | pi-agent + MiniMax 真实链路 | done | [test piagent](../test-reports/2026-09-13-codex-audit-p0-remediation-test-report.md);[container-env](../test-reports/2026-09-13-container-env-configuration-test-report.md) | — | `pi-agent` | | 三环境运维 | dev/test/main + 共享 KB | done | [PROJECT.md](../../PROJECT.md) §4;四环境 healthy | — | `infra` | -| CI / E2E | PG 集成 + 真实 PI E2E | partial | [gap-matrix BUG-08](../remediation/gap-matrix.md);[test pg-ci](../test-reports/2026-09-12-pg-ci-soak-e2e-test-report.md);[UI-13a reviewer 职责同步](../audit/2026-09-13-document-revision-reviewer-duty-sync.md) | CI `TEST_DATABASE_URL` 隔离库;立项/改职责后 reviewer 必须成为受控文档 `reviewer_id`;Heal 以 assigned 为权威;成员写与清槽同事务 | `ci` | +| CI / E2E | PG 集成 + 真实 PI E2E | partial | [gap-matrix BUG-08](../remediation/gap-matrix.md);[test pg-ci](../test-reports/2026-09-12-pg-ci-soak-e2e-test-report.md);[UI-13a reviewer 职责同步](../audit/2026-09-13-document-revision-reviewer-duty-sync.md) | CI `TEST_DATABASE_URL` 隔离库;立项/改职责后 reviewer 必须成为受控文档 `reviewer_id`;Heal 以 assigned 为权威;成员写与 heal 同项目锁 | `ci` | ---