13 KiB
title, created, status, confidence
| title | created | status | confidence |
|---|---|---|---|
| 最终成果保险库与部分恢复边界 | 2026-09-14 | done | 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/<env>/storage/blobs 按 SHA 去重 |
blob 物理文件仍在运行状态根,引用元数据仍在数据库;不独立于工作区/数据库事故 |
| 数据备份 | 现有脚本主要导出 PostgreSQL;数据生命周期方案已要求 DB + state 成对打包 | 尚无跨文档/回复的终态准入、持续发布、独立索引或成果级恢复 API |
当前事故已经证明“数据库记录还在”不代表“运行容器能找到正文”。FAV 可以解决成果副本对工作区根的依赖,但不能替代固定 RUNTIME_STATE_ROOT、storage identity、projection outbox 和数据库备份。
提案中应保留的部分
- 独立持久位置:成果存储不位于 Git worktree、临时集成目录、任务 workspace 或普通 GC 扫描范围。
- 只收终态成果:拒绝流式片段、候选稿、工具轨迹、修复过程、临时报告、租约和未完成任务。
- 支持系统定位:使用稳定 artifact UUID、逻辑资源 ID、来源事件和内容 SHA,而不是依赖数据库自增 ID 或 workspace 路径。
- 辅助部分恢复:允许在数据库关系不匹配时校验并导入已归档内容,同时保留来源和冲突证据。
- 与清理联动:工作区或旧数据集只有在必需成果全部发布并验证后才可进入销毁阶段。
必须修正的部分
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 必须表达恢复语义
每个归档单元至少包含:
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 要求 manifest 覆盖 payload 并验证完整性,支持本方案的 manifest/checksum 设计。
- OCI Descriptor 以 digest 标识并校验内容,支持内容寻址 blob。
- AWS Transactional Outbox guidance 说明业务写入与 outbox 同事务、消费者幂等的必要性。
- PostgreSQL PITR、pg_restore 与 pg_verifybackup 说明完整恢复、选择性恢复依赖和真实 restore 演练的边界。
- AWS S3 Object Lock 说明 governance/compliance/legal hold 的差异,支持按数据类别选择保留模式。
建议动作
- 采用最终成果保险库整改方案,先定义终态协议和 manifest,再选择 filesystem 或对象存储实现。
- 将 FAV 发布完成率加入 dataset inventory/GC 的硬门禁,但不把 FAV 设为当前 409 的绕行修复。
- 首次实现优先覆盖受控文档 approved/agent committed snapshot 与
export_policy=full的 completed assistant reply。 - 先在 dev 用项目 6173 的等价脱敏 corpus 做“生成 → 发布 → 清 workspace → 读取/恢复 → rebuild”演练,再考虑 main 保留策略。