14 KiB
P1R7e Account Source Owner Propagation 真实链路
日期:2026-06-07
结论
P1R-7e Account/Member source owner propagation 主体实现、V20 SQL、Member focused tests、P1R Account Events publish gate tests、dependency tree gate 与 protected diff gate 已形成 needs_verification evidence。
当前仍不能写 completed。本轮证据只代表 Account quota adjustment terminal fact -> Account publish outbox -> Account worker -> EventsPublishApi -> muse_unified_event -> /app-api/muse/events 对 accountUserId 可见的链路推进到证据层,不代表 Events / P1R-7 / Account completed。
本轮未修改 OpenAPI、coverage scanner 或真实 coverage 报告。
已验证事实
工作区与保护文件
- 正确 worktree:
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0。 - 分支:
dev/1.0.0。 - 最新 migration 前置检查:当前 V19 后新增 V20,未发现既有 V20+。
- 受保护文件最终 staged / unstaged diff 均为 exit 0:
docs/api-contracts/account/openapi.yamldocs/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
本轮实现范围
- Member server 新增直接依赖
muse-module-events-api。 - 新增 Account 本域 publish outbox:
AccountEventPublishOutboxDOAccountEventPublishOutboxMapperAccountEventPublishOutboxServiceAccountEventPublishOutboxServiceImplAccountEventPayloadsAccountEventPublishWorkerMuseAccountEventsPropertiesMuseAccountEventsConfiguration
AccountQuotaServiceImpl.adminCreateQuotaAdjustment在每条muse_member_entitlement_audit_log(change_type=quota_adjustment)insert 后,同事务创建 Account Events publish outbox。- command replay 不补历史 outbox。
- 新增 V20 migration:
muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql。
合同边界
- 每条 entitlement audit log 对应一条 outbox,不做 command 聚合,不做历史 backfill。
- Events source tuple:
sourceOwner=accountsourceType=account_quota_adjustmentsourceId=auditLogIdsourceRevision=__none__eventType=notification
- notification type 固定为
quota_alert。 sourceCommandId优先使用原始reqVO.getCommandId();历史 fallback 才从idempotencyKey.lastIndexOf(':')解析。- payload exact allowlist:
- 顶层只允许
type/message/resourceRef/timestamp。 resourceRef只允许resourceType/resourceId。message固定为额度已调整。resourceRef.resourceId保持 Number/Long 语义,值为 entitlement audit log id。EventsPublishReqDTO.resourceId仍按 Events DTO 合同使用 String。
- 顶层只允许
- 禁止发布 requestHash、reason 原文、operatorUserId、before/after snapshot、correlationId、external detail、error stack、credential。
- timestamp 优先使用 audit log
createTime,缺失时回退 outboxcreateTime。 - worker 默认配置关闭:
muse.account.events.publish-worker.enabled=false。 - 终态回写使用
publish_status='running' AND attempt_count=#{claimedAttemptCount}防止过期 claim 覆盖新终态;终态更新 0 行会记录 stale claim 诊断日志。
验证证据
Member focused tests
执行:
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-member/muse-module-member-server -Dtest=AccountEventPublishOutboxServiceTest,AccountEventPublishWorkerTest,AccountQuotaServiceTest -Dsurefire.failIfNoSpecifiedTests=false test
结果:exit 0,36 tests pass,failures/errors/skipped 均为 0。
Surefire XML:
| 测试类 | tests | failures | errors | skipped |
|---|---|---|---|---|
AccountQuotaServiceTest |
17 | 0 | 0 | 0 |
AccountEventPublishOutboxServiceTest |
8 | 0 | 0 | 0 |
AccountEventPublishWorkerTest |
11 | 0 | 0 | 0 |
覆盖点:
- quota adjustment 首次成功路径为每条 entitlement audit log 创建 outbox。
- command replay 不创建 outbox。
- 多 resourceType 产生多条不同 outbox。
- invalid owner / missing audit log id / 非 quota_adjustment 不创建 outbox。
- payload exact allowlist 与敏感字段负断言。
resourceRef.resourceId是数值型 audit log id。- configured
maxAttempt生效。 - timestamp 使用 audit log
createTime,缺失时回退 outboxcreateTime。 - worker disabled 不 claim。
- accepted / duplicate accepted -> published。
- rejected / blocked -> dead_letter。
- CommonResult error / exception -> retryable 或 retry exhausted dead_letter。
- invalid owner / payload invalid -> dead_letter 且不调用 EventsPublishApi。
- terminal update 具备 claim attempt ownership guard。
- stale terminal update 记录诊断日志。
P1R mixed executable gates
执行:
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \
-pl muse-server -am \
-Dtest=P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \
-Dsurefire.failIfNoSpecifiedTests=false
结果:exit 0,73 tests pass,failures/errors/skipped 均为 0。
Surefire XML:
| 测试类 | tests | failures | errors | skipped |
|---|---|---|---|---|
P1rAccountEventsPublishMigrationSqlTest |
3 | 0 | 0 | 0 |
P1rAccountEventsPublishDependencyTest |
2 | 0 | 0 | 0 |
P1rAccountEventsPublishEndToEndTest |
4 | 0 | 0 | 0 |
P1rAccountRealApiGateTest |
4 | 0 | 0 | 0 |
P1rAccountRouteOwnershipTest |
4 | 0 | 0 | 0 |
P1rMarketEventsPublishMigrationSqlTest |
3 | 0 | 0 | 0 |
P1rMarketEventsPublishDependencyTest |
2 | 0 | 0 | 0 |
P1rMarketEventsPublishEndToEndTest |
4 | 0 | 0 | 0 |
P1rKnowledgeEventsPublishMigrationSqlTest |
3 | 0 | 0 | 0 |
P1rKnowledgeEventsPublishDependencyTest |
2 | 0 | 0 | 0 |
P1rKnowledgeEventsPublishEndToEndTest |
5 | 0 | 0 | 0 |
P1rAiEventsPublishMigrationSqlTest |
4 | 0 | 0 | 0 |
P1rAiEventsPublishDependencyTest |
2 | 0 | 0 | 0 |
P1rAiEventsPublishEndToEndTest |
4 | 0 | 0 | 0 |
P1rApiCoverageReportTest |
5 | 0 | 0 | 0 |
P1rEventsRealApiGateTest |
5 | 0 | 0 | 0 |
P1rEventsRouteOwnershipTest |
4 | 0 | 0 | 0 |
P1rAiRealApiGateTest |
6 | 0 | 0 | 0 |
P1rAiRouteOwnershipTest |
7 | 0 | 0 | 0 |
覆盖点:
- Member server 直接依赖
muse-module-events-api,不依赖muse-module-events-server。 - Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。
- V20 SQL 静态断言覆盖字段、唯一约束、check 约束、索引、trigger、BaseDO/TenantBaseDO 审计列、敏感列和外键禁用。
- focused E2E 证明 Account worker 调用真实
EventsPublishServiceImpl后,quota_alert只对accountUserId可见,admin operator 不可见,非 owner 不可见。 - 多条 quota alert 对同一 owner 可见。
- rejected / dead_letter 后 SSE visible query 为空。
- stale claim terminal update attempt mismatch 不覆盖当前 running outbox。
- 同时回归 Account 真实 API gate、Account route ownership gate、AI/Knowledge/Market/Events 既有 P1R gates 与 coverage report gate。
Full reactor build
执行:
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true
结果:exit 0,日志保留在 /tmp/p1r7e-full-reactor-20260608022955.log;机器统计 reactor summary 为 62 个 SUCCESS,包含 BUILD SUCCESS,Finished at 2026-06-08T02:30:46+08:00。
Surefire XML 计数核对
clean build 后已重新运行 focused tests、P1R mixed executable gates 与 Flyway _test,以保留最终 surefire XML。
结果:Account focused + P1R mixed 目标 XML 合计 109 tests,failures=0、errors=0、skipped=0;Flyway _test XML 单独保留 4/4,failures=0、errors=0、skipped=0。
V20 Flyway _test
准备:
source ~/.config/muse-repo/infra.env
psql "host=$MUSE_POSTGRES_HOST port=$MUSE_POSTGRES_PORT user=$MUSE_POSTGRES_USERNAME dbname=postgres" -c 'CREATE DATABASE muse_p1r7e_account_events_publish_test'
已验证 muse_p1r7e_account_events_publish_test 存在。
执行:
source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am \
-Dtest=P1rAccountEventsPublishFlywayMigrationIT \
-Dflyway.postgresql.transactional.lock=false \
-Dp1r.flyway.locations=filesystem:sql/muse \
-Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/muse_p1r7e_account_events_publish_test" \
-Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
-Djava.net.useSystemProxies=false \
-DsocksProxyHost= \
-DsocksProxyPort= \
-Dhttp.proxyHost= \
-Dhttp.proxyPort= \
-Dhttps.proxyHost= \
-Dhttps.proxyPort= \
-Dsurefire.failIfNoSpecifiedTests=false
结果:exit 0,P1rAccountEventsPublishFlywayMigrationIT 4 tests pass,failures/errors/skipped 均为 0。
补证据:clean build 后已重新运行该 Flyway IT,muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishFlywayMigrationIT.xml 已保留,机器校验为 tests=4、failures=0、errors=0、skipped=0。
关键输出:
flyway_success=true
flyway_url=jdbc:postgresql://<host>/muse_p1r7e_account_events_publish_test
migrations_executed=20
successful_migration_count=20
target_schema_version=20
flyway_latest=20:extend account events publish outbox
v20_table=muse_account_event_publish_outbox
v20_indexes=idx_muse_account_event_publish_outbox_claim,idx_muse_account_event_publish_outbox_owner_status,idx_muse_account_event_publish_outbox_audit_log
v20_triggers=trg_muse_account_event_publish_outbox_update_time
Flyway IT 覆盖:
- 只允许
_test数据库。 - 拒绝 JDBC URL 携带 credential query。
- V1-V20 clean migrate。
- V20 表、索引、约束、trigger 存在。
event_type、notification_type、publish_status、account_user_id、audit_log_id/resource_ref_id非法插入会被数据库拒绝。
Dependency tree gate
执行:
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-member/muse-module-member-server -DskipTests dependency:tree > /tmp/p1r7e-member-server-dependency-tree.txt
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-events/muse-module-events-server -DskipTests dependency:tree > /tmp/p1r7e-events-server-dependency-tree.txt
结果:
- Member server tree 命中
muse-module-events-api。 - Member server tree 未命中
muse-module-events-server。 - Events server tree 对 AI / Knowledge / Market / Member / Content server 无命中输出。
- 命令输出
dependency_gate=pass。
Isolated coverage scanner
执行范围:只在 /tmp/p1r7e-coverage-scan.Ozo9Xm 隔离副本运行 scanner,真实 worktree 的 docs/superpowers/reports/* 未被修改。
执行:
tmpdir=$(mktemp -d /tmp/p1r7e-coverage-scan.XXXXXX)
rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/
cd "$tmpdir"
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
结果:exit 0。
summary:
{
"totalOperations": 233,
"completedOperations": 100,
"needsVerificationOperations": 133,
"incompleteOperations": 0,
"genericPersistenceOperations": 0,
"ssePlaceholderOperations": 0
}
边界核对:
- Events
streamEvents=dedicated / needs_verification。 - Account 33 operations 均为
dedicated / needs_verification。 - 未推进 completed。
Diff gates
执行:
git diff --check
git diff --quiet -- docs/api-contracts/account/openapi.yaml 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
git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml 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
结果:
git diff --checkexit 0。- protected unstaged diff exit 0。
- protected staged diff exit 0。
- protected 文件 diff name-only 为空。
风险与后续
- 早前 implementer 用
127.0.0.1:5432/muse_p1r7e_events_publish_test运行 Flyway IT 失败,原因是本地 PostgreSQL 服务不可达;该旧失败已被 controller 使用~/.config/muse-repo/infra.env和muse_p1r7e_account_events_publish_test真实_test库的 4/4 PASS 覆盖。 - 本轮没有修改 OpenAPI、coverage scanner 或真实 coverage report,也没有推进 completed。
- focused E2E 是内存 mapper + 真实
EventsPublishServiceImpl,不是 HTTP/SSE + 真实 DB 全链路;controller 后续可补真实 controller/SSE 验证。 - 最终 fresh implementation review 已双 PASS:Banach correctness/spec PASS、Raman quality/data-integrity/testing PASS,均无 P0/P1/P2/P3 blocker。
- 双 PASS 只代表 P1R-7e Account quota adjustment source owner propagation evidence 可以进入提交整理,不代表 Events/P1R-7/Account completed。
- 当前未 push;提交整理仍不得推进 completed。