test(p1r): 收口 Content Admin RiskAction completed approval 门禁
This commit is contained in:
parent
b0b4b2c557
commit
304f665660
@ -221,3 +221,13 @@ P1R Content Admin Read Completed Approval implementation 当前状态:用户
|
||||
P1R Content Admin Read Completed Approval implementation fresh review 进展:Lorentz correctness review PASS,无 findings;Hegel testing/data-integrity review FAIL,2 个有效问题已修复。P1:`should_listWorksWithFiltersGovernanceTenantIsolationAndNoBodyLeak` 先执行两次 admin read 后再调用测试自身 `deleteGovernanceActions()`,最后只按治理行数 0 做 no-write,可能遮蔽 read 路径新增/删除治理事实。P2:旧 `assertNoAdminReadWrites` 只比较 work/chapter/block/governance/command/outbox 行数,不能发现 admin read 意外 UPDATE 既有字段、revision、status、reason 或 update_time。修复方式:在 `P1rContentAdminReadCompletedApprovalIT` 中新增 `ContentFactSnapshot`,用 `row_to_json(t)::text` 对 `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_governance_action`、`muse_content_command_log`、`muse_content_event_publish_outbox` 做请求前后全表快照比对;每个 admin read happy/error/RBAC/API version 请求都包进 `assertNoContentMutationDuring`;riskFlag 空结果在测试自身删除治理事实后重新采样快照,不把测试 DML 混入 no-write 窗口。修复后已 fresh 重跑 `P1rContentAdminReadCompletedApprovalIT`,`5/5 pass`;Content combined DB gate `P1rContentAdminReadCompletedApprovalIT,P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT` fresh 重跑 `32/32 pass`。当前需要重新派发 fresh implementation correctness/spec re-review + testing/data-integrity re-review;双 PASS 前不得提交、push 或宣称本轮收口。
|
||||
|
||||
P1R Content Admin Read Completed Approval implementation fresh re-review 已双 PASS:Peirce correctness/spec re-review PASS、Carver testing/data-integrity re-review PASS,均无 P0/P1/P2/P3 findings。Peirce 已验证正确 worktree、OpenAPI/Content main/java/SQL migration protected diff 为空、scanner 仅追加 3 个 Content admin read operation、coverage 为 `233/144/89/0/0/0`、Content 为 `13 completed / 38 needs_verification`、`adminRiskAction` 仍为 `dedicated/needs_verification/requiresCommandId=true`、mixed gate 防回退断言同步、Hegel P1/P2 已通过全表快照 no-write 关闭,且 `.agent`/memory 没有宣称 Content 51/51 或总 P1R completed。Carver 已验证 `ContentFactSnapshot` 用 `row_to_json(t)::text` 覆盖 work/chapter/block/governance_action/command_log/event_publish_outbox 的 INSERT/DELETE/UPDATE,所有 admin read happy/error、API version deny、RBAC deny 请求都包进 `assertNoContentMutationDuring`,riskFlag 空结果在测试自身 DML 后重新建立 no-write baseline;修复后主证据 `P1rContentAdminReadCompletedApprovalIT 5/5`、Content combined `32/32`、P1R mixed gates `49/49`、scanner `233/144/89` 均为 fresh 产物。Carver P3 residual 为 focused `ContentAdminServiceTest/AdminContentControllerTest` XML 早于 no-write 修复,但这两个源码本轮未改;该 residual 已通过重新运行 focused Content tests 关闭,fresh 结果为 `ContentAdminServiceTest 13/13`、`AdminContentControllerTest 8/8`,合计 `21/21 pass`。该双 PASS 只代表本轮 Content Admin Read 3 个 operation-level completed approval 具备收口候选条件;不代表 Content 51/51、Account remaining、Market remaining 或总 P1R completed。提交或 push 仍需用户明确指令。
|
||||
|
||||
P1R Content Admin RiskAction Completed Approval 已启动审阅版:`docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md`。本审阅版只推荐下一批 operation-level approval 为 `content:adminRiskAction` 一个治理写命令;不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51,不修改 OpenAPI、scanner、coverage report、Content 业务实现、SQL migration 或 gate test。只读盘点事实:当前 coverage 为 `233/144/89/0/0/0`,Content 为 `13 completed / 38 needs_verification`,`adminRiskAction` 为 `dedicated/needs_verification/requiresCommandId=true`,对应 `POST /admin-api/muse/content/works/{workId}/risk-actions`;OpenAPI `RiskActionRequest` 已要求 `commandId`、`action`、`reason`、`expectedVersion`,Controller 权限点为 `muse:content:risk-action`,Service 已有事务、command reserve、`selectByIdForUpdate`、expectedVersion 校验、targetScope/targetIds 归属校验、governance action insert 和 command/audit succeeded envelope。推荐目标值仅作为获批后执行目标:`233/145/88/0/0/0`,Content 目标为 `14 completed / 37 needs_verification`。下一步必须对该审阅版做 fresh spec/scope review + fresh quality/feasibility/testing review;双 PASS 前不得写执行版、修改 scanner/report/gate、提交、push 或推进 Content completed。
|
||||
|
||||
P1R Content Admin RiskAction Completed Approval 审阅版 fresh review 已双 PASS:Rawls spec/scope review PASS,无 P0/P1/P2/P3 findings,确认审阅版只覆盖 `content:adminRiskAction` 一个 operation-level approval,不推进 Content domain-level completed,不修改 OpenAPI/scanner/report/业务实现,目标 `233/145/88` 与 Content `14/37` 可由当前 `233/144/89` 与 Content `13/38` 加 1 个 operation 机械推导,非目标 37 个 Content operation 与 coverage 差集一致。Kierkegaard quality/feasibility/testing review PASS,无阻塞项,确认 MockMvc HTTP + real PostgreSQL `_test`、method security RBAC、tenant interceptor、command replay/conflict、expectedVersion/row lock、targetScope/targetIds 归属、governance fact、command/audit snapshot、no-write/rollback、Flyway/schema、XML 防空跑和 mixed gate 同步都可落地;非阻断注意为 OpenAPI 已声明 `expectedVersion` required,但生成 DTO 新鲜度不能单独作为合同证据,后续应以 OpenAPI + 服务端 VO validation + HTTP/DB 证据闭合。执行版已新增:`docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md`。执行版只锁定 `content:adminRiskAction`,不把 `content` 加入 domain allowlist,不推进 Content 51/51,不修改 OpenAPI、Content main/java 或 SQL migration;目标 summary 仅作为获批后执行目标:`233/145/88/0/0/0`,Content 目标为 `14 completed / 37 needs_verification`。下一步必须 fresh execution spec/scope review + fresh execution quality/testing review;双 PASS 前不得实现、提交、push 或推进 Content completed。
|
||||
|
||||
P1R Content Admin RiskAction Completed Approval 执行版 fresh review 进展:Chandrasekhar execution spec/scope review PASS,无 findings,确认执行版严格限定 `content:adminRiskAction` 一个 operation-level completed approval、目标 `233/145/88` 与 Content `14/37` 可机械推导、allowed/prohibited diff 与 mixed gate 同步范围覆盖当前硬编码点。Aquinas execution quality/feasibility/testing review FAIL,3 个问题已按 `superpowers:receiving-code-review` 验证为有效并修订:P1 harness 缺必需 admin web/security bean 细节,已补 `@Bean("ss")` test security service、`ApiErrorLogCommonApi` stub、`MuseWebAutoConfiguration`、真实 `ContentCommandServiceImpl`、真实 `ContentAuditServiceImpl`、真实 `ContentAdminServiceImpl`、所有 Content mapper / facade stub 和 tenant interceptor 要求;P1 command/audit gate 漏 command envelope 行级事实,已补 `muse_content_command_log` 的 tenant_id、command_id、command_type、owner_user_id、target_type、target_id、request_hash,以及 `muse_content_governance_action` 的 tenant/operator/command/audit/action/target/expectedVersion/status 断言;P2 `_test` 数据库准备步骤不完整,已补 standalone 与 combined `_test` 建库命令、password env export 和非 `_test` 拒绝说明。由于执行版已在 review 后修订,Chandrasekhar PASS 需重新确认或至少由下一轮 spec/scope reviewer 基于最新文件复核;Aquinas FAIL 不能计入最终 gate。下一步必须重新派发 fresh execution quality/feasibility/testing review,必要时同步 fresh spec/scope re-review;双 PASS 前不得实现、提交、push 或推进 Content completed。
|
||||
|
||||
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。
|
||||
|
||||
@ -0,0 +1,417 @@
|
||||
# P1R Content Admin RiskAction Completed Approval 审阅版
|
||||
|
||||
日期:2026-06-13
|
||||
|
||||
## 结论
|
||||
|
||||
推荐 Content 下一批只推进 1 个管理后台治理写命令的 operation-level completed approval:
|
||||
|
||||
```text
|
||||
content:adminRiskAction
|
||||
```
|
||||
|
||||
不推荐把 Content 剩余 38 个 operation 一次性推进 completed,也不推荐把 `content` 加入 domain-level completed allowlist。
|
||||
|
||||
`adminRiskAction` 与刚收口的 Admin Read 3 个只读 operation 相邻,但它不是读模型。它是治理写路径,必须证明 `commandId`、`expectedVersion`、行锁、目标归属、治理事实写入、command/audit envelope、幂等 replay、冲突拒绝、RBAC、API version、tenant 隔离和事务回滚全部闭合后,才允许进入 completed approval。
|
||||
|
||||
本审阅版只冻结候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Current["当前 Content<br/>13 completed / 38 needs_verification"] --> Candidate["下一包候选<br/>adminRiskAction"]
|
||||
Candidate --> Kind{"路径类型"}
|
||||
Kind -->|"POST 写命令"| Evidence["必须补 completed-grade write evidence"]
|
||||
Evidence --> E1["RBAC + API version"]
|
||||
Evidence --> E2["commandId + replay + conflict"]
|
||||
Evidence --> E3["expectedVersion + row lock"]
|
||||
Evidence --> E4["targetScope / targetIds 归属"]
|
||||
Evidence --> E5["governance fact + audit snapshot"]
|
||||
Evidence --> E6["错误路径 no-write / rollback"]
|
||||
Evidence --> Review["fresh spec/scope review<br/>fresh quality/testing review"]
|
||||
Review --> Exec["双 PASS 后写执行版"]
|
||||
Exec --> Approval{"用户明确批准后才实现"}
|
||||
Approval -->|"否"| Stay["保持 233/144/89"]
|
||||
Approval -->|"是"| Target["目标 233/145/88<br/>Content 14 completed / 37 needs_verification"]
|
||||
```
|
||||
|
||||
## 当前事实状态
|
||||
|
||||
工作区:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
当前分支状态:
|
||||
|
||||
```text
|
||||
dev/1.0.0...origin/dev/1.0.0
|
||||
```
|
||||
|
||||
当前 HEAD:
|
||||
|
||||
```text
|
||||
b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=144
|
||||
needsVerification=89
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
当前 domain 状态:
|
||||
|
||||
```text
|
||||
account completed=10 needsVerification=23
|
||||
content completed=13 needsVerification=38
|
||||
market completed=4 needsVerification=28
|
||||
meta completed=16 needsVerification=0
|
||||
```
|
||||
|
||||
目标 operation 当前状态:
|
||||
|
||||
```text
|
||||
adminRiskAction dedicated needs_verification requiresCommandId=true POST /admin-api/muse/content/works/{workId}/risk-actions
|
||||
```
|
||||
|
||||
本审阅版推荐的下一批目标值只是后续获批后的执行目标:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=145
|
||||
needsVerification=88
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
|
||||
content total=51
|
||||
completed=14
|
||||
needsVerification=37
|
||||
```
|
||||
|
||||
## 推荐候选
|
||||
|
||||
| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence |
|
||||
|---|---|---|---|---|
|
||||
| `content:adminRiskAction` | `POST` | `/admin-api/muse/content/works/{workId}/risk-actions` | admin 治理写命令,当前实现已有 command reserve、expectedVersion、行锁、治理事实和 audit envelope,但仍缺 HTTP + 真实 DB completed approval 证据 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 RBAC、API version、commandId、expectedVersion、replay、command conflict、revision conflict、targetScope / targetIds 归属、治理事实、audit snapshot、tenant 隔离和 no-write / rollback |
|
||||
|
||||
推荐理由:
|
||||
|
||||
- 当前 coverage report 已识别 `adminRiskAction.requiresCommandId=true`,说明它天然属于写命令切片,不能和 admin read 混在一起。
|
||||
- Controller 使用 `@PreAuthorize("@ss.hasPermission('muse:content:risk-action')")`,权限点与 Admin Read 的 `muse:content:query` 不同,必须单独证明。
|
||||
- Service 使用 `@Transactional(rollbackFor = Exception.class)`,写路径应以事务内证据证明成功写入和失败回滚。
|
||||
- Service 先 `reserveCommand`,再 `selectByIdForUpdate` 锁定作品行并校验 `expectedVersion`,适合单独验证幂等、冲突和并发边界。
|
||||
- OpenAPI 已声明 `RiskActionRequest.required=[commandId, action, reason, expectedVersion]`,当前不需要为了本切片修改合同。
|
||||
|
||||
## 不纳入本批的 operation
|
||||
|
||||
以下 37 个 Content operation 必须继续 `dedicated / needs_verification`:
|
||||
|
||||
```text
|
||||
content:adminListExportTasks
|
||||
content:adminListImportTasks
|
||||
content:confirmChapterParseResult
|
||||
content:rejectChapterParseResult
|
||||
content:downloadExportPackage
|
||||
content:getExportTask
|
||||
content:getImportTask
|
||||
content:getParseJob
|
||||
content:listParseJobChapters
|
||||
content:batchConfirmChapters
|
||||
content:retryParseJob
|
||||
content:createWork
|
||||
content:deleteWork
|
||||
content:updateWork
|
||||
content:deleteBlock
|
||||
content:mergeBlocks
|
||||
content:splitBlock
|
||||
content:mergeBlockSuggestion
|
||||
content:createChapter
|
||||
content:deleteChapter
|
||||
content:updateChapter
|
||||
content:createBlock
|
||||
content:reorderChapters
|
||||
content:validateDynamicFields
|
||||
content:exportWork
|
||||
content:createExportTask
|
||||
content:createImportTask
|
||||
content:listMetaProjections
|
||||
content:getMetaProjection
|
||||
content:createParseJob
|
||||
content:listPlanningCandidates
|
||||
content:createPlanningCandidate
|
||||
content:getPlanningCandidate
|
||||
content:confirmPlanningCandidate
|
||||
content:discardPlanningCandidate
|
||||
content:createStyleCheck
|
||||
content:getStyleCheckResult
|
||||
```
|
||||
|
||||
排除原因:
|
||||
|
||||
- `adminListImportTasks` / `adminListExportTasks` 是任务读模型,证据应覆盖导入导出任务状态、产物和 work 关联,不应夹入治理写命令。
|
||||
- 创建、更新、删除、重排、拆分、合并等 Content 写命令有各自的 revision、source snapshot、outbox 和结构一致性边界,应独立切片。
|
||||
- 导入、导出、解析、下载仍缺 FileService、任务产物和真实下载字节闭环 evidence。
|
||||
- Meta projection、AI planning candidate、style check、suggestion 等能力依赖 Meta / AI / Knowledge 外部 owner closure 和运行时证据。
|
||||
|
||||
## 已有实现证据
|
||||
|
||||
### OpenAPI 合同
|
||||
|
||||
OpenAPI 当前 operation:
|
||||
|
||||
```text
|
||||
POST /admin-api/muse/content/works/{workId}/risk-actions
|
||||
operationId: adminRiskAction
|
||||
security: adminBearerAuth
|
||||
```
|
||||
|
||||
请求体 `RiskActionRequest`:
|
||||
|
||||
```text
|
||||
required: commandId, action, reason, expectedVersion
|
||||
action enum: warn, restrict, block_content, force_archive, notify_owner
|
||||
targetScope enum: work, chapter, block
|
||||
targetIds: chapter 或 block 粒度必填
|
||||
expectedVersion: 作品当前版本号,用于乐观锁
|
||||
duration: restrict 动作限制时长
|
||||
```
|
||||
|
||||
响应体 `RiskActionResult`:
|
||||
|
||||
```text
|
||||
actionId
|
||||
status: executed | pending_notification
|
||||
affectedScope
|
||||
auditLogId
|
||||
```
|
||||
|
||||
本切片不需要修改 `docs/api-contracts/content/openapi.yaml`。
|
||||
|
||||
### Controller 入口
|
||||
|
||||
`AdminContentController.adminRiskAction`:
|
||||
|
||||
```text
|
||||
POST /muse/content/works/{workId}/risk-actions
|
||||
@PreAuthorize("@ss.hasPermission('muse:content:risk-action')")
|
||||
requireApiVersion()
|
||||
contentAdminService.adminRiskAction(getLoginUserId(), workId, reqVO)
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- Controller-local mapping 是 `/muse/content/...`;对外 OpenAPI 路径仍以 `/admin-api/muse/...` 为准。
|
||||
- 执行版必须用当前项目既有 admin MockMvc / Web 测试上下文证明最终 admin API 路径可访问。
|
||||
- 权限证据必须来自启用 method security 的 HTTP 请求,不能只靠反射检查注解。
|
||||
|
||||
### Service 写链路
|
||||
|
||||
`ContentAdminServiceImpl.adminRiskAction` 当前链路:
|
||||
|
||||
1. 校验 `expectedVersion` 非空。
|
||||
2. 用请求体和 `workId` 构造 scoped request hash。
|
||||
3. `ContentCommandService.reserveCommand(commandId, "adminRiskAction", operatorUserId, "work", workId, requestHash)`。
|
||||
4. 如果 command 已存在且 hash / target 匹配,返回 `resultSnapshot` replay。
|
||||
5. `WorkMapper.selectByIdForUpdate(workId, tenantId)` 锁定作品行。
|
||||
6. 校验 `work.revision == expectedVersion`。
|
||||
7. 校验 action、targetScope、targetIds。
|
||||
8. 对 chapter / block target 沿 `targetIds -> chapter/block -> work` 校验从属关系。
|
||||
9. 插入 `muse_content_governance_action`。
|
||||
10. 构造 `RiskActionResultRespVO`。
|
||||
11. `ContentCommandAuditService.recordSucceededWithAudit(...)` 记录 command/audit/result snapshot。
|
||||
|
||||
关键语义:
|
||||
|
||||
- `notify_owner` 结果状态为 `pending_notification`。
|
||||
- 其它当前支持 action 结果状态为 `executed`。
|
||||
- `affectedScope` 对 work 粒度为 `work:{workId}`;chapter/block 粒度为 `{targetScope}:{targetIds}`。
|
||||
- 成功审计 target 仍绑定业务目标 work,不能改写成治理动作 id,否则 command replay 会被判为跨目标冲突。
|
||||
|
||||
### Schema 证据来源
|
||||
|
||||
V9 当前定义:
|
||||
|
||||
```text
|
||||
muse_content_governance_action
|
||||
tenant_id
|
||||
work_id
|
||||
operator_user_id
|
||||
action
|
||||
reason
|
||||
target_scope default work
|
||||
target_ids jsonb
|
||||
expected_version not null
|
||||
status
|
||||
audit_log_id
|
||||
command_id not null
|
||||
uk_muse_content_governance_action_command unique (tenant_id, command_id)
|
||||
idx_muse_content_governance_action_work
|
||||
trg_muse_content_governance_action_updated_at
|
||||
```
|
||||
|
||||
执行版不需要新增 migration,但必须 fresh 证明 `_test` 库从 V1 到当前版本 clean migrate。当前 Content 已有 V21 event publish outbox,因此本切片的真实 schema gate 应至少证明 V1-V21 clean migrate 成功,并断言治理动作表、唯一约束、work 索引、JSONB `target_ids`、`tenant_id` / `expected_version` / `command_id` 关键列、updated_at trigger 真实存在。
|
||||
|
||||
## 当前测试证据与缺口
|
||||
|
||||
已有 focused tests 可作为辅助证据:
|
||||
|
||||
- `AdminContentControllerTest.should_requireRbacAnnotationsForAdminContentApis`
|
||||
- `AdminContentControllerTest.should_rejectRiskActionMissingReason`
|
||||
- `AdminContentControllerTest.should_rejectRiskActionMissingExpectedVersionAtValidationLayer`
|
||||
- `AdminContentControllerTest.should_createRiskActionWithCommonResult`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_expectedVersionMismatch`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_workMissing`
|
||||
- `ContentAdminServiceTest.should_createRiskActionWithAudit`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_actionUnsupported`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_targetScopeUnsupported`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_chapterTargetIdsBelongToOtherWork`
|
||||
- `ContentAdminServiceTest.should_rejectRiskAction_when_blockTargetIdsBelongToOtherWork`
|
||||
- `ContentAdminServiceTest.should_replayRiskActionCommand`
|
||||
|
||||
这些测试不能直接等同 completed。缺口包括:
|
||||
|
||||
- 没有 HTTP + 真实 PostgreSQL `_test` 的端到端证据。
|
||||
- 缺 method security 真实拒绝路径证据。
|
||||
- 缺真实 tenant interceptor 下的 cross tenant 不可见 / no-write 证据。
|
||||
- 缺 command table、governance action、Content outbox、work/chapter/block 的全表快照或关键字段 no-write / rollback 证据。
|
||||
- 缺 V1-V21 clean migrate 后 `muse_content_governance_action` / `muse_content_command_log` schema、唯一键、JSONB、trigger、tenant 关键列证据与 XML 防空跑。
|
||||
- 缺 command replay / command conflict 的真实 DB 证据,尤其同 commandId 不同 action、reason、targetScope、targetIds、workId、expectedVersion。
|
||||
- 缺并发/行锁语义的可接受代理证据,例如 `FOR UPDATE` SQL 与真实事务 revision stale / conflict 场景。
|
||||
- 缺 P1R scanner/report/gate 在获批后从 `233/144/89` 推进到 `233/145/88` 的 RED/GREEN 证据。
|
||||
|
||||
## 必补证据标准
|
||||
|
||||
后续执行版必须至少覆盖以下矩阵。
|
||||
|
||||
### HTTP / 权限 / 版本
|
||||
|
||||
- 有 `X-API-Version` 的成功请求。
|
||||
- 缺或不支持 `X-API-Version` 的统一错误响应。
|
||||
- 有 `muse:content:risk-action` 权限时允许进入 service。
|
||||
- 缺 `muse:content:risk-action` 权限时 403,且不写 command、governance action、outbox 或业务表。
|
||||
- 登录 operator user id 必须进入 `operator_user_id` 和 command/audit 事实。
|
||||
- command/audit snapshot 必须显式断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId=workId`、`status=succeeded`、`reason`、`requestSummary.action`、`requestSummary.targetScope`、`requestSummary.targetIds`、`requestSummary.expectedVersion` 和 `result.actionId/status/affectedScope/auditLogId`。
|
||||
|
||||
### 成功写路径
|
||||
|
||||
- work 粒度 happy path:写入 1 条 governance action,写入 1 条 command/audit envelope,返回 actionId/status/affectedScope/auditLogId。
|
||||
- chapter 粒度 happy path:targetIds 必须属于 URL work,返回 chapter affected scope。
|
||||
- block 粒度 happy path:targetIds 必须沿 block -> chapter -> work 归属到 URL work。
|
||||
- `notify_owner` 返回 `pending_notification`。
|
||||
- `block_content` / `warn` / `restrict` / `force_archive` 返回 `executed`。
|
||||
- 结果 snapshot 可被 replay 解析,重复同 commandId / 同请求返回同一结果,不重复插入治理事实。
|
||||
|
||||
### command / revision / target 负路径
|
||||
|
||||
- 缺 `commandId` validation 失败,不写任何事实。
|
||||
- 缺 `expectedVersion` validation 失败,不写任何事实。
|
||||
- `expectedVersion` 与 work revision 不一致,返回 revision conflict,不写治理事实和 succeeded audit。
|
||||
- 同 commandId 不同 action、reason、targetScope、targetIds、workId 或 expectedVersion 返回 command conflict,不写第二条治理事实。
|
||||
- 同 commandId 不同 workId 返回 command conflict 或目标冲突,不泄露目标数据。
|
||||
- unsupported action 返回统一错误并 no-write。
|
||||
- unsupported targetScope 返回统一错误并 no-write。
|
||||
- chapter targetIds 为空、含 null、missing、属于其它 work、属于其它 tenant,均拒绝并 no-write。
|
||||
- block targetIds 为空、含 null、missing、属于其它 work、章节链路不匹配、属于其它 tenant,均拒绝并 no-write。
|
||||
- missing work / cross tenant work 返回不可见或 not found 语义,不写任何事实。
|
||||
|
||||
### no-write / rollback
|
||||
|
||||
错误路径必须对以下表做全表快照或关键字段快照,不只比较行数:
|
||||
|
||||
```text
|
||||
muse_content_work
|
||||
muse_content_chapter
|
||||
muse_content_block
|
||||
muse_content_governance_action
|
||||
muse_content_command_log
|
||||
muse_content_event_publish_outbox
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 成功写路径允许 governance action 与 command/audit 变化。
|
||||
- 错误路径必须证明上述表没有 INSERT / DELETE / UPDATE 污染。
|
||||
- 如果测试自身需要 seed 或清理,必须在 no-write baseline 采样之前完成。
|
||||
|
||||
### Flyway / schema
|
||||
|
||||
执行版必须 fresh 证明:
|
||||
|
||||
- `_test` 库只使用测试库名,禁止连接非 `_test`。
|
||||
- 密码只从环境变量读取,禁止通过 JVM system property 或 JDBC query 传入。
|
||||
- V1-V21 clean migrate 成功。
|
||||
- `muse_content_governance_action` 表、关键列、JSONB `target_ids`、unique command 约束、work 索引和 updated_at trigger 存在。
|
||||
- duplicate `(tenant_id, command_id)` 被数据库唯一约束拒绝。
|
||||
|
||||
## 风险和取舍
|
||||
|
||||
1. `adminRiskAction` 不是 Admin Read 的延伸证据。
|
||||
|
||||
Admin Read 已证明治理事实可读;它不能证明治理事实写入、command replay、expectedVersion、行锁和 audit snapshot。
|
||||
|
||||
2. `selectByIdForUpdate` 是本切片的核心边界。
|
||||
|
||||
执行版必须证明 expectedVersion 校验发生在同一事务的锁定作品行之后;否则 completed 证据不足以支撑并发写路径。
|
||||
|
||||
3. `targetIds` 归属不能只查目标 id 存在。
|
||||
|
||||
chapter 必须属于 URL work;block 必须同时满足 block.workId 与 block.chapterId 对应 chapter.workId 都属于 URL work。
|
||||
|
||||
4. command replay 不能只证明不报错。
|
||||
|
||||
必须证明 replay 返回原 `resultSnapshot`,且没有重复插入治理事实、command/audit 或 outbox。
|
||||
|
||||
5. 不应新增 Content audit 表。
|
||||
|
||||
当前 Content audit snapshot 保存在 `muse_content_command_log.result_snapshot` / command audit envelope 中。执行版不得为了测试方便新增表或修改 schema。
|
||||
|
||||
6. 不把生成 DTO 当成唯一合同证据。
|
||||
|
||||
OpenAPI 已声明 `expectedVersion` required,服务端 `RiskActionReqVO` 也有 `@NotNull` validation。后续执行版如果引用生成物或客户端 DTO,必须说明其新鲜度和同步关系;completed 证据应以 OpenAPI、服务端 VO validation、HTTP 请求和真实 DB 结果闭合。
|
||||
|
||||
## 后续执行版要求
|
||||
|
||||
后续执行版必须包含:
|
||||
|
||||
1. 精确目标 operation 清单:只包括 `content:adminRiskAction`。
|
||||
2. operation-level approval:不把 `content` 加入 domain allowlist。
|
||||
3. 实施前 RED:旧 report 下 P1R coverage gate 必须因 `adminRiskAction` 尚未 completed 而失败。
|
||||
4. HTTP + real PostgreSQL `_test` gate:新增独立 `P1rContentAdminRiskActionCompletedApprovalIT` 或等价命名。
|
||||
5. Web/MyBatis/DataSource 测试上下文:必须启用真实 tenant interceptor、真实 mapper、真实 service、真实 controller 和 method security。
|
||||
6. RBAC / API version / CommonResult 错误响应证据。
|
||||
7. happy / replay / command conflict / revision conflict / validation / missing / cross tenant / target mismatch / no-write matrix。
|
||||
8. focused tests:运行 `ContentAdminServiceTest`、`AdminContentControllerTest` 并做 XML 防空跑。
|
||||
9. P1R mixed gates:同步所有读取全局 summary、Content 状态或非目标 domain 防回退的 gate。
|
||||
10. scanner/report 更新策略:只在用户批准后追加 1 个 `content:adminRiskAction` operation-level allowlist。
|
||||
11. protected diff:OpenAPI、Content main/java、SQL migration 默认不得修改。
|
||||
12. `.agent` 和 `docs/memorys` 留痕。
|
||||
13. rollback:撤回 scanner operation key、coverage report、P1R gate 目标值和新增 `_test`,恢复 `adminRiskAction` 为 `needs_verification`。
|
||||
|
||||
## 审批前置条件
|
||||
|
||||
进入执行版前需要 fresh review 双 PASS:
|
||||
|
||||
- fresh spec/scope review:确认只覆盖 `content:adminRiskAction`,不夹带 admin import/export task、Content CRUD 写命令、Content domain-level completed 或其它 domain。
|
||||
- fresh quality/feasibility/testing review:确认 RBAC、tenant 拦截、command replay、expectedVersion、targetScope / targetIds、no-write / rollback、schema gate、XML 防空跑和 mixed gate 同步可落地。
|
||||
|
||||
执行版双 PASS 后,仍必须由用户明确批准以下事项后才允许实现:
|
||||
|
||||
1. 只审批 `content:adminRiskAction`。
|
||||
2. 继续 operation-level approval,不把 `content` 加入 domain allowlist。
|
||||
3. 允许按执行版修改 scanner、coverage report、P1R gate tests、新增 HTTP+DB `_test` 和 memory。
|
||||
4. 允许同步 P1R mixed gate 的 summary / Content / 非目标域防回退断言。
|
||||
|
||||
## 本审阅版验收标准
|
||||
|
||||
- 候选 operation 和目标值可由当前 report 机械推导。
|
||||
- 非目标 37 个 Content operation 明确保持 `needs_verification`。
|
||||
- `adminRiskAction` 与 Admin Read、admin import/export task、Content CRUD 写命令的边界清晰。
|
||||
- evidence 标准包含 RBAC、tenant 隔离、commandId、expectedVersion、行锁、target 归属、governance action、audit snapshot、replay、conflict、no-write / rollback 和 XML 防空跑。
|
||||
- 未修改 OpenAPI、scanner、coverage report、业务实现、SQL migration 或 P1R gate。
|
||||
@ -0,0 +1,615 @@
|
||||
# P1R Content Admin RiskAction 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
|
||||
|
||||
## 结论
|
||||
|
||||
本执行版只锁定 Content 管理后台治理写命令 1 个 operation-level completed approval:
|
||||
|
||||
```text
|
||||
content:adminRiskAction
|
||||
```
|
||||
|
||||
本轮不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51 completed,不修改 OpenAPI,不修改 Content 业务实现,不修改 SQL migration。
|
||||
|
||||
获批后目标 coverage summary 为:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=145
|
||||
needsVerification=88
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
Content 域目标状态为:
|
||||
|
||||
```text
|
||||
content total=51
|
||||
completed=14
|
||||
needsVerification=37
|
||||
```
|
||||
|
||||
当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Current["当前 Content<br/>13 completed / 38 needs_verification"] --> Exec["执行版<br/>锁定 adminRiskAction 1 op"]
|
||||
Exec --> Review["fresh execution spec/scope review<br/>fresh execution quality/testing review"]
|
||||
Review --> Approval{"用户明确批准<br/>1 op / operation-level / allowed diff / mixed gates"}
|
||||
Approval -->|否| Stay["保持当前 coverage<br/>233/144/89"]
|
||||
Approval -->|是| Red["TDD RED<br/>先改 gate 期望<br/>旧 report 必须失败"]
|
||||
Red --> Runtime["HTTP + real DB _test<br/>RBAC + tenant + command + audit + rollback"]
|
||||
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
|
||||
b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁
|
||||
```
|
||||
|
||||
当前分支:
|
||||
|
||||
```text
|
||||
dev/1.0.0...origin/dev/1.0.0
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
233 144 89 0 0 0
|
||||
```
|
||||
|
||||
当前 domain 状态:
|
||||
|
||||
```text
|
||||
account completed=10 needsVerification=23
|
||||
content completed=13 needsVerification=38
|
||||
market completed=4 needsVerification=28
|
||||
meta completed=16 needsVerification=0
|
||||
```
|
||||
|
||||
当前目标 operation 状态:
|
||||
|
||||
```text
|
||||
adminRiskAction dedicated needs_verification true POST /admin-api/muse/content/works/{workId}/risk-actions
|
||||
```
|
||||
|
||||
已验证的只读实现事实:
|
||||
|
||||
- OpenAPI `RiskActionRequest.required` 已包含 `commandId`、`action`、`reason`、`expectedVersion`。
|
||||
- `AdminContentController.adminRiskAction` 使用 `@PreAuthorize("@ss.hasPermission('muse:content:risk-action')")`,并调用 `requireApiVersion()`。
|
||||
- Controller-local mapping 为 `/muse/content/works/{workId}/risk-actions`;对外 OpenAPI 路径为 `/admin-api/muse/content/works/{workId}/risk-actions`。
|
||||
- `ContentAdminServiceImpl.adminRiskAction` 使用 `@Transactional(rollbackFor = Exception.class)`。
|
||||
- Service 先通过 `ContentCommandService.reserveCommand(...)` 处理 commandId 和 replay,再通过 `WorkMapper.selectByIdForUpdate(workId, tenantId)` 锁定作品行。
|
||||
- Service 在锁定作品行后校验 `expectedVersion`、action、targetScope、targetIds,并校验 chapter / block target 归属 URL work。
|
||||
- 成功路径插入 `muse_content_governance_action`,并通过 `ContentCommandAuditService.recordSucceededWithAudit(...)` 写 command/audit/result snapshot。
|
||||
- 当前实现不写 `muse_content_event_publish_outbox`;本切片不证明 Events propagation。
|
||||
- 现有 `AdminContentControllerTest` / `ContentAdminServiceTest` 只能作为 focused 辅助证据,不能替代 completed-grade HTTP + real PostgreSQL `_test`。
|
||||
|
||||
## 执行边界
|
||||
|
||||
### 必须先获用户明确批准
|
||||
|
||||
实现前必须同时获得以下 4 项批准:
|
||||
|
||||
1. 批准本轮只审批 `content:adminRiskAction` 一个 operation。
|
||||
2. 批准继续使用 operation-level approval,不把 `content` 加入 domain-level completed allowlist。
|
||||
3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Admin RiskAction HTTP+DB `_test`、memory 和 `.agent`。
|
||||
4. 批准同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为 `233/145/88/0/0/0` 与 Content `14 completed / 37 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/P1rContentRealApiGateTest.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/P1rAccountRealApiGateTest.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/P1rContentAdminRiskActionCompletedApprovalIT.java`
|
||||
- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAdminServiceTest.java`,仅限补充本执行版缺失的 focused behavior 证据。
|
||||
- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/admin/AdminContentControllerTest.java`,仅限补充本执行版缺失的 controller contract 证据。
|
||||
- `docs/agent-specs/.agent`
|
||||
- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md`
|
||||
- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md`
|
||||
- `docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md`
|
||||
|
||||
如 implementation 发现必须修改 Content 业务实现、OpenAPI、SQL migration、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。
|
||||
|
||||
### 本轮禁止变更
|
||||
|
||||
- 不修改 7 个 OpenAPI。
|
||||
- 不修改 Content main/java 业务实现来掩盖 coverage 缺口。
|
||||
- 不新增或修改 SQL migration。
|
||||
- 不把 `content` 加入 domain-level completed allowlist。
|
||||
- 不推进 Content 51/51 completed。
|
||||
- 不推进 Account remaining 23、Market remaining 28、Content remaining 37 或总 P1R completed。
|
||||
- 不推进 admin import/export task、Content CRUD 写命令、AI planning candidate、style check、Meta projection、dynamic fields、FileService、import/export/parse operation。
|
||||
- 不把 Admin Read 的 riskFlag 聚合证据当成 `adminRiskAction` completed 证据。
|
||||
- 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。
|
||||
|
||||
## 审批清单
|
||||
|
||||
| operation key | Method | Path | 目标状态 | 完成证据边界 |
|
||||
|---|---|---|---|---|
|
||||
| `content:adminRiskAction` | `POST` | `/admin-api/muse/content/works/{workId}/risk-actions` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 API version、method security RBAC、commandId、replay、command conflict、expectedVersion、row lock、targetScope / targetIds 归属、governance action、command/audit snapshot、tenant 隔离、schema gate、no-write / rollback |
|
||||
|
||||
## 保持 needs_verification 的 Content operation
|
||||
|
||||
以下 37 个 operation 必须保持 `dedicated / needs_verification`:
|
||||
|
||||
```text
|
||||
content:adminListExportTasks
|
||||
content:adminListImportTasks
|
||||
content:confirmChapterParseResult
|
||||
content:rejectChapterParseResult
|
||||
content:downloadExportPackage
|
||||
content:getExportTask
|
||||
content:getImportTask
|
||||
content:getParseJob
|
||||
content:listParseJobChapters
|
||||
content:batchConfirmChapters
|
||||
content:retryParseJob
|
||||
content:createWork
|
||||
content:deleteWork
|
||||
content:updateWork
|
||||
content:deleteBlock
|
||||
content:mergeBlocks
|
||||
content:splitBlock
|
||||
content:mergeBlockSuggestion
|
||||
content:createChapter
|
||||
content:deleteChapter
|
||||
content:updateChapter
|
||||
content:createBlock
|
||||
content:reorderChapters
|
||||
content:validateDynamicFields
|
||||
content:exportWork
|
||||
content:createExportTask
|
||||
content:createImportTask
|
||||
content:listMetaProjections
|
||||
content:getMetaProjection
|
||||
content:createParseJob
|
||||
content:listPlanningCandidates
|
||||
content:createPlanningCandidate
|
||||
content:getPlanningCandidate
|
||||
content:confirmPlanningCandidate
|
||||
content:discardPlanningCandidate
|
||||
content:createStyleCheck
|
||||
content:getStyleCheckResult
|
||||
```
|
||||
|
||||
## 证据分层
|
||||
|
||||
| Evidence | 文件 / 测试 | 目的 |
|
||||
|---|---|---|
|
||||
| Operation inventory / coverage gate | `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest` 和 mixed gates | 证明只推进获批 1 个 operation,非目标 domain 不回退 |
|
||||
| Admin RiskAction HTTP+DB `_test` | 新增 `P1rContentAdminRiskActionCompletedApprovalIT` | 证明 `adminRiskAction` 经 MockMvc HTTP 入口访问真实 service / mapper / PostgreSQL `_test` 数据 |
|
||||
| Tenant / schema gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内验证 V1-V21 clean migrate 与 governance action 依赖 schema | 证明 Work / Chapter / Block / GovernanceAction / CommandLog / Outbox schema 和 tenant interceptor 真实可用 |
|
||||
| Method security gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内启用 method security 与 `ss` bean | 证明 admin RBAC 缺权限拒绝不是注解反射假证据 |
|
||||
| Command / audit gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内读取 `muse_content_command_log.result_snapshot` | 证明 operationId、side、target、requestSummary、result snapshot 和 replay 语义真实落库 |
|
||||
| Focused unit/controller tests | `ContentAdminServiceTest`、`AdminContentControllerTest` | 支撑 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=="content" and .operationId=="adminRiskAction") | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring),.method,.path] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```text
|
||||
233 144 89 0 0 0
|
||||
adminRiskAction dedicated needs_verification true POST /admin-api/muse/content/works/{workId}/risk-actions
|
||||
```
|
||||
|
||||
- [ ] TDD RED:先修改 P1R gate 期望值和 `adminRiskAction` completed 断言,但不修改 scanner/report。
|
||||
|
||||
RED 目标:
|
||||
|
||||
- `P1rApiCoverageReportTest` 期望 summary `completed=145 / needsVerification=88`。
|
||||
- `P1rContentRealApiGateTest` 期望 Content `14 completed / 37 needs_verification`,`adminRiskAction` completed。
|
||||
- 7 个 mixed gate 中所有硬编码 Content `13/38` 或 summary `144/89` 的断言同步为 `14/37` 或 `145/88`。
|
||||
|
||||
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,P1rContentRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rAccountRealApiGateTest,P1rMetaRealApiGateTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
期望:BUILD FAILURE。失败点必须来自旧 report 仍是 `233/144/89` 或 `adminRiskAction` 仍为 `needs_verification`;不得来自编译错误或无关测试失败。
|
||||
|
||||
### Task 2:新增 Admin RiskAction HTTP+DB `_test`
|
||||
|
||||
- [ ] 新增:
|
||||
|
||||
```text
|
||||
muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminRiskActionCompletedApprovalIT.java
|
||||
```
|
||||
|
||||
测试上下文要求:
|
||||
|
||||
- 使用 `@SpringBootTest(webEnvironment = MOCK)` + `WebApplicationContext` + `MockMvcBuilders.webAppContextSetup(...)`。
|
||||
- 真实 `/admin-api/muse/**` 前缀进入 `AdminContentController`。
|
||||
- 导入 `MuseWebAutoConfiguration`,并提供 test-local `ApiErrorLogCommonApi` stub,返回 `CommonResult.success(true)`,满足 `GlobalExceptionHandler` 依赖。
|
||||
- 启用 `@EnableMethodSecurity(securedEnabled = true)`,并提供 `@Bean("ss")` 的 test-local `SecurityFrameworkService`,权限点必须是 `muse:content:risk-action`。
|
||||
- 使用真实 `ContentAdminServiceImpl`、`ContentCommandServiceImpl`、`ContentAuditServiceImpl`、真实 mapper 和真实 PostgreSQL `_test` 数据库。
|
||||
- 注册真实 MyBatis tenant interceptor:`TenantLineInnerInterceptor` + `TenantDatabaseInterceptor(new TenantProperties())`。
|
||||
- 所有 `ContentAdminServiceImpl` 的 `@Resource` 依赖必须可启动;不得只注册 Work/Chapter/Block/GovernanceAction mapper 后让上下文靠 mock 补洞。
|
||||
- Configuration 必须导入所有 ContentAdminServiceImpl 字段依赖需要的 mapper / service / facade stub;如果某个 facade 不属于本切片,应提供 fail-fast stub,并证明本切片未调用它。
|
||||
- `TARGET_VERSION="21"`,执行 V1-V21 clean migrate。
|
||||
- 数据库密码只能来自环境变量,禁止 JVM system property 或 JDBC query 参数传入。
|
||||
- 保存并在 `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user` system property,避免污染同一 Surefire JVM 后续 Flyway 测试。
|
||||
|
||||
建议测试库:
|
||||
|
||||
```text
|
||||
muse_p1r_content_admin_risk_action_completed_approval_test
|
||||
```
|
||||
|
||||
建议环境变量优先级:
|
||||
|
||||
```text
|
||||
P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD
|
||||
P1R_CONTENT_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_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_admin_risk_action_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_CONTENT_ADMIN_RISK_ACTION_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_CONTENT_ADMIN_RISK_ACTION_COMPLETED_APPROVAL_TEST_DB"
|
||||
```
|
||||
|
||||
执行版 implementation 必须显式拒绝非 `_test` 数据库名;如果建库变量不是 `_test` 后缀,测试必须在连接前失败。
|
||||
|
||||
- [ ] 新增基础 seed 与快照工具。
|
||||
|
||||
seed 要求:
|
||||
|
||||
- 当前 tenant 的 work,revision 固定为 `7`。
|
||||
- 当前 tenant 下属于该 work 的 chapter / block。
|
||||
- 当前 tenant 下另一个 work、另一个 chapter / block,用于 cross-work target 拒绝。
|
||||
- 另一个 tenant 的 work / chapter / block,用于 tenant 隔离。
|
||||
- 当前 login user 为 admin user,tenant 与 visitTenant 都设置为当前 tenant。
|
||||
|
||||
错误路径 no-write 快照必须覆盖:
|
||||
|
||||
```text
|
||||
muse_content_work
|
||||
muse_content_chapter
|
||||
muse_content_block
|
||||
muse_content_governance_action
|
||||
muse_content_command_log
|
||||
muse_content_event_publish_outbox
|
||||
```
|
||||
|
||||
快照必须用 `row_to_json(t)::text` 或等价关键字段快照,不能只比对行数。
|
||||
|
||||
- [ ] mandatory test matrix。
|
||||
|
||||
最低测试类行为:
|
||||
|
||||
1. 拒绝 unsafe DB 配置:非 `_test` 数据库、JVM password system property、JDBC credential query。
|
||||
2. 验证 schema:V1-V21 clean migrate;`muse_content_governance_action` 表、关键列、JSONB `target_ids`、unique command 约束、work 索引、updated_at trigger 存在;duplicate `(tenant_id, command_id)` 返回 SQLState `23505`。
|
||||
3. API version / RBAC:缺或不支持 `X-API-Version` no-write;缺 `muse:content:risk-action` 权限返回 403 CommonResult 且 no-write;成功请求必须证明 `ss.hasPermission("muse:content:risk-action")` 被调用。
|
||||
4. work scope happy path:成功写 1 条 governance action 和 1 条 command/audit envelope,返回 `actionId/status/affectedScope/auditLogId`;`affectedScope=work:{workId}`;`outbox` 不写。
|
||||
5. chapter / block scope happy path:targetIds 属于 URL work 才能成功;chapter 与 block affectedScope 正确。
|
||||
6. action status:`notify_owner` 返回 `pending_notification`;`warn` / `restrict` / `block_content` / `force_archive` 返回 `executed`。
|
||||
7. command replay:同 commandId 同请求返回同一 `resultSnapshot`,不重复插入 governance action、command/audit 或 outbox。
|
||||
8. command conflict:同 commandId 不同 action、reason、targetScope、targetIds、workId 或 expectedVersion 返回 command conflict,且 no-write。
|
||||
9. revision conflict:`expectedVersion` 与 work revision 不一致返回 `CONTENT_REVISION_CONFLICT`,且 no-write。
|
||||
10. validation / unsupported:缺 commandId、缺 expectedVersion、unsupported action、unsupported targetScope 均拒绝并 no-write。
|
||||
11. targetIds invalid:chapter / block targetIds 为空、含 null、missing、cross-work、cross-tenant、block -> chapter -> work 链路不匹配,均拒绝并 no-write。
|
||||
12. missing / cross tenant work:返回 not found 或不可见语义,不泄露其它 tenant 数据,且 no-write。
|
||||
13. row-lock proxy:至少用 `WorkMapper.selectByIdForUpdate` 源码 / mapper SQL 断言包含 `FOR UPDATE`,并用真实 DB stale expectedVersion 场景证明锁后 revision 校验会拒绝并 rollback。若要做真实并发事务测试,必须保证可重复、无 sleep 竞态和可诊断日志。
|
||||
14. command/audit row fact:断言 `muse_content_command_log` 行级字段 `tenant_id`、`command_id`、`command_type=adminRiskAction`、`owner_user_id=admin user`、`target_type=work`、`target_id=workId`、`request_hash` 非空且 replay 时稳定。
|
||||
15. governance action row fact:断言 `muse_content_governance_action` 行级字段 `tenant_id`、`work_id`、`operator_user_id=admin user`、`command_id`、`audit_log_id`、`action`、`reason`、`target_scope`、`target_ids`、`expected_version`、`status`。
|
||||
16. command/audit snapshot:解析 `muse_content_command_log.result_snapshot`,断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId=workId`、`status=succeeded`、`reason`、`requestSummary.action`、`requestSummary.targetScope`、`requestSummary.targetIds`、`requestSummary.expectedVersion`、`result.actionId/status/affectedScope/auditLogId`。
|
||||
|
||||
### Task 3:Focused tests
|
||||
|
||||
- [ ] 运行 Content focused tests:
|
||||
|
||||
```bash
|
||||
cd muse-cloud
|
||||
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
|
||||
mvn -o test -pl muse-module-content/muse-module-content-server,muse-server -am \
|
||||
-Dtest=ContentAdminServiceTest,AdminContentControllerTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```text
|
||||
ContentAdminServiceTest: pass
|
||||
AdminContentControllerTest: pass
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
如执行版实现发现 focused tests 缺少审阅版硬门槛,可只在允许范围内补充测试,不得改 main/java。
|
||||
|
||||
### Task 4:GREEN scanner / report
|
||||
|
||||
- [ ] scanner 只追加 operation-level key:
|
||||
|
||||
```text
|
||||
content:adminRiskAction
|
||||
```
|
||||
|
||||
禁止修改:
|
||||
|
||||
```text
|
||||
APPROVED_COMPLETED_DOMAINS
|
||||
```
|
||||
|
||||
- [ ] 重新生成 coverage report:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
期望 summary:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
```
|
||||
|
||||
期望 Content:
|
||||
|
||||
```text
|
||||
completed=14
|
||||
needsVerification=37
|
||||
```
|
||||
|
||||
期望 operation:
|
||||
|
||||
```text
|
||||
adminRiskAction dedicated completed true
|
||||
```
|
||||
|
||||
仍必须抽样断言这些非目标 operation 保持 `needs_verification`:
|
||||
|
||||
```text
|
||||
adminListImportTasks
|
||||
adminListExportTasks
|
||||
createWork
|
||||
updateWork
|
||||
createChapter
|
||||
mergeBlockSuggestion
|
||||
exportWork
|
||||
listPlanningCandidates
|
||||
createStyleCheck
|
||||
getStyleCheckResult
|
||||
```
|
||||
|
||||
### Task 5:P1R mixed gates
|
||||
|
||||
- [ ] 运行 P1R 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,P1rContentRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rAccountRealApiGateTest,P1rMetaRealApiGateTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
- `P1rApiCoverageReportTest` summary 为 `233/145/88/0/0/0`。
|
||||
- `P1rContentRealApiGateTest` Content 为 `14/37`,`adminRiskAction` completed。
|
||||
- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rMetaRealApiGateTest` 内所有 Content partial approval 防回退断言同步为 `14/37`。
|
||||
- AI 41、Knowledge 59、Events 1、Meta 16、Account 10、Market 4 状态不回退。
|
||||
|
||||
### Task 6:Content combined DB gate
|
||||
|
||||
- [ ] 使用独立 `_test` 库组合运行 Content completed approval DB gates:
|
||||
|
||||
```bash
|
||||
source ~/.config/muse-repo/infra.env
|
||||
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_TEST_DB=muse_p1r_content_admin_risk_action_combined_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_CONTENT_ADMIN_RISK_ACTION_COMBINED_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_CONTENT_ADMIN_RISK_ACTION_COMBINED_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=P1rContentAdminRiskActionCompletedApprovalIT,P1rContentAdminReadCompletedApprovalIT,P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT \
|
||||
-Dflyway.postgresql.transactional.lock=false \
|
||||
-Dp1r.flyway.locations=filesystem:sql/muse \
|
||||
-Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_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
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 所有类 tests > 0。
|
||||
- failures=0、errors=0、skipped=0。
|
||||
- 证明新增 Admin RiskAction IT 不污染同 JVM 的 Flyway system properties。
|
||||
- 如 first run 暴露 password env 缺失或 system property 污染,必须修复后重跑,并记录失败根因与修复。
|
||||
|
||||
### Task 7:XML 防空跑
|
||||
|
||||
- [ ] 检查以下 XML 必须存在、mtime 在本轮验证窗口内、tests > 0、failures/errors/skipped 均为 0:
|
||||
|
||||
```text
|
||||
P1rContentAdminRiskActionCompletedApprovalIT
|
||||
P1rContentAdminReadCompletedApprovalIT
|
||||
P1rContentPlanningCompletedApprovalIT
|
||||
P1rContentCoreCompletedApprovalIT
|
||||
P1rContentEventsPublishFlywayMigrationIT
|
||||
ContentAdminServiceTest
|
||||
AdminContentControllerTest
|
||||
P1rApiCoverageReportTest
|
||||
P1rContentRealApiGateTest
|
||||
P1rEventsRealApiGateTest
|
||||
P1rAiRealApiGateTest
|
||||
P1rKnowledgeRealApiGateTest
|
||||
P1rMarketRealApiGateTest
|
||||
P1rAccountRealApiGateTest
|
||||
P1rMetaRealApiGateTest
|
||||
```
|
||||
|
||||
防空跑脚本可用 shell / awk / xmllint / existing local pattern,但不得把 XML 旧产物当 fresh evidence。
|
||||
|
||||
### Task 8:Protected diff / final checks
|
||||
|
||||
- [ ] 运行:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- [ ] 确认 protected diff 为空:
|
||||
|
||||
```bash
|
||||
git diff --name-only -- docs/api-contracts muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse
|
||||
```
|
||||
|
||||
期望:无输出。
|
||||
|
||||
- [ ] 确认 scanner/report 目标:
|
||||
|
||||
```bash
|
||||
jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
jq -r '.operations[] | select(.domain=="content" and (.operationId=="adminRiskAction" or .operationId=="adminListExportTasks" or .operationId=="adminListImportTasks" or .operationId=="createWork" or .operationId=="exportWork" or .operationId=="createStyleCheck")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring)] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
adminRiskAction dedicated completed true
|
||||
adminListExportTasks dedicated needs_verification false
|
||||
adminListImportTasks dedicated needs_verification false
|
||||
createWork dedicated needs_verification false
|
||||
exportWork dedicated needs_verification false
|
||||
createStyleCheck dedicated needs_verification true
|
||||
```
|
||||
|
||||
## Review gate
|
||||
|
||||
本执行版写完后必须先进行 fresh execution review:
|
||||
|
||||
1. fresh execution spec/scope review:
|
||||
- 确认只覆盖 `content:adminRiskAction`。
|
||||
- 确认不夹带 admin import/export task、Content CRUD 写命令、Content domain-level completed 或其它 domain。
|
||||
- 确认目标值 `233/145/88` 与 Content `14/37` 能从当前 `233/144/89` 与 Content `13/38` 加 1 个 operation 机械推导。
|
||||
- 确认 allowed / prohibited diff 自洽。
|
||||
|
||||
2. fresh execution quality/feasibility/testing review:
|
||||
- 确认 HTTP+DB `_test` harness 可复用现有 Admin Read / Planning 模式落地。
|
||||
- 确认 method security、tenant interceptor、command replay/conflict、expectedVersion/row lock、targetScope / targetIds、governance action、command/audit snapshot、no-write / rollback、schema gate、XML 防空跑和 mixed gate 同步都可验证。
|
||||
- 确认不需要改 OpenAPI、Content main/java 或 SQL migration。
|
||||
|
||||
双 PASS 后,仍不代表 `adminRiskAction` completed。必须由用户明确批准执行边界后才允许进入 implementation。
|
||||
|
||||
## Rollback
|
||||
|
||||
如果实现后需要回滚本切片:
|
||||
|
||||
1. 从 `APPROVED_COMPLETED_OPERATIONS` 删除 `content:adminRiskAction`。
|
||||
2. 重新运行 scanner,恢复 report summary 到 `233/144/89/0/0/0`。
|
||||
3. 恢复 `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest` 和 mixed gates 的 summary / Content count / `adminRiskAction` 断言。
|
||||
4. 删除 `P1rContentAdminRiskActionCompletedApprovalIT`。
|
||||
5. 删除或修订对应 memory / `.agent` 状态推进记录。
|
||||
6. 保留 OpenAPI、Content main/java、SQL migration 无 diff。
|
||||
|
||||
## 完成条件
|
||||
|
||||
只有同时满足以下条件,才允许汇报本切片具备提交候选条件:
|
||||
|
||||
- 用户已批准执行版四项前置条件。
|
||||
- RED gate 先失败且失败点正确。
|
||||
- `P1rContentAdminRiskActionCompletedApprovalIT` fresh pass,且 XML 防空跑通过。
|
||||
- Content focused tests fresh pass。
|
||||
- scanner `--check` fresh pass,summary 为 `233/145/88/0/0/0`。
|
||||
- P1R mixed gates fresh pass。
|
||||
- Content combined DB gate fresh pass。
|
||||
- protected OpenAPI / Content main/java / SQL diff 为空。
|
||||
- `git diff --check` 通过。
|
||||
- `.agent` 和 `docs/memorys` 留痕明确说明 Content 仍不是 51/51 completed。
|
||||
- fresh implementation spec/correctness review + fresh quality/data-integrity/testing review 双 PASS。
|
||||
261
docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md
Normal file
261
docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md
Normal file
@ -0,0 +1,261 @@
|
||||
# P1R Content Admin RiskAction 状态推进
|
||||
|
||||
日期:2026-06-13
|
||||
|
||||
## 结论
|
||||
|
||||
本轮只推进 Content 管理端治理写命令 1 个 operation-level completed approval:
|
||||
|
||||
- `content:adminRiskAction`
|
||||
|
||||
Content 仍不是整域 completed。其余 37 个 Content operation 继续 `dedicated/needs_verification`。
|
||||
|
||||
本轮未修改 OpenAPI,未修改 Content 业务实现,未修改 SQL migration,未把 `content` 加入 domain-level completed allowlist。
|
||||
|
||||
## 工作区与基线
|
||||
|
||||
工作区:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
基线 HEAD:
|
||||
|
||||
```text
|
||||
b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁
|
||||
```
|
||||
|
||||
实施前 coverage summary:
|
||||
|
||||
```text
|
||||
233 144 89 0 0 0
|
||||
```
|
||||
|
||||
实施后 coverage summary:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
```
|
||||
|
||||
实施后 Content 状态:
|
||||
|
||||
```text
|
||||
completed=14
|
||||
needs_verification=37
|
||||
```
|
||||
|
||||
目标 operation 状态:
|
||||
|
||||
```text
|
||||
adminRiskAction dedicated completed true
|
||||
```
|
||||
|
||||
代表性非目标 operation 仍保持:
|
||||
|
||||
```text
|
||||
adminListExportTasks dedicated needs_verification false
|
||||
adminListImportTasks dedicated needs_verification false
|
||||
createWork dedicated needs_verification false
|
||||
exportWork dedicated needs_verification false
|
||||
createStyleCheck dedicated needs_verification true
|
||||
getStyleCheckResult dedicated needs_verification false
|
||||
```
|
||||
|
||||
## 修改范围
|
||||
|
||||
本轮修改:
|
||||
|
||||
- `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/P1rContentRealApiGateTest.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/P1rAccountRealApiGateTest.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/P1rContentAdminRiskActionCompletedApprovalIT.java`
|
||||
- `docs/agent-specs/.agent`
|
||||
- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md`
|
||||
- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md`
|
||||
- `docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md`
|
||||
|
||||
未修改:
|
||||
|
||||
- 7 个 OpenAPI。
|
||||
- Content 业务实现。
|
||||
- SQL migration。
|
||||
- Content domain-level completed allowlist。
|
||||
|
||||
## TDD evidence
|
||||
|
||||
RED 阶段先修改 P1R gate 期望值和 `adminRiskAction` completed 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现:
|
||||
|
||||
```text
|
||||
Tests run: 49, Failures: 12, Errors: 0, Skipped: 0
|
||||
BUILD FAILURE
|
||||
```
|
||||
|
||||
失败点来自旧 report 仍为 `completed=144 / needsVerification=89`,以及 `adminRiskAction` 仍为 `needs_verification`;没有编译错误或无关测试失败。
|
||||
|
||||
GREEN 阶段只把 `content:adminRiskAction` 加入 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/P1rContentAdminRiskActionCompletedApprovalIT.java`
|
||||
|
||||
验证方式:
|
||||
|
||||
- 使用 Spring Mock web context + `MockMvc`。
|
||||
- 通过 `/admin-api/muse/content/works/{workId}/risk-actions` HTTP 入口进入 `AdminContentController`。
|
||||
- 使用真实 `ContentAdminServiceImpl`、`ContentCommandServiceImpl`、`ContentAuditServiceImpl`、mapper 和 PostgreSQL `_test` 数据库。
|
||||
- 启用 method security,并用 test-local `ss` bean 验证 `muse:content:risk-action` 权限门禁。
|
||||
- 注册真实 tenant SQL interceptor。
|
||||
- Flyway clean/migrate V1-V21。
|
||||
- 密码只从环境变量读取,拒绝 JVM password system property 和 JDBC credential query。
|
||||
- `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user`,避免污染同一 Surefire JVM 后续 Flyway 测试。
|
||||
|
||||
说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。
|
||||
|
||||
单类验证数据库:
|
||||
|
||||
```text
|
||||
muse_p1r_content_admin_risk_action_completed_approval_test
|
||||
```
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
P1rContentAdminRiskActionCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
覆盖内容:
|
||||
|
||||
- 拒绝非 `_test` 数据库、JVM password system property 和 JDBC credential query。
|
||||
- V1-V21 schema gate,覆盖 `muse_content_governance_action` 的 JSONB `target_ids`、unique command 约束、work 索引和 updated_at trigger。
|
||||
- duplicate `(tenant_id, command_id)` 返回 SQLState `23505`。
|
||||
- `WorkMapper.selectByIdForUpdate` 源码断言包含 `FOR UPDATE`。
|
||||
- API version、RBAC 和 Bean Validation 拒绝路径均 no-write。
|
||||
- work scope happy path 写 governance action、command/audit envelope,不写 Content Events outbox。
|
||||
- chapter / block scope happy path 只允许 target 属于 URL work。
|
||||
- `notify_owner` 返回 `pending_notification`,其它支持 action 返回 `executed`。
|
||||
- command replay 返回首次结果,不重复写 governance action、command/audit 或 outbox。
|
||||
- command conflict、revision conflict、unsupported action、unsupported targetScope、空 targetIds、null targetIds、missing/cross-work/cross-tenant target、block->chapter->work 链路不匹配和 cross-tenant work 都整体回滚。
|
||||
- command row 断言 `tenant_id`、`command_id`、`command_type=adminRiskAction`、`owner_user_id`、`target_type=work`、`target_id`、`request_hash`。
|
||||
- governance action row 断言 `tenant_id`、`work_id`、`operator_user_id`、`command_id`、`audit_log_id`、`action`、`reason`、`target_scope`、`target_ids`、`expected_version`、`status`。
|
||||
- command/audit snapshot 解析断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId`、`status=succeeded`、`reason`、`requestSummary` 和 `result`。
|
||||
- no-write / rollback 使用 `row_to_json(t)::text` 对 `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_governance_action`、`muse_content_command_log`、`muse_content_event_publish_outbox` 做全表快照比对。
|
||||
|
||||
## 组合验证
|
||||
|
||||
组合验证使用真实 PostgreSQL `_test` 库:
|
||||
|
||||
```text
|
||||
muse_p1r_content_admin_risk_action_combined_test
|
||||
```
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
P1rContentAdminRiskActionCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0
|
||||
P1rContentAdminReadCompletedApprovalIT: tests=5, failures=0, errors=0, skipped=0
|
||||
P1rContentPlanningCompletedApprovalIT: tests=11, failures=0, errors=0, skipped=0
|
||||
P1rContentCoreCompletedApprovalIT: tests=12, failures=0, errors=0, skipped=0
|
||||
P1rContentEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0
|
||||
combined: tests=40, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
## focused Content tests
|
||||
|
||||
执行版 focused tests 已单独运行,并确认 XML 为本轮 fresh 产物:
|
||||
|
||||
```text
|
||||
ContentAdminServiceTest: tests=13, failures=0, errors=0, skipped=0
|
||||
AdminContentControllerTest: tests=8, failures=0, errors=0, skipped=0
|
||||
focused total: tests=21, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
## P1R mixed gates
|
||||
|
||||
P1R mixed gates 已 fresh 运行:
|
||||
|
||||
```text
|
||||
P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0
|
||||
P1rContentRealApiGateTest: 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
|
||||
P1rAccountRealApiGateTest: 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
|
||||
```
|
||||
|
||||
## scanner / report
|
||||
|
||||
执行命令:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
通过后确认:
|
||||
|
||||
```text
|
||||
233 145 88 0 0 0
|
||||
51 14 37 0 0 0
|
||||
adminRiskAction dedicated completed true
|
||||
```
|
||||
|
||||
## protected diff
|
||||
|
||||
以下保护面 diff 为空:
|
||||
|
||||
```text
|
||||
docs/api-contracts
|
||||
muse-cloud/muse-module-content/muse-module-content-server/src/main/java
|
||||
muse-cloud/sql/muse
|
||||
```
|
||||
|
||||
`git diff --check` 无输出。
|
||||
|
||||
## fresh implementation review
|
||||
|
||||
Lagrange data-integrity/testing review:
|
||||
|
||||
```text
|
||||
PASS
|
||||
findings=none
|
||||
```
|
||||
|
||||
Lagrange 已只读验证:
|
||||
|
||||
- `P1rContentAdminRiskActionCompletedApprovalIT` 使用 MockMvc HTTP + WebApplicationContext + 真实 PostgreSQL `_test`。
|
||||
- 真实 controller/service/mapper、method security、tenant interceptor、Flyway V1-V21、command/governance 行级事实、command/audit snapshot、no-write / rollback 和 XML evidence 均闭合。
|
||||
- coverage 为 `233/145/88/0/0/0`,Content 为 `14 completed / 37 needs_verification`,非目标 Content operation 仍保持 `needs_verification`。
|
||||
- protected diff 与 `git diff --check` 均无问题。
|
||||
|
||||
Gauss scope review:
|
||||
|
||||
```text
|
||||
PASS
|
||||
findings=none
|
||||
```
|
||||
|
||||
Gauss 已只读验证:
|
||||
|
||||
- diff 只在 scanner/report、P1R gate tests、新 IT、`docs/agent-specs`、`docs/memorys` 范围内。
|
||||
- protected diff 为空。
|
||||
- scanner 只追加 `content:adminRiskAction` operation-level key,`APPROVED_COMPLETED_DOMAINS` 仍为 `{"ai", "knowledge"}`。
|
||||
- summary 为 `233/145/88/0/0/0`,Content 为 `14 completed / 37 needs_verification`。
|
||||
- `adminRiskAction` 为 `dedicated/completed/requiresCommandId=true`,抽样非目标 Content operation 仍为 `dedicated/needs_verification`。
|
||||
- 文档没有宣称 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
## 后续路线
|
||||
|
||||
`content:adminRiskAction` 之后,Content 仍剩余 37 个 `dedicated/needs_verification` operation。后续应继续按 operation-level approval 切片推进,优先只选择证据闭合、外部依赖少、可用 HTTP+DB `_test` 证明的 operation;不要把 Content 51/51、Account remaining、Market remaining 或总 P1R completed 混入本轮结论。
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-05-25T09:36:21+00:00",
|
||||
"generatedAt": "2026-05-25T09:52:35+00:00",
|
||||
"sourceContracts": [
|
||||
"docs/api-contracts/account/openapi.yaml",
|
||||
"docs/api-contracts/ai/openapi.yaml",
|
||||
@ -11,9 +11,9 @@
|
||||
],
|
||||
"summary": {
|
||||
"totalOperations": 233,
|
||||
"completedOperations": 144,
|
||||
"completedOperations": 145,
|
||||
"incompleteOperations": 0,
|
||||
"needsVerificationOperations": 89,
|
||||
"needsVerificationOperations": 88,
|
||||
"catchAllOperations": 0,
|
||||
"genericPersistenceOperations": 0,
|
||||
"ssePlaceholderOperations": 0,
|
||||
@ -7786,7 +7786,7 @@
|
||||
"externalDependencies": [],
|
||||
"targetStage": "P1R-1 Content Real API",
|
||||
"implementationStatus": "dedicated",
|
||||
"completionStatus": "needs_verification",
|
||||
"completionStatus": "completed",
|
||||
"controllerFiles": [
|
||||
"muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/admin/AdminContentController.java"
|
||||
],
|
||||
@ -7823,7 +7823,7 @@
|
||||
"muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/facade/ContentPlanningCandidateFacade.java",
|
||||
"muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/facade/ContentStyleCheckFacade.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": "content",
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# P1R API Coverage Report
|
||||
|
||||
- Generated at: `2026-05-25T09:36:21+00:00`
|
||||
- Generated at: `2026-05-25T09:52:35+00:00`
|
||||
- Total operations: `233`
|
||||
- Completed: `144`
|
||||
- Needs verification: `89`
|
||||
- Completed: `145`
|
||||
- Needs verification: `88`
|
||||
- Incomplete: `0`
|
||||
- Catch-all: `0`
|
||||
- Generic persistence: `0`
|
||||
@ -93,7 +93,7 @@
|
||||
| content | admin | GET | `/admin-api/muse/content/works` | `adminListWorks` | dedicated | completed | P1R-1 Content Real API |
|
||||
| content | admin | GET | `/admin-api/muse/content/works/{workId}` | `adminGetWork` | dedicated | completed | P1R-1 Content Real API |
|
||||
| content | admin | GET | `/admin-api/muse/content/works/{workId}/chapters` | `adminListChapters` | dedicated | completed | P1R-1 Content Real API |
|
||||
| content | admin | POST | `/admin-api/muse/content/works/{workId}/risk-actions` | `adminRiskAction` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | admin | POST | `/admin-api/muse/content/works/{workId}/risk-actions` | `adminRiskAction` | dedicated | completed | P1R-1 Content Real API |
|
||||
| content | app | POST | `/app-api/muse/chapter-parse-results/{resultId}/confirm` | `confirmChapterParseResult` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | POST | `/app-api/muse/chapter-parse-results/{resultId}/reject` | `rejectChapterParseResult` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/downloads/{credentialId}` | `downloadExportPackage` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
|
||||
@ -54,7 +54,7 @@ class P1rAccountRealApiGateTest {
|
||||
"adminListQuotaAdjustments"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -68,7 +68,8 @@ class P1rAccountRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
/** P1R-3 计划第 1 节冻结的 Account operation、方法和路径清单。 */
|
||||
@ -193,12 +194,12 @@ class P1rAccountRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Content 只推进当前已批准的 13 个 operation,防止 Account gate 漏掉 Content 未批准项回退检查。
|
||||
* 验证 Content 只推进当前已批准的 14 个 operation,防止 Account gate 漏掉 Content 未批准项回退检查。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_13_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_14_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -218,8 +219,8 @@ class P1rAccountRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(13, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(38, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(14, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(37, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readAccountOperations() throws IOException {
|
||||
|
||||
@ -85,7 +85,7 @@ class P1rAiRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -99,7 +99,8 @@ class P1rAiRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -144,8 +145,8 @@ class P1rAiRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(144, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 13");
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -171,7 +172,7 @@ class P1rAiRealApiGateTest {
|
||||
|
||||
@Test
|
||||
void should_keep_content_meta_account_dedicated_statuses() throws IOException {
|
||||
assertContentStatusCount(13, 38);
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertMarketStatusCount(4, 28);
|
||||
@ -200,8 +201,8 @@ class P1rAiRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private void assertAccountStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
|
||||
@ -97,7 +97,8 @@ class P1rApiCoverageReportTest {
|
||||
"content:savePlanningItem",
|
||||
"content:adminListWorks",
|
||||
"content:adminGetWork",
|
||||
"content:adminListChapters"
|
||||
"content:adminListChapters",
|
||||
"content:adminRiskAction"
|
||||
);
|
||||
|
||||
/** 已批准的 MetaSchema 管理接口。 */
|
||||
@ -146,7 +147,7 @@ class P1rApiCoverageReportTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** 已批准的 Content 用户端核心、Planning 与 Admin Read 接口,仍保持 operation-level approval。 */
|
||||
/** 已批准的 Content 用户端核心、Planning、Admin Read 与 Admin RiskAction 接口,仍保持 operation-level approval。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -160,7 +161,8 @@ class P1rApiCoverageReportTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -278,7 +280,7 @@ class P1rApiCoverageReportTest {
|
||||
completed++;
|
||||
}
|
||||
}
|
||||
assertEquals(144, completed, "Content Admin Read 3 个 operation-level approval 后 completed 总数只能从 141 增至 144");
|
||||
assertEquals(145, completed, "Content Admin RiskAction operation-level approval 后 completed 总数只能从 144 增至 145");
|
||||
|
||||
assertOperationStatus("events", "streamEvents", "dedicated", "completed");
|
||||
for (String operationId : APPROVED_META_SCHEMA_COMPLETED_OPERATIONS) {
|
||||
@ -307,10 +309,10 @@ class P1rApiCoverageReportTest {
|
||||
assertOperationStatus("content", "exportWork", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "listPlanningCandidates", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "createStyleCheck", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "adminRiskAction", "dedicated", "needs_verification", true);
|
||||
assertOperationStatus("content", "adminRiskAction", "dedicated", "completed", true);
|
||||
assertDomainStatusCount("market", "dedicated", "needs_verification", 28);
|
||||
assertDomainStatusCount("account", "dedicated", "needs_verification", 23);
|
||||
assertDomainStatusCount("content", "dedicated", "needs_verification", 38);
|
||||
assertDomainStatusCount("content", "dedicated", "needs_verification", 37);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -93,7 +93,7 @@ class P1rContentRealApiGateTest {
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
/** 已批准的 Content 用户端核心、Planning 与 Admin Read 接口,仍保持 operation-level approval。 */
|
||||
/** 已批准的 Content 用户端核心、Planning、Admin Read 与 Admin RiskAction 接口,仍保持 operation-level approval。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -107,7 +107,8 @@ class P1rContentRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
/** 必须继续保持 needs_verification 的代表性未批准 Content operation,防止 CRUD、导出、AI planning/style 和 admin 写入误推进。 */
|
||||
@ -121,7 +122,7 @@ class P1rContentRealApiGateTest {
|
||||
"exportWork",
|
||||
"listPlanningCandidates",
|
||||
"createStyleCheck",
|
||||
"adminRiskAction"
|
||||
"getStyleCheckResult"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -204,7 +205,7 @@ class P1rContentRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前 completed approval 只推进已批准的 13 个 Content operation,其余 38 个仍保持 needs_verification。
|
||||
* 验证当前 completed approval 只推进已批准的 14 个 Content operation,其余 37 个仍保持 needs_verification。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@ -228,8 +229,8 @@ class P1rContentRealApiGateTest {
|
||||
operationId + " 未获 Content operation-level 批准,必须继续保持 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(13, completed, "Content Admin Read operation-level completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(38, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(14, completed, "Content Admin RiskAction operation-level completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(37, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
for (String operationId : REPRESENTATIVE_UNAPPROVED_CONTENT_OPERATIONS) {
|
||||
assertContentOperationStatus(operationId, "needs_verification");
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class P1rEventsRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -59,7 +59,8 @@ class P1rEventsRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -105,10 +106,10 @@ class P1rEventsRealApiGateTest {
|
||||
void should_keep_completed_approval_summary_at_approved_operation_boundary() throws IOException {
|
||||
JsonNode summary = readReport().path("summary");
|
||||
|
||||
assertEquals(144, summary.path("completedOperations").asInt(),
|
||||
"completedOperations 只能来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 13");
|
||||
assertEquals(89, summary.path("needsVerificationOperations").asInt(),
|
||||
"needsVerificationOperations 必须扣除已批准的 Events streamEvents、Meta 16、Account 10、Market 4 和 Content 13");
|
||||
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(0, summary.path("incompleteOperations").asInt(),
|
||||
"Events 退出 placeholder 后不应再留下 incomplete operation");
|
||||
assertEquals(0, summary.path("genericPersistenceOperations").asInt(),
|
||||
@ -172,7 +173,7 @@ class P1rEventsRealApiGateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_10_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_14_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -192,8 +193,8 @@ class P1rEventsRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(13, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(38, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(14, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(37, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readSingleEventsOperation() throws IOException {
|
||||
|
||||
@ -107,7 +107,7 @@ class P1rKnowledgeRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -121,7 +121,8 @@ class P1rKnowledgeRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -183,14 +184,14 @@ class P1rKnowledgeRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(144, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 13");
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_existing_dedicated_domains_unchanged() throws IOException {
|
||||
assertDomainStatusCount("ai", 41, "completed");
|
||||
assertContentStatusCount(13, 38);
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertMarketStatusCount(4, 28);
|
||||
@ -219,8 +220,8 @@ class P1rKnowledgeRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -78,7 +78,7 @@ class P1rMarketRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -92,7 +92,8 @@ class P1rMarketRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -150,15 +151,15 @@ class P1rMarketRealApiGateTest {
|
||||
void should_keep_completion_summary_owned_by_approved_scopes_only() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(144, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 13");
|
||||
assertEquals(145, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_existing_domain_statuses_unchanged() throws IOException {
|
||||
assertDomainStatusCount("ai", 41, "completed");
|
||||
assertDomainStatusCount("knowledge", 59, "completed");
|
||||
assertContentStatusCount(13, 38);
|
||||
assertContentStatusCount(14, 37);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
}
|
||||
@ -186,8 +187,8 @@ class P1rMarketRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -76,7 +76,7 @@ class P1rMetaRealApiGateTest {
|
||||
"activateFunctionChainVersion"
|
||||
);
|
||||
|
||||
/** Content completed approval 当前只允许推进 13 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 14 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -90,7 +90,8 @@ class P1rMetaRealApiGateTest {
|
||||
"savePlanningItem",
|
||||
"adminListWorks",
|
||||
"adminGetWork",
|
||||
"adminListChapters"
|
||||
"adminListChapters",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -173,12 +174,12 @@ class P1rMetaRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Content 只推进当前已批准的 13 个 operation,防止 Meta gate 漏掉 Content 未批准项回退检查。
|
||||
* 验证 Content 只推进当前已批准的 14 个 operation,防止 Meta gate 漏掉 Content 未批准项回退检查。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_13_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_14_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -198,8 +199,8 @@ class P1rMetaRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(13, completed, "Content Admin Read completed approval 后只能推进 13 个 operation");
|
||||
assertEquals(38, needsVerification, "Content 剩余 38 个 operation 必须继续 needs_verification");
|
||||
assertEquals(14, completed, "Content Admin RiskAction completed approval 后只能推进 14 个 operation");
|
||||
assertEquals(37, needsVerification, "Content 剩余 37 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readMetaOperations() throws IOException {
|
||||
|
||||
@ -93,6 +93,7 @@ APPROVED_COMPLETED_OPERATIONS = {
|
||||
"content:adminListWorks",
|
||||
"content:adminGetWork",
|
||||
"content:adminListChapters",
|
||||
"content:adminRiskAction",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user