Land the revised modular-monolith scheme on the implementation branch so U1–U4 can proceed against a frozen design, and register the docgov mutex without occupying the integration slot. Co-authored-by: Cursor <cursoragent@cursor.com>
48 KiB
48 KiB
title, status, created, updated, type, owner, deepened
| title | status | created | updated | type | owner | deepened |
|---|---|---|---|---|---|---|
| 模块解耦与文档可靠性重构计划 | planned | 2026-09-14 | 2026-09-14 | refactor | TBD | 2026-09-14 |
模块解耦与文档可靠性重构计划
Goal Capsule
- Objective: 项目文档、任务最终产物和最终智能体回复在并发写入、页面刷新、服务重启、工作区清理和受控恢复后仍能按权限定位到唯一、可校验的内容。
- Means: 采用模块化单体、纵向绞杀、应用 UnitOfWork、数据库互斥、transactional outbox、opaque workspace capability 和 FAV。
- Authority: 本计划受 模块化单体解耦 ADR、文档版本边界 ADR 和 工作流运行时权威 ADR 约束。
- Stop condition: 任一里程碑出现数据不变量无法在单事务维持、授权只能通过放宽实现、旧数据无法安全迁移,或真实页面故障率没有下降时,停止扩面并回到设计评审。
- Execution profile: 高风险、跨模块、迁移和授权敏感;characterization-first,逐纵向切片发布。
- Delivery: 每个里程碑独立 short-term、独立分支和独立验收报告;本计划本身不授权合并、部署或删数据。
Product Contract
Summary
重构先解决文档事实源、事务与恢复链,再扩展其他模块。 架构改造必须用用户可见的刷新、重启、并发和授权结果证明价值,不能只用包数量或代码行数证明完成。
Problem Frame
系统当前同时依赖 PostgreSQL 账本、document_records 指针、workspace output.md 和若干内存状态。
这些状态在提交、刷新和重启时没有统一事务与恢复协议,导致历史上出现正文 404/409、projection missing、缓存竞争和任务完成后文档仍为空。
原解耦方案提出 contracts、单一写者和事件总线,但没有解释单一写者如何维持现有跨表原子性,也没有为多个 event subscriber、跨实例互斥和 FAV 恢复定义可执行协议。
Requirements
事实源与持久成果
- R1. PostgreSQL immutable ledger 是文档版本与业务关系的权威,workspace 只作为可重建投影。
- R2. FAV 只归档服务端确认的终态文档、终态助手回复、类型化 RunManifest 和必要附件,中间结果准入率为零。
- R3. 数据库授权图完整但正文不可用时,系统可按 feature flag 从已验证 FAV 只读降级;授权图不完整时必须拒绝。
- R4. GET、刷新和重载不得隐式修复数据库、覆盖 head 或创建归档。
事务与并发
- R5. 一个业务命令只有一个事务 owner;业务状态、账本、当前指针及该里程碑已启用的 staging/outbox 必须原子提交。
- R6. 单一写者只约束 SQL 和不变量所有权,不允许各属主各自提交导致跨表半完成。
- R7. 多副本正确性必须依赖数据库行锁、事务级 advisory lock、唯一约束、expected version 和 fencing token;进程内 mutex 不能成为唯一正确性条件。
- R8. 所有写命令必须具有 idempotency key、expected version 或两者;相同 key 不同 payload 必须返回冲突。
- R9. 事务不得包含 LLM、PI Runner、对象存储、HTTP/SSE 或长文件 I/O。
事件与恢复
- R10. 持久事件采用至少一次投递、逐消费者 delivery、幂等 inbox、租约、fencing token、重试和死信。
- R11. 同一聚合事件按 aggregate version 有序,不同聚合允许并发;失败消费者不阻塞其他消费者。
- R12. 旧 workspace、旧数据库和 FAV 回填必须可断点续跑、可重复、可审计,异常进入 quarantine。
- R13. 恢复只进入 scratch identity,promotion 需要系统管理员和数据责任人确认,并通过属主端口提交。
授权与隔离
- R14. HTTP、CLI、worker、event subscriber 和恢复工具必须使用服务端构造的 Principal,并按 action/resource/current state 逐动作授权。
- R15. 客户端或事件 payload 中的 user、project、role、duty、backend 和路径不能作为授权事实。
- R16. WorkspaceRef 只能暴露不可伪造 ID、scope 和 fencing token;物理路径不得跨模块传播。
- R17. 路径解析必须拒绝绝对路径、
..、跨项目 ID、symlink/hardlink 逃逸和 storage identity 不匹配。 - R18. 事件、日志、metrics、manifest 和 result 文件不得包含 secret、认证材料、Prompt 或不必要正文。
交付与验证
- R19. 首个独立交付边界是受控文档可靠性纵向试点,未通过停走门前不启动横向包搬移。
- R20. 每个里程碑必须分别提供 unit、PostgreSQL、并发/故障注入、真实 PI、真实浏览器、restart/rebuild 和跨用户负例证据;不适用项必须说明原因。
- R21. 迁移采用 expand/migrate/contract,允许新的 Goose migration,禁止修改已发布 migration。
- R22. 数据清理必须同时满足完整备份恢复、migration corpus、FAV coverage、精确删除 manifest 和用户批准,不得因“已有最终成果”跳过其他门禁。
Success Criteria
- 项目 143 等价脱敏 hazard 和新建项目两条链路在刷新、重登、restart 与 rebuild 后正文成功率为 100%,目标 404/409/projection missing 为 0。
- 同一文档并发写只有一个合法赢家;失败请求不产生孤儿 snapshot、错 head、重复消息、重复通知或重复 artifact。
- 每个应发布终态都存在 publish request;已验证 artifact 可在 workspace 删除后按稳定逻辑 ID 定位并校验 SHA。
- 匿名、非成员、跨用户、跨项目、错误 duty、错误 stage、篡改事件和授权依赖失败全部 fail closed。
- 1、2、4 个应用实例和 dispatcher 下,状态与副作用结果一致;性能阈值由基线报告确认后固化,不能通过降低一致性达标。
- 旧 schema corpus、legacy-v0 workspace 和 previous-release corpus 均可升级、重载和回滚到最近兼容点。
Scope Boundaries
本计划包含
- 文档、taskrun、workspace、artifactvault、可靠事件和授权基础设施。
- chatlog/contextasm、squadrun、app 组合根和 UserProjectContext 的后续选择性扩展。
- 数据生命周期、迁移 corpus、回填、quarantine 和 scratch 恢复的集成门禁。
Deferred to Follow-Up Work
- HTTP 错误信封统一。
- PI Runner stdout/result.json 协议替换。
- 全量 SSE transport 重写。
- 微服务拆分和跨服务消息基础设施。
- main 数据自动清理和未来 tenant 实体。
Planning Contract
Key Technical Decisions
- KTD1. 纵向绞杀优先。 先在原包边界增加窄端口和适配器,迁完消费者并通过行为验收后再物理移动包。
- KTD2. 事务 owner 与表属主分离。 应用用例拥有事务,领域模块拥有 SQL;同一
pgx.Tx通过不透明 TxScope 绑定 repository。 - KTD3. 显式锁策略。 普通状态转换使用
READ COMMITTED、行锁和条件更新;项目级不变量使用事务级 advisory lock;Serializable 只用于经过证明的复杂不变量。PostgreSQL explicit locking - KTD4. 固定锁顺序。 project/workflow instance → document root → version/snapshot/turn → task run → staging/outbox/delivery。
- KTD5. 事务外副作用。 网络、LLM、对象存储、通知和投影由提交后的持久事件驱动。
- KTD6. 至少一次事件。 outbox 不承诺 exactly-once;event ID、consumer inbox 和业务唯一约束共同提供幂等结果。
- KTD7. opaque workspace capability。 跨模块不传物理路径,所有读写使用 scope-bound ID、artifact key 和 fencing token。
- KTD8. 三层权威。 PostgreSQL 管业务关系与授权,workspace 管临时现场,FAV 管已验证终态 payload/manifest/receipt/tombstone。
- KTD9. 授权逐动作重算。 Principal 来自受信适配器;状态改变在取得资源锁后重读当前授权事实,subscriber 的 actor 快照只用于审计。OWASP Authorization Cheat Sheet
- KTD10. 依赖感知回滚。 每个里程碑只保证回到最近兼容点;进入 contract 阶段后须按逆依赖顺序回滚。
- KTD11. 按证据收紧门禁。 arch-check 先记录基线,每条规则在对应纵向切片清零后转 block,不按日历统一切换。
- KTD12. FAV 分支先审后用。
feature/final-artifact-vault的现有实现作为候选,不直接视为 dev 已有能力;集成前重放迁移、测试、安全和并发审计。
High-Level Technical Design
Component topology
flowchart TB
Transport[HTTP SSE CLI] --> UseCases[application use cases]
Jobs[workers and recovery] --> UseCases
UseCases --> Policy[authorization policy]
UseCases --> Unit[UnitOfWork]
Unit --> DG[docgov owner]
Unit --> DR[docrevision owner]
Unit --> TR[taskrun owner]
Unit --> OB[outbox and staging]
UseCases --> WSC[workspace capability]
OB --> Dispatch[delivery dispatcher]
Dispatch --> Projection[projection subscriber]
Dispatch --> AV[artifactvault subscriber]
Dispatch --> Notify[notification subscriber]
Write sequence
sequenceDiagram
participant C as Caller
participant A as App use case
participant P as Policy
participant DB as PostgreSQL
participant O as Outbox worker
participant V as FAV
C->>A: command + idempotency + expected version
A->>P: authorize principal/action/resource
P-->>A: allow or deny
A->>DB: begin and acquire locks in canonical order
A->>DB: ledger + root + staging + outbox
DB-->>A: commit
A-->>C: committed result
O->>DB: claim delivery
O->>V: publish immutable artifact
V-->>O: verified receipt
O->>DB: idempotent ack
Event delivery lifecycle
stateDiagram-v2
[*] --> pending
pending --> leased
leased --> delivered
leased --> retryable_failed
leased --> dead_letter
retryable_failed --> leased
leased --> pending: lease expired
delivered --> [*]
Migration lifecycle
flowchart TB
Expand[Expand schema and compatible readers] --> Shadow[Shadow validation]
Shadow --> Backfill[Checkpointed backfill]
Backfill --> Switch[Switch writer]
Switch --> Observe[Observe and restart test]
Observe --> Contract{Go or rollback?}
Contract -->|go| Remove[Remove old path]
Contract -->|rollback| Previous[Return to prior compatible point]
Concurrency and Lock Matrix
| Resource/operation | Correctness primitive | Conflict result | Retry policy | Forbidden while held |
|---|---|---|---|---|
| Project duty/heal/revoke | transaction advisory lock keyed by project | wait then typed timeout | whole idempotent tx for deadlock only | network, LLM, filesystem copy |
| Workflow stage advance | instance row lock + expected config revision | stale-state conflict | caller refresh; no blind retry | external execution |
| Controlled document edit | document row + open version row + ETag | version conflict with current head metadata | user/client rebases | projection and notification |
| Agent candidate commit | candidate/document/version locks + idempotency key | stale candidate or idempotency conflict | exact same request may replay | PI/LLM call |
| TaskRun claim/finalize | unique idempotency + conditional status/version update | existing run or state conflict | same digest returns prior result | message/SSE side effect |
| Workspace write | DB lease + fencing token + atomic file commit | stale lease/token | reacquire then re-resolve state | arbitrary path access |
| Outbox delivery | row lease + SKIP LOCKED + fencing token |
another worker owns item | retry at next schedule | subscriber work inside claim tx |
| FAV publish | artifact idempotency unique key + no-replace object write | existing verified artifact or digest conflict | same digest resumes | reading mutable workspace |
Authorization Matrix
| Surface | Principal source | Resource facts | Required checks | Failure behavior |
|---|---|---|---|---|
| HTTP/SSE | authenticated session/middleware | DB project/session/document/workflow | membership, owner, duty, stage, action | 401/403/typed conflict; no data |
| CLI ops | service identity + operator session | explicit environment/dataset/resource manifest | capability, exact scope, dry-run where destructive | fail closed and audit |
| Event subscriber | registered service identity; event actor for audit only | DB current aggregate + event scope/digest | producer allowlist, project/resource match, current policy | dead letter/quarantine |
| FAV degraded read | current authenticated Principal | DB relation + vault manifest/tombstone | normal read permission first, then integrity | no fallback if auth graph incomplete |
| Recovery promotion | recovery credential + admin + data steward approval | scratch identity + signed plan + conflict map | two-party approval, no unresolved conflicts | refuse promotion |
| Agent backend | app-resolved BackendRef | project policy + InputBundle sensitivity | backend allowlist, least data, secret injection boundary | reject before dispatch |
Delivery dependency graph
flowchart TB
M0[M0 baseline and contract correction] --> M1[M1 document reliability pilot]
M1 --> M2[M2 durable events and FAV integration]
M2 --> M3[M3 taskrun and workspace finalization]
M3 --> M4[M4 selective domain decoupling]
M4 --> M5[M5 app composition and context ownership]
M5 --> M6[M6 transport boundary cleanup]
M2 --> D[Data corpus and lifecycle rollout]
D --> M3
Assumptions
- 当前 dev 基线为 schema 33;任何实现分支必须在创建 migration 时重新查询最新序号。
feature/final-artifact-vault尚未合 dev,现有代码与测试只能作为候选实现和证据来源。- 当前无独立 tenant 实体,权限范围沿用 environment + project membership/admin;未来 tenant 模型另立 ADR。
- 性能容量目标需要在 M1 压测后固化;本计划先固定正确性和观测项,不伪造吞吐数字。
Phased Delivery
| 里程碑 | 交付边界 | 预计范围 | 进入条件 | 退出/停走门 |
|---|---|---|---|---|
| M0 | 基线与契约纠偏 | 文档/门禁/characterization | 在途 mutex 不冲突 | 事务/锁/授权/事件/FAV 契约评审通过 |
| M1 | 文档可靠性纵向试点 | docflow/docgov/docrevision/docquery | M0 完成,独占 docgov | 刷新、重启、并发、权限和故障注入全 PASS |
| M2 | 持久事件与 FAV | outbox/delivery/artifactvault | M1 数据边界稳定 | 多 worker、重放、归档、降级读取和 tombstone PASS |
| M3 | taskrun/workspace 终态 | taskrun/squadrun/workspace | M2 verified receipt 可用 | 清 workspace 后最终产物仍可定位,重复 finalize 无副作用 |
| M4 | 选择性领域端口化 | chatlog/contextasm/squadrun | M1–M3 复盘证明收益 | 每个子切片独立回归;无纯搬包 |
| M5 | 组合根与上下文收权 | app/chatflow/stageflow/UserProjectContext | M4 稳定 | 每个 use case 单 owner;闭包按事件逐个替换 |
| M6 | 传输收口 | server/SSE | M5 完成 | server 无 SQL/编排/路径;R1–R7 对应规则全 block |
M0–M1 是首个批准范围。 M2–M6 需要在前一里程碑验收报告后重新 go/no-go,不构成一次性整体授权。
Implementation Units
Unit Index
| ID | 标题 | 主要文件 | 依赖 |
|---|---|---|---|
| U1 | 固化架构基线和门禁 | scripts/arch-check.sh, .doc/system-design/module-boundaries.yaml |
无 |
| U2 | 建立应用 UnitOfWork | internal/app/uow, owner repositories |
U1 |
| U3 | 文档命令原子化 | docgov/docrevision/docflow | U2 |
| U4 | 文档授权读模型 | docquery/server/authz | U1, U3 |
| U5 | Workspace capability | workspace/squadrun | U1 |
| U6 | Durable event delivery | outbox/dispatcher/inbox | U2 |
| U7 | FAV 集成与降级读取 | artifactvault/docflow/docquery | U3, U4, U6 |
| U8 | TaskRun/RunManifest 终态 | taskrun/squadrun/workspace | U5, U6, U7 |
| U9 | 数据回填与迁移 corpus | dataset/FAV tooling | U6–U8 |
| U10 | chatlog/contextasm 端口化 | ctxmgr/chat/contextasm | M1–M3 go/no-go |
| U11 | 组合根和 use case 收权 | app/server/bizdev/coordinator | U10 |
| U12 | 传输边界与最终门禁 | server/stream/CI | U11 |
U1. 固化架构基线和可执行门禁
- Goal: 建立不会把现有违规合法化、也不会误报 migration/fixture 的边界基线。
- Requirements: R19–R21;KTD1、KTD11。
- Dependencies: 无;必须避开
intent、chat等在途 mutex,当前只占arch。 - Files:
scripts/arch-check.sh、.doc/system-design/module-boundaries.yaml、internal/archtest/arch_test.go、internal/archtest/arch_test_test.go、.doc/audit/<date>-arch-check-baseline.md。 - Approach:
- 分离 production import、production SQL write、path construction、event producer/consumer 四类规则。
- 表名扫描只识别 production Go 中的写语句;Goose、测试、fixture 和受控运维脚本进入显式类别,不以全仓零出现为目标。
- 为每项既有违规记录 owner、原因、截止里程碑和到期日;新违规立即 block。
- 只为已完成纵向切片的规则切换 block,不能以“两周后统一 block”代替清零。
- Execution note: 先对当前依赖和 SQL 写者生成可重复 characterization 报告,再改门禁。
- Patterns to follow:
go list依赖图、现有 AGENTS mutex、Goose 不可改写规则。 - Test scenarios:
- 合法的 Goose/fixture 表名不触发生产写者违规。
- 非属主 production 包新增
UPDATE document_records时测试失败并定位文件。 - 路径通过字符串拼接、
filepath.Join和间接常量构造时均被检测。 - 无过期日例外、过期例外、新增越权 event producer 均阻断。
- Verification: 基线报告可重放;新增违规阻断;现有违规数只能下降。
U2. 建立应用 UnitOfWork 和事务能力
- Goal: 在单一写者边界内维持跨表业务不变量。
- Requirements: R5–R9;KTD2–KTD5。
- Dependencies: U1。
- Files:
internal/app/uow/uow.go、internal/app/uow/pg_uow.go、internal/app/uow/pg_uow_test.go、internal/docgov/repository.go、internal/docrevision/repository.go、internal/taskrun/repository.go、internal/freedoc/repository.go。 - Approach:
- UnitOfWork 开启、提交和回滚唯一
pgx.Tx,向每个属主提供绑定该 transaction 的窄 repository capability。 - TxScope 不暴露通用
Exec给应用层;只有属主 repository 能执行自己的 SQL。 - 禁止 repository 在已有 TxScope 内嵌套 transaction 或提前 commit。
- 实现统一的完整事务重试 wrapper,只重试明确的 serialization/deadlock 错误,并保持 idempotency context。
- 为锁超时、事务时长、重试和 rollback reason 加结构化指标,不记录业务正文。
- UnitOfWork 开启、提交和回滚唯一
- Test scenarios:
- 第二个属主写入失败时第一个属主写入回滚。
- outbox/staging 写入失败时业务状态和账本均回滚。
- commit 失败时不会返回成功或执行提交后副作用。
40001/40P01重跑完整事务并最终只产生一个业务结果。- version conflict、permission denied、payload digest conflict 不进入自动重试。
- 尝试跨属主执行任意 SQL 无法通过类型边界或架构测试。
- Verification: fault injection 覆盖 begin、每个 owner write、outbox、commit;无部分提交。
U3. 文档提交纵向事务化
- Goal: 将人工编辑、agent commit、批准/驳回和根指针投影收敛到文档用例事务。
- Requirements: R1、R5–R9、R19;KTD2–KTD5。
- Dependencies: U2;实施时独占
docgovmutex。 - Files:
internal/app/docflow/commands.go、internal/app/docflow/commands_pg_test.go、internal/docgov/engine.go、internal/docgov/lease.go、internal/docrevision/pg_mutate.go、internal/docrevision/service.go、internal/docrevision/document_commit_concurrency_pg_test.go、internal/server/document_revision_handlers.go。 - Approach:
- 为每个文档命令明确 aggregate、expected head、idempotency key 和授权 action。
- 版本账本与
document_records指针先在同一 UoW 写入;M1 保留现有通知兼容适配,但任何核心正确性不得依赖提交后的OnOutbox回调。 - 按 KTD4 锁定 document root 与 open version;状态转换采用条件更新。
- 提交返回 ledger/root 已持久化结果;workspace projection 和通知由提交后事件处理。
- 保留现有 ETag 语义,统一 agent、人工与批准路径的冲突分类。
- Execution note: 先补当前行为的 PG characterization,再移动写入所有权;不得先删除旧事务路径。
- Patterns to follow:
internal/docrevision/pg_mutate.go当前同事务投影、internal/docgov/lease.goexpected SHA、现有 document revision idempotency tests。 - Test scenarios:
- 同一 ETag 两个用户并发编辑,只有一个成功,另一个得到当前 head。
- 人工编辑与 agent commit 并发,账本 event_no 连续且 head 指向赢家。
- approve 与 reject 并发,只允许一个合法终态。
- candidate commit 在 root/version/outbox 任一故障点整体回滚。
- 相同 idempotency key 和相同 digest 返回原结果;不同 digest 返回冲突。
- 锁超时返回 typed retryable busy,不泄露 SQL 或路径。
- 两个 app 实例执行同一请求,结果与单实例一致。
- Verification: 文档四类命令的跨表不变量由 PostgreSQL 测试证明;
OnOutbox不再承担正确性。
U4. 建立授权后的文档统一读模型
- Goal: 正文读取不再依赖 handler 拼路径或把一致性错误伪装成 404。
- Requirements: R1、R3、R4、R14、R15、R19;KTD1、KTD8、KTD9。
- Dependencies: U1、U3;实施时按 CURRENT 排队使用
docgov和authzmutex。 - Files:
internal/app/docflow/query.go、internal/app/docflow/query_test.go、internal/docquery/service.go、internal/docquery/service_pg_test.go、internal/server/project_doc_handlers.go、internal/server/document_revision_authz_test.go、web/src/api/projectDocuments.ts、web/src/lib/controlledDocHydrate.ts、web/e2e/controlled-document-reload.spec.ts。 - Approach:
- Principal 由认证适配器构造,DocRef 不携带可覆盖 Principal 的 user/role/duty。
- 先解析数据库 resource→project、workflow、stage 和 duty,再读取 ledger/current pointer。
- workspace projection 只作为校验缓存;缺失时不阻止读取数据库 immutable content。
- ledger/root、projection 和 FAV 的差异映射为 typed consistency state,不再统一变成 404。
- FAV 降级在 U7 接入前保持关闭;接口先保留明确 extension point。
- 列表和版本查询强制分页,BatchGet 首期上限 100;批量授权按 Principal/project 分组并使用有界并发。
- Test scenarios:
- 正常 projection、projection 缺失、projection hash 漂移、ledger 缺失分别返回正确结果或 typed error。
- GET、刷新、重登不产生数据库写入、workspace 文件或归档事件。
- 匿名、非成员、跨用户会话、跨项目、错误 stage/duty 均拒绝且不泄露文档存在性。
- 授权依赖超时/错误时 fail closed。
- BatchGet 的 100/101 边界、重复引用、跨项目混合引用和限流拒绝不泄露资源存在性。
- 项目 143 等价 hazard 和新项目在页面刷新、restart、rebuild 后正文一致。
- Verification: 控制台和服务器目标 404/409/projection missing 为零;所有来源分支有服务端与浏览器证据。
U5. Workspace capability、lease 与文件提交
- Goal: 从类型和运行时同时禁止跨模块裸路径与跨租户工作区访问。
- Requirements: R1、R7、R9、R16–R18;KTD7。
- Dependencies: U1。
- Files:
internal/workspace/types.go、internal/workspace/resolver.go、internal/workspace/capability.go、internal/workspace/capability_test.go、internal/workspace/lease_pg_test.go、internal/squad/workspace_adapter.go、internal/bizdev/service.go、internal/server/project_doc_handlers.go。 - Approach:
- WorkspaceRef 删除 Root/OutputDir/ManifestPath,只保留 scope-bound ID、schema version 和 fencing token。
- workspace service 提供限定 artifact key 的 read、atomic writer、commit projection 和 inspect 能力。
- ID 与 lease 绑定 environment、project、run 和 storage identity;所有写入校验 fencing token。
- 使用 realpath/descriptor-based 检查,拒绝
..、绝对路径、symlink/hardlink 和根外 rename。 - legacy-v0 workspace 只读;首次受控 Resume 校验后原子升级 v1,并保留兼容备份一个发布周期。
- Test scenarios:
..、绝对路径、编码后的分隔符、空 key、跨 project Workspace ID 全部拒绝。- symlink 和 hardlink 在校验后替换的 TOCTOU 场景仍不能逃逸。
- 旧 lease writer 在新 lease 签发后因 fencing token 过期而失败。
- 两个 app 实例并发提交同一 projection,reader 只看到完整 marker。
- v0 读取、升级失败回滚、未知 major 版本拒绝。
- Verification: production 包中 workspace 物理路径只存在于 workspace/squadrun adapter;多实例测试不依赖
sync.Mutex。
U6. Durable outbox、逐消费者 delivery 与 replay
- Goal: 让后台投影、通知和 FAV 发布在崩溃与水平扩容下可恢复。
- Requirements: R5、R9–R11、R14、R18、R21;KTD5、KTD6。
- Dependencies: U2;需要新的 additive Goose migration。
- Files:
deploy/postgres/goose/000xx_*durable_event_delivery.sql、internal/events/envelope.go、internal/events/outbox.go、internal/events/dispatcher.go、internal/events/inbox.go、internal/events/dispatcher_pg_test.go、internal/events/replay.go、cmd/events/main.go。 - Approach:
- 保留业务 outbox record,新增 event envelope、每消费者 delivery/inbox、attempt、lease、next_attempt_at 和 dead-letter 数据。
FOR UPDATE SKIP LOCKED只用于小批次 claim;worker 在 claim transaction 外执行 subscriber。- ack/fail 使用 delivery version/fencing token,过期 worker 不能覆盖新 lease owner。
- 同 aggregate 依据 aggregate version 串行,不同 aggregate 并行;缺号进入等待或 quarantine。
- retry policy 区分 transient、permanent、authorization/integrity failures;后两者默认 dead letter/quarantine。
- replay CLI 先 dry-run,必须限定 consumer/event/project/time/id 范围并记录 operator、reason 和计划 hash。
- 逐个迁移并删除
OnOutbox与闭包桥;切换前 shadow 对账,切换后旧回调不再注册。
- Test scenarios:
- 两个和四个 worker 并发 claim,没有同一 lease 的双 owner。
- worker 在副作用前、后、ack 前崩溃,重启后最终只有一个可见业务结果。
- 一个 subscriber 永久失败不阻塞其他 subscriber。
- lease 过期后旧 worker ack 被 fencing token 拒绝。
- 相同 event 重放被 inbox 去重;相同 aggregate 乱序事件不越过缺失版本。
- 伪造 producer、跨项目 scope、payload digest 不符进入 quarantine。
- dead-letter replay 未授权、无 dry-run 或范围过宽时拒绝。
- Verification: 删除旧单一
published_at语义前完成双读对账;pending age、attempt、lease steal、dead letter 可观测。
U7. FAV 分支审计、终态发布与授权降级读取
- Goal: 将最终成果与 workspace 清理解耦,同时不形成第三套业务真相源。
- Requirements: R1–R4、R10–R15、R18、R21、R22;KTD8、KTD9、KTD12。
- Dependencies: U3、U4、U6;先审计
feature/final-artifact-vault相对当前 dev 的完整差异。 - Files:
internal/artifactvault/、deploy/postgres/goose/000xx_*final_artifact*.sql、internal/app/docflow/artifact.go、internal/docquery/service.go、internal/server/vault.go、cmd/artifact-vault/、internal/artifactvault/*_test.go。 - Approach:
- 重用通过审计的 FAV eligibility、staging、publisher、manifest、tombstone 和 scratch recovery;不直接 cherry-pick 未复核的组合根/handler 改动。
- 重新分配最新 Goose 序号,并验证 migration 与 U6 delivery schema 的组合和 Down 边界。
- 文档终态 transaction 冻结精确 payload/SHA 或 immutable blob ref 并写 publish request;publisher 不重读 workspace 或可变消息行。
- docquery 只在正常数据库授权成功后使用 FAV;manifest 只做 scope/integrity 交叉校验,不能授予权限。
- union-deny:运行删除账本或 vault tombstone 任一侧声明删除即拒绝读取/恢复。
- Execution note: 现有 FAV 分支包含业务代码和 migration,必须先 rebase/merge audit,再按本计划拆分原子提交。
- Test scenarios:
- committed/sealed/approved 文档按准入表归档;ready/rejected/stale/quality_failed 和 workspace 草稿为零。
- publish 重复、对象已存在、每个 fsync/rename/marker 崩溃点都可恢复。
- workspace 删除后,授权用户可读 verified payload;未授权用户看不到 artifact 存在性。
- DB 授权关系缺失、manifest scope 不符、坏 SHA、tombstone、vault 不可用均 fail closed 或返回明确 degraded error。
- GET 零数据库写入、零 workspace 写入、零隐式恢复。
- 跨项目相同 SHA 不共享删除或密钥边界。
- Verification: FAV test report 与当前 dev 重放通过;
FAV_DEGRADED_READ默认关闭,直到 M2 全部负例通过。
U8. TaskRun、RunManifest 与 workspace 清理门禁
- Goal: 任务终态和最终产物在重复 finalize、SSE 断线和 workspace 清理后保持一致。
- Requirements: R1、R2、R5–R12、R16–R18、R22;KTD5–KTD8。
- Dependencies: U5、U6、U7。
- Files:
internal/taskrun/manager.go、internal/taskrun/store.go、internal/taskrun/finalize.go、internal/taskrun/finalize_pg_test.go、internal/squad/manifest.go、internal/app/stageflow/execute.go、internal/server/task_finalize.go、internal/server/task_run_idempotent_sse_test.go。 - Approach:
- RunManifest 核心字段类型化并持久化规范字节、SHA、workspace/storage identity 和成员列表。
- taskrun terminal finalize transaction 写状态、assistant terminal turn、manifest、artifact staging 和 outbox。
- SSE done 只投影已提交状态;客户端断线与重试复用 task run idempotency key。
- workspace 清理只依赖已冻结 staging 和 policy;需长期保留的最终成果还必须取得 verified receipt。
- backend 由服务端 BackendRef policy 解析,InputBundle 携带数据分类和最小权限范围。
- Test scenarios:
- 同一 finalize 重复调用返回同结果,不重复消息、事件、manifest 或 artifact。
- finalize 与 cancel 并发只有一个合法终态。
- SSE 在 commit 前后断线并重试,历史中只有一个 assistant turn。
- workspace 在 finalize 后立即清理,publisher 仍从 staging 归档原 SHA。
- manifest digest、storage identity、artifact member 不一致阻止 verified/cleanup。
- 未授权 backend、敏感 InputBundle、凭证回显和 result 残留全部拒绝或清理。
- Verification: “完成但没有 durable manifest/publish request”和“已显示完成但正文仍模板”两类状态不可产生。
U9. 存量回填、migration corpus 与数据生命周期
- Goal: 只保留可解释的演示/回归数据,并持续验证旧数据迁移、重载和受控恢复。
- Requirements: R2、R12、R13、R21、R22;KTD8、KTD10。
- Dependencies: U6–U8。
- Files:
cmd/dataset/、internal/dataset/、internal/artifactvault/backfill.go、scripts/backup-system-db.sh、scripts/restore-system-db-drill.sh、.doc/test-fixtures/、.doc/test-reports/。 - Approach:
- 建立 dataset registry,分类 pinned、demo、regression、migration baseline、ephemeral 和 quarantine。
- backfill 只枚举 immutable document snapshot、approved/commit event 和 completed chat turn,禁止扫描 workspace。
- 大表按稳定主键 checkpoint 分批处理;多个 worker 使用
SKIP LOCKED或互斥 shard,限速避免挤占在线连接池。 - 每个 batch 记录 source schema/commit/storage identity、范围、成功、quarantine 和重试水位。
- dev 先蓝绿建立新数据根,导入 6–10 个批准 dataset;旧库/状态根只读 quarantine 7–14 天后,需用户再次批准才销毁。
- 每个 release 验证 clean、oldest-supported、previous、hazard、import、reload、rollback 和 artifact recovery 八条轨道。
- Test scenarios:
- backfill 中断、重复和并发新终态时分母/分子可复算且不重复 artifact。
- legacy-v0 workspace、旧 schema、高 ID、projection missing/fork、缺 snapshot 分别升级或 quarantine。
- restore 强制 scratch,拒绝 dev/test/main DSN、错误 storage identity、篡改 manifest 和缺权限凭证。
- FAV coverage、完整备份 restore 或 migration corpus 任一失败都阻止 destroy。
- dev 选择性导入后项目关系、文件 SHA、sequence、授权和 reload 一致。
- Verification: 数据清理有精确 manifest、可恢复位置、观察窗口和批准记录;不修改历史 Goose。
U10. chatlog/contextasm 原地端口化
- Goal: 在不重复付费搬包的前提下分离对话记录和 prompt 上下文装配。
- Requirements: R7、R14、R15、R19、R20;KTD1、KTD9。
- Dependencies: M1–M3 go/no-go;必须等待当前
chatmutex 释放。 - Files:
internal/ctxmgr/、internal/chat/、internal/app/chatflow/、internal/ctxmgr/chat_auth_pg_test.go、internal/server/chat_history_auth_test.go。 - Approach:
- 先定义 chatflow 所需的消费者侧 ChatLog 和 ContextAssembler ports。
- 在现有 ctxmgr 内提供 adapters,迁移调用者并保持表所有权不变。
- 统计接口 fan-in、DTO 转换和同类改动触达文件数;收益成立后再物理拆为 chatlog/contextasm。
- 所有读取携带 Principal/action,不接受请求体 user/project 作为权限事实。
- Test scenarios:
- 刷新前后同一授权消息集合的正文、分类和计数一致。
- 跨用户、跨项目、已退出成员和服务端后台导出负例。
- context assembly 只读取允许集合,摘要/缓存不放大不可见消息。
- 原地 adapter 与最终拆包实现的 contract tests 等价。
- Verification: 同类对话变更触达面相对基线下降;拆包前已获得独立行为收益。
U11. 组合根迁移、事件替换和共享上下文收权
- Goal: 让每个跨模块业务流程具有唯一应用用例 owner,而不是集中在 server/bizdev 闭包和共享指针。
- Requirements: R5、R7、R9–R11、R14、R15、R19、R20;KTD1、KTD5、KTD6、KTD9。
- Dependencies: U10;按四个独立子切片执行,不建立一个巨型 Wave。
- Files:
internal/app/app.go、internal/app/chatflow/、internal/app/stageflow/、internal/app/docflow/、internal/server/server.go、internal/bizdev/service.go、internal/coordinator/、对应 contract/integration tests。 - Approach:
- U11a 只把构造和生命周期迁到唯一 app composition root。
- U11b 一次只替换一种闭包事件;每种事件单独迁移、对账和回滚。
- U11c 逐个迁移 ExecuteTask/executeStageInternal 用例,不同时修改无关领域。
- U11d 将 UserProjectContext 改为只读 snapshot + 类型化 ContextUpdate,单一 use case 应用增量。
- Test scenarios:
- app start/stop/restart 不丢 worker,重复启动不创建重复 subscriber。
- 每个闭包新旧路径 shadow 结果一致,切换后旧路径无调用。
- ContextUpdate 并发应用使用版本检查,不发生字段级 lost update。
- server、CLI 和 worker 调用同一 use case 得到一致授权和事务结果。
- Verification: 每个子切片独立发布、独立回滚;server 不再保存业务闭包,UserProjectContext 不被多方直接修改。
U12. 传输边界收口和最终架构门禁
- Goal: 在领域行为稳定后缩减 server/stream 耦合并全面启用已清零规则。
- Requirements: R14、R18–R21;KTD1、KTD11。
- Dependencies: U11。
- Files:
internal/server/、internal/stream/transport/、scripts/arch-check.sh、.doc/system-design/module-boundaries.yaml、CI 配置、web/e2e/。 - Approach:
- server handler 只保留参数、Principal、use case 调用和 wire serialization。
- SSE transport 只编码中性 progress/event frame,不拥有业务状态或重试决策。
- 对已清零的 import、SQL writer、path、producer/consumer 规则逐项转 block。
- 错误信封和 PI Runner 协议保持独立 follow-up,不进入本单元。
- Test scenarios:
- HTTP、SSE 断线重连和 CLI 对同一业务状态观察一致。
- server 新增领域实现 import、SQL 或路径拼接时 CI 失败。
- SSE 慢客户端、断开和重复连接不阻塞业务 transaction 或 dispatcher。
- 全套授权、重启和多实例回归保持通过。
- Verification: R1–R7 的修订版规则全 block;server 行数只作观察指标,不作替代行为验收。
Verification Contract
Evidence layers
| 层级 | 必须证明 | 不得替代 |
|---|---|---|
| Unit | 状态机、错误分类、schema、路径和 policy | PostgreSQL 原子性 |
| PostgreSQL integration | 事务回滚、锁、幂等、migration、并发 | 浏览器用户体验 |
| Fault injection | 每个 commit/delivery/publish 崩溃窗口 | 真实进程 restart |
| Concurrency/load | 多 app/worker、热点与非热点资源、积压和公平性 | 正确性断言 |
| Real PI | 真实 agent 生成、manifest、finalize、归档 | stub 或固定假输出 |
| Browser | 页面创建/打开/编辑/刷新/重登、错误与 degraded 状态 | API 直接调用 |
| Restart/rebuild | app、worker、workspace root、存储 identity 重载 | health 单点检查 |
| Authorization negative | 匿名/跨用户/跨项目/阶段/duty/后台/recovery | 前端隐藏按钮 |
| Recovery | 完整 DB restore、migration corpus、FAV scratch 恢复 | 仅列出 dump 内容 |
Repository gates
go vet ./...go test -short ./...- 目标模块 PostgreSQL 集成和 race 测试;若全包 race 存在已知 TempDir flake,报告必须分离目标模块结果和基建债。
scripts/arch-check.sh对当前里程碑已收紧规则为 block,对未迁移规则输出基线差异。web单元测试与 production build。- dev 容器必须 rebuild 后检查
/health与/ready,不能用 restart 证明新代码已部署。 - 真实 PI、真实浏览器、post-restart 和多用户负例按独立证据分栏。
Concurrency test matrix
| 维度 | 场景 | 必须断言 |
|---|---|---|
| App replicas | 1/2/4 | 结果和不变量一致,进程内 mutex 关闭仍正确 |
| Dispatcher replicas | 1/2/4 | 无双 owner ack,无重复可见副作用,无永久饥饿 |
| Resource shape | 同一文档热点/不同文档并行 | 热点正确冲突,非热点不被全局锁串行化 |
| Failure point | before commit/after commit/before ack/after side effect | 无丢事件,重复交付幂等 |
| Lock behavior | timeout/deadlock/serialization | 分类和完整事务重试正确,无无限等待 |
| Lease behavior | expiry/renew/steal/stale fencing | 旧 owner 无法覆盖新 owner |
| Load | steady/burst/backlog recovery | 记录 p50/p95/p99、锁等待、连接池、oldest event age |
Authorization test matrix
每个状态改变和读取接口至少覆盖:匿名、已登录非成员、错误项目成员、会话非 owner、错误 duty、错误 stage、过期 lease、跨环境 ID、服务身份越权、授权依赖失败和系统管理员合法路径。 FAV 额外覆盖 manifest project 不符、tombstone、删除账本不一致和授权图缺失。 状态改变必须覆盖授权检查后的成员撤销、duty 变化、stage 推进和 lease 失效,证明用例在取得资源锁后重新验证当前事实。
Migration and rollback matrix
| 阶段 | Forward proof | Rollback proof |
|---|---|---|
| Expand | 新 schema 与旧代码兼容 | migration Down 或旧代码继续运行 |
| Shadow | 新旧读取差异为零或有解释的 quarantine | 关闭 flag 回旧路径 |
| Backfill | checkpoint、幂等、并发新写一致 | 停止 worker,不撤销已安全扩展列 |
| Switch | 新 writer 唯一、旧 writer 只读 | flag 回最近兼容 writer |
| Observe | restart/rebuild/浏览器/PI/指标稳定 | 切回旧 reader/writer 与只读 quarantine 数据 |
| Contract | 引用扫描和用户批准 | contract 后只能逆序部署兼容版本,不能单 commit revert |
Risk Analysis and Mitigation
| 风险 | 触发信号 | 缓解和停止条件 |
|---|---|---|
| contracts 成为新万能包 | fan-in、共享 DTO、ADR 数持续上升 | 消费者侧端口;按域拆公共契约;超过阈值回评边界 |
| 跨属主事务过大 | 锁等待、事务 p99、deadlock 上升 | 缩小原子集合;事务外副作用;固定锁序;停止扩面 |
| advisory lock 形成全局瓶颈 | 非热点资源也排队 | 只对项目级不变量使用;key 分区;检查 pg_locks |
| outbox backlog 放大 | oldest age/attempt/dead letter 超阈值 | 独立 worker 连接预算、批次和退避;按 aggregate 并行 |
| poison event 无限重试 | 同 event 高频失败 | typed permanent failure、dead letter、受控 replay |
| FAV 成为授权旁路 | DB 关系缺失仍返回内容 | 正常授权先行、union-deny、默认关闭 degraded read |
| 工作区链接逃逸 | realpath/scope mismatch | opaque ID、descriptor 检查、fencing、负例测试 |
| 回填压垮在线库 | lock wait/replication lag/连接池饱和 | checkpoint、小批次、限速、独立连接预算、可暂停 |
| 双读长期不收口 | exception 数和差异长期不降 | 明确 owner、到期日和 go/no-go;禁止无限兼容 |
| 物理搬包造成大冲突 | import churn 大于行为收益 | 原地端口化;行为稳定后再移动;按 mutex 排队 |
| FAV 分支漂移 | migration 冲突、dev API 已变化 | rebase audit,不整分支盲合;按单元摘取并重测 |
| 数据清理误删 | coverage/backup/corpus 任一未知 | 默认不删;蓝绿迁移;quarantine 观察;用户二次批准 |
Operational and Rollout Notes
Feature flags
- 新 docquery reader、单写路径、dispatcher consumer、FAV degraded read 和 legacy workspace reader 分别使用独立 flag。
- flag 只选择已兼容路径,不能跳过授权、完整性或终态准入。
- 每个 flag 有 owner、默认值、环境范围、启用前置和删除里程碑。
Resource budgets
- app transaction、dispatcher、backfill 和 FAV publisher 使用分离的连接池或明确配额,避免后台积压耗尽在线请求连接。
- worker batch、lease、heartbeat、retry 和 concurrency 由配置管理;M1/M2 基线报告确定初值和告警阈值。
- 对同 aggregate 设置最大在途数 1;跨 aggregate 并行度按数据库与 vault 指标调节。
以下是首轮试点的安全起始值,不是产品 SLO;压测报告必须记录调整依据:
| 参数 | 起始值 | 调整约束 |
|---|---|---|
| 写事务 lock wait | 最多 2 秒且不超过请求剩余 deadline | 不允许无限等待;热点冲突优先返回 typed busy |
| serialization/deadlock retry | 最多 3 次,指数退避并带 jitter | 只重跑完整幂等事务 |
| dispatcher claim batch | 50 | claim transaction 只认领,不执行 subscriber |
| delivery lease/heartbeat | 30 秒 / 10 秒 | heartbeat 失败后旧 worker 不得 ack |
| delivery attempts | 12 次后 dead letter | permanent/auth/integrity failure 不做 12 次盲重试 |
| retry backoff ceiling | 15 分钟 | oldest age 仍受告警和人工处置约束 |
| backfill batch | 每批 200 个 aggregate | 按 lock wait、replication lag 和在线池占用自动降速 |
| backfill workers | dev 初始 1,验证后最多 4 | 每个 shard/checkpoint 不重叠 |
Observability
- Dashboard:transaction/lock、document consistency、outbox delivery、workspace lease、FAV publication、authorization deny、backfill/quarantine。
- Alert:oldest event age、dead letter、storage identity mismatch、projection mismatch、FAV coverage gap、恢复/删除拒绝。
- Trace 使用 correlation/causation ID 串联 HTTP/use case/transaction/event/subscriber,但不得携带正文或 secret。
Documentation lifecycle
- 每个里程碑开始前新建 short-term 和 CURRENT claim。
- 每个里程碑结束生成 test report,回链本计划 U-ID 和验收清单。
- 完成项移入 archive;未解决问题进入 audit/remediation,不通过口头说明关闭。
Sources and Research
- 模块解耦方案复核记录当前源码中的事务、outbox、workspace 和授权缺口。
internal/docrevision/pg_mutate.go是现有账本与根指针同事务模式,U2–U3 不得降低其原子性。internal/docduty/revoke.go已使用事务级 project advisory lock,是 KTD3 的本地先例。internal/taskrun/manager.go、internal/taskrun/store.go已有进程内幂等锁与数据库唯一键,U8 需要把正确性完全落到跨实例可用的数据库协议。feature/final-artifact-vault已实现候选 FAV 能力;U7 必须按当前 dev 重新审计,而不是把分支状态当成当前发布事实。- PostgreSQL
SKIP LOCKED只用于队列式 claim,不用于普通一致性读取。 - PostgreSQL serialization failure handling要求 serialization/deadlock 重试覆盖完整事务逻辑。
- OWASP Business Logic Security支持服务端重新推导身份、所有权和安全相关状态。
Definition of Done
- U1–U9 完成并通过 M0–M3 的逐里程碑 go/no-go;U10–U12 只在收益复盘批准后执行。
- 文档提交不存在跨表半完成,所有提交后副作用来自持久事件。
- 多实例、多 worker、崩溃和 lease 过期场景保持相同业务结果。
- workspace 不再是正文或最终 manifest 的唯一事实源,裸路径不跨模块传播。
- FAV 只含终态成果,中间结果准入率为零,授权缺失时不可降级读取。
- 项目 143 等价 hazard 与新项目完成真实 PI、页面刷新、重登、restart/rebuild 和跨用户负例。
- clean/oldest/previous/hazard/import/reload/rollback/artifact recovery 八条迁移轨通过。
- arch-check 规则按纵向切片清零后全部 block,例外有 owner 和到期日。
- CURRENT、FEATURE-MATRIX、gap-matrix、ADR、系统设计、test report 和 checklist 状态一致。
- 放弃方案、临时代码、双写旁路、过期 flags 和无消费者契约已移除,不留在最终 diff。
Related Links
- Audit: 模块解耦方案复核
- Architecture: 模块化单体解耦架构
- ADR: 模块化单体解耦
- Planning task: 本次计划修订任务
- Acceptance: 重构验收清单
- FAV dependency:
feature/final-artifact-vault中的.doc/remediation/2026-09-14-final-artifact-vault-remediation.md - Data lifecycle dependency:
feature/final-artifact-vault中的.doc/remediation/2026-09-14-runtime-data-lifecycle-and-migration-corpus-remediation.md