oh-my-muse/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md
zizi 3a27e7ec2b test(p1r): 收口 Account Security Events completed approval 门禁
- approve only appListSecurityEvents and appGetSecurityEvent at operation level

- keep appAcknowledgeSecurityEvent needs_verification

- add HTTP+DB completed approval IT and update coverage gates/docs
2026-06-14 01:25:30 +08:00

30 KiB
Raw Blame History

P1R Account Security Events Completed Approval 执行版

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task only after fresh execution review double PASS and explicit user approval. Steps use checkbox syntax for tracking.

日期2026-06-13

结论

本执行版只锁定 Account 用户安全事件只读 2 个 operation-level completed approval

account:appListSecurityEvents
account:appGetSecurityEvent

本轮不把 account 加入 domain-level completed allowlist不推进 Account 33/33 completed不修改 OpenAPI不修改 Account 业务实现,不修改 SQL migration。account:appAcknowledgeSecurityEventsession_revoked 公开合同与当前实现不闭合,继续保持 needs_verification

获批后目标 coverage summary 为:

total=233
completed=147
needsVerification=86
incomplete=0
genericPersistence=0
ssePlaceholder=0

Account 域目标状态为:

account total=33
completed=12
needsVerification=21

当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。

flowchart TB
    Current["当前 Account<br/>10 completed / 23 needs_verification"] --> Exec["执行版<br/>锁定 Security Events read 2 ops"]
    Exec --> Review["fresh execution spec/scope review<br/>fresh execution quality/testing review"]
    Review --> Approval{"用户明确批准<br/>2 ops / operation-level / allowed diff / mixed gates"}
    Approval -->|否| Stay["保持当前 coverage<br/>233/145/88"]
    Approval -->|是| Red["TDD RED<br/>先改 gate 期望<br/>旧 report 必须失败"]
    Red --> Runtime["HTTP + real DB _test<br/>owner + tenant + latest ack + no-write"]
    Runtime --> Green["scanner operation allowlist<br/>regenerate report"]
    Green --> Verify["focused tests + P1R mixed gates<br/>XML 防空跑 + protected diff"]
    Verify --> FreshReview["fresh implementation review"]

当前事实

正确 worktree

/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0

当前 HEAD

304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁

当前 coverage summary

233	145	88	0	0	0

当前 domain 状态:

account  completed=10  needsVerification=23
content  completed=14  needsVerification=37
market   completed=4   needsVerification=28
meta     completed=16  needsVerification=0
events   completed=1   needsVerification=0

当前目标 operation 状态:

appListSecurityEvents  dedicated  needs_verification  false  GET  /app-api/muse/account/security-events
appGetSecurityEvent    dedicated  needs_verification  false  GET  /app-api/muse/account/security-events/{eventId}

必须保持 needs_verification 的相关写命令:

appAcknowledgeSecurityEvent  dedicated  needs_verification  true  POST  /app-api/muse/account/security-events/{eventId}/acknowledge

已验证的只读实现事实:

  • AppAccountSecurityController 使用 AccountApiVersionGuard.requireVersion(...)getLoginUserId()
  • AccountSecurityServiceImpl.appListSecurityEvents 按当前 owner + severity 查询安全事件,并合并最新 ack。
  • AccountSecurityServiceImpl.appGetSecurityEvent 按 eventId + owner 查询详情,并通过 AccountConvert 脱敏 IP / userAgent。
  • AccountSecurityServiceImpl.requireUser() 依赖 MemberUserMapper.selectByIdP1R sql/muse 当前 V1-V21 不创建 member_user
  • SecurityEventAcknowledgeReqVO 与 OpenAPI 当前包含 session_revoked,但 OpenAPI 文案承诺真实 session revoke当前实现只返回 fail-closed 文案;本执行版不推进 acknowledge。

执行边界

必须先获用户明确批准

实现前必须同时获得以下 4 项批准:

  1. 批准本轮只审批 account:appListSecurityEventsaccount:appGetSecurityEvent 两个 operation。
  2. 批准继续使用 operation-level approval不把 account 加入 domain-level completed allowlist。
  3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Security Events HTTP+DB _test、memory 和 .agent
  4. 批准同步 mixed gate 的 summary / Account / 非目标域防回退断言,目标为 233/147/86/0/0/0 与 Account 12 completed / 21 needs_verification

