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

This commit is contained in:
zizi 2026-06-13 20:56:07 +08:00
parent b0b4b2c557
commit 304f665660
16 changed files with 2713 additions and 63 deletions

View File

@ -221,3 +221,13 @@ P1R Content Admin Read Completed Approval implementation 当前状态:用户
P1R Content Admin Read Completed Approval implementation fresh review 进展Lorentz correctness review PASS无 findingsHegel testing/data-integrity review FAIL2 个有效问题已修复。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 已双 PASSPeirce 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 已双 PASSRawls 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 FAIL3 个问题已按 `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 已双 PASSPeirce 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 已双 PASSGauss 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。

View File

@ -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 pathtargetIds 必须属于 URL work返回 chapter affected scope。
- block 粒度 happy pathtargetIds 必须沿 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 workblock 必须同时满足 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 diffOpenAPI、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。

View File

@ -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 1Preflight 与 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 的 workrevision 固定为 `7`
- 当前 tenant 下属于该 work 的 chapter / block。
- 当前 tenant 下另一个 work、另一个 chapter / block用于 cross-work target 拒绝。
- 另一个 tenant 的 work / chapter / block用于 tenant 隔离。
- 当前 login user 为 admin usertenant 与 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. 验证 schemaV1-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 pathtargetIds 属于 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 invalidchapter / 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 3Focused 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 4GREEN 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 5P1R 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 6Content 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 7XML 防空跑
- [ ] 检查以下 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 8Protected 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 passsummary 为 `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。

View 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 混入本轮结论。

View File

@ -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",

View File

@ -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 |

View File

@ -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 {

View File

@ -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 {

View File

@ -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);
}
/**

View File

@ -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");
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -93,6 +93,7 @@ APPROVED_COMPLETED_OPERATIONS = {
"content:adminListWorks",
"content:adminGetWork",
"content:adminListChapters",
"content:adminRiskAction",
}