18 KiB
18 KiB
P1R7 Source Owner Propagation 全局审阅版
结论
推荐把 P1R-7 后续主线从“AI 单 owner 先做”提升为“全局 source owner propagation 分层方案”,但实现仍必须按 owner 分阶段切片推进。
全局推荐方案:
- Events owner 只负责统一事件投影、幂等、payload 校验、脱敏、可见性过滤和 SSE,不反向读取任何 source owner 业务表。
- 每个 source owner 只负责本域权威事实和本域 publish outbox;worker 通过
muse-module-events-api调用EventsPublishApi。 - 不建立一个跨 owner 的中心 source 模块,也不让 Events server 直接拥有 source propagation 业务事实。
- 当前 AI 第一切片实现草稿不能直接作为全局方案完成证据;它只能作为后续 P1R-7b 的候选实现素材,必须先按本全局方案复审和修订。
P1R-7 后续拆分建议:
| 阶段 | 目标 | 状态口径 |
|---|---|---|
| P1R-7b | 全局 propagation 规则冻结 + AI terminal event 第一 owner 切片 | 只推进 needs_verification 证据 |
| P1R-7c | Knowledge source status / projection event propagation | 只推进 needs_verification 证据 |
| P1R-7d | Market lifecycle / account projection / governance event propagation | 只推进 needs_verification 证据 |
| P1R-7e | Account(Member) security / entitlement / usage notification propagation | 只推进 needs_verification 证据 |
| P1R-7f | Content canonical change / block saved / export task event propagation | 只推进 needs_verification 证据 |
| P1R-7 completed approval | 对 P1R-7 做单独完成审批 | 必须用户单独批准 |
本审阅版不实现代码、不修改 OpenAPI、不修改 scanner、不修改 coverage JSON/Markdown,不把 Events、P1R-7、Market 或任何 owner 标为 completed。
已验证事实
工作区与状态
- 正确工作区为
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0。 - 当前分支为
dev/1.0.0,git pull --ff-only origin dev/1.0.0输出Already up to date.。 - 当前 HEAD 为
fa29753 test(p1r): 收口 Events SSE 门禁。 - 当前 worktree 有未提交的 P1R-7b AI outbox 草稿、V17 migration 草稿和
docs/agent-specs/文档草稿;本审阅版不清理、不回退这些文件。 - 受保护文件当前定向
git status --short无输出:docs/api-contracts/market/openapi.yamldocs/api-contracts/ai/openapi.yamldocs/api-contracts/knowledge/openapi.yamldocs/api-contracts/events/openapi.yamlmuse-cloud/scripts/p1r-audit-api-coverage.pydocs/superpowers/reports/p1r-api-coverage.jsondocs/superpowers/reports/p1r-api-coverage.md
Coverage 与阶段边界
- 当前 coverage summary:
totalOperations = 233completedOperations = 100needsVerificationOperations = 133incompleteOperations = 0genericPersistenceOperations = 0ssePlaceholderOperations = 0
- Events 当前唯一 operation 为
streamEvents GET /app-api/muse/events = dedicated / needs_verification。 - Market 当前 32 operations 均为
dedicated / needs_verification。 docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md明确 P1R-7a 未接入 source owner propagation,未运行 source owner publish tests。
已有全局设计约束
CLAUDE.md明确本仓是设计文档 SSOT,核心架构决策中 Source 传播采用“事件驱动 + 各模块自治”,统一实时通信为 SSE。design-docs/架构-02-核心数据结构与双轨模型.md明确:来源 owner 发出状态变化事件,各消费模块监听事件并自行处理反应逻辑,不需要独立 source 模块。design-docs/后端-03-关键流程实现与接口契约.md明确:AI 调用、投影、索引、导出、评估、来源传播、New-API 归属和通知走异步任务,异步失败不得回滚已提交 Canonical。docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md明确:跨 owner 发布模式固定为 source owner 本域 outbox + worker 调用 Events API;Events server 不允许反向依赖 source owner server,也不允许统一 SSE Controller 直接扫多域业务表。
Events owner 当前能力
EventsPublishApi.publish(EventsPublishReqDTO)已存在于muse-module-events-api。EventsPublishReqDTO已包含commandId、tenantId、ownerUserId、sourceOwner、sourceType、sourceId、sourceRevision、eventType、resourceType、resourceId、payloadSummary、emittedAt。EventsPublishServiceImpl已按 commandId 优先、source tuple 次级做幂等回查。EventsPublishServiceImpl使用insertIgnore,避免用唯一约束异常作为正常幂等路径。UnifiedEventMapper.selectVisibleEventsForOwner只查询publish_status = accepted、同租户、同 ownerUserId、未删除、sequenceNo大于 cursor 的事件。EventsStreamServiceImpl将统一投影中的done/error/notification等事件转成 SSE。
Source owner 当前能力差异
- AI 已有
muse_ai_task_eventterminal fact、MuseAiRuntimeJobDispatcher、MuseAiJobMapper.claimNextQueuedRuntimeJob()和FOR UPDATE SKIP LOCKEDworker 风格;当前未提交草稿新增了 AI event publish outbox,但尚未按全局方案完成复审。 - Knowledge 已有
muse_knowledge_source_event和muse_knowledge_projection_task;当前MuseKnowledgeSourceEventService同步更新 projection 后把 task 记录为completed,注释明确避免制造无执行者的 queued task。 - Market 已有
MarketAccountProjectionOutboxService,但接口只有recordSynced/recordBlocked,职责是 Market 到 Member Account projection 的同步状态,不是 Events publish worker。 - Member(Account) 已有
MemberSecurityEventDO、AccountIntegrationCallDO、调用归因 job 和安全事件脱敏转换;当前 mapper 以查询/归因记录为主,未发现完整 Events publish outbox claim/retry/dead-letter 链路。 - Content 正式设计中已有
BlockSavedEventoutbox 方向,但本次只读未验证当前 Content 已有可直接复用到 Events 的 publish worker。
推断
- 全局 source propagation 的核心不是新增一个“大 source 服务”,而是冻结所有 owner 都必须遵守的事件发布合同、幂等合同、失败补偿合同和审计合同。
- 直接把当前 AI outbox 草稿继续推进,容易把“第一切片实现细节”误当成全局架构决策;应先用全局审阅版统一约束,再修订 AI 执行版。
- Market 的 projection outbox 名称容易误导;它当前表达的是 Market -> Account 读模型同步状态,不适合直接复用为 Events publish outbox。
- Knowledge 已有 source event,但目前 projection task 同步完成,缺少异步 publish worker;进入 P1R-7c 前需要先补本域发布补偿语义。
- Account security event 适合做用户通知类 propagation,但必须先冻结安全 payload allowlist,避免把设备、IP、风控原始详情或内部判断泄露到统一 SSE。
- Content 的 BlockSavedEvent 更接近内部 followup trigger,不一定都应该进入用户 SSE;需要先区分“内部领域事件”和“用户可见统一事件”。
假设
- P1R-7 后续允许在各 source owner 内新增 publish outbox 表或等价本域状态表。
- P1R-7 后续允许在 source owner server 中新增
muse-module-events-api依赖,但不允许依赖muse-module-events-server。 - 当前
EventsPublishReqDTOenvelope 足够承载后续 owner 的用户可见事件;如果后续发现字段不足,应另起 OpenAPI / contract 变更审批,而不是在实现里绕过。 - 当前 P1R-7 完成状态仍需用户单独批准;本方案只定义如何补证据链。
全局目标
- 建立跨 owner 的统一 source propagation 规则,保证每个 owner 都按相同可审核链路发布事件。
- 保持 source owner 自治:事实归 owner,发布补偿归 owner,统一可见投影归 Events。
- 让
/app-api/muse/eventsSSE 成为用户可见事件的统一出口,而不是跨表扫描器。 - 为 P1R-7 completed approval 准备真实证据:成功路径、幂等、失败、重试、dead-letter、权限、审计、数据库落库和 SSE 可见性。
本文只定义“用户可见 Events 发布链路”。它不替代正式 Source / Authorization 内部传播链路;内部来源状态、授权快照、传播到目标 owner 的阻断和禁用仍由正式设计中的 Source Status Event / Source Propagation Job 或等价本域机制承担。
非目标
- 不建立独立 source 模块。
- 不把 Events server 改造成 source owner 编排器。
- 不让 Events server 反向依赖 AI / Knowledge / Market / Member / Content server。
- 不把所有内部领域事件都暴露给 SSE。
- 不复用职责不匹配的 Market Account projection outbox。
- 不用修改 OpenAPI、scanner 或 coverage 报告掩盖缺口。
- 不把当前 P1R-7b AI 草稿视为已完成实现。
- 不推进 completed 状态。
推荐架构
flowchart LR
subgraph Owner["每个 Source Owner"]
Fact["本域权威事实<br/>Canonical 或 Shadow terminal fact"]
Outbox["本域 publish outbox<br/>queued/running/retryable/published/dead_letter"]
Worker["本域 worker<br/>claim + lease + retry"]
Fact --> Outbox --> Worker
end
Worker --> Api["EventsPublishApi<br/>events-api only"]
subgraph Events["Events Owner"]
Api --> Publish["幂等发布服务<br/>commandId + source tuple"]
Publish --> Store[("muse_unified_event<br/>accepted/rejected/blocked")]
Store --> Stream["SSE stream service<br/>tenant + owner + sequence"]
end
Stream --> Client["muse-studio fetch SSE"]
关键原则:
- Source owner 只发布已经落库的、可审计的本域事实,不发布临时内存状态。
- Source owner outbox 与本域事实绑定,但不能因为 Events 暂时不可用回滚已提交 Canonical。
- Worker 不拼接自由 payload,只从 outbox 固化的 allowlist payload 发布。
- Events owner 只校验和投影,不反查 source owner 表补上下文。
- SSE 只读
muse_unified_eventaccepted 事件,不直接查询 AI / Knowledge / Market / Account / Content 表。
全局发布合同
每个 owner 的发布请求必须满足:
| 字段 | 全局规则 |
|---|---|
commandId |
稳定、短、幂等;推荐 <owner>_evt:<sha256-32>,长度不超过 128 |
tenantId |
必填;worker 线程必须恢复租户上下文 |
ownerUserId |
必填;决定 SSE 可见性 |
sourceOwner |
固定 owner 枚举,如 ai、knowledge、market、account、content |
sourceType |
固定业务事实类型,不使用表名泄露内部结构 |
sourceId |
本域事实稳定 id 或业务 id |
sourceRevision |
本域事实版本;没有版本时使用明确占位,但 owner 执行版必须解释 |
eventType |
只能使用 Events OpenAPI 已声明类型;新增类型必须另起合同审批 |
resourceType/resourceId |
用户界面定位对象;不得代替 source tuple 幂等 |
payloadSummary |
只包含 OpenAPI schema 允许字段和安全摘要 |
emittedAt |
本域事实发生时间,不是 worker 发布时间 |
payload 全局禁令:
- 不发布 prompt、provider request/response raw body、完整正文、知识资料原文、授权头、token、apiKey、secret、password。
- 不发布内部风控原始规则、管理员备注原文、失败堆栈、数据库错误详情。
- 不发布未在 OpenAPI 中声明的扩展字段。
- 不把
rejected或blocked事件作为 SSE 可见事件。
全局 outbox 状态机
stateDiagram-v2
[*] --> queued: owner fact 已落库并创建 publish intent
queued --> running: worker claim + lease
retryable --> running: retry 到期 claim
running --> running: stale lease reclaim
running --> published: Events accepted 或 duplicate accepted
running --> retryable: temporary failure
running --> dead_letter: payload invalid / rejected / blocked / retry exhausted
retryable --> dead_letter: retry exhausted
published --> [*]
dead_letter --> [*]
全局要求:
- claim 必须是原子 SQL,优先使用
FOR UPDATE SKIP LOCKED或等价安全领取机制。 - claim 必须包含 lease,避免 crash-after-claim 后永久 running。
- attempt 只在 claim 时递增,失败处理不得二次递增。
- 幂等插入必须优先使用
ON CONFLICT DO NOTHING/insertIgnore+ 回查,不把唯一约束异常当正常路径;PostgreSQL 同事务异常可能让事务进入 aborted 状态。 dead_letter不能自动 replay;只有确认是临时故障的记录才能被重置为retryable。- 所有状态转换必须有中文日志,至少包含 owner、tenantId、ownerUserId、source tuple、outboxId、attempt 和错误摘要。
- owner 执行版必须显式冻结最小字段词汇:
max_attempt、next_retry_at、claim_expires_at、固定退避策略、claim 索引和依赖树验证命令。
用户可见事件判定准则
只有同时满足以下条件的事件才进入统一 SSE:
- 用户需要实时感知该变化,且变化能影响当前用户下一步操作。
- 事件 payload 可以被压缩成 OpenAPI 已声明 schema 内的安全摘要。
- 事件不要求前端读取内部风控、授权、source propagation 或 worker 调度细节。
- 事件 owner 能提供稳定 source tuple、幂等 commandId 和 ownerUserId。
默认不进入统一 SSE 的事件:
- 只服务内部 followup trigger 的领域事件,例如
BlockSavedEvent。 - projection rebuild、source propagation target、retry job、worker heartbeat 等内部任务状态。
- 需要完整正文、知识资料、provider raw body、授权详情或安全风控原始证据才能解释的事件。
阶段化路线
P1R-7b:全局规则冻结 + AI terminal event 第一切片
目标:
- 修订现有 AI 第一切片执行版,使其服从本全局方案。
- AI terminal
done/error通过本域 outbox + worker 发布到 Events。 - 修正当前草稿中可能存在的同事务唯一冲突异常风险,采用
ON CONFLICT DO NOTHING/ insert-ignore + 回查。 - 证明 AI 只依赖 events-api,不依赖 events-server。
不做:
- 不把 AI 以外 owner 混入 P1R-7b 实现。
- 不标 completed。
P1R-7c:Knowledge propagation
目标:
- 选择 Knowledge source status / projection summary 中最小用户可见事件。
- 在 Knowledge owner 内新增或复用 publish outbox 语义,补 claim/retry/dead-letter。
- 区分内部 projection task 和用户可见 Events notification。
不做:
- 不把同步完成的 projection task 伪装成异步 worker。
- 不发布知识资料原文。
P1R-7d:Market propagation
目标:
- 覆盖 publish review、asset delist/recall、license/install/purchase 对用户可见状态的通知。
- 保留 Market Account projection outbox 的原职责,另建或扩展专门 Events publish outbox。
不做:
- 不把 Market 32 operations 标为 completed。
- 不让 Market 事件接管源作品、源智能体或源知识库事实 owner。
P1R-7e:Account(Member) propagation
目标:
- 覆盖 security event、entitlement/quota notification、New-API attribution summary 中适合用户可见的事件。
- 复用既有安全事件脱敏转换,但新增统一 Events payload allowlist。
- 如果后续目标偏“用户通知”而不是“source status propagation”,Account security event 可以作为 Knowledge 之后的低风险 second slice 备选。
不做:
- 不发布设备风控原始细节、IP 内部判断、接口调用原始日志或成本原始 authority。
P1R-7f:Content propagation
目标:
- 评估 BlockSavedEvent、export task、import task 中哪些应进入用户可见 SSE。
- 明确内部 followup trigger 与用户通知的分界。
不做:
- 不把正文保存的每次内部事件都推给 SSE。
- 不发布正文全文或 block content。
完成审批门槛
P1R-7 completed approval 必须另起任务,并至少具备:
- Events owner 自身 gate:route ownership、OpenAPI schema、payload sanitizer、idempotency、SSE replay、frontend fetch SSE。
- 至少覆盖 AI、Knowledge、Market、Account(Member)、Content 中被声明为 P1R source owner 的真实发布链路。
- 每个 owner 都有成功、重复幂等、临时失败重试、retry exhausted、dead-letter、敏感 payload fail-closed、跨 owner 依赖方向测试。
- PostgreSQL
_testFlyway / migration 证据覆盖新增 outbox 表和muse_unified_event。 - HTTP / MockMvc / SSE 或等价 focused E2E 证据证明当前用户只能看到同 tenant + ownerUserId 的 accepted 事件。
- 审计或日志证据证明事件发布链路可追踪、可排查、可回放。
- 用户明确批准从
needs_verification推进到completed。
风险
- 抽象过早:如果现在先做共享 publish 库,可能在第二个 owner 前抽错接口;因此先冻结合同和测试模板,等 AI + Knowledge 两个 owner 通过后再抽小型 support module。
- 事务风险:同事务内 outbox 插入如果靠捕获唯一约束异常处理,PostgreSQL 可能使事务不可继续;实现必须使用 insert-ignore 或先锁定事实行。
- Payload 风险:不同 owner 的“摘要”含义不同,必须逐 owner allowlist,不允许泛化 Map 透传。
- 完成状态风险:source propagation evidence 增加不等于 P1R-7 completed;completed 必须另有审批。
- Dirty baseline 风险:当前 worktree 已有 AI 草稿,后续执行前必须决定是修订沿用、拆分提交,还是用户批准后重置对应草稿;本审阅版不处理。P1R-7b 执行版修订并双 review PASS 前,当前 AI outbox / V17 / docs 草稿不得继续测试、提交、推送或作为完成证据。
待确认项
- 是否确认以本文作为新的 P1R-7 source owner propagation 全局主线,替代此前 AI-only 审阅版作为最高层方案。
- 是否确认 P1R-7b 仍从 AI terminal event 第一切片落地,但必须先修订执行版以符合本文全局规则。
- 是否确认 completed approval 另起任务,当前所有阶段只推进
needs_verification证据。
下一步
- 对本文进行 fresh spec compliance review。
- 对本文进行 fresh quality / feasibility review。
- 双 PASS 后修订
docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md,把 AI 第一切片执行版降级为全局方案下的 P1R-7b 子计划。 - P1R-7b 子计划双 review PASS 后,才允许处理当前 AI 草稿实现和后续 worker 任务。