未获上述批准前,不得实施本执行版。

本轮允许变更

获批后只允许修改以下路径:

  • muse-cloud/scripts/p1r-audit-api-coverage.py
  • docs/superpowers/reports/p1r-api-coverage.json
  • docs/superpowers/reports/p1r-api-coverage.md
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountSecurityEventsCompletedApprovalIT.java
  • docs/agent-specs/.agent
  • docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md
  • docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md
  • docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md

AccountSecurityServiceTestAppAccountSecurityControllerTest 以及既有 mapper/convert focused tests 只允许作为验证命令和 XML 防空跑证据被运行、读取,不列入本轮可修改路径。如 implementation 发现必须修改 Account 业务实现、OpenAPI、SQL migration、既有 focused tests、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。

本轮禁止变更

  • 不修改 7 个 OpenAPI。
  • 不修改 Account main/java 业务实现来掩盖 coverage 缺口。
  • 不新增或修改 SQL migration。
  • 不把 account 加入 domain-level completed allowlist。
  • 不推进 Account 33/33 completed。
  • 不推进 account:appAcknowledgeSecurityEvent
  • 不推进 Account remaining 21、Market remaining 28、Content remaining 37 或总 P1R completed。
  • 不推进 New-API binding / recheck / quota request、FileService/export/download、call attribution、usage attribution、Market purchase/license/publish projection、integration-call 查询。
  • 不把安全事件读证据写成安全事件所有生产来源 completed。
  • 不通过 mock MemberUserMapper 或 production SQL 修改绕过 member_user 前置依赖。
  • 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。

审批清单

operation key Method Path 目标状态 完成证据边界
account:appListSecurityEvents GET /app-api/muse/account/security-events dedicated / completed MockMvc HTTP + 真实 PostgreSQL _test,覆盖 owner/tenant、severity、分页、latest ack、敏感字段不泄露、API version、read no-write
account:appGetSecurityEvent GET /app-api/muse/account/security-events/{eventId} dedicated / completed MockMvc HTTP + 真实 PostgreSQL _test,覆盖 detail 脱敏、跨 owner/跨 tenant/missing 不可见、非数字 eventId、ack 合并、read no-write

保持 needs_verification 的 Account operation

以下 21 个 operation 必须保持 dedicated / needs_verification

account:adminCreateCallAttributionJob
account:adminGetCallAttributionJob
account:adminGetIntegrationCallByCorrelation
account:adminListNewApiBindings
account:adminListPurchaseRecords
account:adminListUsageRecords
account:adminCreateNewApiBinding
account:adminCreateQuotaRequest
account:appDownloadExport
account:appCreateExportTask
account:appGetExportTask
account:appGetIntegrationCallByCorrelation
account:appListLicenses
account:getAppNewApiBinding
account:appRecheckNewApiBinding
account:appListPublishRecords
account:appListPurchases
account:appCreateQuotaRequest
account:appGetQuotaRequest
account:appAcknowledgeSecurityEvent
account:getAppUsage

证据分层

Evidence 文件 / 测试 目的
Operation inventory / coverage gate P1rApiCoverageReportTestP1rAccountRealApiGateTest 和 mixed gates 证明只推进获批 2 个 operation非目标 domain 不回退
Security Events HTTP+DB _test 新增 P1rAccountSecurityEventsCompletedApprovalIT 证明 2 个 read operation 经 MockMvc HTTP 入口访问真实 service / mapper / PostgreSQL _test 数据
Tenant / schema gate P1rAccountSecurityEventsCompletedApprovalIT 内验证 V1-V21 clean migrate 与 V2/V11 安全事件依赖 schema 证明安全事件表、ack 表、索引、trigger、JSONB 和 tenant 字段在当前 live migration baseline 下真实可用
Auth fixture gate P1rAccountSecurityEventsCompletedApprovalIT 内创建 test-local member_user fixture 满足 requireUser() 的真实 mapper 前置,但不计入 P1R schema evidence
App security context gate P1rAccountSecurityEventsCompletedApprovalIT 内设置 app LoginUser 与 tenant context 证明 owner 不能由请求参数覆盖,跨 owner / 跨 tenant 不可见
No-write gate P1rAccountSecurityEventsCompletedApprovalIT 对 read happy/error 前后做表快照 证明 list/detail 不写 security event、ack、command、audit 或 outbox
Focused unit/controller tests AccountSecurityServiceTestAppAccountSecurityControllerTest、mapper/convert tests 支撑 service/controller contract不替代 HTTP+DB _test

