Move the reviewed durable-event, HTTP envelope, and PI typed-result designs off the docs/ prefix so Track E can start from latest dev. Co-authored-by: Cursor <cursoragent@cursor.com>
9.9 KiB
title, status, created, updated, author, confidence
| title | status | created | updated | author | confidence |
|---|---|---|---|---|---|
| 模块解耦与 IPC/RPC 交互现状评估(复核修订版) | done | 2026-09-15 | 2026-09-15 | Cline | high |
调研:模块解耦与 IPC/RPC 交互现状评估
结论
- 系统采用模块化单体,不应把所有模块调用改成 IPC/RPC。当前已运行的外部传输边界有两条:Web 与 server 的 HTTP/SSE、Go server 与 PI Agent Runner 的 HTTP。
cmd/events和 FAV worker 是计划中的独立进程,不是已运行事实。 - 端口、组合根、事件信封、RunManifest、幂等键和 arch-check 提供了可执行的边界基础;
import=0/event=0只表示没有超出现有 allowlist/baseline 的新违规,不能证明语义解耦已经完成。 - 首要可靠性缺口是事件“部分生产、没有生产级消费闭环”。
internal/events/pg.go尚无原子 lease、ack/fail、retry/dead-letter 的完整实现;现有 dispatcher 测试覆盖 MemoryBus,不是 PostgreSQL 运行证据。 - 最大结构扩散源仍是 bizdev service locator。另有
docgov → docrevision单向 canonical/hash 工具依赖、chat/coordinator 扇出和intent → bizdev依赖。此前“docgov↔docrevision 双向 import”的结论不成立。 - HTTP 错误信封与 PI Runner typed result 是独立协议债,不应阻塞持久事件链关闭。FAV 分支也不应成为 notify/projection 交付的前置条件。
1. 通信边界
| 边界 | 机制 | 当前状态 |
|---|---|---|
| Web ↔ server | /api/v1/* HTTP + SSE |
已运行;错误信封仍未统一 |
| Go ↔ PI Agent Runner | HTTP + Bearer;health/run/cancel/search | 已运行;结果仍依赖 legacy stdout/result.json 路径 |
| 单体内模块 | 消费者端口 + internal/app 组合根 |
已运行;bizdev locator 和部分直依赖仍扩大改动面 |
| 领域事件 | PG envelope/delivery schema | 写侧部分存在;生产 dispatcher 未闭环 |
cmd/events |
计划中的独立 worker | 尚未实现/部署 |
| FAV worker | 独立审计轨道 | 分支未整支合入;不得作为本轮验收前置 |
不建议把单体内端口改成 RPC。那会增加序列化、延迟、部分失败与运维成本,却不消除当前 locator、授权、幂等和事件消费缺口。
2. 已有契约与真实边界
已有且可校验
.doc/system-design/module-boundaries.yaml与internal/archtest管理 import、SQL write、path 和 event 规则。events.Envelopeschema 1 包含 event/aggregate/correlation/causation/idempotency/digest/producer 等字段,并校验 digest 与敏感字段。taskrun.RunManifestschema 1 使用 canonical JSON 与 SHA-256。- TaskRun 同 idempotency key 异 request digest 返回冲突;文档写使用 expected head/ETag。
- backend 选择由服务端 allowlist 控制,出站数据经过 secret stripping。
module-boundaries.yaml已有event_producers与event_consumers段。后续工作是补全并校验 runtime registry parity,不是新建段落。
尚未闭合
PGSink.Append/InsertEnvelope已存在,但ClaimIDs仍是 SELECT-only claim;没有生产级 lease 获取、heartbeat、fenced ack/fail、retry、dead-letter 和 immutable attempt audit 闭环。event_deliveries每个 event/consumer 一行,只保存当前状态和累计 attempt;无法还原每次 lease/attempt 历史。internal/events/replay.go只检查 dry-run 与 consumer/operator/reason 等基础字段,尚未形成带认证主体、env/project/event scope、plan digest 和 apply 审计的应用用例。docflowenvelope 仍存在Environment:"dev"、ProjectID:0,且Fav.StageAgentCommit(ctx, 0, ...)使用错误项目作用域。- 部分 server 路径临时构造
docflow.Commands,没有稳定注入 EventSink/UoW;不能假设 candidate-ready、downstream-impact、approved 三条链已经产生可供 shadow 的事件。 - 闭包 hook 没有版本、重试、delivery 或 replay 记录。
3. 耦合点
3.1 bizdev service locator
bizdev.Service 暴露 Pool()/CAS()/DocGov()/DocRevision()/ProjectWorkflow()/FreeformDoc()/KB()/ContextManager()。调用方可以绕过端口取得数据库池和其他模块实现,令签名或构造变化向多包扩散。
整改应先加 no-new-callers 门禁,再逐调用方迁移,最后在一个 contract 提交删除 8 个 getter。门禁应禁止领域/用例 API 暴露或转发 pool/tx,同时允许 app/cmd 组合根向表属主构造器注入 pool,并允许登记属主从 opaque TxScope 解出 tx。
3.2 docgov 到 docrevision 的单向工具依赖
生产依赖方向是 docgov → docrevision。直接用途集中在 Markdown canonicalization 与 SHA helper;docrevision 不生产 import docgov。
目标不是“破双向环”,而是把 canonical Markdown + digest 提取为有行为、内聚、带 golden vectors 的共享模块,例如 internal/doccontent。它不应进入只放无行为值对象的 vocabulary。
3.3 chat、coordinator 与 intent
- coordinator 仍以
bizdev.TaskRunRequest/Result为词汇。 - chat 编排跨多个模块,需按用例逐批迁入 chatflow。
- intent 与 bizdev 的解绑是较小且独立的迁移,不应和 chat 大迁移绑成一个分支。
3.4 闭包桥与未接线事件
当前批准后的推进与通知仍可通过 docrevision.OnOutbox、docgov.AddOnApprovedHook 及组合根闭包执行。闭包在提交后的进程内调用不能提供 durable delivery、重试、dead-letter、replay 或 attempt audit。
不能直接删除闭包。正确顺序是:
- 证明生产入口在同一 UoW 写入领域变化、envelope 和目标 delivery。
- 对历史事件做 dry-run inventory,隔离 scope/schema/sequence 异常。
- 完成真实 PG dispatcher。
- shadow 只计算 intended outcome,不执行业务副作用。
- 以时间、样本和状态分支覆盖作为切换门。
- 按 notify、workflow advance、projection 独立切换和回滚。
4. 并发、数据与权限风险
并发
FOR UPDATE SKIP LOCKED只有在 claim 事务同时更新 lease owner/token/until 并 RETURNING 时才完成所有权转移;当前 SELECT-onlyClaimIDs不够。- Fencing 不能只保护 ack。token 必须进入 consumer command,并在数据库副作用或 projection marker 的最终提交点校验。heartbeat 失败应取消 handler。
- 同一 consumer/aggregate 最多一个 inflight;不同 aggregate 并行。领域版本可能稀疏,不能直接当无缺口事件序号。
- at-least-once delivery 通过 inbox/唯一键/CAS/no-replace 得到 exactly-one visible outcome;不能声称 delivery 本身 exactly once。
历史数据
旧记录可能含 project 0、错误 environment、未知 consumer、未知 payload schema 或稀疏 aggregate version。启用 live claim 前必须生成 manifest,并分类 safe/repair/quarantine/expire。任何修复保留 old/new digest、operator、reason、approval;本计划不删历史数据。
权限
后台 consumer 使用按 env/consumer/action 约束的 service principal。处理事件时按 ProjectID/DocID 重读资源归属、成员/duty、workflow config revision、活动 stage 和当前状态。事件 actor 只用于审计,不能把历史用户权限带到延迟执行时。
Replay 必须是应用用例:管理员 capability、精确范围、reason/approval、dry-run plan digest、apply exact digest 和不可变审计。多项目或批量 apply 需要操作者与批准人分离。
日志、dead-letter 和 reconciliation 只能保存 ID、digest、typed error code 与字段 allowlist 生成的摘要,不能保存原始 payload、正文、SQL error、绝对路径、token 或 prompt。
5. 优先级建议
P0:Track E 持久事件交付
- 修正 docflow 生产入口、env/project 作用域和 UoW 原子写。
- 补全既有 event allowlist 与 runtime registry parity。
- 审计历史事件、sequence/checkpoint 模型和现有唯一约束。
- 实现
cmd/events的 PG lease/heartbeat/fencing/attempt/dead-letter。 - notify、workflow advance、projection 逐个 shadow、live、回滚。
- 用真实 browser + PG + worker restart 证明刷新后正文恢复,无 projection-missing。
P1:Track S 结构解耦
- D4:bizdev locator 调用方迁移与最终 getter 原子删除。
- D5:stageflow 自有 use-case 类型,coordinator 解绑 bizdev DTO。
- D6:提取 doccontent canonical 行为模块,移除单向工具依赖。
- D7a:intent 单独解绑 bizdev。
- D7b:chat 按用例迁入 chatflow。
这些单元分别 go/no-go,不作为事件可靠性 blocker 的关闭条件。
P2:Track P 独立协议
- HTTP/SSE 错误信封另立 remediation;公开 details 必须按 code allowlist 并脱敏。
- PI Runner 任务命名为 typed-result-v1 rollout,不混称“协议 v2”。typed 字段存在但无效时 fail closed;只有字段缺失且握手声明旧 Runner 时才允许 legacy。
6. 证据索引
| 事实 | 证据 |
|---|---|
| 模块化单体决策 | .doc/adr/2026-09-14-modular-monolith-decoupling.md |
| PG delivery 当前实现 | internal/events/pg.go、deploy/postgres/goose/00035_2026-09-14-durable-event-delivery.sql |
| MemoryBus 测试边界 | internal/events/dispatcher_test.go、internal/events/bus.go |
| replay 当前校验 | internal/events/replay.go |
| 事件字段硬编码 | internal/app/docflow/commands.go |
| 闭包桥 | internal/app/docnotify/subscriber.go、internal/docgov/engine.go、internal/app/app.go |
| bizdev locator | internal/bizdev/service.go:229-260 |
| 单向 canonical 依赖 | internal/docgov/lease.go、internal/docrevision canonical helper |
| allowlist 已存在 | .doc/system-design/module-boundaries.yaml |
建议动作
下一项只立 Track E 的 E0A:事件生产、契约与历史数据门。Dispatcher 必须保持 off,直到 PG 测试证明事件源和旧数据分类正确。完整执行计划见 Phase 2 持久事件交付与后续解耦路线。