13 KiB
13 KiB
P1R7d Market Source Owner Propagation 审阅版
结论
推荐进入 P1R-7d,但第一切片只覆盖 Market governance / source status 的用户可见通知,不覆盖 purchase、install、handoff、Account projection 同步或目标 owner 授权消费。
推荐方案:
- 选择
AdminMarketGovernanceServiceImpl已落库的muse_market_governance_action/muse_market_source_status_event作为 P1R-7d 第一批 source fact。 - 新增 Market 本域 Events publish outbox / worker,调用
EventsPublishApi,发布 Events 已声明的notification/governance_action。 - 不复用
MarketAccountProjectionOutboxService作为 Events publish outbox;它只表达 Market -> Member Account 投影同步状态。 - 不修改 OpenAPI、scanner 或 coverage report;不推进 Events / P1R-7 / Market completed。
flowchart LR
Admin["Admin governance command<br/>delist / recall"] --> Fact["Market source fact<br/>governance_action / source_status_event"]
Fact --> Outbox["Market events publish outbox<br/>queued / running / retryable / published / dead_letter"]
Outbox --> Worker["Market worker<br/>claim + retry + audit log"]
Worker --> Api["EventsPublishApi<br/>events-api only"]
Api --> Unified[("muse_unified_event<br/>notification / governance_action")]
Unified --> SSE["/app-api/muse/events<br/>publisher visible"]
本审阅版不实现代码,只冻结目标、非目标、取舍、影响范围、风险和验收标准。执行版、实现和提交必须在本审阅版 fresh spec review + fresh quality review 双 PASS 后再继续。
已验证事实
工作区与状态
- 正确 worktree:
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0。 - 分支:
dev/1.0.0。 - 当前本地与
origin/dev/1.0.0对齐到e55618f test(p1r): 收口 Knowledge 事件传播真实链路门禁。 - 受保护文件 diff 为空:
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
全局 P1R-7 边界
- P1R-7 全局审阅版建议阶段:
- P1R-7b:AI terminal event。
- P1R-7c:Knowledge source status / projection event。
- P1R-7d:Market lifecycle / account projection / governance event。
- P1R-7 completed approval 必须另起任务并由用户单独批准。
- P1R-7 全局执行版要求每个 owner 都必须 fresh implementer + fresh spec review + fresh quality / feasibility review。
- 全局合同要求 source owner 只能依赖
muse-module-events-api,Events server 不得反向依赖 AI / Knowledge / Market / Member / Content server。 - 当前 coverage summary 仍是
completedOperations=100、needsVerificationOperations=133、incompleteOperations=0、genericPersistenceOperations=0、ssePlaceholderOperations=0。 - Events
streamEvents与 Market 32 operations 均仍为dedicated / needs_verification。
Events 可复用合同
EventsPublishReqDTO已包含commandId、tenantId、ownerUserId、sourceOwner、sourceType、sourceId、sourceRevision、eventType、resourceType、resourceId、payloadSummary、emittedAt。EventsPublishServiceImpl先按commandId回放,再按 source tuple 回放。EventsPublishServiceImpl已声明事件类型包含notification。- Events OpenAPI 的
SSENotificationEvent.data.type已包含governance_action。 UnifiedEventMapper.selectVisibleEventsForOwner只返回accepted、同租户、同 owner、未删除、已到 visible time 的事件。
Market 当前事实模型
AdminMarketGovernanceServiceImpl.delistAsset在治理命令成功后写入muse_market_governance_action,字段包括actionId、previewId、assetId、actorUserId、ownerUserId、actionType、commandId、requestHash、status、revision、actionSnapshot、reason。AdminMarketGovernanceServiceImpl.recallAsset在治理命令成功后写入muse_market_governance_action,并额外写入muse_market_source_status_event,其中sourceStatus固定为合同状态如recalled,needs_recheck只写入actionPolicy/recheckReasons。MuseMarketGovernanceActionMapper支持按 tenant + asset 查询最新或列表,但没有 publish claim / retry / dead_letter 语义。MuseMarketSourceStatusEventMapper支持按sourceEventId或 tenant + asset 查询最新,但没有 publish claim / retry / dead_letter 语义。MarketAccountProjectionOutboxServiceImpl只有recordSynced/recordBlocked,职责是 Market -> Member Account projection 同步状态;当前没有领取 retryable outbox、重试成功、dead_letter 或 replay worker。MarketAccountProjectionProvider明确把 Account 查询读模型写入 member 表,失败时记录 Market 侧 blocked outbox 后抛错,防止伪成功。MarketHandoffServiceImpl保存 handoff token hash 和生命周期状态,但 token 明文只在创建响应中返回;后续 P1R-7d 第一切片不发布 handoff token 或授权详情。
推断
- Market P1R-7d 第一切片应优先选择 governance/source status,而不是 purchase/install/handoff。原因是 governance/source status 已有明确 publisher owner、source fact、actionId/sourceEventId 和安全摘要空间,且用户需要实时知道资产被下架或召回。
- 直接复用
muse_market_account_projection作为 Events publish outbox 会混淆职责:它记录 Account 投影同步,不记录 Events publish claim、lease、retry、published event 或 dead_letter。 notification/governance_action已在 Events 合同内,可避免本阶段修改 OpenAPI。- recall 比 delist 更适合作为第一条 source status 证据,因为它同时写 governance action 和 source status blocked 事实;但执行版仍可把 delist 作为同一事件族的正向治理通知。
假设
- P1R-7d 允许在 Market owner 内新增独立 Events publish outbox 表或等价状态表。
- P1R-7d 允许
muse-module-market-server新增对muse-module-events-api的直接依赖,但不允许依赖muse-module-events-server。 - Market publisher 是第一批通知的
ownerUserId;admin 操作者只作为actorUserId写入 Market fact / 安全摘要,不作为 SSE 可见 owner。 notification/governance_action的 payload 最小字段可以收敛为type、message、可选resourceRef、timestamp,不需要新增 OpenAPI 字段。
目标
- 证明 Market source owner 的真实治理事实可以通过 Market 本域 publish outbox / worker 发布到 Events。
- 让发布者用户可以通过统一 SSE 看到自己的资产治理通知。
- 建立 Market 与 Events 的单向依赖证据:Market server 依赖 events-api,不依赖 events-server;Events server 不反向依赖 Market server。
- 为后续 Market source propagation / Account projection / handoff 事件扩展保留清晰边界。
非目标
- 不把 Market 32 operations 从
needs_verification推进到completed。 - 不修改 Market / Events OpenAPI。
- 不修改 coverage scanner 或 coverage report。
- 不把 purchase、install、license、handoff token、authorization snapshot 或 Account projection 同步纳入第一切片。
- 不把
muse_market_account_projection复用为 Events publish outbox。 - 不发布授权快照、安装详情、handoff token、目标 owner 私有事实、治理 preview 原始 riskSummary、管理员内部备注原文或完整 actionSnapshot。
- 不让 Events server 查询 Market 表。
推荐方案
事件选择
第一批只允许:
| Market source fact | 触发入口 | Events eventType | notification type | SSE owner |
|---|---|---|---|---|
muse_market_governance_action(actionType=delist) |
adminDelistAsset |
notification |
governance_action |
asset.publisherId |
muse_market_governance_action(actionType=recall) |
adminRecallAsset |
notification |
governance_action |
asset.publisherId |
muse_market_source_status_event(sourceStatus=recalled) |
adminRecallAsset |
notification |
governance_action |
event.ownerUserId |
不把 previewGovernanceImpact 发布到 SSE。preview 只是管理员决策前的影响预览,不是用户必须实时感知的终态事实。
Payload allowlist
第一批 payload 只允许:
{
"type": "governance_action",
"message": "资产治理状态已更新",
"resourceRef": {
"resourceType": "market_asset",
"resourceId": 1001
},
"timestamp": "2026-06-07T12:00:00"
}
执行版可以把 message 细化为“资产已下架”或“资产已召回”,但不能放入管理员备注原文、治理证据、授权快照、handoff token、target owner 内部细节或错误堆栈。
Outbox 边界
推荐新增独立 Market Events publish outbox:
- source table:
muse_market_governance_action/muse_market_source_status_event。 - source tuple:
sourceOwner=market,sourceType=market_governance_action或market_source_status,sourceId=actionId/sourceEventId,sourceRevision=revision。 - commandId:稳定短幂等键,建议
market_evt:<sha256-32>。 - 状态机:
queued/running/retryable/published/dead_letter。 - claim:必须原子领取,具备 lease / retry / attempt ownership。
- 终态回写:必须防 stale worker 覆盖新 claim 终态。
- worker 默认关闭,配置启用后才 claim。
依赖边界
- Market server 可以依赖
muse-module-events-api。 - Market server 不得依赖
muse-module-events-server。 - Events server 不得依赖 Market server。
muse-server作为装配层可以同时包含 Market server 与 Events server。
关键取舍
- 选择 governance/source status,不选择 Account projection:前者是 Market source owner 的用户可见事实;后者是 Account 查询读模型同步状态,复用会制造职责混乱。
- 选择 publisher 可见,不选择 admin 可见:SSE app 侧事件面向资源 owner;管理员操作已由管理端审计和治理记录承载。
- 使用
notification/governance_action,不新增 event type:Events 合同已经声明该 subtype,足够承载第一批摘要。 - 第一切片不发布 handoff:handoff 涉及 token、授权摘要和目标 owner 消费,泄露面和跨 owner 行为更复杂,应后续单独设计。
影响范围
- 直接影响:
- Market governance/source status 事实发布链路。
- Market server 依赖 events-api。
- Market 本域 outbox / worker / migration / focused tests。
- Events unified event focused E2E。
- 间接影响:
- P1R-7 completed approval 的证据链更完整。
- 不影响:
- OpenAPI 合同。
- coverage scanner / coverage report。
- Market 32 operations completion status。
- Account projection 查询读模型。
- handoff token 和目标 owner 授权消费。
风险与兼容性
- 事件重复风险:同一治理动作 replay 不能创建多条 unified event;必须用 commandId 与 source tuple 双重幂等。
- 并发风险:worker terminal update 必须绑定本次 claim attempt,避免 stale worker 覆盖新 claim。
- 敏感信息风险:
reason、actionSnapshot、previewriskSummary和 target owner 细节不能进入 payload。 - 职责混淆风险:Account projection outbox 只能继续服务 Account 读模型同步,不参与 Events publish。
- 可见性风险:ownerUserId 必须是 publisher / source fact owner,不能使用 adminUserId,否则发布者无法通过 app SSE 看到通知。
- 兼容性:新增 outbox 与 worker 默认关闭,不改变现有 Market API 同步返回语义。
验收标准
审阅版通过后,执行版必须包含:
- Market outbox DO / Mapper / Service / Worker / 配置 / V19 migration 的文件边界。
AdminMarketGovernanceServiceImpl创建 governance/source status fact 后创建 outbox 的事务边界。- payload allowlist 与敏感字段拒绝策略。
- worker success / duplicate / retryable / rejected / blocked / dead_letter / stale claim tests。
- Market governance source fact -> Market outbox -> worker -> EventsPublishApi ->
muse_unified_event-> SSE visible focused E2E。 - dependency gate:Market server 有 events-api、无 events-server;Events server 无 Market server。
- V19 SQL 静态 gate 与 Flyway
_test。 - P1R mixed gates 继续确认 Events / AI / Knowledge 已有 source owner gates 不回退,Market 仍为
dedicated / needs_verification。 - coverage scanner 只在
/tmp隔离副本运行,真实 worktree protected diff 为空。
待确认项
- P1R-7d 第一切片是否确认只做 governance/source status,而不做 purchase/install/handoff。
notification/governance_action的 message 是否允许按动作区分为“资产已下架 / 资产已召回”,还是统一为“资产治理状态已更新”。- 如果 recall 同时产生 governance action 和 source status event,执行版是否只发布一条 governance action 通知,还是允许两条不同 sourceType 的通知。