TDD / 实施步骤

Task 1Preflight 与 RED gate

  • 确认当前 worktree
git -c core.quotePath=false status --short --branch

期望:

## dev/1.0.0...origin/dev/1.0.0

允许存在本执行版和审阅版文档 diff不得存在 OpenAPI、业务实现、SQL、scanner、coverage report 或 gate test 的未授权 diff。

  • 确认当前 coverage
jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
jq -r '.operations[] | select(.domain=="account" and (.operationId=="appListSecurityEvents" or .operationId=="appGetSecurityEvent" or .operationId=="appAcknowledgeSecurityEvent")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring),.method,.path] | @tsv' docs/superpowers/reports/p1r-api-coverage.json

期望:

233	145	88	0	0	0
appListSecurityEvents	dedicated	needs_verification	false	GET	/app-api/muse/account/security-events
appGetSecurityEvent	dedicated	needs_verification	false	GET	/app-api/muse/account/security-events/{eventId}
appAcknowledgeSecurityEvent	dedicated	needs_verification	true	POST	/app-api/muse/account/security-events/{eventId}/acknowledge
  • TDD RED先修改 P1R gate 期望值和 2 个 read operation completed 断言,但不修改 scanner/report。

RED 目标:

  • P1rApiCoverageReportTest 期望 summary completed=147 / needsVerification=86
  • P1rAccountRealApiGateTest 期望 Account 12 completed / 21 needs_verification2 个 read operation completedappAcknowledgeSecurityEvent 继续 needs_verification
  • 7 个 mixed gate 中所有硬编码 Account 10/23 或 summary 145/88 的断言同步为 12/21147/86

RED 命令:

cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \
  -Dsurefire.failIfNoSpecifiedTests=false

期望BUILD FAILURE。失败点必须来自旧 report 仍是 233/145/88 或 2 个 read operation 仍为 needs_verification;不得来自编译错误或无关测试失败。

Task 2新增 Security Events HTTP+DB _test

  • 新增:
muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountSecurityEventsCompletedApprovalIT.java

