Files
Agent-System/.doc/remediation/2026-09-14-final-artifact-vault-remediation.md
T

22 KiB
Raw Blame History

title, status, created, updated, deepened
title status created updated deepened
最终成果保险库与受控恢复整改方案 planned 2026-09-14 2026-09-14 2026-09-14

最终成果保险库与受控恢复整改方案

背景

提案评审见最终成果保险库调研。当前受控文档账本、助手消息和附件 CAS 分别覆盖局部持久化,但都仍与运行 PostgreSQL 或 RUNTIME_STATE_ROOT 绑定;工作区 split-brain 已证明成果定位不能依赖 output.md 路径。

本方案把 FAV 作为运行数据生命周期方案的长期成果层。它不替代当前事故的固定数据根整改,也不替代完整数据库备份。

目标

  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。

高层架构

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]

发布状态机

stateDiagram-v2
  [*] --> requested
  requested --> publishing
  publishing --> verified
  publishing --> retryable_failed
  retryable_failed --> publishing
  verified --> tombstoned
  tombstoned --> purged

只有 verified 成果可用于清理门禁和恢复。manifest 写完但没有 commit marker 的对象视为未发布;tombstoned 内容不能被读取或恢复。

数据与存储契约

运行库注册表

新增等价模型:

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 对象布局

scopes/<environment>/<project_external_key>/blobs/sha256/<2>/<2>/<sha256>
manifests/<artifact_uuid>/<manifest_sha256>.json
commits/<artifact_uuid>/<manifest_sha256>.json
tombstones/<artifact_uuid>/<event_uuid>.json
catalog/checkpoints/<catalog_version>.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,忽略合法删除和数据最小化。

相关链接