From d1e2cc929d78cd8ce1287da47500a6083a14f222 Mon Sep 17 00:00:00 2001 From: weichen Date: Mon, 14 Sep 2026 10:01:40 +0000 Subject: [PATCH] docs(remediation): rewrite data-root plan after review Split incident remount from outbox/governance so Wave 0 is executable without identity bootstrap, and record preempt-but-wait authorization. Co-authored-by: Cursor --- ...09-14-dev-runtime-data-root-split-brain.md | 8 +- .doc/ops/CURRENT.md | 7 +- .../2026-09-14-dev-runtime-data-root-guard.md | 44 +-- ...me-data-root-and-projection-remediation.md | 267 +++++++++++------- .doc/remediation/INDEX.md | 2 +- .doc/remediation/gap-matrix.md | 2 +- .doc/status/FEATURE-MATRIX.md | 5 +- 7 files changed, 213 insertions(+), 122 deletions(-) diff --git a/.doc/audit/2026-09-14-dev-runtime-data-root-split-brain.md b/.doc/audit/2026-09-14-dev-runtime-data-root-split-brain.md index 49743b3f..b10b43d9 100644 --- a/.doc/audit/2026-09-14-dev-runtime-data-root-split-brain.md +++ b/.doc/audit/2026-09-14-dev-runtime-data-root-split-brain.md @@ -158,4 +158,10 @@ Compose 按 project working directory 解析相对路径。`scripts/env-service. ## 开放问题 - 恢复前需再次冻结并备份错误根,确认 08:40 UTC 后是否已有新会话、附件、runner 输出或 agent session 写入;08:54 UTC 抽样未发现 `output.md`,不能替代完整差异清单。 -- test/main 当前挂载源需在实施 Wave 0 只读核对;本轮未扩大操作范围。 +- test/main 当前挂载源需在实施 Unit A 只读核对;本轮未扩大操作范围。 + +## 审查附记(2026-09-14 ~09:23 UTC) + +- 现场复核:`shuimu-agent-app-dev` 仍 created `08:40:44Z`,bind source 仍为 `/tmp/as-dev-wf409-merge/data/dev`;`/health` `/ready` 仍 200,`schema_version=33`。 +- 主仓 CURRENT 集成槽仍为 `implementer_cursor` / `e2e`。worktree 副本不得写成 idle。 +- 方案已审查修订:见 [整改方案](../remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md)。用户选择事故抢占 + 先改方案再批实施。08:54 的 `f283...f039` 不得再当 Wave 0 硬门槛。 diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index a3c12a80..dfb032e7 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -5,7 +5,7 @@ ## 焦点 -1. **RUNTIME-DATA-ROOT**: [dev 临时 worktree 数据根 split-brain](../audit/2026-09-14-dev-runtime-data-root-split-brain.md)(critical;当前 app/pi/cursor 挂到 `/tmp/as-dev-wf409-merge/data/dev*`,DB 仍为原 dev 卷;整改包含[数据精简与迁移语料](../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md)) +1. **RUNTIME-DATA-ROOT**: [dev 临时 worktree 数据根 split-brain](../audit/2026-09-14-dev-runtime-data-root-split-brain.md)(critical;审查修订已完成。现场仍错挂 `/tmp/as-dev-wf409-merge`;Unit A 事故抢占纠挂与 Unit B 防复发**待再批**。数据精简为 sibling 方案,不挡 Unit A) 2. **CHAT-INTENT**: [对话意图运行时状态](../audit/2026-09-13-conversation-intent-runtime-state.md)(`fix/conversation-intent-state`;与文档账本运行时无关) 3. **CHAT**: [对话历史刷新](../audit/2026-09-13-chat-history-refresh-disappears.md)(认领仍 `in_progress`;gap 行已 PASS,以代码/handoff 为准收敛) @@ -49,6 +49,7 @@ | implementer_cursor | workflow-plan-409-frozen-and-optional-duties | fix/workflow-plan-409-frozen-and-optional-duties | .worktrees/fix-workflow-plan-409-frozen-and-optional-duties | workflow | done | | implementer_cursor | governance-action-level-authz | fix/governance-action-level-authz | .worktrees/fix-governance-action-level-authz | workflow | done | | Codex | dev-runtime-data-root-guard | fix/dev-runtime-data-root-guard | .worktrees/fix-dev-runtime-data-root-guard | infra-runtime-state | done | +| Cursor | dev-runtime-data-root-guard-plan-rewrite | fix/dev-runtime-data-root-guard | .worktrees/fix-dev-runtime-data-root-guard | infra-runtime-state | in_progress | | Codex | runtime-data-lifecycle-plan | fix/dev-runtime-data-root-guard | .worktrees/fix-dev-runtime-data-root-guard | infra-runtime-state | done | > 新任务开始前登记。`branch` 必须为 `feature/` 或 `fix/`,**禁止** `dev` / `test` / `main`。同一 `mutex` 同时仅一个 `in_progress`。 @@ -59,7 +60,9 @@ | holder | phase | since | scope | queue | |--------|-------|-------|-------|-------| -| - | idle | - | - | - | +| implementer_cursor(主仓 SSOT) | e2e | 2026-09-14 | workflow-plan 409 frozen + optional duties | governance-action-level-authz;Unit A 事故抢占待再批 | + +> 槽位以**主仓** `.doc/ops/CURRENT.md` 与 `docker inspect` 为准。本 worktree 不得把槽位写成 idle 覆盖主仓。Unit A 获批后才能登记 `incident-preempt` 并停 `agent-dev` 的 app/pi/cursor。 **phase**: `idle` | `merging` | `smoke` | `e2e` diff --git a/.doc/ops/short-term/2026-09-14-dev-runtime-data-root-guard.md b/.doc/ops/short-term/2026-09-14-dev-runtime-data-root-guard.md index 15533ddd..d3e1f40c 100644 --- a/.doc/ops/short-term/2026-09-14-dev-runtime-data-root-guard.md +++ b/.doc/ops/short-term/2026-09-14-dev-runtime-data-root-guard.md @@ -1,57 +1,61 @@ --- -title: "dev runtime data root split-brain 诊断与系统整改设计" -status: done +title: "dev runtime data root split-brain 诊断、方案审查与修订" +status: in_progress created: 2026-09-14 updated: 2026-09-14 -owner: Codex +owner: Cursor branch: fix/dev-runtime-data-root-guard worktree: .worktrees/fix-dev-runtime-data-root-guard mutex: infra-runtime-state --- -# dev runtime data root split-brain 诊断与系统整改设计 +# dev runtime data root split-brain 诊断、方案审查与修订 ## 目标 -定位项目 143 刷新后正文 409 的当前根因,量化影响,并形成防复发的系统整改方案;不实施业务代码或共享环境变更。 +定位项目 143 刷新 409 的当前根因,经审查后把整改方案改成可执行、可分批批准的文本;**不实施**纠挂或业务代码,直到用户再批 Unit A / Unit B。 ## 范围 -- 用户报错链、正文读取/投影代码; -- dev PostgreSQL、主仓文件、容器实际 mount、health/readiness; -- Compose 与 `env-service.sh` 的工作目录语义; -- 即时恢复、state-root 隔离、storage identity、projection outbox 与验收设计。 +- 诊断:用户报错链、dev PostgreSQL、主仓文件、容器 mount、health/ready、Compose / `env-service.sh` +- 审查:原文六波次方案的内部矛盾、可行性、抢占与身份首启风险 +- 修订:Unit A / Unit B / Deferred 拆分与可执行 Wave 0 Runbook ## 非目标 -- 不操作真实用户会话; -- 不写数据库、不覆盖正文、不删除错误根; -- 不占共享集成槽、不 rebuild; -- 不变更 test/main。 +- 不操作真实用户会话 +- 不写数据库、不覆盖正文、不删除错误根 +- 本轮不 rebuild、不占集成槽执行纠挂(抢占授权已写入方案,实施另批) +- 不变更运行中的 test/main ## 步骤 1. [x] 完成仓库启动链、既有 canonical hash/refresh 事故文档复核。 2. [x] 比对项目 143 DB record、ledger head、主仓 `output.md` 与容器内路径。 3. [x] 审计 app/pi/cursor mounts、Compose working dir、健康检查与影响面。 -4. [x] 完成 audit、remediation、gap/feature/current SSOT 更新。 +4. [x] 完成初版 audit、remediation、gap/feature/current SSOT。 +5. [x] 多视角审查原文方案;现场复核挂载与集成槽仍为事故态。 +6. [x] 按审查结论重写整改方案(Unit A/B/Deferred + 可执行 Runbook)。 +7. [ ] 用户再批后实施 Unit A(事故抢占纠挂)。 +8. [ ] 用户再批后实施 Unit B(绝对状态根 + 门禁 + `/ready`)。 ## 验收标准 - [x] 直接根因有数据库、文件和容器三方证据。 - [x] 明确区分旧 hash drift、真正 ETag 409 与本次 projection missing。 -- [x] 方案覆盖部署根因、运行时双写、自愈安全边界、负例与真实 UI 验收。 +- [x] 修订方案不再把 Wave 0 依赖尚未存在的 identity / outbox;不再把 Deferred 当事故关门条件。 - [x] 未污染真实会话或修改共享运行态。 +- [ ] Unit A/B 实施与验收(另批)。 ## 相关链接 - Audit: [../../audit/2026-09-14-dev-runtime-data-root-split-brain.md](../../audit/2026-09-14-dev-runtime-data-root-split-brain.md) - Remediation: [../../remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md](../../remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md) -- Test report: 待方案获批并实施后创建 +- Test report: 待 Unit A 实施后创建 ## 证据 / 备注 -- 诊断分支:`fix/dev-runtime-data-root-guard`,基线 `dev@a8f9451`。 -- 2026-09-14 08:54 UTC:app/pi/cursor 均挂 `/tmp/as-dev-wf409-merge/data/dev*`;DB 仍为 `agent-dev-context-pgdata`。 -- 项目 143 主仓 `output.md` SHA 与 DB/head 一致;容器内同路径 missing。 -- `/health`、`/ready` 均错误地返回 200。 +- 诊断分支:`fix/dev-runtime-data-root-guard`,基线 `dev@a8f9451`,诊断 commit `f1edb70`。 +- 2026-09-14 08:54 UTC 诊断:app/pi/cursor 均挂 `/tmp/as-dev-wf409-merge/data/dev*`;DB 仍为 `agent-dev-context-pgdata`。 +- 2026-09-14 ~09:23 UTC 审查复核:同一挂载仍在;`/health` `/ready` 200,`schema_version=33`;主仓 CURRENT 槽位仍为 `implementer_cursor` e2e。 +- 用户选择:Wave 0 = 事故抢占;先改方案再批实施。 diff --git a/.doc/remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md b/.doc/remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md index 3e767829..84d9d29a 100644 --- a/.doc/remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md +++ b/.doc/remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md @@ -1,139 +1,216 @@ --- -title: "dev 运行数据根隔离与文档投影自愈整改方案" +title: "dev 运行数据根隔离与文档投影整改方案(审查修订)" status: planned created: 2026-09-14 updated: 2026-09-14 --- -# dev 运行数据根隔离与文档投影自愈整改方案 +# dev 运行数据根隔离与文档投影整改方案(审查修订) -## 背景 +## 审查结论 -现场根因见 [dev rebuild 从临时 worktree 挂载空数据根](../audit/2026-09-14-dev-runtime-data-root-split-brain.md)。当前 dev 的数据库仍是原持久卷,但 app、PI Runner、Cursor Runner 已绑定 `/tmp/as-dev-wf409-merge/data/dev`,导致项目 143 等正文投影在容器中不可见,GET 返回 409。 +2026-09-14 多视角审查确认:**诊断成立,原文不可整包实施。** -本方案不仅恢复项目 143,而是消除“源码 checkout/worktree 决定运行数据根”的系统耦合,并让账本到兼容文件投影成为可观测、可重试、可恢复的持久状态机。 +现场在审查当时(约 09:23 UTC)仍为 split-brain: + +| 项 | 值 | +|---|---| +| `shuimu-agent-app-dev` created | `2026-09-14T08:40:44Z` | +| Compose working dir | `/tmp/as-dev-wf409-merge` | +| app `/app/data` | `/tmp/as-dev-wf409-merge/data/dev` | +| pi/cursor workspaces | `/tmp/as-dev-wf409-merge/data/dev/workspaces` | +| `/health` `/ready` | 200,`schema_version=33` | +| 主仓 CURRENT 集成槽 | `implementer_cursor` / `e2e` / workflow-plan | + +用户已拍板: + +1. **Wave 0 授权方式 = 事故抢占**:不排队等 e2e 结束;获批实施后立即停 runner/app,只读保全两端数据根,再从主仓目录纠挂。 +2. **本轮只改方案**:消解波次矛盾后再单独批准实施。本文未授权 rebuild、未授权写库、未授权删除 `/tmp`。 + +原文把「恢复可读」和「投影 outbox / 数据治理 / 完整 SLO」绑成同一批准与同一关门条件。修订后拆成三个互不阻塞的批准单元。 + +## 批准单元 + +| 单元 | 目的 | 关闭事故? | 状态 | +|---|---|---|---| +| **Unit A** | 事故遏制与纠挂:停写、保全、从主仓 remount | 恢复当前可读/可写 | 方案已修订,**实施待再批** | +| **Unit B** | 防复发:绝对状态根 + 脚本硬门禁 + readiness 身份门禁 | 阻止同类错挂再次显示 healthy | 方案已修订,**实施待再批** | +| **Deferred** | ledger 只读降级、projection outbox、历史 work_dir 治理、监控产品、迁仓外路径 | 否 | 记 long-term,不挡 Unit A/B 关门 | + +`RUNTIME-DATA-ROOT-SPLIT-BRAIN` 与 FEATURE-MATRIX `infra` 在 **Unit A 验收 + Unit B 负例** 后关闭。不得等 Deferred。同 worktree 上的数据生命周期方案是 sibling:可以并行设计,**不得**成为 Unit A 纠挂的前置或本 gap 的关门条件。 ## 目标 -1. 任意 build source/worktree 都不能改变 dev/test/main 的持久数据根。 -2. 数据库、app、PI Runner、Cursor Runner 必须对同一环境 storage identity 达成一致,否则进程不 ready、部署脚本失败。 -3. ledger head 可用时,安全的投影 missing 不再使 UI 正文不可读;真实 fork 仍 fail closed。 -4. DB head 与 `output.md` 的双写失败可持久观测、幂等重试和自动补偿,不依赖用户反复刷新。 -5. rebuild、restart、宿主重启、linked worktree、并发保存和磁盘故障均有自动化负例;最终以真实页面刷新/重登/重建后持久化验收。 +1. 任意 build source / linked worktree / `/tmp` checkout 都不能改变已部署环境的持久数据根。 +2. 错挂或身份不一致时 `/ready` 必须 503;`/health` 只表示进程存活,不得作为部署就绪依据。 +3. Unit A 完成后,项目 143 等活跃受控正文在正确数据根上可打开;不以 08:54 历史 SHA 作为事后硬门槛。 +4. 真实内容分叉仍 409 并保全双方;安全 missing 的自动重建与 outbox **不在本方案实施范围**。 + +## 术语(强制) + +| 术语 | 定义 | +|---|---| +| `SOURCE_ROOT` | 本次构建源码目录,可来自受控集成 worktree | +| `RUNTIME_STATE_ROOT` | 各环境状态目录的**绝对父路径**。短期默认 = 主仓 `…/Agent-System/data` | +| `ENV_STATE_DIR` | `${RUNTIME_STATE_ROOT}/`,例如 `…/data/dev` | +| `SHARED_STATE_ROOT` | 共享知识库状态根,**不**拼接 `/`,不随 `SOURCE_ROOT` 变化 | +| bind mount source | `docker inspect` 的宿主机 `Mounts.Source` | +| build context | 镜像构建用的源码目录,允许与状态根不同 | + +禁止把 `RUNTIME_STATE_ROOT` 理解成已经带 env 的 `data/dev`。Compose 公式一律是 `${RUNTIME_STATE_ROOT}/${ENV}/...`。`.storage-identity.json`(Unit B)落在 `ENV_STATE_DIR/`。 + +禁止把 Wave 0「容器 source」读成 `SOURCE_ROOT`。Wave 0 验收的是 **bind mount source**。 ## 核心决策 -### D1 — 代码根与状态根彻底分离 +### D1 — 代码根与状态根分离(Unit B;Wave 0 不依赖新变量) -- `SOURCE_ROOT`:本次构建源码,可来自受控的集成 worktree。 -- `RUNTIME_STATE_ROOT`:环境持久状态根,必须是绝对、canonical、非 worktree 相对路径;dev/test/main 各自固定。 -- Compose 的 app/runner bind mount 全部改为 `${RUNTIME_STATE_ROOT}//...`,不得再写 `./data/`。 -- `.regulations`、agent sessions、attachments 等可变状态一并纳入状态根清单;只读代码资源继续来自 source root。 +- Compose 可变 bind 改为 `${RUNTIME_STATE_ROOT}/${ENV}/...`,禁止 `./data/`。 +- 短期兼容:用主仓 `data/` 作为 `RUNTIME_STATE_ROOT`。**本方案禁止把状态根迁到 `/var/lib/agent-system`。** +- 禁止硬编码个人 HOME。解析顺序:显式环境文件 → Git common-dir 推出的 **primary** 根下的 `data/`。 +- 显式 `RUNTIME_STATE_ROOT` **不能**把任意绝对路径变成合法根。解析结果必须:绝对、`realpath`、等于宿主机 allowlist、环境后缀与命令一致、不落在任何 linked worktree toplevel、不是 `/tmp` 集成目录。 +- shared compose 使用 `SHARED_STATE_ROOT`,不套用 `/${ENV}`。 +- `.regulations` 若仍从 `SOURCE_ROOT` 相对挂载,必须在 Unit B 清单里单独点名;不得假装已被 data identity 覆盖。 +- **否决记录**:Postgres 逃过本次事故是因为 named volume。本轮仍保持宿主机 bind,以便 Unit A 做两端差异保全。不用 named volume 收口 `/app/data`,也不把「固定 `--project-directory`」当成 Unit B 的全部答案——脚本必须同时导出绝对 bind,否则合法集成 rebuild 仍会解析到调用目录。 -建议短期兼容现有数据,将 primary repository 的 `data/` 解析为默认状态根;中期迁移到仓库外(例如 `/var/lib/agent-system` 或管理员配置路径)。禁止硬编码个人 HOME,脚本通过显式配置或 Git common-dir 推导 primary root。 +### D2 — 存储身份是配对仪式,不是可复制哨兵(Unit B) -### D2 — 存储身份握手,而不只比较路径字符串 +- 每个 `ENV_STATE_DIR` 放受保护的 `.storage-identity.json`:`environment`、UUID、schema version、**canonical host path**;不含 secret。 +- PostgreSQL 用**新的单行表**,不复用 `iam_system_config`。 +- **禁止自动初始化。** app 启动只校验,不 mint、不把文件 UUID 写进 DB,也不把 DB UUID 写到空根。 +- 真首启(允许 pair 命令写入同一 UUID)当且仅当:DB 无业务行(至少 `document_records` / `document_snapshots` 为空)**且**文件根无业务布局。 +- 已有业务库 + 文件根无合法 sentinel → fail closed / `/ready` 503。现场错误根已有 27 个文件,**「空目录」不是可用谓词**。 +- DB 有 UUID、文件缺失 → 503,不回写。 +- 文件有 UUID、DB 无 → 503,防止新卷认领旧根。 +- UUID 一致但 `realpath` ≠ DB 固化 canonical path → 503(克隆/备份根)。 +- runner 一致性**不**靠进程内 HTTP:`cursor-agent` 现为 `alpine sleep`,pi/cursor 只挂 `workspaces`,读不到状态根哨兵。Unit B 由部署脚本 `docker inspect` 三个容器的 `Mounts.Source` + 文件 sentinel + DB 行。进程内 `/ready` 只比 app 可见 sentinel 与 DB。 +- 未认证 `/ready` 只返回聚合布尔和稳定错误码,禁止 UUID、路径、mount source、doc_id、计数明细。 -- 每个环境状态根创建受保护的 `.storage-identity.json`:`environment`、随机 UUID、schema version;不含 secret。 -- PostgreSQL 新增单行环境 identity(或复用 settings 表),首次受控初始化时写入同一 UUID。 -- app 启动读取文件 identity 并与数据库 identity 比对;app、PI Runner、Cursor Runner 都暴露/上报相同 UUID。 -- UUID 缺失、环境名不符、根不可写、runner identity 不一致时 fail startup/readiness;不得自动初始化一个已含业务数据库对应的空文件根。 +### D3 — 读取真源与自动重建拆开(Deferred / 后续) -该机制可以识别“路径看起来合法但实际是另一份空目录/备份目录”的场景。 +- Unit A **不**改 GET 语义;纠挂后投影文件回到视野,现有 fail-closed 即可恢复 200。 +- 以后若做「ledger head 可读」,必须:identity 已通过、GET **只读**、真实 fork 仍 409 `DOCUMENT_CONTENT_PROJECTION_DRIFT`、不得在 GET 路径写盘。 +- 不得把「此刻 stat 不到 `output.md`」单独当成可自动重建。曾出现 fork/mismatch 的文档禁止降级为 MISSING 自愈。 +- 本方案**删除**原文 D3「MISSING 后台从 head 原子重建」。任何写回只允许在独立 Deferred 产品里设计,且必须有路径沙箱(`Lstat` + `EvalSymlinks`,realpath 严格位于 `ENV_STATE_DIR`)。 -### D3 — ledger 是读取真源,projection 是受管兼容视图 +### D4 — transactional outbox 降为 Deferred -- 已有 ledger 的 `GET .../content` 从 immutable blob/head 返回正文,不再因单纯 `MISSING` 阻断 UI。 -- 同时返回 `projection_status` 或响应头,标识 `aligned | pending | missing | fork | failed`;真实 `CONTENT_FORK` 仍 409,并禁止覆盖双方。 -- 只有 storage identity 已通过且 work_dir 在允许根内时,`MISSING` 才允许后台从 head 原子重建;挂载身份错误时不自愈,避免在错误空根批量制造“看似恢复”的第二份数据。 -- 无 ledger 的 legacy 文档仍从文件 bootstrap,并沿用 hard-break 安全 canonicalization 与迁移审计。 - -### D4 — 用 transactional outbox 收口双写 - -新增/扩展持久 projection job: - -```text -doc_id, target_snapshot_id, target_sha256, storage_uuid, -state(pending/applying/aligned/failed/fork), attempts, -last_error_code, next_retry_at, updated_at -``` - -- 人工 PATCH、agent commit/reject 在同一 PostgreSQL 事务中更新 head 并 upsert projection job。 -- projector 使用 `doc_id + target_snapshot_id` 幂等执行,校验 storage UUID 与允许路径,临时文件 fsync + rename + parent fsync 后再标 aligned。 -- 同文档任务合并为最新 head;旧任务不得回写覆盖新 head。 -- missing 可重建;mismatch/fork 保存两侧 hash/路径后转人工处理;失败采用有上限退避并告警。 -- 同步写成功可快速返回 200;超时/失败返回明确 `projection_status=pending/failed`,不得让用户误以为账本未保存。 +- 当前 409 的根因是错挂空根,不是缺 outbox。 +- 仓库已有 `InspectProjection`、`ProjectOutputMarkdown`、`fsutil.WriteFileAtomic`、`document-content-reconcile`。新 job 表 / projector 进程是第二产品。 +- PATCH 若以后暴露 `projection_status`:ledger commit 成功则 **HTTP 一律 200**,状态放 body/头;禁止用非 2xx 表示「账本未保存」。 +- fork:服务继续,该文档 409,保全双方,立即人工处置。**不是**停 app/runner。 ## 分波次计划 -| 波次 | 内容 | 验收 | -|---|---|---| -| Wave 0 事故遏制与恢复 | 占集成槽;暂停 dev 文档写入/PI;导出 app/pi/cursor 实际 mounts、DB storage 状态;分别备份主仓根与错误 `/tmp` 根的 08:40 后增量;生成冲突清单;确认无分叉后从固定状态根重建三容器 | 项目 143 文件不改内容且三方 SHA 仍为 `f283...f039`;容器 source 指向固定状态根;目标 content 200;错误根只读保全,不盲拷贝/删除 | -| Wave 1 脚本硬门禁 | `env-service.sh` 解析 primary/common Git root与显式 `RUNTIME_STATE_ROOT`;mutating action 校验绝对路径、环境 identity、分支/集成模式;打印 build source 与 state root;worktree 未显式指定稳定 state root 时直接失败 | linked worktree 默认执行 rebuild 失败且不替换现有容器;合法集成构建的所有 bind source 保持固定 | -| Wave 2 Compose 解耦 | dev/test/main/shared compose 的可变 bind 全部改为绝对 state-root 变量;为 app/pi/cursor 添加 storage UUID/环境标签;CI 检查 `docker compose config` 不出现工作树内 `data/` | 从两个不同 source roots 渲染 config,数据挂载 source 完全相同;代码 build context 可不同 | -| Wave 3 启动与 readiness | 引入文件/DB storage identity;readiness 增加 `storage_ready`、`storage_id_match`、runner mount/identity;部署后脚本核对 container labels + mounts + sentinel + active-doc projection 摘要 | 错误空根启动被拒绝;本次 split-brain 复现实验 `/ready` 必须 503,不能再显示 healthy | -| Wave 4 投影状态机 | ledger read 与兼容 projection 解耦;实现 projection outbox、幂等 projector、missing 安全重建、fork 隔离和结构化状态/告警 | DB commit 后 kill -9、只读目录、磁盘满、延迟重试均不丢 head;恢复后自动 aligned;fork 不被覆盖 | -| Wave 5 数据治理 | 按[数据生命周期与迁移语料子方案](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md)执行蓝绿精简;真实项目、demo、migration corpus、临时测试分 namespace;增加 orphan/missing/fork 巡检与 TTL | dev 常驻 6–10 个可解释项目;受支持旧 schema corpus 可重复迁移;测试临时路径不污染运行库 | -| Wave 6 验收与回滚演练 | 工程测试、容器 rebuild/recreate、宿主重启、真实 UI 修改/刷新/重登、双用户冲突、真实 PI 修改、回滚演练 | 下述验收矩阵全 PASS,报告归档后才恢复 FEATURE-MATRIX `done` 和 gap PASS | +| 波次 | 单元 | 内容 | 验收 | +|---|---|---|---| +| Wave 0 | A | 事故抢占:停 pi/cursor/app(保留 Postgres 卷);只读保全主仓根与 `/tmp` 错误根;三方 disposition;**仅从主仓 checkout** 用现有相对 bind 纠挂三容器 | 见下方 Runbook;**不**验 storage identity,**不**改 143 正文 | +| Wave 1 | B | `env-service.sh` + compose 同时改:导出绝对 `RUNTIME_STATE_ROOT`,可变 bind 全部改公式;worktree / `/tmp` 默认 mutating 失败且不 recreate 现网 | 两个不同 `SOURCE_ROOT` 渲染后,app/pi/cursor 的 host mount source 字节级相同,且等于 allowlist 中的 `ENV_STATE_DIR` | +| Wave 2 | B | 与 Wave 1 同一批落地。CI 禁相对 `./data/`;**允许**绝对路径落在 primary checkout 的 `data/`。shared 只用 `SHARED_STATE_ROOT` | 合法配置不会被「工作树内 data/」误杀;dev 命令不能挂 test/main 根 | +| Wave 3 | B | 文件/DB identity + pair 命令;`/ready` 增加 `storage_ready` 布尔;dev compose healthcheck 改为 `/api/v1/ready` | 复现本次 split-brain 时 `/ready` 503、容器 unhealthy;`/health` 仍可 200 | +| — | Deferred | ledger GET 与 MISSING 解耦;outbox/projector;历史 work_dir 治理;五条时序指标与 60s SLO;迁仓外路径 | 另开 long-term,不进本 gap 关门 | -## 即时恢复 Runbook(批准实施后执行) +Wave 1 若只改脚本、不改 compose,从 `/tmp` 做「合法 rebuild」仍会把 `./data/dev` 解析到空根。因此 **Wave 1 与 Wave 2 必须同批**。 -1. 在 CURRENT 占 `infra-runtime-state` mutex 与集成槽,记录容器 ID/created/mount source;暂停售写。 -2. 对主仓状态根和错误 `/tmp/as-dev-wf409-merge/data/dev` 做只读 inventory:相对路径、size、mtime、SHA;特别核对 08:40 UTC 后新增/修改项。 -3. 若错误根存在新业务写入,按 DB task run、snapshot、source_run_id 和文件 hash 逐项判定;不得 `cp -r` 覆盖主根。 -4. 备份差异和 Compose inspect 证据;用固定 state root rebuild app/pi/cursor。数据库 named volume不变。 -5. 验证 storage identity、mount source、`/ready`、项目 143 content/revision、活跃项目 projection 摘要。 -6. 仅在页面完成项目 143 打开、保存唯一 marker、硬刷新、退出重登;随后真实 PI 修改一份新文档并验证 app rebuild 后恢复。 -7. 错误根在报告签字前只读保留;确认无独有数据后再按独立授权处置,本方案不授权删除。 +## Unit A — 即时恢复 Runbook(实施待再批) -## 自动化验收矩阵 +授权:事故抢占。主仓 CURRENT 即使仍显示 `implementer_cursor` / `e2e`,实施时也要写入抢占记录,停止该 e2e 对 `agent-dev` 的使用,并通知 queue 中的 `governance-action-level-authz`。以**主仓** CURRENT 与 `docker inspect` 为准,禁止信任任何 worktree 副本里的 `idle`。 + +**冻结 = 停进程,不是文档 mutex,也不是「请大家别保存」。** 仓库没有 maintenance 开关。 + +1. 在主仓 CURRENT 登记抢占:`holder=incident-preempt`,`scope=RUNTIME-DATA-ROOT Unit A`,`mutex=infra-runtime-state`。记录三容器 ID / created / `Mounts.Source` / compose working dir。 +2. **先停写再盘点**(保留 `shuimu-context-pg-dev`): + + ```bash + docker stop shuimu-pi-agent-dev shuimu-cursor-agent-dev shuimu-agent-app-dev + ``` + + 未停止不得开始差异扫描。test/main 容器禁止触碰。 +3. 对主仓 `…/Agent-System/data/dev` 与 `/tmp/as-dev-wf409-merge/data/dev` 做只读 inventory:相对路径、size、mtime、SHA。使用 `find -P` / 不跟随 symlink;先 `Lstat` 跳过非常规文件。特别列出 08:40 UTC 之后的新增/修改,以及 `agent_sessions`、attachments、CAS、非 `output.md`。 +4. 备份目的地必须在仓库外、权限 `0700`、与运行数据根分离。`agent_sessions` / token / `.env` 只记存在性与大小,不拷贝明文。禁止把 `/tmp` 备份写进 `data/dev` 或 git 工作区。禁止 `cp -r` 覆盖主根。 +5. 按 **DB/ledger vs 主根 vs 错误根** 出 disposition 清单(保留主根 / 回收错误根独有项 / 标 orphan)。项目 143 只是抽样锚点,不能代表 100 条记录或 68 条主仓历史 missing。冻结时刻重新计算三方 hash;**删除「必须仍为 `f283...f039`」硬门槛**。仅当冻结快照上 143 仍三方一致时,才允许「只纠挂、不改该文件」。 +6. 纠挂**必须**在主仓执行,禁止 `env-service.sh` 从 worktree 或 `/tmp/as-dev-wf409-merge` 调用: + + ```bash + PRIMARY=/home/weichen/projects/Agent-System + docker compose --project-directory "$PRIMARY" \ + -f "$PRIMARY/docker-compose.dev.yml" \ + up -d --build \ + app pi-agent cursor-agent + ``` + + 数据库 named volume 不变。纠挂后立刻 `docker inspect`:三个 bind source 必须是 `$PRIMARY/data/dev` 及其 `workspaces`,不得再指向 `/tmp`。 +7. 验证:`GET /api/v1/docgov/documents/doc_proj143_intake/content` 在冻结快照仍一致时应 200;抽查若干活跃非模板文档的投影可见性。`/ready` 仍按现状理解(此时还没有 storage identity)。 +8. 错误根只读保留到报告签字;本方案**不授权删除** `/tmp/as-dev-wf409-merge`。 +9. **禁止**在 Wave 0 对项目 143 保存 marker、禁止真实 PI 改文。那些属于 Unit B 之后的页面验收,另批。 + +### Wave 0 验收(仅这些) + +- [ ] 停写发生在 inventory 之前;Postgres 未删卷、未重建。 +- [ ] 两端 inventory + 仓库外备份 + disposition 清单落盘到 test-reports assets。 +- [ ] app/pi/cursor 的 bind mount source 回到主仓 `data/dev`(或 `…/workspaces`),compose working dir 不再是 `/tmp/as-dev-wf409-merge`。 +- [ ] 项目 143 在「冻结快照仍一致」时 content 200,文件内容与冻结 hash 一致。 +- [ ] 错误根仍在且未被覆盖/删除。 +- [ ] 主仓 CURRENT 记录抢占与恢复结果;不以 `/health` 200 作为通过。 + +## Unit B — 防复发验收矩阵 ### 脚本与 Compose -- [ ] primary repo、linked worktree、`/tmp` integration checkout 三种 source root 下,合法 config 的 state bind source 相同。 -- [ ] 未配置/相对/不存在/环境名不符的 state root 均 fail fast,已有容器不被 recreate。 -- [ ] dev 命令不能挂 test/main 根;storage UUID 交叉时拒绝。 -- [ ] app、pi-agent、cursor-agent 三者 mount source 和 storage UUID 一致。 +- [ ] primary repo、linked worktree、`/tmp` integration checkout 三种 `SOURCE_ROOT` 下,合法 config 的 state bind source 相同,且等于 allowlist 中的绝对 `ENV_STATE_DIR`。 +- [ ] 未配置 / 相对 / 不存在 / 环境名不符 / 指向 worktree 或 `/tmp` 的 state root 均 fail fast,已有容器不被 recreate。 +- [ ] 显式 `RUNTIME_STATE_ROOT` 不在 allowlist 时失败(不能靠「绝对路径」绕过)。 +- [ ] dev 命令不能挂 test/main 根。 +- [ ] `docker inspect`:app、pi-agent、cursor-agent 三者 mount source 同一 realpath 前缀。 +- [ ] CI 断言的是「禁止相对 `./data/` + 两次渲染 source 相同」,**不是**「不得出现 primary 工作树内的绝对 `data/dev`」。 -### 后端与故障注入 +### Readiness -- [ ] ledger head + projection missing + identity correct:GET 200 返回 head,job pending 后原子重建并 aligned。 -- [ ] ledger head + projection missing + identity mismatch:readiness 503,不执行自愈写入。 -- [ ] ledger head + projection fork:409 `DOCUMENT_CONTENT_PROJECTION_DRIFT`,保全双方。 -- [ ] PATCH 在 ledger commit 后 projector 失败:状态可见、同幂等键不重复增 r、后台恢复后 aligned。 -- [ ] projector 处理中又产生新 head:旧 job 不得覆盖新 snapshot。 -- [ ] app/projector/runner restart 与 kill -9 后任务可恢复;告警包含 doc_id、snapshot、storage UUID、correlation id,不含正文或 secret。 - -### 真实页面验收 - -- [ ] 项目 143 正文首次打开无 409;人工保存后 r+1、硬刷新、退出重登仍可见。 -- [ ] 保存后立即刷新与连续刷新不会触发 loading 死锁或重复错误风暴。 -- [ ] 真实 PI/小队修改新项目文档,候选确认/拒绝、change report、最终正文均可见;fallback 明确标识。 -- [ ] dev app + runner rebuild/recreate 后,旧项目和新项目正文/版本/overlay 都恢复。 -- [ ] 双浏览器旧 ETag 保存仍显示真正的 `HEAD_CONFLICT`,不能与 projection 409 混淆。 - -## 监控与 SLO - -- 指标:`document_projection_total{state}`、`projection_repair_total{result}`、`projection_age_seconds`、`storage_identity_mismatch_total`、`content_read_fallback_total`。 -- 告警:任一 storage identity mismatch 立即 P0;active 文档 `pending/failed` 超过 60 秒告警;fork 立即人工处置。 -- 日志:部署开始/结束必须记录 source commit、compose working dir、state root identity、实际 mount source hash;禁止记录正文与凭证。 -- `/ready`:数据库/schema/task store/storage identity/runner identity 为硬门禁;projection 汇总按 active 用户文档设阈值并列出计数,不泄露 doc 内容。 +- [ ] 错误空根、业务库无 sentinel、canonical path 不符:`/ready` 503,dev compose healthcheck 显示 unhealthy。 +- [ ] `/health` 保持 liveness,部署脚本不得用它当就绪。 +- [ ] `/ready` 不泄露 UUID/路径/doc 内容。 ## 回滚策略 -- Wave 1–3 可回滚代码,但不得回滚/重建 storage UUID;旧相对 bind mount 禁止作为回滚方案。 -- Wave 4 使用 feature flag 分阶段启用 ledger-read 与 projector;关闭 projector 时保留 pending jobs,不删除。 -- 所有自动修复只覆盖 `MISSING + identity matched`;任何 fork 保留证据并停机式处理。 +- Unit B 代码可回滚,但 **不得回滚/重建已配对的 storage UUID**;旧相对 bind **禁止**作为回滚方案。 +- Unit A 失败:保持停写,不要把容器重新绑回 `/tmp`。 +- 任何自动修复(若未来另批)只覆盖 `MISSING + identity matched + 无历史 fork`;fork 保留证据,服务继续。 ## 方案边界 -- 本轮不修改业务代码、不重建容器、不修复/覆盖任何正文。 -- 当前 409 的即时恢复与系统实现必须由用户批准后,在独占集成槽执行。 -- test/main 仅做只读挂载审计;未经环境晋升授权不得改动。 +| 阶段 | 允许 | 禁止 | +|---|---|---| +| 审查修订(本轮) | 改 `.doc` 方案与 SSOT | 改业务代码、rebuild、写库、覆盖/删除正文或 `/tmp` | +| Unit A(再批后) | 停 dev app/pi/cursor;只读保全;从主仓纠挂三容器 | 改 test/main 运行态;删错误根;Wave 0 写 143;从 worktree 调 `env-service` | +| Unit B(再批后) | 改仓库内 compose/脚本/`/ready`;test/main **文件**可改绝对变量 | 对运行中的 test/main 做 mutating env-service;自动 mint identity;迁仓外 | +| Deferred | 另开 long-term | 当作本事故关门条件 | + +## 监控(Unit B 最小集 / 其余 Deferred) + +Unit B 只要求:部署日志记录 source commit、compose working dir、state root、实际 mount source hash;禁止记录正文与凭证。`storage_identity_mismatch` 作为 `/ready` 布尔即可。 + +五条 Prometheus 指标、60 秒 pending SLO、`/ready` 投影计数阈值整节移出本方案。 ## 相关链接 - Audit: [dev rebuild 从临时 worktree 挂载空数据根](../audit/2026-09-14-dev-runtime-data-root-split-brain.md) -- Short-term: [dev runtime data root guard 诊断与设计](../ops/short-term/2026-09-14-dev-runtime-data-root-guard.md) -- Data lifecycle: [运行数据生命周期与迁移语料库](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) +- Short-term: [dev runtime data root guard](../ops/short-term/2026-09-14-dev-runtime-data-root-guard.md) - gap-matrix 行: `RUNTIME-DATA-ROOT-SPLIT-BRAIN` + +## 审查修订记录 + +原文矛盾已在本文消除,不再保留互斥表述: + +- `RUNTIME_STATE_ROOT` 同时被写成「每环境根」和「父目录+/」 +- 「本轮不重建」与 Wave 0 重建互相否决(现按阶段表) +- Wave 0 冻结 143 SHA 与 Runbook 要求保存 marker +- Wave 0 验收 storage identity(Wave 3 才有) +- Wave 1 宣称 bind 固定但 compose 仍相对 +- Wave 2 CI 禁止工作树内 `data/` 与短期默认根互斥 +- fork「停机式处理」与 409 继续服务互斥 +- PATCH 投影失败的 HTTP 状态分叉 +- GET `projection_status` 与 job state 混用 +- `CONTENT_FORK` 与 `DOCUMENT_CONTENT_PROJECTION_DRIFT` 对外码漂移 +- shared compose 套用每环境公式 +- 「暂停售写」未定义 +- 恢复与 outbox/治理/完整 SLO 同一关门条件 diff --git a/.doc/remediation/INDEX.md b/.doc/remediation/INDEX.md index 320fe23d..bf37c415 100644 --- a/.doc/remediation/INDEX.md +++ b/.doc/remediation/INDEX.md @@ -3,7 +3,7 @@ | 文档 | 说明 | |------|------| | [gap-matrix.md](./gap-matrix.md) | 发布缺口 SSOT | -| [2026-09-14-dev-runtime-data-root-and-projection-remediation.md](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) | 临时 worktree 错挂 dev 数据根、storage identity、readiness 与 projection outbox 系统整改 | +| [2026-09-14-dev-runtime-data-root-and-projection-remediation.md](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) | 临时 worktree 错挂 dev 数据根。审查修订:Unit A 纠挂 / Unit B 状态根与 readiness / Deferred outbox | | [2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) | dev/test 数据精简、migration corpus、蓝绿换库、TTL 与延迟销毁方案 | | [2026-09-14-workflow-plan-409-frozen-and-optional-duties-remediation.md](./2026-09-14-workflow-plan-409-frozen-and-optional-duties-remediation.md) | 项目设置 409 冻结态守卫 + 非必需文档三权全有或全无 + 成员/门禁/权限一致性 | | [2026-09-14-controlled-content-canonical-hash-remediation.md](./2026-09-14-controlled-content-canonical-hash-remediation.md) | 项目 143 正文 canonical/raw 哈希漂移、错误语义与重复 hydration 整改 | diff --git a/.doc/remediation/gap-matrix.md b/.doc/remediation/gap-matrix.md index 30d3066c..5dd3f6e1 100644 --- a/.doc/remediation/gap-matrix.md +++ b/.doc/remediation/gap-matrix.md @@ -57,7 +57,7 @@ | ID | 描述 | 状态 | 证据 | 计划动作 | |---|---|---|---|---| -| RUNTIME-DATA-ROOT-SPLIT-BRAIN | dev rebuild 从临时集成 worktree 解析 `./data/dev`,app/pi/cursor 挂到空 `/tmp` 数据根,而 PostgreSQL 沿用原持久卷;100 条非模板记录在当前容器根抽样均 missing,health/ready 仍 200 | **FAIL / CRITICAL** | [audit](../audit/2026-09-14-dev-runtime-data-root-split-brain.md)。项目 143 主仓文件、DB、ledger head SHA 一致,容器内同路径不存在;Compose working dir 与三容器 mount source 均为 `/tmp/as-dev-wf409-merge` | [runtime remediation](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) + [data lifecycle](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md)。先恢复固定根,再以蓝绿新库、最小迁移 corpus 和延迟销毁精简 dev;恢复前禁止用 dev healthy 或文档 E2E 签字 | +| RUNTIME-DATA-ROOT-SPLIT-BRAIN | dev rebuild 从临时集成 worktree 解析 `./data/dev`,app/pi/cursor 挂到空 `/tmp` 数据根,而 PostgreSQL 沿用原持久卷;100 条非模板记录在当前容器根抽样均 missing,health/ready 仍 200 | **FAIL / CRITICAL** | [audit](../audit/2026-09-14-dev-runtime-data-root-split-brain.md)。2026-09-14 ~09:23 UTC 复核:挂载仍为 `/tmp/as-dev-wf409-merge`,`/ready` 仍 200/`schema=33`。审查已否决「恢复与 outbox 同批关门」 | 关门 = [审查修订方案](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) 的 Unit A 纠挂 + Unit B 绝对状态根/门禁/`/ready` 503。outbox 为 Deferred。[data lifecycle](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) 不替代事故修复,也不挡 Unit A | | WORKFLOW-PLAN-FROZEN-OPTIONAL-DUTIES | active 项目仍展示可编辑职责表单,409 提示在长表单视口外;现有规划又不支持非必需文档“三权全空”,且后端缺少职责人在任成员复核 | **PASS(设置页)** | [audit](../audit/2026-09-14-workflow-plan-409-frozen-and-optional-duties.md);[test-report](../test-reports/2026-09-14-workflow-plan-409-frozen-and-optional-duties-test-report.md)。项目 143 只读 frozen、A full+none 激活、B partial 不发 PUT、C 零治理拒绝、双标签转 frozen、成员无配置入口、刷新后 143 仍 frozen | 真实 PI 生成 full/none 并走审批/阶段不阻塞未纳入本轮页面矩阵,不作为本 gap 关闭的替代证据,另立项 | | DOC-CONTENT-CANONICAL-HASH-DRIFT | 旧受控文档 bootstrap 裁剪 Markdown 双空格硬换行并将 canonical SHA 写入账本/DocGov,但未同步文件;正文读取把完整性冲突误报 404,lease/list sync 又放大并制造刷新后加载死锁 | **PASS(真实 UI)** | [audit](../audit/2026-09-14-project143-controlled-content-404.md);[UI report](../test-reports/2026-09-14-controlled-content-ui-acceptance.md)。代码已合 dev、schema 32、项目 143 定向 `migration_repair`;项目 143 `clean-run3` 与新项目 `full-chain-final` 主链及 restart 全部纯页面 PASS,目标 404=0 | [remediation](./2026-09-14-controlled-content-canonical-hash-remediation.md)。隔离 `TestReconcile_LegacyRepair_PG` 未执行,按用户“本轮只允许页面测试”的边界保留为工程测试债,不作为本轮 UI PASS 的替代证据 | | CHAT-HISTORY-REFRESH | 工作流激活后刷新,对话正文被阶段过滤器清空,但分类数量与锚点仍按全量返回 | **PASS** | [test-report](../test-reports/2026-09-13-chat-history-refresh-test-report.md);代码:`message_scope.go`、`chat_history_auth.go`、goose 00026/00027 | 真实 PI 刷新 E2E 待 dev 容器补录(自动化/PG 已 PASS) | diff --git a/.doc/status/FEATURE-MATRIX.md b/.doc/status/FEATURE-MATRIX.md index d4799dc0..ba28dd2f 100644 --- a/.doc/status/FEATURE-MATRIX.md +++ b/.doc/status/FEATURE-MATRIX.md @@ -21,11 +21,12 @@ | 工作流治理 | 三正式工作流 + 阶段门禁 | partial | [remediation/three-workflows](../remediation/2026-09-12-three-workflows-strict-governance-remediation.md);[test 2026-09-12](../test-reports/2026-09-12-three-workflows-governance-remediation-test-report.md);[冻结态/职责可选整改](../remediation/2026-09-14-workflow-plan-409-frozen-and-optional-duties-remediation.md);[Wave 5 页面矩阵](../test-reports/2026-09-14-workflow-plan-409-frozen-and-optional-duties-test-report.md);[职责-动作鉴权](../test-reports/2026-09-14-governance-action-level-authz-test-report.md) | 设置页/DutyMode + 动作级鉴权/未激活 202-blocked/关闭创建直通已落地。后续:none 文档真实 PI 审批长链路、CI 三工作流 E2E | `workflow` | | 纯软件项目自举 | 软件项目创建、Bootstrap 基线、Delivery 迭代与受控工程交接 | planned | [详细设计](../system-design/2026-09-14-software-project-bootstrap-workflow.md);[现状调研](../ops/research/2026-09-14-software-project-bootstrap-current-state.md);[验收计划](../ops/checklists/2026-09-14-software-project-bootstrap-acceptance.md) | 当前仅完成方案;目录/模板/执行器/创建向导/真实 PI UI 均未实施。受控代码执行单列后续 Epic | `workflow-design` | | IAM / 鉴权 | 项目级 API 鉴权 + RBAC | partial | [audit/authorization](../audit/authorization-matrix-v2.md);[刷新恢复专项审计](../audit/2026-09-13-chat-history-refresh-disappears.md) | 历史读取端点补统一会话 owner + 阶段授权;旧 API 410 标注(BUG-11 partial) | `authz` | -| 文档治理 | 法定文档生命周期 + 租约 + 受控正文读取 | partial | [数据根 split-brain 审计](../audit/2026-09-14-dev-runtime-data-root-split-brain.md);[test docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md);[项目 143 canonical 漂移审计](../audit/2026-09-14-project143-controlled-content-404.md);[既有真实 UI 报告](../test-reports/2026-09-14-controlled-content-ui-acceptance.md) | canonical/hash 与 UI 竞态旧缺陷已修;但当前 dev 因临时 worktree 数据根 split-brain 再次不可读。须完成固定 state root、storage identity、projection outbox/自愈和 rebuild 负例后恢复 `done` | `docgov` | +| 文档治理 | 法定文档生命周期 + 租约 + 受控正文读取 | partial | [数据根 split-brain 审计](../audit/2026-09-14-dev-runtime-data-root-split-brain.md);[test docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md);[项目 143 canonical 漂移审计](../audit/2026-09-14-project143-controlled-content-404.md);[既有真实 UI 报告](../test-reports/2026-09-14-controlled-content-ui-acceptance.md) | canonical/hash 与 UI 竞态旧缺陷已修;当前 dev 因数据根 split-brain 再次不可读。Unit A 纠挂后标 incident-mitigated;`done` 等 Unit B 负例,不等 outbox | `docgov` | | 文档版本与修改认知 | 智能体版本边界、人工修订标记、任意版本比较、变更报告与负责人通知 | partial | [管理测试](../test-reports/2026-09-13-document-revision-management-test-report.md);[运行时 UI Round20](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md);[运行时审计](../audit/2026-09-13-document-revision-runtime-404.md);[ADR](../adr/2026-09-13-document-version-boundary.md) | **运行时 UI PASS**(6159 / V3/r2)。仍 `partial`:change report=`fallback`(真实 diff+lineage,非完整 PI 叙事)。系统级 `CHAT-INTENT` 见 gap-matrix | `docgov` | +| 最终成果归档与恢复 | 终态文档/助手回复的独立归档、稳定定位、清理门禁与受控部分恢复 | planned | [调研](../ops/research/2026-09-14-final-artifact-vault.md);[整改方案](../remediation/2026-09-14-final-artifact-vault-remediation.md) | 当前仅有 PostgreSQL 文档快照、消息正文和运行态 CAS;缺独立 storage identity、终态 outbox、manifest/receipt、授权降级读取和 scratch 恢复工具 | `artifact-vault` | | 前端工作台 | 三栏 + 块文档 + 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 | partial | [PROJECT.md](../../PROJECT.md) §4;[数据根 split-brain 审计](../audit/2026-09-14-dev-runtime-data-root-split-brain.md);[数据生命周期方案](../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) | dev 当前错挂 `/tmp` 空数据根且 readiness 假阳性;须完成绝对 state root/身份门禁,并以蓝绿换库将 366 个混杂项目收敛为 demo/regression/migration corpus + TTL 测试数据 | `infra` | +| 三环境运维 | dev/test/main + 共享 KB | partial | [PROJECT.md](../../PROJECT.md) §4;[数据根 split-brain 审计](../audit/2026-09-14-dev-runtime-data-root-split-brain.md);[审查修订方案](../remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.md);[数据生命周期方案](../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) | 现场仍错挂 `/tmp`。本行 `done` 等 Unit A 纠挂 + Unit B 门禁负例;蓝绿换库/corpus/FAV 为 sibling,不挡事故关闭 | `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 Round20](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md) | CI `TEST_DATABASE_URL` 隔离库仍缺;文档运行时真实 UI 已按 UI_PHASE 分文件且 post-restart 断言最终 A1 正文。`-race` 全包并行 TempDir flake 为基建残留 | `ci` | ---