From ded40d071e116cdfb4ca77e68f4e9fd81d7384d1 Mon Sep 17 00:00:00 2001 From: weichen Date: Mon, 14 Sep 2026 10:09:32 +0000 Subject: [PATCH] docs(remediation): design final artifact vault --- .doc/ops/CURRENT.md | 4 +- .../2026-09-14-final-artifact-vault.md | 149 +++++++++++ ...ime-data-retention-and-migration-corpus.md | 3 + .../2026-09-14-final-artifact-vault-plan.md | 61 +++++ .../2026-09-14-runtime-data-lifecycle-plan.md | 4 + ...-09-14-final-artifact-vault-remediation.md | 247 ++++++++++++++++++ ...ecycle-and-migration-corpus-remediation.md | 13 +- .doc/remediation/INDEX.md | 1 + .doc/remediation/gap-matrix.md | 2 +- .doc/status/FEATURE-MATRIX.md | 2 +- 10 files changed, 482 insertions(+), 4 deletions(-) create mode 100644 .doc/ops/research/2026-09-14-final-artifact-vault.md create mode 100644 .doc/ops/short-term/2026-09-14-final-artifact-vault-plan.md create mode 100644 .doc/remediation/2026-09-14-final-artifact-vault-remediation.md diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index dfb032e7..bc27dd41 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;审查修订已完成。现场仍错挂 `/tmp/as-dev-wf409-merge`;Unit A 事故抢占纠挂与 Unit B 防复发**待再批**。数据精简为 sibling 方案,不挡 Unit A) +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 防复发**待再批**。数据精简与[最终成果保险库](../remediation/2026-09-14-final-artifact-vault-remediation.md)为 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 为准收敛) @@ -51,6 +51,7 @@ | 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 | +| Codex | final-artifact-vault-plan | fix/dev-runtime-data-root-guard | .worktrees/fix-dev-runtime-data-root-guard | artifact-vault | done | > 新任务开始前登记。`branch` 必须为 `feature/` 或 `fix/`,**禁止** `dev` / `test` / `main`。同一 `mutex` 同时仅一个 `in_progress`。 @@ -115,3 +116,4 @@ | dev 数据根 split-brain 审计 | [../audit/2026-09-14-dev-runtime-data-root-split-brain.md](../audit/2026-09-14-dev-runtime-data-root-split-brain.md) | | dev 数据根与投影系统整改 | [../remediation/2026-09-14-dev-runtime-data-root-and-projection-remediation.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](../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) | +| 最终成果保险库与受控恢复 | [../remediation/2026-09-14-final-artifact-vault-remediation.md](../remediation/2026-09-14-final-artifact-vault-remediation.md) | diff --git a/.doc/ops/research/2026-09-14-final-artifact-vault.md b/.doc/ops/research/2026-09-14-final-artifact-vault.md new file mode 100644 index 00000000..67c52cf7 --- /dev/null +++ b/.doc/ops/research/2026-09-14-final-artifact-vault.md @@ -0,0 +1,149 @@ +--- +title: "最终成果保险库与部分恢复边界" +created: 2026-09-14 +status: done +confidence: high +--- + +# 调研:最终成果保险库与部分恢复边界 + +## 结论 + +提案可行,建议采用,但不应实现成“把最终文件复制到一个固定目录”。应建设独立的**最终成果保险库(Final Artifact Vault,FAV)**:只接收服务端已经确认终态、具有稳定标识和内容哈希的成果,以不可变 blob、语义 manifest 和提交回执保存,并与工作区清理完全解耦。 + +FAV 是 PostgreSQL 完整备份/PITR 的互补层,不是第三套完整业务事实源。它只对“已归档成果的精确内容与出处”负责,可以在数据库损坏或选择性重建时恢复文档、最终回复和有限的来源关系;成员权限、工作流活动态、租约、待办任务、幂等记录等仍须由数据库备份或其他权威记录恢复。 + +## 当前能力与缺口 + +| 能力 | 当前事实 | 与提案的差距 | +|---|---|---| +| 受控文档版本 | `document_content_blobs` 与 `document_snapshots` 在 PostgreSQL 内保存不可变 Markdown;工作区 `output.md` 是投影 | 数据库与账本一起丢失时,没有独立副本;当前 GET 仍会被缺失工作区投影阻断 | +| 助手最终回复 | `chat_messages.content` 保存正文,`chat_turns.assistant_message_id` 可在 completed 回合绑定最终消息 | 占位正文会原地更新,没有独立不可变回复快照;删除 session 会级联删除消息 | +| 回复导出 | `replyexport` 已区分 completed、status/error/card,提供 `content_sha256` 与 `export_policy` | `export-source` 仍从当前消息行临时构造 snapshot,不是持久归档;部分调用在缺少真实回合状态时默认按 completed 评估 | +| 附件 CAS | `storage_blobs`/`file_references` + `data//storage/blobs` 按 SHA 去重 | blob 物理文件仍在运行状态根,引用元数据仍在数据库;不独立于工作区/数据库事故 | +| 数据备份 | 现有脚本主要导出 PostgreSQL;数据生命周期方案已要求 DB + state 成对打包 | 尚无跨文档/回复的终态准入、持续发布、独立索引或成果级恢复 API | + +当前事故已经证明“数据库记录还在”不代表“运行容器能找到正文”。FAV 可以解决成果副本对工作区根的依赖,但不能替代固定 `RUNTIME_STATE_ROOT`、storage identity、projection outbox 和数据库备份。 + +## 提案中应保留的部分 + +1. **独立持久位置**:成果存储不位于 Git worktree、临时集成目录、任务 workspace 或普通 GC 扫描范围。 +2. **只收终态成果**:拒绝流式片段、候选稿、工具轨迹、修复过程、临时报告、租约和未完成任务。 +3. **支持系统定位**:使用稳定 artifact UUID、逻辑资源 ID、来源事件和内容 SHA,而不是依赖数据库自增 ID 或 workspace 路径。 +4. **辅助部分恢复**:允许在数据库关系不匹配时校验并导入已归档内容,同时保留来源和冲突证据。 +5. **与清理联动**:工作区或旧数据集只有在必需成果全部发布并验证后才可进入销毁阶段。 + +## 必须修正的部分 + +### 1. “最终”必须由状态机判定 + +不能按文件名、目录、时间或“任务进程退出 0”判断最终。建议的准入规则: + +| 成果类型 | 可归档条件 | 明确排除 | +|---|---|---| +| 智能体生成文档 | agent candidate 已 `committed`,且 output snapshot 成为账本 head;归档该次生成的不可变 snapshot | ready、needs_confirmation、rejected、stale、quality_failed、workspace 草稿 | +| 受控文档正式稿 | `approved` 时归档被冻结的 review snapshot;版本被 sealed 时可归档该版 final head | drafting/reviewing 中尚未冻结的当前文件、lease 中内容 | +| 自由文档 | 已形成不可变 version,且用户显式“发布/归档”或被正式成果引用;显式操作不能绕过终态检查 | 自动保存中的连续修订、临时私密摘录 | +| 助手回复 | `chat_turn.status=completed`、绑定唯一 `assistant_message_id`、正文 SHA 匹配,且 `export_policy=full`;`copy_only` 只在 completed 后显式 pin 时进入 | status/error/cancelled/system、纯操作卡、流式 chunk、placeholder、中间 agent 消息;未完成回复即使 pin 也拒绝 | +| 最终报告/附件 | 被上述成果 manifest 引用并通过合规/病毒/类型校验 | 未引用附件、日志、Prompt、工具输入输出、agent session | + +“每次成功保存的人工 revision”仍进入 PostgreSQL 不可变账本,但不自动进入 FAV。这样既保留文档历史,又不会把编辑过程伪装成最终成果。 + +### 2. 保险库不是第三套完整真相源 + +建议采用三层责任边界: + +| 层 | 权威范围 | 可否清理 | +|---|---|---| +| PostgreSQL 运行库 | 项目/用户/权限、工作流、任务、当前指针、完整版本账本和归档发布状态 | 按环境生命周期与完整备份策略处理 | +| Runtime workspace/state | Agent 计算现场、`output.md` 投影、临时转换文件和附件运行副本 | 可重建/可清理,但须受发布门禁保护 | +| Final Artifact Vault | 已准入成果的精确字节、manifest、来源证据、保留/删除状态和发布回执 | 独立保留;只能按成果保留策略和删除账本处理 | + +正常读写仍以数据库授权和业务指针为准。数据库仍能完成授权判定但正文缺失时,可从 FAV 只读降级返回并标记 `source=vault/degraded`;若权限关系也缺失,FAV 不得绕过授权直接对用户开放,只能进入管理员隔离恢复流程。 + +### 3. 发布必须避免双写裂缝 + +数据库事务内同时提交业务终态、精确 payload/SHA 的不可变 staging 和 `artifact.publish.requested` outbox;事务外 publisher 只从 staging 幂等写 blob、manifest,校验后最后写 commit marker,再回填 receipt。对象存储 I/O 不进入数据库事务。不能让异步 publisher 重读仍可更新或随 session 级联删除的消息行。 + +清理流程只认 `verified` receipt,不认“消息发过”“目录存在”或“有一个 SHA”。重复投递必须由 `artifact_uuid + source_event_id + payload_sha256` 去重;相同逻辑资源的新终态生成新 manifest,不覆盖旧 manifest。 + +### 4. Manifest 必须表达恢复语义 + +每个归档单元至少包含: + +```text +artifact_schema_version +artifact_uuid / artifact_type / logical_resource_id +project_external_key / environment +source_event_id / source_run_id / source_message_id / source_snapshot_id +payload members: name / media_type / byte_size / sha256 / required +terminal_evidence: state / decided_at / actor_ref / policy_version +lineage: parent artifact / document version / chat turn +retention_class / sensitivity / encryption_key_ref +source_database_identity / source_schema_version / source_commit +published_at / publisher_version / manifest_sha256 +``` + +manifest 使用稳定外部标识,同时保留旧数据库 ID 作为来源提示。它必须能在没有原数据库的情况下建立离线目录,但不得声称自己包含未列出的业务依赖。 + +### 5. 恢复必须经过隔离与对账 + +FAV 支持两种恢复结果: + +- `recover-as-collection`:把验证通过的成果导入新的恢复集合/文档,保留原来源与 hash,不伪造原工作流状态。这是默认模式。 +- `relink`:只有项目、用户、权限、会话和版本依赖均存在且无冲突时,才把成果重新连接到原逻辑资源。 + +恢复顺序为:离线 inventory → manifest/schema/SHA 校验 → 删除账本/保留策略检查 → scratch DB 导入 → 依赖与 ID 映射 → 冲突预览 → 人工确认 → 单事务 promotion → 审计报告。禁止 GET 接口因发现不一致而自动写回数据库,也禁止直接覆盖既有 head 或消息。 + +## 可恢复与不可恢复边界 + +| 可从 FAV 恢复 | 不能只靠 FAV 恢复 | +|---|---| +| 最终 Markdown/附件字节与 MIME/SHA | 用户认证凭证、session、token | +| 已归档助手最终回复及其稳定 turn/message 来源 | 项目成员关系、文档 duty、当时的完整授权图 | +| 文档版本/快照的有限来源、父子和生成 run 线索 | 活跃 lease、pending candidate、未完成 task/run | +| 最终报告、批准/提交终态证据 | 通知投递状态、outbox 消费位置、worker 状态 | +| 恢复所需 schema/producer/storage identity 元数据 | 未进入准入清单的草稿、中间过程和完整数据库约束 | + +因此灾难恢复仍须保留 PostgreSQL base backup/WAL 或经验证的完整逻辑备份;FAV 解决的是“最终成果可独立找回”和“有限业务内容可重建”。 + +## 存储、保留与安全 + +- 第一阶段可用独立 filesystem backend,但必须有独立 `vault_storage_identity`、最小权限凭证和 mount,不允许退回 `./data/dev`;凭证只从受管 secret/env 注入。生产目标应支持 TLS、静态加密及异机/对象存储复制。 +- 当前仓库没有 tenant 实体,安全边界应使用现有 `environment + project_members/role-admin` 授权模型,不得伪造 `tenant_id`。blob 采用**项目作用域加密 CAS**:同项目按 SHA-256 去重,跨项目相同 SHA 不共享密钥和删除边界;未来引入组织/租户层须另立 ADR 和迁移。 +- manifest-member 到 blob 的引用账本决定删除:tombstone 先撤销单个成果读取与 key wrapping;仅当项目作用域内最后有效引用消失且 retention/legal hold 允许时物理清除。manifest 和 commit marker 只追加,定期执行 checksum scrub 和离线目录重建。 +- filesystem backend 必须以同文件系统临时文件、校验、文件 `fsync`、原子 no-replace rename、父目录 `fsync` 实现持久化,确认 blob/manifest 落盘后才提交 marker;不能把“最后写 marker”误当成掉电安全。 +- WORM/对象锁按数据类别启用,不对所有内容一律永久锁定。普通保留使用可治理模式;法律保全单独使用 legal hold。 +- project/user 授权仍由服务端复用正常读取的既有判定;授权关系缺失或不确定时 fail closed。归档正文不得进入日志、metrics 或通知 payload。 +- 自动归档前执行服务端敏感内容准入:结构化排除 Prompt/工具轨迹,secret、认证材料、restricted 或分类失败均 fail closed,且正文不进入 outbox/staging。 +- 建立删除/法务保留账本。满足删除条件时先禁止读取和恢复,再删除密钥/对象;任何历史 restore 都必须重放 tombstone,避免已删除内容复活。 +- manifest 不保存 secret、Prompt、内部推理、工具轨迹或认证材料;敏感元数据最小化,必要时使用不可逆外部键映射。 + +## 与数据精简方案的关系 + +FAV 与 migration corpus、完整备份是三个不同产品: + +| 机制 | 目的 | 内容 | +|---|---|---| +| Final Artifact Vault | 持续保存终态成果,供定位、只读降级和有限恢复 | 小而长期、按终态事件持续追加 | +| Migration corpus | 验证旧 schema 到新 schema 的迁移、异常和 reload | 小而可重复、脱敏、带 assertions | +| DB + state backup/PITR | 完整灾难恢复与回滚 | 大而完整、按 RPO/RTO 生成 | + +清理不能因为“FAV 有最终正文”就跳过完整备份,也不能用完整备份替代成果级准入。正确门禁是三者分别验证:最终成果覆盖率、迁移 corpus 可升级、完整备份可恢复。 + +首次启用 FAV 时还必须做一次**存量终态回填**:只从权威数据库的不可变 snapshot、批准/提交事件和 completed chat turn 枚举,以来源事件 + payload SHA 幂等产生请求。缺正文或终态证据的记录进入 quarantine;批准排除必须记录原因、批准人和有效期。禁止为了达到 coverage 扫描 workspace 或批量把“旧数据”标为已覆盖。 + +## 外部依据 + +- [RFC 8493 BagIt](https://www.rfc-editor.org/rfc/rfc8493.html) 要求 manifest 覆盖 payload 并验证完整性,支持本方案的 manifest/checksum 设计。 +- [OCI Descriptor](https://github.com/opencontainers/image-spec/blob/main/descriptor.md) 以 digest 标识并校验内容,支持内容寻址 blob。 +- [AWS Transactional Outbox guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html) 说明业务写入与 outbox 同事务、消费者幂等的必要性。 +- [PostgreSQL PITR](https://www.postgresql.org/docs/17/continuous-archiving.html)、[pg_restore](https://www.postgresql.org/docs/17/app-pgrestore.html) 与 [pg_verifybackup](https://www.postgresql.org/docs/16/app-pgverifybackup.html) 说明完整恢复、选择性恢复依赖和真实 restore 演练的边界。 +- [AWS S3 Object Lock](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html) 说明 governance/compliance/legal hold 的差异,支持按数据类别选择保留模式。 + +## 建议动作 + +1. 采用[最终成果保险库整改方案](../../remediation/2026-09-14-final-artifact-vault-remediation.md),先定义终态协议和 manifest,再选择 filesystem 或对象存储实现。 +2. 将 FAV 发布完成率加入 dataset inventory/GC 的硬门禁,但不把 FAV 设为当前 409 的绕行修复。 +3. 首次实现优先覆盖受控文档 approved/agent committed snapshot 与 `export_policy=full` 的 completed assistant reply。 +4. 先在 dev 用项目 6173 的等价脱敏 corpus 做“生成 → 发布 → 清 workspace → 读取/恢复 → rebuild”演练,再考虑 main 保留策略。 diff --git a/.doc/ops/research/2026-09-14-runtime-data-retention-and-migration-corpus.md b/.doc/ops/research/2026-09-14-runtime-data-retention-and-migration-corpus.md index 179f5c3e..daa1ca58 100644 --- a/.doc/ops/research/2026-09-14-runtime-data-retention-and-migration-corpus.md +++ b/.doc/ops/research/2026-09-14-runtime-data-retention-and-migration-corpus.md @@ -15,6 +15,7 @@ confidence: high 1. 清理历史运行数据只能降低数据转换量和现场噪声,不能替代对受支持旧 schema 的迁移兼容;应把有价值的旧状态压缩成小型、可重复、脱敏的 migration corpus。 2. 不应在当前库上逐表删除后继续使用;当前跨项目数据存在非完整级联和 DB/文件双事实源,优先采用“新库 + 新状态根 + 选择性导入 + 旧卷隔离”的蓝绿轮换。 +3. 对最终成果再增加独立 [Final Artifact Vault](./2026-09-14-final-artifact-vault.md),但它只辅助成果定位与部分恢复,不能替代完整 DB+state 备份或旧 schema migration corpus。 ## 当前数据画像 @@ -138,6 +139,7 @@ manifest 至少记录 dataset 版本、source schema、目标 schema、生成 co - 项目 143 等真实内容不能直接固化为仓库 fixture;应保留 hash/结构特征并脱敏重建。 - migration corpus 不能只有 happy path,否则清场后会失去发现 legacy bug 的能力。 - 任何清理必须先完成隔离 restore,并验证数据库与文件包属于同一 storage identity。 +- 任何清理还必须验证应归档终态成果均已取得 FAV verified receipt;“有归档正文”不能替代完整关系闭包恢复。 - 错误 `/tmp` 数据根属于当前事故证据;在 split-brain 恢复报告签字前不纳入普通清理。 ## 建议动作 @@ -145,3 +147,4 @@ manifest 至少记录 dataset 版本、source schema、目标 schema、生成 co - 采用 [运行数据生命周期与迁移语料整改方案](../../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md)。 - 首轮只处理 dev;test 改为短 TTL 隔离环境;main 仅盘点,不删除;shared KB 排除。 - 先实现 dataset inventory/export/restore/verify,再执行蓝绿精简,不先写清理 SQL。 +- 并行实施[最终成果保险库整改](../../remediation/2026-09-14-final-artifact-vault-remediation.md),把成果覆盖率加入 GC/销毁门禁。 diff --git a/.doc/ops/short-term/2026-09-14-final-artifact-vault-plan.md b/.doc/ops/short-term/2026-09-14-final-artifact-vault-plan.md new file mode 100644 index 00000000..e983a986 --- /dev/null +++ b/.doc/ops/short-term/2026-09-14-final-artifact-vault-plan.md @@ -0,0 +1,61 @@ +--- +title: "最终成果保险库调研与整改方案" +status: done +created: 2026-09-14 +updated: 2026-09-14 +owner: Codex +branch: fix/dev-runtime-data-root-guard +worktree: .worktrees/fix-dev-runtime-data-root-guard +mutex: artifact-vault +--- + +# 最终成果保险库调研与整改方案 + +## 目标 + +评估并完善“只归档最终生成物和最终智能体回复”的提案,形成不会与数据库、文档账本和工作区争夺事实源,且能支持受控部分恢复的系统方案。 + +## 范围 + +- 最终文档版本、最终助手回复和必要恢复元数据的归档边界; +- 终态判定、原子发布、不可变清单、校验、授权、保留与删除; +- 工作区清理门禁、降级读取、隔离恢复和数据库重建边界; +- 与现有数据生命周期、迁移语料、文档账本和对话持久化方案衔接。 + +## 非目标 + +- 本轮不实现业务代码、数据库迁移或对象存储; +- 不归档候选稿、流式片段、工具轨迹、租约、临时工作区或其他中间结果; +- 不把保险库定义为运行数据库的完整镜像或自动覆盖线上数据的入口; +- 不执行数据删除、卷切换或恢复写入。 + +## 步骤 + +1. [x] 登记任务和独立 mutex。 +2. [x] 核对现有文档快照、CAS、最终助手消息、任务终态和备份恢复边界。 +3. [x] 评审提案,明确可采用部分、必须修正部分和反模式。 +4. [x] 编写专项调研与整改方案,并并入运行数据生命周期方案。 +5. [x] 更新 CURRENT、FEATURE-MATRIX、gap-matrix 与整改索引。 +6. [x] 完成文档一致性、安全、可行性和对抗性复核。 + +## 验收标准 + +- [x] 明确“最终”的服务端判定规则和禁止进入保险库的内容。 +- [x] 明确数据库、工作区、保险库三者权威边界,不产生静默双写。 +- [x] 方案含 manifest/checksum、幂等发布、清理前门禁、降级读取和隔离恢复流程。 +- [x] 明确可恢复与不可恢复的数据范围、安全/删除要求及恢复演练。 +- [x] 本轮未实施业务代码或破坏性操作。 + +## 相关链接 + +- Research: [最终成果保险库与部分恢复边界](../research/2026-09-14-final-artifact-vault.md) +- Remediation: [最终成果保险库与受控恢复](../../remediation/2026-09-14-final-artifact-vault-remediation.md) +- Test report: 待用户批准实施后创建 + +## 证据 / 备注 + +- 当前受控文档不可变正文位于 PostgreSQL `document_content_blobs`,工作区 `output.md` 是投影。 +- 当前附件 CAS 物理 blob 位于运行状态根,引用元数据位于 PostgreSQL。 +- 当前任务完成摘要通过 `FinalizeAssistantTaskMessage` 写回 `chat_messages`,尚无独立终态成果发布协议。 +- 文档复核补齐:显式发布不可绕过终态、不可变 payload staging、项目作用域加密 CAS/引用删除、存量回填、掉电持久化、敏感内容准入、既有项目授权降级和 scratch 恢复权限。 +- 独立 Claude 安全/对抗复核结果已收敛;whole-document peer 因结构化输出重试耗尽被丢弃,未作为结论依据。 diff --git a/.doc/ops/short-term/2026-09-14-runtime-data-lifecycle-plan.md b/.doc/ops/short-term/2026-09-14-runtime-data-lifecycle-plan.md index 9a358b17..32eb9432 100644 --- a/.doc/ops/short-term/2026-09-14-runtime-data-lifecycle-plan.md +++ b/.doc/ops/short-term/2026-09-14-runtime-data-lifecycle-plan.md @@ -20,6 +20,7 @@ mutex: infra-runtime-state - dev/test/main 数据量、schema、项目/文档/run 关系与文件状态; - 备份/恢复、选择性导出、迁移 corpus、蓝绿轮换; - retention 分类、TTL、quarantine、GC 和验收门禁。 +- 最终成果保险库 coverage 与清理门禁,但不以其替代完整备份。 ## 非目标 @@ -35,6 +36,7 @@ mutex: infra-runtime-state 3. [x] 审计现有 backup/restore drill 能力与隔离缺口。 4. [x] 制定数据分类、最小保留集、migration corpus 和蓝绿轮换方案。 5. [x] 更新父整改、CURRENT、gap 与 remediation 索引。 +6. [x] 并入最终成果保险库、终态 coverage 和受控部分恢复边界。 ## 验收标准 @@ -42,11 +44,13 @@ mutex: infra-runtime-state - [x] 首轮 retain 建议基于能力覆盖,不按时间盲留。 - [x] 方案包含备份配对、真实 restore、选择性导入、reload/rebuild、rollback 和延迟销毁。 - [x] 本轮未实施破坏性操作。 +- [x] 明确最终成果保险库、migration corpus 和完整 DB+state backup 三者互补且分别验收。 ## 相关链接 - Research: [../research/2026-09-14-runtime-data-retention-and-migration-corpus.md](../research/2026-09-14-runtime-data-retention-and-migration-corpus.md) - Remediation: [../../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md](../../remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) +- Final Artifact Vault: [../../remediation/2026-09-14-final-artifact-vault-remediation.md](../../remediation/2026-09-14-final-artifact-vault-remediation.md) - Test report: 待用户批准实施后创建 ## 证据 / 备注 diff --git a/.doc/remediation/2026-09-14-final-artifact-vault-remediation.md b/.doc/remediation/2026-09-14-final-artifact-vault-remediation.md new file mode 100644 index 00000000..ed11b8bd --- /dev/null +++ b/.doc/remediation/2026-09-14-final-artifact-vault-remediation.md @@ -0,0 +1,247 @@ +--- +title: "最终成果保险库与受控恢复整改方案" +status: planned +created: 2026-09-14 +updated: 2026-09-14 +deepened: 2026-09-14 +--- + +# 最终成果保险库与受控恢复整改方案 + +## 背景 + +提案评审见[最终成果保险库调研](../ops/research/2026-09-14-final-artifact-vault.md)。当前受控文档账本、助手消息和附件 CAS 分别覆盖局部持久化,但都仍与运行 PostgreSQL 或 `RUNTIME_STATE_ROOT` 绑定;工作区 split-brain 已证明成果定位不能依赖 `output.md` 路径。 + +本方案把 FAV 作为[运行数据生命周期方案](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md)的长期成果层。它不替代当前事故的固定数据根整改,也不替代完整数据库备份。 + +## 目标 + +1. 只有服务端确认的终态文档、终态助手回复及其必要附件进入 FAV,中间结果准入率为 0。 +2. 每个成果都有稳定 UUID、语义 manifest、payload SHA、来源终态证据、保留策略和可验证发布回执。 +3. FAV 使用独立存储身份,工作区清理、worktree 删除和 app rebuild 不影响已验证成果。 +4. 数据库正文缺失但授权图完整时可只读降级;数据库关系损坏时只能经 scratch 恢复、冲突预览和人工 promotion。 +5. 完整数据库恢复、migration corpus 与成果级恢复分别演练、分别签字。 + +## 核心决策 + +1. **互补而非替代**:PostgreSQL 是运行关系与授权权威;FAV 只对已归档 payload 与 manifest 权威;workspace 是可清理投影。 +2. **事件准入而非目录扫描**:由 `agent_commit`、`approved`、`sealed`、显式 publish 和 completed chat turn 发起准入判断,禁止扫描 workspace 猜测“final”;显式 publish/pin 只是请求,不能绕过终态 allowlist。 +3. **事务 outbox**:业务终态与 publish request 同事务;异步 publisher 幂等写对象并最后写 commit marker。 +4. **默认只归档高价值回复**:completed + `export_policy=full` 自动进入;`copy_only` 仅显式 pin;`none` 永不进入。 +5. **恢复默认生成新受控对象**:`recover-as-collection` 保留来源;只有依赖、授权和 ID 无冲突时才允许 `relink`。 +6. **清理受覆盖率门禁**:应归档而未 `verified` 的成果会阻止受管 dataset/持久状态源销毁;普通计算 workspace 在终态 payload 已冻结后可按 TTL 清理,不能再成为成果事实源。 + +### 终态准入协议(U1 必须固化) + +| 类型 | 允许的权威数据库事实 | payload 来源 | 拒绝条件 | +|---|---|---|---| +| Agent 文档提交 | `document_agent_candidates.status='committed'`,存在 `output_snapshot_id`,且对应 `document_change_events.change_kind='agent_commit'` 的 `to_snapshot_id` 与该 snapshot 一致 | `document_content_blobs` 对应不可变 snapshot | ready/needs_confirmation/rejected/stale/quality_failed、snapshot/event 不一致 | +| sealed 文档版本 | `document_versions.status='sealed'` 且 `sealed_at` 非空 | 该版本 `head_snapshot_id` | open、head 缺失 | +| approved 受控文档 | `document_records.current_state='approved'`、`approved_at` 非空且 `review_snapshot_id` 非空 | 冻结的 `review_snapshot_id`,不得取 workspace 当前文件 | reviewing/approving/rejected、review snapshot 缺失 | +| 自由文档发布 | 显式 publish 事件引用已存在的不可变 ledger snapshot | 事件指向的 snapshot | 只有 UI 动作但无不可变 version,或事件后才读取“最新”正文 | +| 助手最终回复 | `chat_turns.status='completed'`、`completed_at` 和唯一 `assistant_message_id` 均存在;同事务对精确正文计算的 policy snapshot 为 `full`,或为 `copy_only` 且存在 completed 后的显式 pin 事件 | 同事务冻结到 staging 的精确消息 revision/SHA | pending/streaming/failed/cancelled、placeholder/card/status、policy none/restricted/分类失败、pin 时未 completed | + +HTTP/SSE handler 只能调用这一套领域 eligibility 服务,不能自己根据字符串或响应结束推断终态。U1 先以当前数据库列和约束写 characterization test;若发现任一完成链缺少上述事实,先补领域状态机/事务边界,再允许接入 FAV。 + +## 高层架构 + +```mermaid +flowchart TB + A[Document or chat terminal transaction] --> B[Operational PostgreSQL] + A --> C[artifact.publish.requested outbox] + C --> D[Idempotent publisher] + D --> E[Content-addressed blobs] + D --> F[Semantic manifest] + F --> G[Commit marker and receipt] + G --> H[Cleanup coverage gate] + G --> I[Authorized degraded read] + G --> J[Scratch recovery and reconciliation] +``` + +### 发布状态机 + +```mermaid +stateDiagram-v2 + [*] --> requested + requested --> publishing + publishing --> verified + publishing --> retryable_failed + retryable_failed --> publishing + verified --> tombstoned + tombstoned --> purged +``` + +只有 `verified` 成果可用于清理门禁和恢复。manifest 写完但没有 commit marker 的对象视为未发布;`tombstoned` 内容不能被读取或恢复。 + +## 数据与存储契约 + +### 运行库注册表 + +新增等价模型: + +```text +final_artifacts + artifact_uuid, artifact_type, logical_resource_id + environment, project_id, project_external_key, source_event_id + source_snapshot_id / source_message_id / source_run_id + payload_sha256, manifest_sha256, vault_storage_identity + terminal_state, policy_version, retention_class + publication_status, requested_at, verified_at, tombstoned_at + +artifact_payload_staging + staging_uuid, project_id, source_event_id, payload_sha256 + encrypted_payload / content_blob_ref, classification, created_at, releasable_at + +artifact_blob_references + artifact_uuid, project_scope, payload_sha256, envelope_key_ref + reference_status, retention_class, legal_hold, timestamps + +artifact_publication_attempts + artifact_uuid, idempotency_key, attempt_no, error_code, receipt, timestamps + +artifact_deletion_ledger + artifact_uuid, reason, authority, effective_at, applied_at, audit_ref +``` + +实现时优先复用现有 `outbox_events` 投递框架;归档注册与文档/消息终态的原子边界需由所属服务事务负责,不能由 HTTP handler 事后拼接。 + +幂等 key 由业务终态事务确定性生成:`artifact_type + environment + project_external_key + logical_resource_id + source_event_kind + source_event_id + payload_sha256 + policy_version`。数据库对该 key 建唯一约束;同一终态重试/重新发布复用 key,新终态事件或新 payload 生成新 artifact,不用 outbox event ID 或 publisher 本地状态充当业务幂等键。 + +当前仓库没有 tenant 实体;本期安全边界沿用现有 `environment + project_members/role-admin` 模型,不新增无法校验的 `tenant_id`。若未来引入组织/租户层,须另立 ADR 和数据迁移,不能仅改路径字段。 + +终态事务必须把**精确 payload 字节或不可变 content blob 引用、SHA、来源版本**一并冻结到 staging,outbox 只引用 staging;publisher 不得在事务后重读可修改或会级联删除的消息行。staging 在成果 `verified` 或依法 `tombstoned` 前不得回收,且正文不能进入普通 outbox payload、日志或指标。 + +### Vault 对象布局 + +```text +scopes///blobs/sha256/<2>/<2>/ +manifests//.json +commits//.json +tombstones//.json +catalog/checkpoints/.json +``` + +路径不包含用户文件名、Prompt 或数据库自增 ID。manifest 记录逻辑标识、来源、成员、hash、终态证据、schema/producer/storage identity、敏感级别和保留策略;commit marker 最后生成。 + +U1 必须交付版本化 `manifest.schema.json`、`commit-marker.schema.json` 和 `tombstone.schema.json`,并由同一 Go 类型/validator 验证。schema 采用 major/minor:读端必须兼容同 major 的旧 minor;未知 major fail closed 并进入迁移队列,不能静默猜字段。commit marker 至少绑定 artifact UUID、manifest SHA、全部 required member SHA、storage identity、committed_at 和 publisher version。 + +CAS 只在项目安全作用域内去重,并采用项目作用域 envelope key;`artifact_blob_references` 是 tombstone、legal hold 和物理回收的权威引用账本。tombstone 立即撤销该 artifact 的读取和密钥包装;仅当该作用域内最后一个有效引用消失且 retention/legal hold 允许时,才清除 key wrapping 与 blob。跨项目相同 SHA 必须保持密码学与删除边界隔离。 + +删除事务在运行库写 `artifact_deletion_ledger` 与删除 outbox,vault tombstone 是独立持久的拒绝恢复证据。读取/恢复采用 **union-deny**:任一侧声明删除都拒绝;两侧不一致进入 quarantine 和告警,不得恢复。purge 后仍永久保留最小 tombstone(或经校验的压缩删除目录),完整备份恢复后重放删除账本,防止旧备份复活数据。 + +## 分波次计划 + +| 波次 | 内容 | 验收 | +|---|---|---| +| V0 协议与 ADR | 固化三层权威边界、终态准入矩阵、manifest schema、恢复语义和删除模型 | ADR 与 JSON schema 评审通过;所有成果类型都有允许/拒绝状态 | +| V1 存储与发布内核 | 建 `internal/artifactvault`、注册表/staging/outbox/attempt/reference/deletion migration、filesystem backend、项目作用域加密 CAS、manifest/commit marker 和幂等 publisher | 重复投递只生成一个逻辑成果;崩溃点重试不产生半发布可见对象;坏 SHA 拒绝 verified;跨项目同 SHA 不共享删除或密钥边界 | +| V2 文档接入 | 从账本 snapshot 接入 agent committed、sealed、approved 和显式 publish;approved hook 改读账本而非 workspace | ready/rejected/stale/quality_failed 不入库;清空 workspace 后仍能校验/读取归档正文 | +| V3 助手回复接入 | 把消息正文定稿、chat turn completed 和 publish request 收敛到原子服务边界;复用 `replyexport` 分类并增加真实回合状态校验 | placeholder/stream/error/card 不入库;completed+full 精确归档一次;SSE done 前已有 durable request | +| V3.5 存量终态回填 | 只从权威数据库账本、批准事件和 completed chat turn 枚举既有终态,以来源事件 + payload SHA 生成确定性请求;缺正文/证据项进入 quarantine 或批准排除 | 重跑不增加逻辑成果;不扫描 workspace;每个排除项有原因、批准人和审计记录;清理 coverage 有可达分母 | +| V4 查找与降级读取 | 建授权后的 artifact lookup;正文缺失且授权完整时返回带 degraded 标记的 FAV 内容;权限图缺失时 fail closed | 跨用户/跨项目负例通过;GET 不写数据库;vault 不可用时返回 typed degraded/error 而非伪 404 | +| V5 恢复工具 | 建 inventory/verify/recover-plan/recover-apply;强制验证 scratch identity,使用无运行库权限的恢复凭证;默认 recover-as-collection,relink 需完整依赖、授权和人工确认 | DB 全删的隔离环境可重建成果目录;运行库目标、未授权操作者、计划篡改、冲突、删除账本和缺权限依赖均阻止 promotion | +| V6 清理与运维门禁 | dataset inventory/GC 加 coverage;readiness 暴露 identity/connectivity/lag,metrics/alerts/checksum scrub/复制与 runbook 落地 | 未 verified 成果阻止销毁;workspace 清空、app rebuild、离线副本恢复演练均 PASS | + +## 实施单元与文件落点 + +### U1. 终态协议、manifest 与数据库模型 + +- **目标**:先建立可审计的准入和恢复契约,避免把所有“完成”字符串当终态。 +- **文件**:`.doc/adr/` 新 ADR;`deploy/postgres/goose/000xx_*final_artifact*.sql`;`internal/artifactvault/types.go`、`manifest.go`、`eligibility.go`、`manifest.schema.json`、`commit-marker.schema.json`、`tombstone.schema.json` 及对应测试。 +- **测试场景**:每类允许事件生成确定 artifact key;所有候选/失败/中间状态拒绝;未终态的显式 publish/pin 仍拒绝;secret、认证材料、restricted 或分类失败均在正文进入 outbox/staging 前 fail closed;manifest 必填字段、版本和 SHA 错误 fail closed;迁移 Up/Down 与约束验证。 +- **完成证据**:准入矩阵与 schema 一致,未分类类型默认拒绝。 + +### U2. 独立存储与幂等 publisher + +- **依赖**:U1。 +- **文件**:`internal/artifactvault/store.go`、`filesystem_store.go`、`publisher.go`、`publisher_test.go`;`internal/config/config.go`;环境配置与 Compose 校验。 +- **执行说明**:vault 使用独立最小权限身份;凭证只从受管 secret/env 注入,不进入仓库、manifest、日志或业务数据库。后端必须启用传输加密和静态加密,manifest 只保存 key reference。filesystem 后端采用同文件系统临时文件、校验、文件 `fsync`、原子 no-replace rename、父目录 `fsync`,blob 与 manifest 持久化后才提交 marker。 +- **测试场景**:同一 outbox 重复消费;blob 已存在;每个写入/fsync/rename 边界强制终止后重启;损坏 blob;错误 storage identity;凭证轮换;vault 暂时不可用后重试;跨项目同 SHA、单方 tombstone、legal hold 和最后引用 purge。 +- **完成证据**:只有完整且已持久化的 manifest + commit marker 可见,publisher 不读 workspace 推断成果;任一项目删除不破坏其他项目成果。 + +### U3. 文档账本与治理终态接入 + +- **依赖**:U1、U2。 +- **文件**:`internal/docrevision/pg_mutate.go`、`internal/docgov/engine.go`、`internal/bizdev/service.go`、`internal/server/document_revision_handlers.go` 及 PG/服务测试。 +- **执行说明**:先补 characterization/PG 测试,再调整事务边界和 approved hook。 +- **测试场景**:agent commit、sealed final、approved review snapshot、已满足终态 allowlist 的显式 publish;未终态显式 publish、pending/reject/stale/quality failed 负例;投影缺失但账本和归档一致;同 run 重试。 +- **完成证据**:归档 payload 来自不可变账本 snapshot,不读取 `output.md` 作为事实源。 + +### U4. 最终助手回复原子接入 + +- **依赖**:U1、U2。 +- **文件**:`internal/ctxmgr/chat_turns.go`、`message_update.go`、`internal/server/task_finalize.go`、`internal/chat/session.go`、`internal/replyexport/policy.go` 及对应测试。 +- **执行说明**:先刻画同步回复、异步任务、SSE 断线重试三条完成链,再收敛到一个 terminal-finalize 领域事务。该事务以锁定的 message revision/content 重新计算并冻结 policy snapshot,不能信任 handler 传入或较早缓存的 `export_policy`;pin 是对已 completed 的精确 revision/SHA 追加独立终态事件。 +- **测试场景**:completed+full、completed+copy_only+显式 pin;未 completed 的 pin/publish、凭证回显、restricted/分类失败、placeholder 更新、failed/cancelled、card/status、重复 finalize、SSE done 前后崩溃;请求后修改/删除原消息再消费仍归档原 SHA;日志和指标不含正文。 +- **完成证据**:消息精确正文、completed turn、不可变 staging 和 publish request 同事务,不存在“完成但永远未请求归档”或“发布时读到后来正文”的窗口。 + +### U7. 存量终态回填与覆盖率基线 + +- **依赖**:U3、U4。 +- **文件**:`cmd/artifact-vault/backfill.go`、`internal/artifactvault/backfill.go` 及 PG 测试;dataset inventory 工具。 +- **执行说明**:只枚举不可变 document snapshot/终态事件和 completed chat turn,不读 workspace;用稳定来源事件 + payload SHA 幂等生成 staging/request。证据不足或正文缺失项进入 quarantine;排除需理由、批准人、有效期和审计记录,不能用“旧数据”批量豁免。 +- **测试场景**:回填重复执行、并发新终态、旧消息已删、缺 snapshot、批准排除过期、quarantine 修复后重跑。 +- **完成证据**:存量 coverage 的分子、分母、quarantine 和批准排除均可复算,V6 清理门禁不靠人工改数字通过。 + +### U5. 授权查找与只读降级 + +- **依赖**:U2–U4。 +- **文件**:`internal/artifactvault/catalog.go`;`internal/server/project_doc_handlers.go`、`reply_export_api.go`、`handlers.go` 及鉴权测试。 +- **执行说明**:降级读取不是独立权限或直连 vault 能力;必须先由运行库中的 resource→project 关系定位资源,再用现有 `requireProjectAccess`/系统管理员判定完成与正常读取相同的服务端授权。manifest 只用于交叉校验 project external key/sensitivity,绝不自行授予权限;任一授权依赖缺失或结果不确定即 fail closed。 +- **测试场景**:DB 正常时不走 FAV;投影/正文缺失且现有项目授权完整时降级;项目关系缺失、越权、跨项目同 SHA、tombstone、manifest/SHA 异常全部 fail closed;GET 零写入。 +- **完成证据**:响应显式标识来源与降级原因,不再把存储不一致伪装为 404。 + +### U6. 隔离恢复、清理门禁与演练 + +- **依赖**:U2–U5、U7。 +- **文件**:`cmd/artifact-vault/`、`internal/artifactvault/recovery.go` 与测试;`scripts/backup-system-db.sh`、`restore-system-db-drill.sh`;dataset 生命周期工具;`.doc/test-reports/`。 +- **执行说明**:`recover-apply` 先核验数据库 identity 并拒绝 dev/test/main 与任何非 scratch 目标,只接受无运行库权限的恢复专用凭证。操作者须通过 `role-admin` 与明确恢复能力校验;relink/promotion 还需目标项目授权和指定数据责任人确认。actor、plan hash、目标 identity 和 promotion 结果写不可变审计。恢复计划显式分类 UUID collision、SHA mismatch、partial dependency、authorization gap、metadata conflict;默认全部 fail,只有 recover-as-collection 可对允许的孤立成果生成新 UUID,relink 不允许 skip/prompt 后带冲突提升。 +- **清理门禁**:只允许 `dataset inventory/gc/destroy` 和环境 teardown 脚本执行受管销毁,preflight 服务不可用或 coverage 非 PASS 时 fail closed;vault/app 身份没有删除 dataset 的权限,运维删除身份单独审计。普通计算 workspace 可按 TTL 清理,因为终态确认前已把 payload 冻结进 staging;原始 shell/root 删除无法由应用层绝对阻止,必须靠主机权限与操作制度限制,不能在验收中声称“拦截任意 rm”。 +- **测试场景**:空 scratch 库 recover-as-collection;依赖齐全 relink;五类冲突;运行库 DSN、未授权操作者、计划 hash 被篡改、缺 blob、删除账本/tombstone 不一致与重放;未 verified 或 preflight 不可用阻止受管 GC;workspace 清空与 app rebuild;异机副本恢复。 +- **完成证据**:项目 6173 等价脱敏 corpus 完成“终态生成 → 发布 → 清 workspace → 读取 → scratch 恢复 → rebuild”真实链路。 + +## 验收标准 + +- [ ] 中间状态、流式片段、工具轨迹和 workspace 草稿进入 FAV 的测试结果为 0。 +- [ ] 应归档终态成果的 publish request 覆盖率为 100%,verified 延迟与失败积压可观测。 +- [ ] 任一 publisher 崩溃点都可幂等恢复,半成品不对读/恢复可见。 +- [ ] 工作区全部删除或换根后,已 verified 成果仍可由稳定逻辑 ID 定位并通过 SHA 校验。 +- [ ] 授权图缺失时 fail closed;跨环境/project/user 的读取和恢复负例通过;当前实现不伪造 tenant 边界。 +- [ ] 显式 publish/pin 不能绕过终态 allowlist;敏感分类失败或命中禁止内容时正文不进入 staging/outbox/vault/日志/指标。 +- [ ] 存量终态回填可幂等重跑;quarantine 与批准排除均有可复算审计,coverage 分母不可手工绕过。 +- [ ] 项目作用域引用账本保证 tombstone 立即不可读,最后有效引用且无 hold 时才清除密钥和对象。 +- [ ] FAV 可以在空 scratch 库恢复成果集合,但不能伪造权限、活动工作流、lease、pending task 或完整历史。 +- [ ] tombstone/删除账本在 restore 后重放,已删除内容不会复活。 +- [ ] PostgreSQL 完整 restore、migration corpus 升级和 FAV 部分恢复三条演练均有独立报告。 +- [ ] dev 清理只有在完整备份可恢复、migration corpus PASS、FAV coverage PASS 后才能进入销毁审批。 + +## 风险与缓解 + +| 风险 | 缓解 | +|---|---| +| FAV 变成第三真相源 | 限定只对 payload/manifest 权威;运行指针和授权始终由数据库决定 | +| “最终”判定过宽导致存满中间结果 | 状态机 allowlist + 未分类拒绝 + 准入指标/抽样审计 | +| 数据库提交与归档双写丢事件 | 同事务 outbox + 幂等 publisher + cleanup coverage gate | +| outbox 指向可变或已删除正文 | 终态事务冻结精确 payload/SHA 到不可变 staging,publisher 只读 staging | +| 存量终态不触发新 hook | 权威数据库幂等回填 + quarantine/批准排除审计,禁止 workspace 猜测 | +| 归档可读但无法重建业务关系 | manifest 声明依赖与恢复能力;默认 recover-as-collection;完整灾备继续依赖 DB backup/PITR | +| WORM、共享 blob 与删除义务冲突 | 项目作用域加密 CAS + manifest-member 引用账本;按 retention class 选 governance/compliance;单独 legal hold;最后引用后密钥/对象清除 | +| 同机独立目录仍无法抗磁盘故障 | 第一阶段独立 mount/identity,生产要求异机或对象存储复制并做恢复演练 | +| 归档泄露敏感上下文 | 结构化排除 Prompt/轨迹;服务端敏感分类 fail closed;最小化 metadata;传输/静态加密、项目授权、凭证隔离和访问审计 | + +## 明确不采用 + +- 定时扫描 `**/output.md` 并复制“最新文件”。 +- 把所有 assistant 消息、SSE event 或 agent session 全量归档。 +- 在业务事务内同步调用对象存储,或反过来先写对象再提交业务状态。 +- DB 不一致时由 GET 请求静默重建/覆盖数据。 +- 因为 FAV 有最终正文就删除完整备份、WAL 或 migration corpus。 +- 把所有对象永久 compliance lock,忽略合法删除和数据最小化。 + +## 相关链接 + +- Research: [最终成果保险库与部分恢复边界](../ops/research/2026-09-14-final-artifact-vault.md) +- Parent remediation: [运行数据生命周期与迁移语料库](./2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md) +- Incident remediation: [dev 数据根隔离与投影自愈](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) +- Short-term: [最终成果保险库方案](../ops/short-term/2026-09-14-final-artifact-vault-plan.md) +- gap-matrix: `RUNTIME-DATA-ROOT-SPLIT-BRAIN` diff --git a/.doc/remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md b/.doc/remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md index 393de773..c58cf6ae 100644 --- a/.doc/remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md +++ b/.doc/remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md @@ -25,6 +25,7 @@ updated: 2026-09-14 3. 每次发布同时验证 clean install、受支持旧版本升级、选择性数据导入、reload/rebuild 和 rollback。 4. 数据库 dump 与文件状态根成对归档,具备 storage identity、SHA manifest 和自动断言。 5. 清理不产生孤儿 snapshot/run/blob/workspace,不误删共享 blob,不接触未授权 main/KB 数据。 +6. 最终成果在清理前进入独立 [Final Artifact Vault](./2026-09-14-final-artifact-vault-remediation.md) 并取得 verified 回执;该回执不能替代完整备份和 migration corpus。 ## 数据保留策略 @@ -52,6 +53,7 @@ retention_until pinned_reason contains_sensitive_data manifest_uri / manifest_sha256 +required_artifact_count / verified_artifact_count status: building | verified | active | quarantined | expired | destroyed ``` @@ -97,6 +99,10 @@ status: building | verified | active | quarantined | expired | destroyed 首轮 dev 推荐不在旧库运行 GC,而是蓝绿新建后切换;GC 用于后续小规模过期数据。 +### 6. `artifact coverage` + +按服务端终态事件盘点应归档成果,并与 FAV verified receipt 对账。任何必需成果处于 requested/publishing/failed/missing,或 vault storage identity 不匹配时,受管 dataset/持久状态源不得销毁。普通计算 workspace 只有在终态事务已冻结精确 payload 后才可按 TTL 清理;准入、manifest、恢复和删除边界见[最终成果保险库整改](./2026-09-14-final-artifact-vault-remediation.md)。 + ## 迁移测试矩阵 每个 release candidate 必须执行: @@ -110,6 +116,7 @@ status: building | verified | active | quarantined | expired | destroyed | Import | latest schema + selected demo/regression package | 关系闭包、文件 SHA、权限、通知、版本账本 | | Reload | import 后 app/pi rebuild + restart + 宿主级重载 | storage identity、mount、task 恢复、正文/overlay | | Rollback | 切换失败后接回 quarantine 旧卷 | RTO、旧 storage identity、无新写丢失 | +| Artifact recovery | 已归档终态成果 + 空 scratch DB | manifest/SHA、删除账本、recover-as-collection、冲突预览、权限 fail closed | 迁移通过必须包含真实 restore,不接受仅跑 Goose 幂等测试;产品签字至少包含真实页面登录、项目列表、对话、文档打开/修改、刷新、退出重登和 rebuild 后恢复。 @@ -119,7 +126,7 @@ status: building | verified | active | quarantined | expired | destroyed |---|---|---| | D0 冻结与标注 | 先完成当前 split-brain 遏制;冻结 dev 写入;对 366 个项目 inventory;人工确认 pinned/敏感/owner | 100% 项目有建议动作;未分类项不删除 | | D1 工具安全修补 | 移除备份脚本内默认凭证;backup 同时归档 DB+state;restore 强制 scratch 且真实恢复;加入 manifest/checksum/assertions | 恢复演练能从零创建隔离环境并通过 SHA/关系断言 | -| D2 构建 corpus | 建 schema 25、28/29、32/33 三个最小迁移包;构建 143 等价脱敏 hazard;选择 6173 current regression;6159 标记一周期过期 | 每包可重复生成/恢复,不依赖真实 main/test 数据 | +| D2 构建 corpus 与成果覆盖 | 建 schema 25、28/29、32/33 三个最小迁移包;构建 143 等价脱敏 hazard;选择 6173 current regression;盘点保留项目的终态成果并生成 FAV coverage | 每包可重复生成/恢复;必需成果均 verified 或有批准的排除理由,不依赖真实 main/test 数据 | | D3 新建 green dev | latest schema 空库 + 固定新 state root;导入选中的 6–10 项;执行 projection/CAS/sequence/integrity 校验 | 无 orphan;active 文档 aligned;ready/storage identity PASS | | D4 全链验收 | clean/oldest/previous/hazard/import/reload/rollback;真实页面和真实 PI 跑主链 | 报告区分工程、PG、PI、浏览器、restart/rebuild 证据 | | D5 原子切换 | 切换 dev 到 green identity;原 blue DB/文件根转只读 quarantine | 新环境持续观察 24–72h,无新增 blocker,可一键回接 blue | @@ -152,6 +159,8 @@ status: building | verified | active | quarantined | expired | destroyed - [ ] 三个 migration baselines 与 hazard corpus 均能升到 latest 并通过断言。 - [ ] 无 orphan task run、revision snapshot/version/event/report、CAS blob 或 workspace。 - [ ] 项目 143/6173 页面打开、修改、刷新、重登、rebuild 后恢复 PASS。 +- [ ] 保留项目的应归档成果覆盖率为 100%,清空 workspace 后仍能定位并校验最终正文/最终回复。 +- [ ] FAV 在空 scratch 库完成受控部分恢复;未声称或伪造权限、lease、pending task 等未归档状态。 - [ ] 新测试数据带 TTL,PASS 后自动销毁,失败数据按策略到期。 - [ ] test/main/shared KB 未被误操作;所有破坏性动作有精确 manifest 和延迟销毁记录。 @@ -161,11 +170,13 @@ status: building | verified | active | quarantined | expired | destroyed - 在 dev 库中写一份巨型级联 DELETE:当前关系与文件引用复杂,容易留下孤儿或误删共享 blob。 - 只保留最新时间的项目:会丢失最有价值的旧 schema/hazard 覆盖。 - 把整份真实旧库永久当 migration fixture:迁移慢、不可重复、可能含敏感数据。 +- 把 FAV 当完整数据库替代品,或按 `output.md`/mtime 扫描猜测终态:会引入第三事实源并归档中间结果。 - 修改历史 Goose migration 迎合新空库:已发布迁移不可重写;修正必须新增 migration。 ## 相关链接 - Research: [运行数据精简、迁移语料与演示数据保留策略](../ops/research/2026-09-14-runtime-data-retention-and-migration-corpus.md) +- Final Artifact Vault: [最终成果保险库与受控恢复](./2026-09-14-final-artifact-vault-remediation.md) - Parent remediation: [dev 数据根隔离与投影自愈](./2026-09-14-dev-runtime-data-root-and-projection-remediation.md) - Short-term: [运行数据生命周期方案](../ops/short-term/2026-09-14-runtime-data-lifecycle-plan.md) - gap-matrix: `RUNTIME-DATA-ROOT-SPLIT-BRAIN` diff --git a/.doc/remediation/INDEX.md b/.doc/remediation/INDEX.md index bf37c415..dd38fff4 100644 --- a/.doc/remediation/INDEX.md +++ b/.doc/remediation/INDEX.md @@ -5,6 +5,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 数据根。审查修订: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-final-artifact-vault-remediation.md](./2026-09-14-final-artifact-vault-remediation.md) | 只收终态文档/助手回复的独立保险库、项目作用域加密 CAS、清理门禁与受控部分恢复 | | [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 整改 | | [2026-09-13-document-revision-reviewer-duty-sync.md](./2026-09-13-document-revision-reviewer-duty-sync.md) | UI-01 已分配 B=reviewer,受控文档 reviewer_id 硬编码 2,矩阵降为 viewer | diff --git a/.doc/remediation/gap-matrix.md b/.doc/remediation/gap-matrix.md index 5dd3f6e1..a3aa5928 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)。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 | +| 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) 与 [FAV](./2026-09-14-final-artifact-vault-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 dd1f3c8c..75867747 100644 --- a/.doc/status/FEATURE-MATRIX.md +++ b/.doc/status/FEATURE-MATRIX.md @@ -23,7 +23,7 @@ | 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 因数据根 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` | +| 最终成果归档与恢复 | 终态文档/助手回复的独立归档、稳定定位、清理门禁与受控部分恢复 | planned | [调研](../ops/research/2026-09-14-final-artifact-vault.md);[整改方案](../remediation/2026-09-14-final-artifact-vault-remediation.md) | 方案完成并经安全/可行性/对抗复核;尚未实现独立 storage identity、不可变 staging/outbox、项目作用域加密 CAS、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-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 为 sibling,不挡事故关闭 | `infra` |