测试上下文要求:

  • 使用 @SpringBootTest(webEnvironment = MOCK) + WebApplicationContext + MockMvcBuilders.webAppContextSetup(...)
  • 真实 /app-api/muse/** 前缀进入 AppAccountSecurityController
  • 导入 MuseWebAutoConfiguration,并提供 test-local ApiErrorLogCommonApi stub返回 CommonResult.success(true),满足 GlobalExceptionHandler 依赖。
  • 设置 app LoginUser 与 tenant context请求参数中的 userId 或其它伪 owner 参数不得改变 owner。
  • 使用真实 AccountSecurityServiceImpl、真实 mapper 和真实 PostgreSQL _test 数据库;不得 mock MemberSecurityEventMapperAccountSecurityEventAckMapper
  • MemberUserMapper 必须使用真实 mapper。由于 sql/muse 不包含 member_user,测试可以在隔离 _test 库完成 Flyway migrate 后创建 test-local member_user fixture 表并 seed 登录用户;该 fixture 只能作为 requireUser() auth 前置,不计入 P1R schema evidence不允许修改 production SQL。
  • test-local member_user DDL 必须覆盖 MemberUserDOTenantBaseDOBaseDO 当前映射列,至少包含:idmobilepasswordstatusregister_ipregister_terminallogin_iplogin_datenicknameavatarnamesexbirthdayarea_idmarkpointtag_idslevel_idexperiencegroup_idtenant_idcreatorcreate_timeupdaterupdate_timedeleted。不得只建 id/nickname 等最小列,也不得通过修改 mapper 或 production SQL 回避真实 MemberUserMapper.selectById
  • 注册真实 MyBatis tenant interceptorTenantLineInnerInterceptor + TenantDatabaseInterceptor(new TenantProperties())
  • TARGET_VERSION="21",执行 V1-V21 clean migrate 后再创建 test-local auth fixtureAccount 专属 P1rAccountEventsPublishFlywayMigrationIT 可以继续作为 V20 Account outbox 迁移证据复跑,但不能替代本 IT 的当前 live V21 baseline。
  • 数据库密码只能来自环境变量,禁止 JVM system property 或 JDBC query 参数传入。
  • 保存并在 @AfterAll 恢复 p1r.flyway.url / p1r.flyway.user system property避免污染同一 Surefire JVM 后续 Flyway 测试。

建议测试库:

muse_p1r_account_security_events_completed_approval_test

建议环境变量优先级:

P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD
P1R_ACCOUNT_COMPLETED_PASSWORD
P1R_FLYWAY_PASSWORD
MUSE_POSTGRES_PASSWORD

standalone 建库与运行前置:

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB=muse_p1r_account_security_events_completed_approval_test

PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB'" | grep -q 1 || \
PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -c "CREATE DATABASE $P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB"

必测矩阵:

  1. schema gate
    • V1-V21 clean migrate 成功,migrationsExecuted=21
    • muse_member_security_eventmuse_account_security_event_ack 存在。
    • muse_member_security_event 有 tenant_id、account_user_id、device_info JSONB、ip_address、acknowledged、acknowledged_at。
    • muse_account_security_event_ack 有 tenant_id、event_id、account_user_id、action、command_id、risk_summary JSONB。
    • idx_muse_member_security_usertrg_muse_member_security_updated_at 存在。
    • uk_muse_account_security_event_ack_commandidx_muse_account_security_event_ack_useridx_muse_account_security_event_ack_eventtrg_muse_account_security_event_ack_updated_at 存在。
    • test-local member_user fixture 表存在,覆盖真实实体映射列,并只用于 seed 登录用户。
  2. appListSecurityEvents
    • 同 tenant / same owner 返回 list。
    • severity filter 只返回目标严重度。
    • 只合并当前 owner 最新 ack。
    • 返回摘要不泄露原始 IP / device_info。
    • 跨 owner、跨 tenant 不可见。
    • 缺少 X-API-Version 不调用 service、不写库。
    • read no-write请求前后 muse_member_security_eventmuse_account_security_event_ackmuse_account_commandmuse_account_auditmuse_account_event_publish_outbox 全表快照一致。
    • no-write 快照必须使用 row_to_json(t)::text 级别内容比对并按稳定键排序,至少按 tenant_id, id 排序;不能退化成只比较 row count。
  3. appGetSecurityEvent
    • detail 返回脱敏 sourceIp、deviceInfo.userAgent。
    • 合并最新 ack 的 acknowledgedAt / acknowledgedAction。
    • missing、非数字 eventId、跨 owner、跨 tenant 都返回 not found 语义且不泄露事件存在性。
    • read no-write使用与 list 相同的 row_to_json(t)::text 全表快照集合。

最低 XML tests 数:建议 P1rAccountSecurityEventsCompletedApprovalIT >= 8。

Task 3TDD GREEN最小 scanner/report 状态推进

只修改 muse-cloud/scripts/p1r-audit-api-coverage.py

  • 不修改 APPROVED_COMPLETED_DOMAINS
  • 只向 APPROVED_COMPLETED_OPERATIONS 追加:
account:appListSecurityEvents
account:appGetSecurityEvent
  • 保持 completed 必须仍是 dedicated 的校验。

生成 report

python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check

预期 summary

total=233
completed=147
needsVerification=86
incomplete=0
genericPersistence=0
ssePlaceholder=0

预期 Account

completed=12
needsVerification=21

Task 4Account focused verification

运行 Account security focused tests

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB=muse_p1r_account_security_events_completed_approval_test
export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB}"

PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB'" | grep -q 1 || \
PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -c "CREATE DATABASE $P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB"

cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-module-member/muse-module-member-server,muse-server -am \
  -Dtest=AccountSecurityServiceTest,AppAccountSecurityControllerTest,AccountSecurityEventAckMapperTest,MemberSecurityEventMapperTest,AccountConvertTest,P1rAccountSecurityEventsCompletedApprovalIT,P1rAccountRealApiGateTest,P1rApiCoverageReportTest \
  -Dflyway.postgresql.transactional.lock=false \
  -Dp1r.flyway.locations=filesystem:sql/muse \
  -Dp1r.flyway.url="$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL" \
  -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
  -Dp1r.account.security-events.completed-approval.jdbc-url="$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL" \
  -Dp1r.account.security-events.completed-approval.jdbc-user="$MUSE_POSTGRES_USERNAME" \
  -Dsurefire.failIfNoSpecifiedTests=false

必须读取以下 XML 并检查 tests>0failures=0errors=0skipped=0

Test class 最低 tests
AccountSecurityServiceTest 8
AppAccountSecurityControllerTest 6
AccountSecurityEventAckMapperTest 4
MemberSecurityEventMapperTest 2
AccountConvertTest 8
P1rAccountSecurityEventsCompletedApprovalIT 8
P1rAccountRealApiGateTest 5
P1rApiCoverageReportTest 6

AccountSecurityServiceTestAccountSecurityEventAckMapperTest 中涉及 ack 或 session_revoked 的既有断言,只作为现状回归和 XML 防空跑证据,不计入本轮 completed evidenceappAcknowledgeSecurityEvent 仍必须保持 needs_verification

如实现修改了 Account gate 测试数量XML 检查的最低 tests 可高于上表,但不能低于上表。

建议使用以下 XML 防空跑脚本检查 Task 4、Task 5 和 Task 6 的目标 XML

python3 - <<'PY'
from pathlib import Path
import sys
import xml.etree.ElementTree as ET

expected = {
    "TEST-cn.iocoder.muse.module.member.application.account.AccountSecurityServiceTest.xml": 8,
    "TEST-cn.iocoder.muse.module.member.controller.app.account.AppAccountSecurityControllerTest.xml": 6,
    "TEST-cn.iocoder.muse.module.member.dal.mysql.account.AccountSecurityEventAckMapperTest.xml": 4,
    "TEST-cn.iocoder.muse.module.member.dal.mysql.account.MemberSecurityEventMapperTest.xml": 2,
    "TEST-cn.iocoder.muse.module.member.convert.account.AccountConvertTest.xml": 8,
    "TEST-cn.iocoder.muse.server.framework.api.P1rAccountSecurityEventsCompletedApprovalIT.xml": 8,
    "TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishFlywayMigrationIT.xml": 4,
    "TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml": 6,
    "TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml": 5,
    "TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml": 5,
    "TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml": 7,
    "TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeRealApiGateTest.xml": 8,
    "TEST-cn.iocoder.muse.server.framework.api.P1rMarketRealApiGateTest.xml": 6,
    "TEST-cn.iocoder.muse.server.framework.api.P1rContentRealApiGateTest.xml": 5,
    "TEST-cn.iocoder.muse.server.framework.api.P1rMetaRealApiGateTest.xml": 4,
}

roots = [
    Path("muse-module-member/muse-module-member-server/target/surefire-reports"),
    Path("muse-server/target/surefire-reports"),
]
failures = []
for name, minimum in expected.items():
    xml_path = next((root / name for root in roots if (root / name).exists()), None)
    if xml_path is None:
        failures.append(f"missing {name}")
        continue
    suite = ET.parse(xml_path).getroot()
    tests = int(suite.attrib.get("tests", "0"))
    failed = int(suite.attrib.get("failures", "0"))
    errors = int(suite.attrib.get("errors", "0"))
    skipped = int(suite.attrib.get("skipped", "0"))
    if tests < minimum or failed or errors or skipped:
        failures.append(f"{name}: tests={tests}, failures={failed}, errors={errors}, skipped={skipped}, minimum={minimum}")

if failures:
    print("\n".join(failures))
    sys.exit(1)
print("P1R Account Security Events XML gate passed")
PY

Task 5Account Flyway _test gate

fresh rerun P1rAccountEventsPublishFlywayMigrationIT。使用独立 _test 库,不能使用生产库或非 _test 库:

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_ACCOUNT_TEST_DB=muse_p1r_account_security_events_flyway_test

PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_ACCOUNT_TEST_DB'" | grep -q 1 || \
PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -c "CREATE DATABASE $P1R_ACCOUNT_TEST_DB"

cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
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/$P1R_ACCOUNT_TEST_DB" \
  -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
  -Djava.net.useSystemProxies=false \
  -DsocksProxyHost= -DsocksProxyPort= \
  -Dhttp.proxyHost= -Dhttp.proxyPort= \
  -Dhttps.proxyHost= -Dhttps.proxyPort= \
  -Dsurefire.failIfNoSpecifiedTests=false

必须检查 XML

Test class 最低 tests
P1rAccountEventsPublishFlywayMigrationIT 4

Task 6P1R mixed gate

运行 mixed gates

cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \
  -Dsurefire.failIfNoSpecifiedTests=false

必须读取以下 XML

Test class 最低 tests
P1rApiCoverageReportTest 6
P1rAccountRealApiGateTest 5
P1rEventsRealApiGateTest 5
P1rAiRealApiGateTest 7
P1rKnowledgeRealApiGateTest 8
P1rMarketRealApiGateTest 6
P1rContentRealApiGateTest 5
P1rMetaRealApiGateTest 4

每个 XML 均必须 failures=0errors=0skipped=0

Task 7报告与 diff gate

检查 summary

jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json

预期:

233	147	86	0	0	0

检查 Account 12/21

python3 - <<'PY'
import json
from collections import Counter
from pathlib import Path
data = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text())
counter = Counter(op["completionStatus"] for op in data["operations"] if op["domain"] == "account")
print(counter)
PY

预期:

Counter({'needs_verification': 21, 'completed': 12})

检查目标 operation

jq -r '.operations[] | select(.domain=="account" and (.operationId=="appListSecurityEvents" or .operationId=="appGetSecurityEvent" or .operationId=="appAcknowledgeSecurityEvent")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring)] | @tsv' docs/superpowers/reports/p1r-api-coverage.json

预期:

appListSecurityEvents	dedicated	completed	false
appGetSecurityEvent	dedicated	completed	false
appAcknowledgeSecurityEvent	dedicated	needs_verification	true

检查 OpenAPI protected diff

git diff --quiet -- docs/api-contracts
git diff --cached --quiet -- docs/api-contracts

检查 business implementation 不被修改:

git diff --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java
git diff --cached --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java

检查 SQL migration 不被修改:

git diff --quiet -- muse-cloud/sql/muse
git diff --cached --quiet -- muse-cloud/sql/muse

检查 whitespace

git diff --check

检查 allowed diff

git -c core.quotePath=false status --short

输出只能包含本执行版允许路径。若出现 OpenAPI、业务实现、迁移或非批准 gate 文件,必须停止。

Task 8memory 与 .agent

实现通过后新增:

docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md

内容必须记录:

  • 批准的 2 个 Account operation。
  • 保持 needs_verification 的 21 个 Account operation 及原因。
  • appAcknowledgeSecurityEvent 暂缓原因:session_revoked 合同与实现不闭合。
  • scanner/report/gate 修改范围。
  • 所有验证命令、结果、XML 计数、Flyway _test 库名。
  • protected diff 与 allowed diff 结果。
  • 明确说明不代表 Account 33/33 completed不代表 Market/Content/总 P1R completed。

同时更新 docs/agent-specs/.agent,但只能记录事实,不写未验证结论。

Task 9fresh implementation review

实现、验证和文档留痕后,必须派发两类 fresh reviewer

  • spec/correctness reviewer检查 2 个 operation 是否严格匹配本执行版21 个 operation 是否仍 needs_verificationAccount domain allowlist 是否未新增。
  • quality/data-integrity/testing reviewer检查 HTTP+DB _test、test-local auth fixture、scanner/report/gate、XML 防空跑、Flyway _test、protected diff、allowed diff、rollback 是否可靠。

双 PASS 前不得提交、push 或宣称 Account Security Events completed approval 已收口。

回滚策略

如实现后需要撤回:

  1. APPROVED_COMPLETED_OPERATIONS 移除 2 个 account:* key。
  2. 重新运行:
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
  1. 恢复 gate 期望:
summary completed=145
summary needsVerification=88
account completed=10
account needsVerification=23
  1. 删除或修订本轮 memory保留失败原因。
  2. 重新运行 Account/P1R focused gates 和 git diff --check

验收标准

执行版本身可以进入实现前批准点的条件:

  1. 本文件已写入 docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md
  2. .agent 记录执行版当前状态。
  3. git diff --check 通过。
  4. OpenAPI、scanner、coverage report、业务实现、SQL migration 当前无新增 diff。
  5. fresh execution spec/scope review PASS。
  6. fresh execution quality/feasibility/testing review PASS。

实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。