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
This commit is contained in:
parent
304f665660
commit
3a27e7ec2b
@ -231,3 +231,15 @@ P1R Content Admin RiskAction Completed Approval 执行版 fresh review 进展:
|
||||
P1R Content Admin RiskAction Completed Approval 执行版 fresh re-review 已双 PASS:Peirce execution spec/scope re-review PASS,确认最新执行版仍只覆盖 `content:adminRiskAction` 一个 operation-level completed approval,不推进 Content domain-level completed,不夹带 admin import/export task、Content CRUD 写命令、OpenAPI、Content main/java 或 SQL migration,目标 `233/145/88` 与 Content `14/37` 可由当前 `233/144/89` 与 Content `13/38` 加 1 个 operation 机械推导;补充的 harness bean set、command/governance 行级断言和 standalone/combined `_test` 建库命令都仍落在测试与门禁范围内。Arendt execution quality/feasibility/testing re-review PASS,确认上一轮 3 项 FAIL 已关闭:执行版已补 exact admin harness bean set、command/audit row + snapshot gate、governance action row fact、standalone / combined `_test` DB 准备、Flyway system property restore、XML 防空跑和 V1-V21 schema gate;未发现会导致 fake pass 或启动失败的 P0/P1/P2/P3 blocker。该双 PASS 只代表执行版可进入用户批准点,不代表 `content:adminRiskAction` completed;下一步必须由用户明确批准四项执行边界后才允许 implementation,并且 implementation 后仍需 fresh implementation 双 review。
|
||||
|
||||
P1R Content Admin RiskAction Completed Approval implementation 当前状态:用户已批准执行版四项前置条件,本轮只把 `content:adminRiskAction` 一个治理写命令加入 operation-level completed approval,未把 `content` 加入 domain-level allowlist,未修改 OpenAPI、Content 业务实现或 SQL migration。scanner 仍保持 `APPROVED_COMPLETED_DOMAINS={"ai","knowledge"}`,只追加 `content:adminRiskAction`;coverage summary 已推进为 `233/145/88/0/0/0`,Content 为 `14 completed / 37 needs_verification`。admin import/export task、Content CRUD 写命令、AI planning candidate、style check、Meta projection、FileService、import/export/parse operation 仍保持 `needs_verification`。TDD RED 已验证旧 report 下 focused P1R gates 因目标 summary 仍为 144、`adminRiskAction` 仍为 `needs_verification` 而失败;GREEN 后 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 通过并重生成 report。新增 `P1rContentAdminRiskActionCompletedApprovalIT` 使用 MockMvc HTTP 入口、真实 `AdminContentController` / `ContentAdminServiceImpl` / `ContentCommandServiceImpl` / `ContentAuditServiceImpl` / mapper、method security + test-local `ss` bean 与 PostgreSQL `_test` 库,单类验证 `8/8 pass`,覆盖安全配置拒绝、V1-V21 schema、`target_ids` JSONB、duplicate command SQLState 23505、`FOR UPDATE` 行锁合同、API version、RBAC、Bean Validation no-write、work/chapter/block happy path、status、replay、command conflict、revision conflict、unsupported action/scope、invalid targetIds、cross-work/cross-tenant target、missing/cross tenant work、command/governance 行级事实、command/audit snapshot 和全表 no-write / rollback。Content focused `ContentAdminServiceTest,AdminContentControllerTest` fresh 运行 `21/21 pass`;Content combined DB gate `P1rContentAdminRiskActionCompletedApprovalIT,P1rContentAdminReadCompletedApprovalIT,P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT` 使用 `_test` 库 `muse_p1r_content_admin_risk_action_combined_test` fresh 运行 `40/40 pass`;P1R mixed gates fresh 运行 `49/49 pass`;XML 防空跑覆盖 15 个目标类全部 tests>0 且 failures/errors/skipped=0;`git diff --check` 通过;protected OpenAPI diff、Content main/java diff、SQL migration diff 均为空。fresh implementation review 已双 PASS:Gauss scope review PASS、Lagrange data-integrity/testing review PASS,均无 findings。新增 memory:`docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md`。当前具备提交候选条件,但仍不代表 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
P1R Account Security Events Completed Approval 已启动并按 testing review 修订审阅版与执行版:`docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md`、`docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md`。首轮 scope review(Laplace)PASS,但 testing review(Dalton)FAIL;有效阻塞项为 `appAcknowledgeSecurityEvent` 的 `session_revoked` OpenAPI 合同仍承诺真实 session revoke,而当前实现仅返回“未接入 session 管理服务”的 fail-closed 文案,且 HTTP+DB `_test` 需要明确 `MemberUserMapper.selectById` 依赖的 `member_user` 表 fixture。已修订为本轮候选只覆盖 `account:appListSecurityEvents`、`account:appGetSecurityEvent` 两个 read operation-level completed approval;`account:appAcknowledgeSecurityEvent` 继续 `dedicated/needs_verification`。不把 `account` 加入 domain-level completed allowlist,不推进 Account 33/33,不修改 OpenAPI、scanner、coverage report、Account 业务实现或 SQL migration。当前 coverage 仍为 `233/145/88/0/0/0`,Account 仍为 `10 completed / 23 needs_verification`;执行版目标仅作为获批后目标:`233/147/86/0/0/0`,Account `12 completed / 21 needs_verification`。执行版明确允许在隔离 `_test` 库创建 test-local `member_user` auth fixture 满足 `requireUser()`,但不计入 P1R schema evidence、不修改 production SQL、不允许 mock 目标 mapper。下一步必须重新派发 fresh execution spec/scope review + fresh execution quality/feasibility/testing review;双 PASS 前不得实现、提交、push 或推进 Account completed。
|
||||
|
||||
P1R Account Security Events Completed Approval 执行版 review 收到新的 scope P1 并已修订:Kepler fresh scope review FAIL,唯一有效问题是执行版 allowed diff 把既有 `AccountSecurityServiceTest` 与 `AppAccountSecurityControllerTest` 列成可修改路径,超出本轮最小 scope。已按建议收紧执行版 allowed diff:既有 focused tests、mapper/convert tests 只允许作为验证命令和 XML 防空跑证据被运行、读取,不列入本轮可修改路径;如 implementation 必须改既有 focused tests、Account 业务实现、OpenAPI、SQL migration、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。Kepler FAIL 之前的 PASS 结论和任何基于旧 allowed diff 的 PASS 均不能作为最终 gate;下一步必须基于收紧后的执行版重新派发 fresh execution spec/scope review 与 fresh quality/feasibility/testing review。当前仍只改文档,未修改 scanner、coverage report、OpenAPI、Account 业务实现、SQL migration 或 gate test,coverage 仍为 `233/145/88/0/0/0`。
|
||||
|
||||
P1R Account Security Events Completed Approval 执行版 fresh testing review(Planck)FAIL,两个 P1 已验证有效并修订:第一,新 `P1rAccountSecurityEventsCompletedApprovalIT` 原固定 `TARGET_VERSION="20"` / V1-V20 clean migrate,但当前 `sql/muse` live baseline 已到 `V21__extend_content_events_publish_outbox.sql`,因此执行版与审阅版已改为新 HTTP+DB IT 必须 `TARGET_VERSION="21"`、V1-V21 clean migrate、`migrationsExecuted=21`;Account 专属 `P1rAccountEventsPublishFlywayMigrationIT` 可继续作为 V20 Account outbox 迁移证据复跑,但不能替代新 IT 的 V21 baseline。第二,test-local `member_user` fixture 只有原则说明,缺可执行 DDL 约束;已补要求 fixture 在隔离 `_test` 库 Flyway migrate 后创建,DDL 至少覆盖 `MemberUserDO`、`TenantBaseDO`、`BaseDO` 当前映射列,包括 `id/mobile/password/status/register_ip/register_terminal/login_ip/login_date/nickname/avatar/name/sex/birthday/area_id/mark/point/tag_ids/level_id/experience/group_id/tenant_id/creator/create_time/updater/update_time/deleted`,不得只建最小列、不得 mock `MemberUserMapper`、不得修改 production SQL。Planck FAIL 和此前任何基于旧 V20/fixture 口径的 PASS 均不能作为最终 gate;下一步必须基于最新版重新派发 fresh execution spec/scope review 与 fresh quality/feasibility/testing review。当前仍只改文档,未修改 scanner、coverage report、OpenAPI、Account 业务实现、SQL migration 或 gate test。
|
||||
|
||||
P1R Account Security Events Completed Approval 执行版 fresh testing review(Anscombe)FAIL,1 个 P1 与 2 个 P2 已验证并修订:P1 为 Task 4 focused verification 会运行新增 `P1rAccountSecurityEventsCompletedApprovalIT`,但命令未传 `_test` JDBC、`p1r.flyway.url/user/locations`,会导致新 IT 缺真实 PostgreSQL `_test` 与 V1-V21 migrate 前置;已补 Task 4 命令,复用 `muse_p1r_account_security_events_completed_approval_test`,导出 `P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL`,创建/复用 `_test` 库,并向 Maven 传 `-Dp1r.flyway.locations=filesystem:sql/muse`、`-Dp1r.flyway.url`、`-Dp1r.flyway.user`、新 IT 专属 JDBC user/url 属性,密码仍只来自环境变量。P2 已补具体 schema 对象名:`idx_muse_member_security_user`、`trg_muse_member_security_updated_at`、`uk_muse_account_security_event_ack_command`、`idx_muse_account_security_event_ack_user`、`idx_muse_account_security_event_ack_event`、`trg_muse_account_security_event_ack_updated_at`。P2 已补 no-write gate 必须对 `muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_command`、`muse_account_audit`、`muse_account_event_publish_outbox` 使用 `row_to_json(t)::text` 全表快照并按稳定键排序,不能只比较 row count。Anscombe FAIL 和此前任何基于旧 Task 4 命令的 PASS 均不能作为最终 gate;下一步必须基于最新版重新派发 fresh execution spec/scope review 与 fresh quality/feasibility/testing review。当前仍只改文档,未修改 scanner、coverage report、OpenAPI、Account 业务实现、SQL migration 或 gate test。
|
||||
|
||||
P1R Account Security Events Completed Approval 执行版最新版 fresh review 已双 PASS:Mencius scope review PASS,无 P0/P1/P2 findings;Bohr testing/feasibility review PASS,无 P0/P1 blocker。Mencius 确认执行版可进入用户批准点:只审批 `account:appListSecurityEvents`、`account:appGetSecurityEvent` 两个 operation;继续 operation-level approval,不把 `account` 加入 domain allowlist,不推进 Account 33/33;`account:appAcknowledgeSecurityEvent` 继续 `dedicated/needs_verification`;allowed diff 只覆盖 scanner、coverage report、P1R gate tests、新增 `P1rAccountSecurityEventsCompletedApprovalIT`、memory、`.agent`、两份 spec,既有 focused tests 只允许运行/读取;mixed gate 目标为 summary `233/147/86`、Account `12/21`。Bohr 确认 HTTP+DB `_test`、V1-V21 baseline、member_user fixture、no-write、schema/XML/protected diff/rollback 主证据链可进入批准点;其 P2 建议已作为低风险文档补强处理:执行版已明确既有 ack/session_revoked focused tests 只作为现状回归和 XML 防空跑、不计入本轮 completed evidence,并补可复制的 XML 防空跑 Python 校验脚本。该双 PASS 只代表执行版可进入用户批准点,不代表 2 个 Account Security Events read operation completed;未获用户明确批准四项前不得实施 scanner/report/gate/IT 修改。
|
||||
|
||||
P1R Account Security Events Completed Approval implementation 当前状态:用户已批准执行版四项前置条件,本轮只把 `account:appListSecurityEvents`、`account:appGetSecurityEvent` 两个只读 operation 加入 operation-level completed approval,未把 `account` 加入 domain-level allowlist,未修改 OpenAPI、Account 业务实现或 SQL migration;`account:appAcknowledgeSecurityEvent` 仍为 `dedicated/needs_verification/requiresCommandId=true`。scanner 仍保持 `APPROVED_COMPLETED_DOMAINS={"ai","knowledge"}`,只追加两个 `account:*` operation key。TDD RED 已验证旧 report 下 P1R focused gates 失败,失败点为 expected completed=147 but was 145、Account expected 12 completed but was 10、`appListSecurityEvents` 仍为 needs_verification;GREEN 后 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 输出 summary `233/147/86/0/0/0`,Account 为 `33/12/21/0/0/0`。新增 `P1rAccountSecurityEventsCompletedApprovalIT` 使用 MockMvc HTTP 入口、真实 `AppAccountSecurityController` / `AccountSecurityServiceImpl` / mapper、PostgreSQL `_test` 库 `muse_p1r_account_security_events_completed_approval_test`,clean migrate V1-V21 后创建 test-local `member_user` fixture,单类验证 `8/8 pass`,覆盖 list/detail、missing/invalid API version、invalid severity、missing user、非数字 eventId、missing/cross owner/cross tenant 404、owner 不能由请求参数覆盖、latest ack 合并、IP/userAgent/token 脱敏、V2/V11/V20/V21 schema、no-write 全表快照和 `_test`/credential safety。Account focused command fresh 运行 muse-server 19/19 pass;member-server focused XML 显示 `AccountSecurityServiceTest 8/8`、`AppAccountSecurityControllerTest 6/6`、`AccountSecurityEventAckMapperTest 4/4`、`MemberSecurityEventMapperTest 2/2`、`AccountConvertTest 9/9`。Account V20 Flyway gate `P1rAccountEventsPublishFlywayMigrationIT` 使用 `_test` 库 `muse_p1r_account_security_events_flyway_test` fresh 运行 `4/4 pass`,输出 `flyway_success=true`、`migrations_executed=20`、`successful_migration_count=20`、`target_schema_version=20`、`flyway_latest=20:extend account events publish outbox`。P1R mixed gates fresh 运行 `49/49 pass`;XML 防空跑覆盖 10 个目标类,全部 tests>0 且 failures/errors/skipped=0;`git diff --check` 通过;protected OpenAPI diff、Account main/java diff、SQL migration diff 均为空。首轮 fresh implementation review 结果:Darwin correctness PASS;Banach testing PASS 且提出 P2 residual(新增 IT 未直接覆盖 `X-API-Version: 2`)已按 `superpowers:receiving-code-review` 验证为低风险有效项并补测试关闭,补充后已 fresh 重跑新增 IT `8/8 pass`、Account focused muse-server `19/19 pass`、P1R mixed gates `49/49 pass`、scanner/report `233/147/86`、XML 防空跑与 protected diff。新增 memory:`docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md`。当前必须重新派发 fresh implementation spec/correctness re-review + fresh implementation quality/data-integrity/testing re-review,双 PASS 前不得提交、push 或宣称 Account 33/33、Market remaining、Content remaining 或总 P1R completed。
|
||||
|
||||
@ -0,0 +1,258 @@
|
||||
# P1R Account Security Events Completed Approval 审阅版
|
||||
|
||||
日期:2026-06-13
|
||||
|
||||
## 结论
|
||||
|
||||
推荐 Account 下一批只推进 2 个用户安全事件只读 operation-level completed approval:
|
||||
|
||||
```text
|
||||
account:appListSecurityEvents
|
||||
account:appGetSecurityEvent
|
||||
```
|
||||
|
||||
暂不推进 `account:appAcknowledgeSecurityEvent`。原因是 OpenAPI 当前仍承诺 `session_revoked` 会联动 session 管理并使相关会话失效,但当前实现返回“未接入 session 管理服务”的 fail-closed 文案。operation-level completed 不能排除单个 action 子路径;在 OpenAPI 或真实 session revoke 未闭合前,把整个 acknowledge operation 标为 completed 会制造 fake completed。
|
||||
|
||||
本切片 completed 口径只覆盖:对既有 `muse_member_security_event` 安全事件事实的当前登录用户可见读取、详情脱敏、latest ack 合并、owner/tenant 隔离、API version 和 read no-write 闭环。
|
||||
|
||||
本审阅版只冻结候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Current["当前 Account<br/>10 completed / 23 needs_verification"] --> Candidate["下一批候选<br/>Security Events read 2 ops"]
|
||||
Candidate --> Read["list / detail<br/>owner 可见 + 脱敏 + latest ack"]
|
||||
Candidate --> Exclude["暂缓 acknowledge<br/>session_revoked 合同不闭合"]
|
||||
Read --> Review["fresh spec/scope review<br/>fresh quality/testing review"]
|
||||
Exclude --> Keep["继续 needs_verification"]
|
||||
Review --> Exec["双 PASS 后进入审批点"]
|
||||
Exec --> Approval{"用户明确批准后才实现"}
|
||||
Approval -->|"否"| Stay["保持 233/145/88"]
|
||||
Approval -->|"是"| Target["目标 233/147/86<br/>Account 12 completed / 21 needs_verification"]
|
||||
```
|
||||
|
||||
## 当前事实状态
|
||||
|
||||
工作区:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
当前 HEAD:
|
||||
|
||||
```text
|
||||
304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=145
|
||||
needsVerification=88
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
当前 domain 状态:
|
||||
|
||||
```text
|
||||
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 当前状态:
|
||||
|
||||
```text
|
||||
appListSecurityEvents dedicated needs_verification requiresCommandId=false GET /app-api/muse/account/security-events
|
||||
appGetSecurityEvent dedicated needs_verification requiresCommandId=false GET /app-api/muse/account/security-events/{eventId}
|
||||
```
|
||||
|
||||
获批后的目标值:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=147
|
||||
needsVerification=86
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
|
||||
account total=33
|
||||
completed=12
|
||||
needsVerification=21
|
||||
```
|
||||
|
||||
## 推荐候选
|
||||
|
||||
| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence |
|
||||
|---|---|---|---|---|
|
||||
| `account:appListSecurityEvents` | `GET` | `/app-api/muse/account/security-events` | 读当前登录用户安全事件摘要,支持 severity 筛选和 latest ack 合并,无写入副作用 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 owner/tenant 隔离、severity filter、分页、latest ack、敏感字段不泄露、missing API version 拒绝、read no-write |
|
||||
| `account:appGetSecurityEvent` | `GET` | `/app-api/muse/account/security-events/{eventId}` | 读当前登录用户安全事件详情,服务端按 eventId + accountUserId 查询并脱敏 IP / User-Agent | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 detail 脱敏、跨 owner/跨 tenant/missing 不可见、ack detail 合并、非数字 eventId 404、read no-write |
|
||||
|
||||
推荐理由:
|
||||
|
||||
- 2 个 operation 都属于 Account 本域 `AccountSecurityServiceImpl`,不需要调用 New-API、FileService、Market、Content、AI 或 Knowledge 外部 owner。
|
||||
- App 端 Controller 使用 `getLoginUserId()`,不接受请求参数覆盖 owner,适合用 HTTP + DB 证明 owner 隔离。
|
||||
- OpenAPI 的 list/detail 合同与当前实现方向一致:当前账户可见、安全事件详情、敏感值脱敏。
|
||||
- 读侧需要合并 `muse_account_security_event_ack` latest ack,但不需要证明 ack 写命令 completed。
|
||||
|
||||
## 不纳入本批的 Account operation
|
||||
|
||||
以下 21 个 Account operation 必须继续 `dedicated / needs_verification`:
|
||||
|
||||
```text
|
||||
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
|
||||
```
|
||||
|
||||
排除原因:
|
||||
|
||||
- `appAcknowledgeSecurityEvent` 的 `session_revoked` OpenAPI 合同承诺与当前实现 fail-closed 文案不闭合;必须单独处理合同或实现后再审批。
|
||||
- New-API binding、recheck、quota request 和 integration correlation 仍依赖真实 New-API runtime 或外部调用归因闭环。
|
||||
- FileService / export download 仍缺真实文件交付、下载字节、对象存储和凭证消费端到端 evidence。
|
||||
- call attribution job 当前 create 路径只创建 queued job 事实,真实归因执行仍由后续 owner/facade 接入;可作为后续独立切片,但不能夹入本批。
|
||||
- Market purchase / license / publish 仍缺 Market 主流程到 Account projection 的跨 owner 一致性 evidence。
|
||||
- usage summary / admin usage 仍受 `muse_member_usage_record` 和 attribution source 质量影响,本批不绕过上游事实缺口。
|
||||
|
||||
## 已有实现证据
|
||||
|
||||
### OpenAPI 合同
|
||||
|
||||
OpenAPI 当前包含 2 个本批候选读 operation:
|
||||
|
||||
```text
|
||||
GET /app-api/muse/account/security-events
|
||||
GET /app-api/muse/account/security-events/{eventId}
|
||||
```
|
||||
|
||||
本切片不需要修改 `docs/api-contracts/account/openapi.yaml`。但 OpenAPI 中 acknowledge 的 `session_revoked` 文案当前是阻塞项,因此本批明确不推进 `appAcknowledgeSecurityEvent`。
|
||||
|
||||
### Controller 入口
|
||||
|
||||
`AppAccountSecurityController` 当前链路:
|
||||
|
||||
```text
|
||||
GET /muse/account/security-events
|
||||
GET /muse/account/security-events/{eventId}
|
||||
AccountApiVersionGuard.requireVersion(apiVersion)
|
||||
getLoginUserId()
|
||||
AccountSecurityService
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- Controller-local mapping 是 `/muse/account/...`;对外 OpenAPI 路径仍以 `/app-api/muse/...` 为准。
|
||||
- 执行版必须用当前项目既有 app MockMvc / Web 测试上下文证明最终 app API 路径可访问。
|
||||
- `list` / `detail` 都必须证明缺少 `X-API-Version` 时不调用 service。
|
||||
|
||||
### Service 读链路
|
||||
|
||||
`AccountSecurityServiceImpl.appListSecurityEvents`:
|
||||
|
||||
1. `requireUser(accountUserId)`。
|
||||
2. normalize severity,只允许 `info / warning / critical`。
|
||||
3. `MemberSecurityEventMapper.selectPageByAccountUserIdAndSeverity(...)`。
|
||||
4. 批量读取当前 owner 最新 ack。
|
||||
5. 转换摘要 VO,敏感字段不直接外泄。
|
||||
|
||||
`AccountSecurityServiceImpl.appGetSecurityEvent`:
|
||||
|
||||
1. `requireUser(accountUserId)`。
|
||||
2. 解析 eventId,非数字或空值按 not found 处理。
|
||||
3. `MemberSecurityEventMapper.selectByIdAndAccountUserId(...)`。
|
||||
4. 读取当前 owner 最新 ack。
|
||||
5. 转换详情 VO,脱敏 IP / userAgent。
|
||||
|
||||
### Schema 与测试前置
|
||||
|
||||
P1R SQL 当前定义目标事实表:
|
||||
|
||||
```text
|
||||
V2: muse_member_security_event
|
||||
V11: muse_account_security_event_ack
|
||||
```
|
||||
|
||||
但 `AccountSecurityServiceImpl.requireUser()` 会通过 `MemberUserMapper.selectById` 读取 `member_user`,而 `sql/muse` 当前 V1-V21 不创建 `member_user`。执行版必须明确:HTTP+DB `_test` 可以在隔离库完成 V1-V21 clean migrate 后创建 test-local `member_user` auth fixture 表并 seed 登录用户,用于满足认证前置;该 fixture 不计入 P1R Account schema evidence,不能修改 production SQL,也不能替代目标安全事件表/ack 表证据。
|
||||
|
||||
执行版必须 fresh 证明:
|
||||
|
||||
- V1-V21 clean migrate 成功,且新 HTTP+DB IT 使用当前 live migration baseline。
|
||||
- 目标表 `muse_member_security_event`、`muse_account_security_event_ack` 的列、索引、trigger 和 JSONB 字段真实存在。
|
||||
- test-local `member_user` fixture 只在 `_test` 库、Flyway migrate 后创建,并在测试内记录为 auth fixture;DDL 至少覆盖 `MemberUserDO`、`TenantBaseDO`、`BaseDO` 当前映射列:`id`、`mobile`、`password`、`status`、`register_ip`、`register_terminal`、`login_ip`、`login_date`、`nickname`、`avatar`、`name`、`sex`、`birthday`、`area_id`、`mark`、`point`、`tag_ids`、`level_id`、`experience`、`group_id`、`tenant_id`、`creator`、`create_time`、`updater`、`update_time`、`deleted`。
|
||||
|
||||
## 风险与取舍
|
||||
|
||||
1. `appAcknowledgeSecurityEvent` 暂缓是必须的。
|
||||
- 当前 OpenAPI 对 `session_revoked` 承诺真实 session revoke。
|
||||
- 当前实现只返回 fail-closed 文案。
|
||||
- 在合同或实现未闭合前,不能把整个 operation 标为 completed。
|
||||
|
||||
2. `member_user` 是测试 harness 前置,不是目标业务证据。
|
||||
- 不能 mock `MemberUserMapper` 来掩盖真实 service 依赖。
|
||||
- 也不能修改 `sql/muse` 迁移补这个表。
|
||||
- 允许在隔离 `_test` 库、V1-V21 migrate 后创建 test-local fixture,并明确不计入 P1R schema evidence。
|
||||
- fixture DDL 必须覆盖真实 `MemberUserMapper.selectById` 会读取的当前实体映射列,不能只建 `id/nickname` 等最小列。
|
||||
|
||||
3. 安全事件生产路径不是本批完成条件。
|
||||
- 已知 `AccountExportServiceImpl.insertSensitiveExportEvent` 会写安全事件 source fact。
|
||||
- 但本批不证明高敏导出文件交付或所有安全事件来源 completed。
|
||||
|
||||
4. App HTTP + real DB gate 必须避免假绿。
|
||||
- 不能只靠 Mockito service/controller tests。
|
||||
- 必须读取新鲜 Surefire XML,检查 tests>0、failures=0、errors=0、skipped=0。
|
||||
|
||||
## 后续执行版必须包含
|
||||
|
||||
1. 2 个目标 operation 的审批清单、当前状态和目标状态。
|
||||
2. 保持 `needs_verification` 的 21 个 Account operation 及原因。
|
||||
3. TDD RED:先改 P1R gate 期望,旧 report 必须失败。
|
||||
4. TDD GREEN:只把 2 个 `account:*` operation 加入 operation-level allowlist,不加 `account` domain allowlist。
|
||||
5. 新增 `P1rAccountSecurityEventsCompletedApprovalIT`,使用 MockMvc HTTP + 真实 PostgreSQL `_test`。
|
||||
6. App 端登录用户上下文、tenant interceptor、API version、真实 mapper、test-local `member_user` auth fixture 的组合方式。
|
||||
7. schema gate:新 HTTP+DB IT 必须执行 V1-V21 clean migrate;V2/V11 安全事件表、ack 表、唯一约束、索引、trigger、JSONB 字段断言;Account 专属 `P1rAccountEventsPublishFlywayMigrationIT` 可继续作为 V20 outbox 证据,但不能替代新 IT 的 V21 baseline。
|
||||
8. runtime matrix:happy、empty page、severity filter、latest ack、detail 脱敏、cross owner、cross tenant、missing、invalid eventId、read no-write。
|
||||
9. P1R mixed gates:至少覆盖 Account、Coverage、Events、AI、Knowledge、Market、Content、Meta。
|
||||
10. XML 防空跑、protected diff、allowed diff 和 rollback 策略。
|
||||
11. `.agent` 与 `docs/memorys` 留痕要求。
|
||||
|
||||
## 验收标准
|
||||
|
||||
本审阅版可以视为完成的条件:
|
||||
|
||||
1. 文件写入 `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md`。
|
||||
2. `.agent` 记录 Account Security Events completed approval 审阅版状态。
|
||||
3. `git diff --check` 通过。
|
||||
4. OpenAPI、scanner、coverage report、业务实现、SQL migration 当前无新增 diff。
|
||||
5. fresh spec/scope review PASS。
|
||||
6. fresh quality/feasibility/testing review PASS。
|
||||
|
||||
## 待确认项
|
||||
|
||||
1. 是否确认 Account 下一批只做这 2 个 security events read operation-level completed approval。
|
||||
2. 是否确认 `appAcknowledgeSecurityEvent` 因 `session_revoked` 合同不闭合继续保持 `needs_verification`。
|
||||
3. 是否确认本批不把 `account` 加入 domain-level completed allowlist。
|
||||
4. 是否确认允许 HTTP+DB `_test` 在隔离 `_test` 库内创建 test-local `member_user` auth fixture,但不修改 production SQL、不把该 fixture 计入 P1R schema evidence。
|
||||
@ -0,0 +1,700 @@
|
||||
# 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:
|
||||
|
||||
```text
|
||||
account:appListSecurityEvents
|
||||
account:appGetSecurityEvent
|
||||
```
|
||||
|
||||
本轮不把 `account` 加入 domain-level completed allowlist,不推进 Account 33/33 completed,不修改 OpenAPI,不修改 Account 业务实现,不修改 SQL migration。`account:appAcknowledgeSecurityEvent` 因 `session_revoked` 公开合同与当前实现不闭合,继续保持 `needs_verification`。
|
||||
|
||||
获批后目标 coverage summary 为:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=147
|
||||
needsVerification=86
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
Account 域目标状态为:
|
||||
|
||||
```text
|
||||
account total=33
|
||||
completed=12
|
||||
needsVerification=21
|
||||
```
|
||||
|
||||
当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
当前 HEAD:
|
||||
|
||||
```text
|
||||
304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
```
|
||||
|
||||
当前 domain 状态:
|
||||
|
||||
```text
|
||||
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 状态:
|
||||
|
||||
```text
|
||||
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` 的相关写命令:
|
||||
|
||||
```text
|
||||
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.selectById`;P1R `sql/muse` 当前 V1-V21 不创建 `member_user`。
|
||||
- `SecurityEventAcknowledgeReqVO` 与 OpenAPI 当前包含 `session_revoked`,但 OpenAPI 文案承诺真实 session revoke,当前实现只返回 fail-closed 文案;本执行版不推进 acknowledge。
|
||||
|
||||
## 执行边界
|
||||
|
||||
### 必须先获用户明确批准
|
||||
|
||||
实现前必须同时获得以下 4 项批准:
|
||||
|
||||
1. 批准本轮只审批 `account:appListSecurityEvents`、`account: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`
|
||||
|
||||
`AccountSecurityServiceTest`、`AppAccountSecurityControllerTest` 以及既有 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`:
|
||||
|
||||
```text
|
||||
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 | `P1rApiCoverageReportTest`、`P1rAccountRealApiGateTest` 和 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 | `AccountSecurityServiceTest`、`AppAccountSecurityControllerTest`、mapper/convert tests | 支撑 service/controller contract,不替代 HTTP+DB `_test` |
|
||||
|
||||
## TDD / 实施步骤
|
||||
|
||||
### Task 1:Preflight 与 RED gate
|
||||
|
||||
- [ ] 确认当前 worktree:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false status --short --branch
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```text
|
||||
## dev/1.0.0...origin/dev/1.0.0
|
||||
```
|
||||
|
||||
允许存在本执行版和审阅版文档 diff;不得存在 OpenAPI、业务实现、SQL、scanner、coverage report 或 gate test 的未授权 diff。
|
||||
|
||||
- [ ] 确认当前 coverage:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```text
|
||||
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_verification`,2 个 read operation completed,`appAcknowledgeSecurityEvent` 继续 `needs_verification`。
|
||||
- 7 个 mixed gate 中所有硬编码 Account `10/23` 或 summary `145/88` 的断言同步为 `12/21` 或 `147/86`。
|
||||
|
||||
RED 命令:
|
||||
|
||||
```bash
|
||||
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`
|
||||
|
||||
- [ ] 新增:
|
||||
|
||||
```text
|
||||
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 `MemberSecurityEventMapper` 或 `AccountSecurityEventAckMapper`。
|
||||
- `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 必须覆盖 `MemberUserDO`、`TenantBaseDO`、`BaseDO` 当前映射列,至少包含:`id`、`mobile`、`password`、`status`、`register_ip`、`register_terminal`、`login_ip`、`login_date`、`nickname`、`avatar`、`name`、`sex`、`birthday`、`area_id`、`mark`、`point`、`tag_ids`、`level_id`、`experience`、`group_id`、`tenant_id`、`creator`、`create_time`、`updater`、`update_time`、`deleted`。不得只建 `id/nickname` 等最小列,也不得通过修改 mapper 或 production SQL 回避真实 `MemberUserMapper.selectById`。
|
||||
- 注册真实 MyBatis tenant interceptor:`TenantLineInnerInterceptor` + `TenantDatabaseInterceptor(new TenantProperties())`。
|
||||
- `TARGET_VERSION="21"`,执行 V1-V21 clean migrate 后再创建 test-local auth fixture;Account 专属 `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 测试。
|
||||
|
||||
建议测试库:
|
||||
|
||||
```text
|
||||
muse_p1r_account_security_events_completed_approval_test
|
||||
```
|
||||
|
||||
建议环境变量优先级:
|
||||
|
||||
```text
|
||||
P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD
|
||||
P1R_ACCOUNT_COMPLETED_PASSWORD
|
||||
P1R_FLYWAY_PASSWORD
|
||||
MUSE_POSTGRES_PASSWORD
|
||||
```
|
||||
|
||||
standalone 建库与运行前置:
|
||||
|
||||
```bash
|
||||
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_event`、`muse_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_user`、`trg_muse_member_security_updated_at` 存在。
|
||||
- `uk_muse_account_security_event_ack_command`、`idx_muse_account_security_event_ack_user`、`idx_muse_account_security_event_ack_event`、`trg_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_event`、`muse_account_security_event_ack`、`muse_account_command`、`muse_account_audit`、`muse_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 3:TDD GREEN,最小 scanner/report 状态推进
|
||||
|
||||
只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`:
|
||||
|
||||
- 不修改 `APPROVED_COMPLETED_DOMAINS`。
|
||||
- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加:
|
||||
|
||||
```text
|
||||
account:appListSecurityEvents
|
||||
account:appGetSecurityEvent
|
||||
```
|
||||
|
||||
- 保持 completed 必须仍是 `dedicated` 的校验。
|
||||
|
||||
生成 report:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
预期 summary:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=147
|
||||
needsVerification=86
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
预期 Account:
|
||||
|
||||
```text
|
||||
completed=12
|
||||
needsVerification=21
|
||||
```
|
||||
|
||||
### Task 4:Account focused verification
|
||||
|
||||
运行 Account security focused tests:
|
||||
|
||||
```bash
|
||||
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>0`、`failures=0`、`errors=0`、`skipped=0`:
|
||||
|
||||
| Test class | 最低 tests |
|
||||
|---|---:|
|
||||
| `AccountSecurityServiceTest` | 8 |
|
||||
| `AppAccountSecurityControllerTest` | 6 |
|
||||
| `AccountSecurityEventAckMapperTest` | 4 |
|
||||
| `MemberSecurityEventMapperTest` | 2 |
|
||||
| `AccountConvertTest` | 8 |
|
||||
| `P1rAccountSecurityEventsCompletedApprovalIT` | 8 |
|
||||
| `P1rAccountRealApiGateTest` | 5 |
|
||||
| `P1rApiCoverageReportTest` | 6 |
|
||||
|
||||
`AccountSecurityServiceTest`、`AccountSecurityEventAckMapperTest` 中涉及 ack 或 `session_revoked` 的既有断言,只作为现状回归和 XML 防空跑证据,不计入本轮 completed evidence;`appAcknowledgeSecurityEvent` 仍必须保持 `needs_verification`。
|
||||
|
||||
如实现修改了 Account gate 测试数量,XML 检查的最低 tests 可高于上表,但不能低于上表。
|
||||
|
||||
建议使用以下 XML 防空跑脚本检查 Task 4、Task 5 和 Task 6 的目标 XML:
|
||||
|
||||
```bash
|
||||
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 5:Account Flyway `_test` gate
|
||||
|
||||
fresh rerun `P1rAccountEventsPublishFlywayMigrationIT`。使用独立 `_test` 库,不能使用生产库或非 `_test` 库:
|
||||
|
||||
```bash
|
||||
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 6:P1R mixed gate
|
||||
|
||||
运行 mixed gates:
|
||||
|
||||
```bash
|
||||
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=0`、`errors=0`、`skipped=0`。
|
||||
|
||||
### Task 7:报告与 diff gate
|
||||
|
||||
检查 summary:
|
||||
|
||||
```bash
|
||||
jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
233 147 86 0 0 0
|
||||
```
|
||||
|
||||
检查 Account 12/21:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
Counter({'needs_verification': 21, 'completed': 12})
|
||||
```
|
||||
|
||||
检查目标 operation:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
appListSecurityEvents dedicated completed false
|
||||
appGetSecurityEvent dedicated completed false
|
||||
appAcknowledgeSecurityEvent dedicated needs_verification true
|
||||
```
|
||||
|
||||
检查 OpenAPI protected diff:
|
||||
|
||||
```bash
|
||||
git diff --quiet -- docs/api-contracts
|
||||
git diff --cached --quiet -- docs/api-contracts
|
||||
```
|
||||
|
||||
检查 business implementation 不被修改:
|
||||
|
||||
```bash
|
||||
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 不被修改:
|
||||
|
||||
```bash
|
||||
git diff --quiet -- muse-cloud/sql/muse
|
||||
git diff --cached --quiet -- muse-cloud/sql/muse
|
||||
```
|
||||
|
||||
检查 whitespace:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
检查 allowed diff:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false status --short
|
||||
```
|
||||
|
||||
输出只能包含本执行版允许路径。若出现 OpenAPI、业务实现、迁移或非批准 gate 文件,必须停止。
|
||||
|
||||
### Task 8:memory 与 `.agent`
|
||||
|
||||
实现通过后新增:
|
||||
|
||||
```text
|
||||
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 9:fresh implementation review
|
||||
|
||||
实现、验证和文档留痕后,必须派发两类 fresh reviewer:
|
||||
|
||||
- spec/correctness reviewer:检查 2 个 operation 是否严格匹配本执行版,21 个 operation 是否仍 needs_verification,Account 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. 重新运行:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
3. 恢复 gate 期望:
|
||||
|
||||
```text
|
||||
summary completed=145
|
||||
summary needsVerification=88
|
||||
account completed=10
|
||||
account needsVerification=23
|
||||
```
|
||||
|
||||
4. 删除或修订本轮 memory,保留失败原因。
|
||||
5. 重新运行 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。
|
||||
|
||||
实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。
|
||||
274
docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md
Normal file
274
docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md
Normal file
@ -0,0 +1,274 @@
|
||||
# P1R Account Security Events 状态推进
|
||||
|
||||
日期:2026-06-13
|
||||
|
||||
## 结论
|
||||
|
||||
本轮只推进 Account 用户安全事件只读 2 个 operation-level completed approval:
|
||||
|
||||
- `account:appListSecurityEvents`
|
||||
- `account:appGetSecurityEvent`
|
||||
|
||||
Account 仍不是整域 completed。`account:appAcknowledgeSecurityEvent` 继续保持 `dedicated/needs_verification/requiresCommandId=true`。
|
||||
|
||||
本轮未修改 OpenAPI,未修改 Account 业务实现,未修改 SQL migration,未把 `account` 加入 domain-level completed allowlist。
|
||||
|
||||
## 工作区与基线
|
||||
|
||||
工作区:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
基线 HEAD:
|
||||
|
||||
```text
|
||||
304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁
|
||||
```
|
||||
|
||||
实施前 coverage summary:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
```
|
||||
|
||||
实施后 coverage summary:
|
||||
|
||||
```text
|
||||
233 147 86 0 0 0
|
||||
```
|
||||
|
||||
实施后 Account 状态:
|
||||
|
||||
```text
|
||||
total=33
|
||||
completed=12
|
||||
needs_verification=21
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
目标 operation 状态:
|
||||
|
||||
```text
|
||||
appListSecurityEvents dedicated completed false GET /app-api/muse/account/security-events
|
||||
appGetSecurityEvent dedicated completed false GET /app-api/muse/account/security-events/{eventId}
|
||||
```
|
||||
|
||||
必须保持 needs_verification:
|
||||
|
||||
```text
|
||||
appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge
|
||||
```
|
||||
|
||||
## 修改范围
|
||||
|
||||
本轮修改:
|
||||
|
||||
- `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/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`
|
||||
|
||||
未修改:
|
||||
|
||||
- 7 个 OpenAPI。
|
||||
- Account 业务实现。
|
||||
- SQL migration。
|
||||
- Account domain-level completed allowlist。
|
||||
- 既有 Account focused tests。
|
||||
|
||||
## TDD evidence
|
||||
|
||||
RED 阶段先修改 P1R gate 期望值和 2 个 Security Events read operation completed 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现:
|
||||
|
||||
```text
|
||||
Tests run: 49, Failures: 10, Errors: 0, Skipped: 0
|
||||
BUILD FAILURE
|
||||
```
|
||||
|
||||
失败点来自旧 report 仍为 `completed=145 / needsVerification=88`,Account 仍为 `10 completed / 23 needs_verification`,以及 `appListSecurityEvents` / `appGetSecurityEvent` 仍为 `needs_verification`;没有编译错误或无关测试失败。
|
||||
|
||||
GREEN 阶段只把两个 `account:*` key 加入 scanner operation-level allowlist,重新生成 report,并完成 HTTP+DB `_test` evidence 和 focused gates。
|
||||
|
||||
## MockMvc HTTP 入口 + DB 证据
|
||||
|
||||
新增:
|
||||
|
||||
- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountSecurityEventsCompletedApprovalIT.java`
|
||||
|
||||
验证方式:
|
||||
|
||||
- 使用 Spring Mock web context + `MockMvc`。
|
||||
- 通过 `/app-api/muse/account/security-events` 与 `/app-api/muse/account/security-events/{eventId}` HTTP 入口进入 `AppAccountSecurityController`。
|
||||
- 使用真实 `AccountSecurityServiceImpl`、mapper、tenant SQL interceptor 和 PostgreSQL `_test` 数据库。
|
||||
- Flyway clean/migrate V1-V21。
|
||||
- 在隔离 `_test` 库创建 test-local `member_user` fixture,满足 `requireUser()` 的真实 `MemberUserMapper.selectById` 前置;该 fixture 不计入 P1R production schema evidence。
|
||||
- 密码只从环境变量读取,拒绝 JVM password system property 和 JDBC credential query。
|
||||
- `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user`,避免污染同一 Surefire JVM 后续 Flyway 测试。
|
||||
|
||||
单类验证数据库:
|
||||
|
||||
```text
|
||||
muse_p1r_account_security_events_completed_approval_test
|
||||
```
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
P1rAccountSecurityEventsCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
覆盖内容:
|
||||
|
||||
- 拒绝非 `_test` 数据库、JVM password system property 和 JDBC credential query。
|
||||
- V1-V21 schema gate,覆盖 `muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_event_publish_outbox` 和 test-local `member_user`。
|
||||
- `idx_muse_member_security_user`、`trg_muse_member_security_updated_at`、`uk_muse_account_security_event_ack_command`、ack user/event 索引和 updated_at trigger。
|
||||
- list 支持 severity、分页、owner/tenant 隔离,并合并当前用户最新 ack。
|
||||
- detail 支持 eventId + owner 查询,返回脱敏 IP、脱敏 userAgent、脱敏 description / suggestedActions。
|
||||
- 请求参数不能覆盖当前登录 owner。
|
||||
- missing / invalid API version、invalid severity、missing user、missing event、非数字 eventId、跨 owner、跨 tenant 都不泄露目标事实。
|
||||
- read happy/error 都使用 `row_to_json(t)::text` 对 `member_user`、`muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_command`、`muse_account_audit`、`muse_account_event_publish_outbox` 做全表快照 no-write。
|
||||
|
||||
## 组合验证
|
||||
|
||||
Account focused Maven 命令通过,muse-server 部分:
|
||||
|
||||
```text
|
||||
P1rAccountSecurityEventsCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0
|
||||
P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0
|
||||
focused muse-server total: tests=19, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
member-server focused XML:
|
||||
|
||||
```text
|
||||
AccountSecurityServiceTest: tests=8, failures=0, errors=0, skipped=0
|
||||
AppAccountSecurityControllerTest: tests=6, failures=0, errors=0, skipped=0
|
||||
AccountSecurityEventAckMapperTest: tests=4, failures=0, errors=0, skipped=0
|
||||
MemberSecurityEventMapperTest: tests=2, failures=0, errors=0, skipped=0
|
||||
AccountConvertTest: tests=9, failures=0, errors=0, skipped=0
|
||||
focused member-server total: tests=29, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
Account V20 Flyway 迁移门禁使用真实 PostgreSQL `_test` 库:
|
||||
|
||||
```text
|
||||
muse_p1r_account_security_events_flyway_test
|
||||
```
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
P1rAccountEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0
|
||||
flyway_success=true
|
||||
migrations_executed=20
|
||||
successful_migration_count=20
|
||||
target_schema_version=20
|
||||
flyway_latest=20:extend account events publish outbox
|
||||
```
|
||||
|
||||
P1R mixed gates:
|
||||
|
||||
```text
|
||||
P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0
|
||||
P1rAccountRealApiGateTest: tests=5, failures=0, errors=0, skipped=0
|
||||
P1rEventsRealApiGateTest: tests=7, failures=0, errors=0, skipped=0
|
||||
P1rAiRealApiGateTest: tests=7, failures=0, errors=0, skipped=0
|
||||
P1rKnowledgeRealApiGateTest: tests=8, failures=0, errors=0, skipped=0
|
||||
P1rMarketRealApiGateTest: tests=6, failures=0, errors=0, skipped=0
|
||||
P1rContentRealApiGateTest: tests=5, failures=0, errors=0, skipped=0
|
||||
P1rMetaRealApiGateTest: tests=5, failures=0, errors=0, skipped=0
|
||||
mixed total: tests=49, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
fresh implementation testing review 后处理:
|
||||
|
||||
```text
|
||||
Banach testing/data-integrity review: PASS
|
||||
P2 residual: 新增 IT 未直接覆盖 X-API-Version: 2
|
||||
处理结果: 已补 invalid API version HTTP+DB no-write 断言
|
||||
复验: P1rAccountSecurityEventsCompletedApprovalIT 8/8 pass
|
||||
复验: Account focused muse-server 19/19 pass
|
||||
复验: P1R mixed gates 49/49 pass
|
||||
复验: scanner/report 233/147/86
|
||||
复验: XML 防空跑与 protected diff 通过
|
||||
```
|
||||
|
||||
## scanner / report 验证
|
||||
|
||||
命令:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```text
|
||||
Generated docs/superpowers/reports/p1r-api-coverage.json
|
||||
Generated docs/superpowers/reports/p1r-api-coverage.md
|
||||
233 147 86 0 0 0
|
||||
```
|
||||
|
||||
Account 聚合:
|
||||
|
||||
```text
|
||||
33 12 21 0 0 0
|
||||
```
|
||||
|
||||
目标 operation 明细:
|
||||
|
||||
```text
|
||||
account appListSecurityEvents dedicated completed false GET /app-api/muse/account/security-events
|
||||
account appGetSecurityEvent dedicated completed false GET /app-api/muse/account/security-events/{eventId}
|
||||
account appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge
|
||||
```
|
||||
|
||||
## protected diff
|
||||
|
||||
通过:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
通过:
|
||||
|
||||
```bash
|
||||
git diff --quiet -- docs/api-contracts muse-cloud/muse-module-member/muse-module-member-server/src/main/java muse-cloud/sql/muse
|
||||
git diff --cached --quiet -- docs/api-contracts muse-cloud/muse-module-member/muse-module-member-server/src/main/java muse-cloud/sql/muse
|
||||
```
|
||||
|
||||
结论:
|
||||
|
||||
```text
|
||||
protected_diff_empty=true
|
||||
```
|
||||
|
||||
## 后续边界
|
||||
|
||||
本轮完成候选只代表 2 个 Account Security Events read operation-level completed approval。
|
||||
|
||||
仍不代表:
|
||||
|
||||
- Account 33/33 completed。
|
||||
- `account:appAcknowledgeSecurityEvent` completed。
|
||||
- Account remaining 21 completed。
|
||||
- Market remaining completed。
|
||||
- Content remaining completed。
|
||||
- 总 P1R completed。
|
||||
|
||||
提交或 push 前仍需要 fresh implementation spec/correctness review + fresh implementation quality/data-integrity/testing review 双 PASS。
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-05-25T09:52:35+00:00",
|
||||
"generatedAt": "2026-05-25T14:04:44+00:00",
|
||||
"sourceContracts": [
|
||||
"docs/api-contracts/account/openapi.yaml",
|
||||
"docs/api-contracts/ai/openapi.yaml",
|
||||
@ -11,9 +11,9 @@
|
||||
],
|
||||
"summary": {
|
||||
"totalOperations": 233,
|
||||
"completedOperations": 145,
|
||||
"completedOperations": 147,
|
||||
"incompleteOperations": 0,
|
||||
"needsVerificationOperations": 88,
|
||||
"needsVerificationOperations": 86,
|
||||
"catchAllOperations": 0,
|
||||
"genericPersistenceOperations": 0,
|
||||
"ssePlaceholderOperations": 0,
|
||||
@ -2130,7 +2130,7 @@
|
||||
],
|
||||
"targetStage": "P1R-3 Account Real API",
|
||||
"implementationStatus": "dedicated",
|
||||
"completionStatus": "needs_verification",
|
||||
"completionStatus": "completed",
|
||||
"controllerFiles": [
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityController.java"
|
||||
],
|
||||
@ -2194,7 +2194,7 @@
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/service/user/MemberUserService.java",
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/service/user/MemberUserServiceImpl.java"
|
||||
],
|
||||
"notes": "Dedicated entry exists; later P1R stage must verify DTO, state machine, persistence, and real external closure."
|
||||
"notes": "Dedicated entry exists; user-approved P1R acceptance evidence allows this operation to advance to completed coverage."
|
||||
},
|
||||
{
|
||||
"domain": "account",
|
||||
@ -2210,7 +2210,7 @@
|
||||
],
|
||||
"targetStage": "P1R-3 Account Real API",
|
||||
"implementationStatus": "dedicated",
|
||||
"completionStatus": "needs_verification",
|
||||
"completionStatus": "completed",
|
||||
"controllerFiles": [
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityController.java"
|
||||
],
|
||||
@ -2274,7 +2274,7 @@
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/service/user/MemberUserService.java",
|
||||
"muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/service/user/MemberUserServiceImpl.java"
|
||||
],
|
||||
"notes": "Dedicated entry exists; later P1R stage must verify DTO, state machine, persistence, and real external closure."
|
||||
"notes": "Dedicated entry exists; user-approved P1R acceptance evidence allows this operation to advance to completed coverage."
|
||||
},
|
||||
{
|
||||
"domain": "account",
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# P1R API Coverage Report
|
||||
|
||||
- Generated at: `2026-05-25T09:52:35+00:00`
|
||||
- Generated at: `2026-05-25T14:04:44+00:00`
|
||||
- Total operations: `233`
|
||||
- Completed: `145`
|
||||
- Needs verification: `88`
|
||||
- Completed: `147`
|
||||
- Needs verification: `86`
|
||||
- Incomplete: `0`
|
||||
- Catch-all: `0`
|
||||
- Generic persistence: `0`
|
||||
@ -40,8 +40,8 @@
|
||||
| account | app | GET | `/app-api/muse/account/purchases` | `appListPurchases` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | POST | `/app-api/muse/account/quota-requests` | `appCreateQuotaRequest` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/quota-requests/{requestId}` | `appGetQuotaRequest` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/security-events` | `appListSecurityEvents` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/security-events/{eventId}` | `appGetSecurityEvent` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/security-events` | `appListSecurityEvents` | dedicated | completed | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/security-events/{eventId}` | `appGetSecurityEvent` | dedicated | completed | P1R-3 Account Real API |
|
||||
| account | app | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `appAcknowledgeSecurityEvent` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/account/usage` | `getAppUsage` | dedicated | needs_verification | P1R-3 Account Real API |
|
||||
| account | app | GET | `/app-api/muse/me` | `getCurrentUser` | dedicated | completed | P1R-3 Account Real API |
|
||||
|
||||
@ -40,7 +40,7 @@ class P1rAccountRealApiGateTest {
|
||||
"blocked"
|
||||
);
|
||||
|
||||
/** 已批准的 Account 第一批本域闭合接口,仍保持 operation-level approval。 */
|
||||
/** 已批准的 Account 本域闭合接口,仍保持 operation-level approval。 */
|
||||
private static final Set<String> APPROVED_ACCOUNT_COMPLETED_OPERATIONS = Set.of(
|
||||
"getCurrentUser",
|
||||
"getProfile",
|
||||
@ -51,7 +51,9 @@ class P1rAccountRealApiGateTest {
|
||||
"adminGetBalanceSnapshots",
|
||||
"getAppBalanceSnapshots",
|
||||
"adminCreateQuotaAdjustment",
|
||||
"adminListQuotaAdjustments"
|
||||
"adminListQuotaAdjustments",
|
||||
"appListSecurityEvents",
|
||||
"appGetSecurityEvent"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
@ -189,8 +191,8 @@ class P1rAccountRealApiGateTest {
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(10, completed, "Account 第一批只能有 10 个 operation completed");
|
||||
assertEquals(23, needsVerification, "Account 剩余 23 个 operation 必须继续 needs_verification");
|
||||
assertEquals(12, completed, "Account Security Events read completed approval 后只能有 12 个 operation completed");
|
||||
assertEquals(21, needsVerification, "Account 剩余 21 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -145,8 +145,8 @@ class P1rAiRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
assertEquals(147, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 12 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -174,7 +174,7 @@ class P1rAiRealApiGateTest {
|
||||
void should_keep_content_meta_account_dedicated_statuses() throws IOException {
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertAccountStatusCount(12, 21);
|
||||
assertMarketStatusCount(4, 28);
|
||||
}
|
||||
|
||||
@ -225,8 +225,8 @@ class P1rAiRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(33, total, "Account operation 数量必须保持 33");
|
||||
assertEquals(expectedCompleted, completed, "Account 第一批 completed approval 后必须有 10 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 23 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Account Security Events read completed approval 后必须有 12 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 21 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private void assertMetaStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
|
||||
@ -81,6 +81,8 @@ class P1rApiCoverageReportTest {
|
||||
"account:getAppBalanceSnapshots",
|
||||
"account:adminCreateQuotaAdjustment",
|
||||
"account:adminListQuotaAdjustments",
|
||||
"account:appListSecurityEvents",
|
||||
"account:appGetSecurityEvent",
|
||||
"market:getMarketplaceAsset",
|
||||
"market:listMarketplaceCategories",
|
||||
"market:favoriteAsset",
|
||||
@ -125,7 +127,7 @@ class P1rApiCoverageReportTest {
|
||||
"activateFunctionChainVersion"
|
||||
);
|
||||
|
||||
/** 已批准的 Account 第一批本域闭合接口,仍保持 operation-level approval。 */
|
||||
/** 已批准的 Account 本域闭合接口,仍保持 operation-level approval。 */
|
||||
private static final Set<String> APPROVED_ACCOUNT_COMPLETED_OPERATIONS = Set.of(
|
||||
"getCurrentUser",
|
||||
"getProfile",
|
||||
@ -136,7 +138,9 @@ class P1rApiCoverageReportTest {
|
||||
"adminGetBalanceSnapshots",
|
||||
"getAppBalanceSnapshots",
|
||||
"adminCreateQuotaAdjustment",
|
||||
"adminListQuotaAdjustments"
|
||||
"adminListQuotaAdjustments",
|
||||
"appListSecurityEvents",
|
||||
"appGetSecurityEvent"
|
||||
);
|
||||
|
||||
/** 已批准的 Market 第一批详情、分类和收藏接口,仍保持 operation-level approval。 */
|
||||
@ -280,7 +284,7 @@ class P1rApiCoverageReportTest {
|
||||
completed++;
|
||||
}
|
||||
}
|
||||
assertEquals(145, completed, "Content Admin RiskAction operation-level approval 后 completed 总数只能从 144 增至 145");
|
||||
assertEquals(147, completed, "Account Security Events read completed approval 后 completed 总数只能从 145 增至 147");
|
||||
|
||||
assertOperationStatus("events", "streamEvents", "dedicated", "completed");
|
||||
for (String operationId : APPROVED_META_SCHEMA_COMPLETED_OPERATIONS) {
|
||||
@ -302,6 +306,9 @@ class P1rApiCoverageReportTest {
|
||||
assertOperationStatus("market", "listMarketplaceRecommendations", "dedicated", "needs_verification");
|
||||
assertOperationStatus("market", "favoriteAsset", "dedicated", "completed", true);
|
||||
assertOperationStatus("market", "unfavoriteAsset", "dedicated", "completed", true);
|
||||
assertOperationStatus("account", "appListSecurityEvents", "dedicated", "completed");
|
||||
assertOperationStatus("account", "appGetSecurityEvent", "dedicated", "completed");
|
||||
assertOperationStatus("account", "appAcknowledgeSecurityEvent", "dedicated", "needs_verification", true);
|
||||
assertOperationStatus("content", "createWork", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "updateWork", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "createChapter", "dedicated", "needs_verification");
|
||||
@ -311,7 +318,7 @@ class P1rApiCoverageReportTest {
|
||||
assertOperationStatus("content", "createStyleCheck", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "adminRiskAction", "dedicated", "completed", true);
|
||||
assertDomainStatusCount("market", "dedicated", "needs_verification", 28);
|
||||
assertDomainStatusCount("account", "dedicated", "needs_verification", 23);
|
||||
assertDomainStatusCount("account", "dedicated", "needs_verification", 21);
|
||||
assertDomainStatusCount("content", "dedicated", "needs_verification", 37);
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ class P1rEventsRealApiGateTest {
|
||||
/** P1R-6 Market 合同固定 operation 数量,P1R-7a 不能顺手改变 Market 覆盖状态。 */
|
||||
private static final int EXPECTED_MARKET_OPERATION_COUNT = 32;
|
||||
|
||||
/** P1R-3 Account 合同固定 operation 数量,Account completed approval 只能推进第一批 10 个。 */
|
||||
/** P1R-3 Account 合同固定 operation 数量,Account completed approval 当前只允许推进 12 个。 */
|
||||
private static final int EXPECTED_ACCOUNT_OPERATION_COUNT = 33;
|
||||
|
||||
/** Events 真实入口门禁必须拒绝的占位或缺失状态。 */
|
||||
@ -106,10 +106,10 @@ class P1rEventsRealApiGateTest {
|
||||
void should_keep_completed_approval_summary_at_approved_operation_boundary() throws IOException {
|
||||
JsonNode summary = readReport().path("summary");
|
||||
|
||||
assertEquals(145, summary.path("completedOperations").asInt(),
|
||||
"completedOperations 只能来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
assertEquals(88, summary.path("needsVerificationOperations").asInt(),
|
||||
"needsVerificationOperations 必须扣除已批准的 Events streamEvents、Meta 16、Account 10、Market 4 和 Content 14");
|
||||
assertEquals(147, summary.path("completedOperations").asInt(),
|
||||
"completedOperations 只能来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 12 + Market 4 + Content 14");
|
||||
assertEquals(86, summary.path("needsVerificationOperations").asInt(),
|
||||
"needsVerificationOperations 必须扣除已批准的 Events streamEvents、Meta 16、Account 12、Market 4 和 Content 14");
|
||||
assertEquals(0, summary.path("incompleteOperations").asInt(),
|
||||
"Events 退出 placeholder 后不应再留下 incomplete operation");
|
||||
assertEquals(0, summary.path("genericPersistenceOperations").asInt(),
|
||||
@ -150,7 +150,7 @@ class P1rEventsRealApiGateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_account_partial_approval_at_10_completed_operations() throws IOException {
|
||||
void should_keep_account_partial_approval_at_12_completed_operations() throws IOException {
|
||||
JsonNode accountOperations = readAccountOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -168,8 +168,8 @@ class P1rEventsRealApiGateTest {
|
||||
needsVerification++;
|
||||
}
|
||||
}
|
||||
assertEquals(10, completed, "Account 第一批 completed approval 只能推进 10 个 operation");
|
||||
assertEquals(23, needsVerification, "Account 剩余 23 个 operation 必须继续 needs_verification");
|
||||
assertEquals(12, completed, "Account Security Events read completed approval 后只能推进 12 个 operation");
|
||||
assertEquals(21, needsVerification, "Account 剩余 21 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -184,8 +184,8 @@ class P1rKnowledgeRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
assertEquals(147, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 12 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -193,7 +193,7 @@ class P1rKnowledgeRealApiGateTest {
|
||||
assertDomainStatusCount("ai", 41, "completed");
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertAccountStatusCount(12, 21);
|
||||
assertMarketStatusCount(4, 28);
|
||||
}
|
||||
|
||||
@ -285,8 +285,8 @@ class P1rKnowledgeRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(33, total, "Account operation 数量必须保持 33");
|
||||
assertEquals(expectedCompleted, completed, "Account 第一批 completed approval 后必须有 10 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 23 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Account Security Events read completed approval 后必须有 12 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 21 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private void assertMarketStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
|
||||
@ -151,8 +151,8 @@ class P1rMarketRealApiGateTest {
|
||||
void should_keep_completion_summary_owned_by_approved_scopes_only() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
assertEquals(147, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 12 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -161,7 +161,7 @@ class P1rMarketRealApiGateTest {
|
||||
assertDomainStatusCount("knowledge", 59, "completed");
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertAccountStatusCount(12, 21);
|
||||
}
|
||||
|
||||
private void assertContentStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
@ -247,8 +247,8 @@ class P1rMarketRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(33, total, "Account operation 数量必须保持 33");
|
||||
assertEquals(expectedCompleted, completed, "Account 第一批 completed approval 后必须有 10 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 23 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Account Security Events read completed approval 后必须有 12 个 operation completed");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Account 剩余 21 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private void assertMetaStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
|
||||
@ -74,6 +74,9 @@ APPROVED_COMPLETED_OPERATIONS = {
|
||||
"account:getAppBalanceSnapshots",
|
||||
"account:adminCreateQuotaAdjustment",
|
||||
"account:adminListQuotaAdjustments",
|
||||
# P1R Account Security Events read completed approval 只推进两个只读接口。
|
||||
"account:appListSecurityEvents",
|
||||
"account:appGetSecurityEvent",
|
||||
# P1R Market completed approval 第一批仍使用 operation-level 清单,避免整域 Market 被误升 completed。
|
||||
"market:getMarketplaceAsset",
|
||||
"market:listMarketplaceCategories",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user