diff --git a/.doc/audit/2026-09-13-workflow-access-chat-members-ui01-02.md b/.doc/audit/2026-09-13-workflow-access-chat-members-ui01-02.md new file mode 100644 index 00000000..34255adc --- /dev/null +++ b/.doc/audit/2026-09-13-workflow-access-chat-members-ui01-02.md @@ -0,0 +1,59 @@ +--- +title: "无 live 误拒 + chat 缺项目授权 + 立项成员未落库(UI-01/02)" +status: done +created: 2026-09-13 +updated: 2026-09-13 +severity: critical +--- + +# 无 live 误拒 + chat 缺项目授权 + 立项成员未落库(UI-01/02) + +## 现象 + +1. **UI-02**:项目 6071 `creator_user_id`/`project_members` owner 正确,但 `project_workflow_instances` 零记录;页面首轮 intake 返回 `WORKFLOW_ACCESS_DENIED`。 +2. **安全连锁**:若仅把「无 live」改为 unrestricted,而不在 `handleChat`/`handleChatStream` 做项目成员校验,任意登录用户只要猜到 `project_id` 即可进入项目对话。 +3. **UI-01/UI-13**:页面勾选 B 为 reviewer,但 `project_members` 仅有 creator A;B 新浏览器看不到项目。 + +## 根因分析 + +### A. ResolveStageAccess 与 GetLiveInstance 语义错位 + +- 注释写明「无 live 不限制(legacy)」,但 `err != nil || live == nil` 分支设 `Restricted=true`,空允许名单 → 新建未激活项目被全拒。 +- `GetLiveInstance` / `HasLiveInstance`(PG)仅返回 active 系;**completed/terminated 在 PG 上表现为 live=nil**。若把 live=nil 一律 unrestricted,会把已结束实例误判为从未激活,使 `CheckDocTaskAccess` 的 `WORKFLOW_INSTANCE_CLOSED` 在生产 PG 成死代码。 + +### B. Chat 入口缺项目成员门禁 + +- `handleChat` / `handleChatStream` 仅有 stage ABAC(`allowActiveStageChatAccess`),**无** `RequireProjectAccess`。 +- `handleChatStream` 在鉴权前即 `Content-Type: text/event-stream` + `Flush()`,拒绝时已是 200 SSE。 + +### C. 立项成员只写本地 + +- `App.handleCreateProjectConfirm` 在 `createProject` 成功后构造本地 members/sessions,**从未**调用 `addProjectMember`;服务端仅有 creator owner。 + +## 严重度 + +`critical`(错误拒绝阻断 UI-02;修复 A 而不修 B 会开放跨项目对话;C 阻断跨设备协同与 UI-13) + +## 本轮修复 + +1. **Store**:新增 `GetGoverningInstanceForAccess`(优先 live,否则最新 completed/terminated;无则 nil)。`HasLiveInstance`/`GetLiveInstance` 保持 active-only。mem 与 PG 对齐。 +2. **ABAC**:`ResolveStageAccess` / `CheckDocTaskAccess` 改用 governing 实例;无实例 unrestricted;closed 全拒;存储错误 fail-closed/上抛。 +3. **Chat**:`enforceChatProjectAccess`(成员/创建者;系统管理员按既有策略);同步与流式均在 stage 之前;SSE **headers/Flush 之前**结构化 JSON 403。 +4. **立项**:`ensureCreatedProjectWithMembers` + `persistInitialProjectMembers`;失败显式报错并保留 bootstrap project id 幂等重试;成功后才关弹窗。 + +## 证据 + +- 实战:项目 6071 零实例 + `WORKFLOW_ACCESS_DENIED`;成员表无 B +- PG:`TestPG_GoverningAccess_*`(never / active outsider / completed|terminated / query error) +- PG:`TestChatProjectAccess_*`(nonmember 403 无 event-stream;creator/member 200 SSE) +- FE:`createProjectMembersPersist.test.ts` + +## 关联整改 + +- Short-term: [../ops/short-term/2026-09-13-document-revision-runtime.md](../ops/short-term/2026-09-13-document-revision-runtime.md) +- 前序: [2026-09-13-admin-user-directory-sync-ui01.md](./2026-09-13-admin-user-directory-sync-ui01.md) + +## 开放问题 + +- 跨设备用户目录(非 admin 成员目录 API)仍见前序 audit residual +- 立项 UI 角色 reviewer→API `member` 为既有契约;文档三权仍在工作流 plan duties,非 `project_members.project_role` diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index c20f546e..d2c80515 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -1,11 +1,11 @@ # 当前焦点 **更新**: 2026-09-13 -**production_ready**: no(DOCREV 集成槽 e2e;UI-00 PASS;UI-01 目录死 prop 已在 fix 分支修复,待重跑真实 UI;跨设备成员目录仍为 residual blocker) +**production_ready**: no(DOCREV 集成槽 e2e;UI-00 PASS;UI-01/02 工程修复已入 fix 分支:governing 实例 + chat 项目门禁 + 立项成员落库;待重跑真实 UI) ## 焦点 -1. **DOCREV-RT**: [运行时整改收口](short-term/2026-09-13-document-revision-runtime.md)(`fix/document-revision-runtime`:上传注册 + auth 映射 + 管理员用户目录同步;占槽真实 UI E2E;不 push) +1. **DOCREV-RT**: [运行时整改收口](short-term/2026-09-13-document-revision-runtime.md)(`fix/document-revision-runtime`:上传注册 + auth 映射 + 用户目录同步 + 无 live/chat 授权 + 立项成员持久化;占槽真实 UI E2E;不 push) 2. **CHAT**: [刷新恢复整改已实施](short-term/2026-09-13-chat-history-refresh-remediation.md)(待合 dev + PI E2E 补录) 3. **CI**: `TEST_DATABASE_URL` + 真实 PI E2E 实录;INTENT 发布 blocker 见 conversation-intent short-term diff --git a/.doc/ops/short-term/2026-09-13-document-revision-runtime.md b/.doc/ops/short-term/2026-09-13-document-revision-runtime.md index 3b5595e1..056125d3 100644 --- a/.doc/ops/short-term/2026-09-13-document-revision-runtime.md +++ b/.doc/ops/short-term/2026-09-13-document-revision-runtime.md @@ -66,6 +66,8 @@ cd web && npm test && npm run build UI-01 目录同步补丁门禁(2026-09-13):`node --test adminUserDirectorySync.test.ts` 3 PASS;`web npm test` 219+27 PASS;`npm run build` PASS;`go vet ./...` + `go test -short ./...` PASS。未提交 `web/e2e/*` 与 `test-reports/assets/`。 +UI-01/02 访问与成员落库门禁(2026-09-13):`go test ./internal/projectinstance -short` PASS;`TEST_DATABASE_URL=… go test ./internal/projectinstance -run TestPG_GoverningAccess` PASS;`TEST_DATABASE_URL=… go test ./internal/server -run TestChatProjectAccess` PASS;`go vet ./...` + `go test -short ./...`;`web npm test` + `npm run build`。未提交 e2e/assets。 + 复核补强(同日): - A:`MaterializeAgentOutput` 错误写入 `TaskRunResult.Success=false` + `ErrorReason`,不再空白忽略 - B:cand 非空且 report invalid/unassessed 时禁止自动提交(候选标 needs_confirmation) @@ -82,10 +84,11 @@ UI-01 目录同步补丁门禁(2026-09-13):`node --test adminUserDirectory - M:**上传注册事实源**:发送前 `await registerUploadAttachments` → `POST /api/v1/freeform-documents`(`client_key` 幂等);失败 UI 显式错误且不发消息、不生成本地 `doc-upload` orphan;成功使用 `doc-free-*` 写入 deliverable/fileStore 并挂 toolbar - N:**Auth 契约映射(UI-00 实战)**:Go `UserAccount` 为 snake_case;前端在 API 边界 `mapUserAccount`/`mapLoginResult`/`mapMeResult` 统一转 camelCase(login/me/admin users/create/role/status);`BootstrapAdminFromConfig` 的 `UpdateUserRole` 错误不再静默。详见 [../../audit/2026-09-13-auth-useraccount-snake-case-mapping.md](../../audit/2026-09-13-auth-useraccount-snake-case-mapping.md) - O:**全局用户目录同步(UI-01 实战)**:管理员创建/角色/状态成功后必须经 `syncAdminUserDirectories` 调用父级 `onRefreshUsers`(禁止死 prop / 静默 catch);同浏览器立项候选可立即看到新账号。跨设备/清缓存缺口见 audit 残留风险,**不**下放 admin users API。详见 [../../audit/2026-09-13-admin-user-directory-sync-ui01.md](../../audit/2026-09-13-admin-user-directory-sync-ui01.md) +- P:**无 live 访问 + chat 项目门禁 + 立项成员落库(UI-01/02)**:`GetGoverningInstanceForAccess` 区分从未激活 vs completed/terminated;chat/chatStream 先 `RequireProjectAccess` 再 stage(SSE 拒绝不得先 200);`createProject` 后持久化非创建者成员。详见 [../../audit/2026-09-13-workflow-access-chat-members-ui01-02.md](../../audit/2026-09-13-workflow-access-chat-members-ui01-02.md) ### 用户 UI 实战验收 -见扩展 checklist(UI-01…UI-16)。工具条已加 `revisionRefreshKey`(保存/hydrate 后刷新 r)与「变更报告」入口(`change_report_id`)。**骨架已就绪**;Antigravity:UI-00 真实 PASS;UI-01 曾因目录死 prop 产品失败(本轮工程修复,待重跑 UI)。 +见扩展 checklist(UI-01…UI-16)。Antigravity:UI-00 PASS;UI-01 目录死 prop 已修;UI-02 暴露无 live 误拒 + 成员未落库(本轮工程修复,待重跑 UI)。 ### Blockers @@ -96,4 +99,4 @@ UI-01 目录同步补丁门禁(2026-09-13):`node --test adminUserDirectory ### Commits(历史) -- `7ea686e` … `7f8331f`(见 git log);本轮追加见会话结束提交号 +- `7ea686e` … `ec8a45a`(见 git log);本轮追加见会话结束提交号