oh-my-muse/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md

205 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# P1R7d Market Source Owner Propagation 审阅版
## 结论
推荐进入 P1R-7d但第一切片只覆盖 Market governance / source status 的用户可见通知,不覆盖 purchase、install、handoff、Account projection 同步或目标 owner 授权消费。
推荐方案:
1. 选择 `AdminMarketGovernanceServiceImpl` 已落库的 `muse_market_governance_action` / `muse_market_source_status_event` 作为 P1R-7d 第一批 source fact。
2. 新增 Market 本域 Events publish outbox / worker调用 `EventsPublishApi`,发布 Events 已声明的 `notification/governance_action`
3. 不复用 `MarketAccountProjectionOutboxService` 作为 Events publish outbox它只表达 Market -> Member Account 投影同步状态。
4. 不修改 OpenAPI、scanner 或 coverage report不推进 Events / P1R-7 / Market completed。
```mermaid
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.yaml`
- `docs/api-contracts/ai/openapi.yaml`
- `docs/api-contracts/knowledge/openapi.yaml`
- `docs/api-contracts/events/openapi.yaml`
- `muse-cloud/scripts/p1r-audit-api-coverage.py`
- `docs/superpowers/reports/p1r-api-coverage.json`
- `docs/superpowers/reports/p1r-api-coverage.md`
### 全局 P1R-7 边界
- P1R-7 全局审阅版建议阶段:
- P1R-7bAI terminal event。
- P1R-7cKnowledge source status / projection event。
- P1R-7dMarket 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 字段。
## 目标
1. 证明 Market source owner 的真实治理事实可以通过 Market 本域 publish outbox / worker 发布到 Events。
2. 让发布者用户可以通过统一 SSE 看到自己的资产治理通知。
3. 建立 Market 与 Events 的单向依赖证据Market server 依赖 events-api不依赖 events-serverEvents server 不反向依赖 Market server。
4. 为后续 Market source propagation / Account projection / handoff 事件扩展保留清晰边界。
## 非目标
1. 不把 Market 32 operations 从 `needs_verification` 推进到 `completed`
2. 不修改 Market / Events OpenAPI。
3. 不修改 coverage scanner 或 coverage report。
4. 不把 purchase、install、license、handoff token、authorization snapshot 或 Account projection 同步纳入第一切片。
5. 不把 `muse_market_account_projection` 复用为 Events publish outbox。
6. 不发布授权快照、安装详情、handoff token、目标 owner 私有事实、治理 preview 原始 riskSummary、管理员内部备注原文或完整 actionSnapshot。
7. 不让 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 只允许:
```json
{
"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 typeEvents 合同已经声明该 subtype足够承载第一批摘要。
- 第一切片不发布 handoffhandoff 涉及 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`、preview `riskSummary` 和 target owner 细节不能进入 payload。
- 职责混淆风险Account projection outbox 只能继续服务 Account 读模型同步,不参与 Events publish。
- 可见性风险ownerUserId 必须是 publisher / source fact owner不能使用 adminUserId否则发布者无法通过 app SSE 看到通知。
- 兼容性:新增 outbox 与 worker 默认关闭,不改变现有 Market API 同步返回语义。
## 验收标准
审阅版通过后,执行版必须包含:
1. Market outbox DO / Mapper / Service / Worker / 配置 / V19 migration 的文件边界。
2. `AdminMarketGovernanceServiceImpl` 创建 governance/source status fact 后创建 outbox 的事务边界。
3. payload allowlist 与敏感字段拒绝策略。
4. worker success / duplicate / retryable / rejected / blocked / dead_letter / stale claim tests。
5. Market governance source fact -> Market outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible focused E2E。
6. dependency gateMarket server 有 events-api、无 events-serverEvents server 无 Market server。
7. V19 SQL 静态 gate 与 Flyway `_test`
8. P1R mixed gates 继续确认 Events / AI / Knowledge 已有 source owner gates 不回退Market 仍为 `dedicated / needs_verification`
9. coverage scanner 只在 `/tmp` 隔离副本运行,真实 worktree protected diff 为空。
## 待确认项
1. P1R-7d 第一切片是否确认只做 governance/source status而不做 purchase/install/handoff。
2. `notification/governance_action` 的 message 是否允许按动作区分为“资产已下架 / 资产已召回”,还是统一为“资产治理状态已更新”。
3. 如果 recall 同时产生 governance action 和 source status event执行版是否只发布一条 governance action 通知,还是允许两条不同 sourceType 的通知。