test(p1r): 收口 Content Planning completed approval 门禁
将 content:getPlanning 与 content:savePlanningItem 纳入 operation-level completed approval,并保留 Content 其余 operation 的 needs_verification 边界。
This commit is contained in:
parent
3ced553cb2
commit
2692af32e7
@ -183,3 +183,25 @@ P1R Content 第一批 operation-level completed approval implementation 当前
|
||||
P1R Content 第一批 implementation fresh review 第一轮结果:Bohr correctness review FAIL、Euclid testing/data-integrity review FAIL,同源 P1 有效:`P1rContentCoreCompletedApprovalIT` 未覆盖执行版 mandatory 的 `saveBlock` 缺 `expectedRevision` 与缺 `sourceSnapshot` 两个 HTTP+DB no-write 负路径。已按 `superpowers:receiving-code-review` 验证反馈有效:执行版第 513 行明确要求这两项,`BlockSaveReqVO` 对两字段均有 required 语义。已最小修复:在 `should_rejectSaveBlockValidationRevisionConflictAndCommandConflictWithoutDirtyWrite` 中补 `p1r-content-missing-expected-revision` 与 `p1r-content-missing-source-snapshot` 两个请求,并复用 `assertSnapshotUnchanged` 断言 block/source/outbox/command 未写入。修复后已重跑 `P1rContentCoreCompletedApprovalIT` tests=12、failures=0、errors=0、skipped=0;已重跑 Core+Flyway 组合验证,tests=16、failures=0、errors=0、skipped=0,两个目标 XML 均为新产物。当前仍需重新派发 fresh implementation spec/correctness review + quality/data-integrity/testing review;双 PASS 前不得提交、push 或宣称 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
P1R Content 第一批 operation-level completed approval fresh implementation re-review 已双 PASS:Mendel correctness review PASS,无 findings、无 residual risks;Anscombe testing/data-integrity review PASS,无 findings、无 testing gaps。Mendel 已验证 Bohr/Euclid P1 已关闭,`P1rContentCoreCompletedApprovalIT` 覆盖缺 `expectedRevision` 与缺 `sourceSnapshot` 两个 `saveBlock` HTTP 负路径并复用 no-write 断言,coverage summary 为 `233/139/94/0/0/0`,Content 精确为 `8 completed / 43 needs_verification`,scanner 未加入 `content` domain allowlist,只追加 8 个 `content:*` operation key,OpenAPI、Content main/java 与 SQL migration diff 均为空,Core 12/12、Core+Flyway 16/16、P1R mixed gates 49/49 通过。Anscombe 已验证 testing/data-integrity 口径,无新增 blocker;唯一残余风险是 raw Surefire XML 仍可能包含本地 DB host/user,但未发现 password 泄露,外发前需清洗。该双 PASS 只代表 Content 第一批 8 个 operation-level completed approval 具备提交条件;不代表 Content 51/51、Account remaining、Market remaining 或总 P1R completed。提交或 push 仍需用户明确指令。
|
||||
|
||||
P1R Content Planning Completed Approval 已启动下一批审阅版:`docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md`。本审阅版只推荐 formal planning section 两个 operation-level completed approval:`content:getPlanning` 与 `content:savePlanningItem`;不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51,不修改 OpenAPI、scanner、coverage report、Content 业务实现、SQL migration 或 gate test。只读盘点事实:当前 HEAD 为 `3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁`,coverage summary 为 `233/139/94/0/0/0`,Content 为 `8 completed / 43 needs_verification`;`SavePlanningItemRequest` OpenAPI required 为 `commandId/content/expectedRevision`,与 `SavePlanningItemReqVO` required 口径一致;V9 已有 `muse_content_planning_section`、unique `(tenant_id, work_id, section_key)`、work index 与 update trigger;`ContentPlanningServiceImpl` 对 `getPlanning` / `savePlanningItem` 只依赖 Content owner 自有 Work / PlanningSection / Command / Audit 事实。下一步必须对该审阅版做 fresh spec/scope review + fresh quality/feasibility review;双 PASS 前不得写执行版、修改 scanner/report/gate、提交、push 或推进 Content completed。
|
||||
|
||||
P1R Content Planning Completed Approval 审阅版首轮 review:Lovelace spec/scope review PASS,无 findings;Kierkegaard quality/feasibility/testing review FAIL,两个 P1 有效:第一,`savePlanningItem` 后续执行版 matrix 缺同 commandId 但不同 request hash / sectionKey / workId 的 `CONTENT_COMMAND_ID_CONFLICT` no-write 证明;第二,V9 planning schema mandatory gate 只列 table/unique/index/trigger,不足以证明 `tenant_id`、JSONB 类型、默认值、revision 和 `update_time` 行为。审阅版已修订:新增 command conflict no-write mandatory matrix;扩展 V9 schema gate 到列类型、nullable/default、tenant 隔离、JSONB 存储、revision/schema/projection 默认、duplicate unique 拒绝、CAS 后 revision+1 与 `update_time` 推进。由于审阅版已在 review 后修改,Lovelace PASS 失效;下一步必须重新派发 fresh spec/scope review + fresh quality/feasibility/testing review。
|
||||
|
||||
P1R Content Planning Completed Approval 审阅版第二轮 review:Hume spec/scope review PASS,无 findings;Aquinas quality/feasibility/testing review FAIL,P1 有效:command conflict matrix 写成“至少覆盖不同 content、不同 sectionKey 或不同 workId”,会允许执行版只测其中一类而遗漏 path scope hash 证明。审阅版已修订为三个独立 mandatory HTTP+real DB 子用例:同 commandId 不同 content、同 commandId 不同 sectionKey、同 commandId 不同 workId,三者都必须返回 `CONTENT_COMMAND_ID_CONFLICT` 并断言 planning row count/content/revision、command count、audit/result snapshot 保持首次成功状态。由于审阅版已在 review 后再次修改,Hume PASS 失效;下一步必须重新派发 fresh spec/scope review + fresh quality/feasibility/testing review。
|
||||
|
||||
P1R Content Planning Completed Approval 审阅版第三轮 fresh review 已双 PASS:Darwin spec/scope review PASS、Erdos quality/feasibility/testing review PASS,均无 P0/P1/P2/P3 blocker。Darwin 已验证审阅版只推荐 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level completed approval,41 个继续 `needs_verification` 的 Content operation 与 coverage 差集一致,目标值 `233/141/92` 与 Content `10/41` 可由当前 `233/139/94`、Content `8/43` 加 N=2 机械推导,未获批准前不得修改 scanner/report/gate、实现、提交或 push。Erdos 已验证 OpenAPI 与 VO required 口径一致且 `sourceSnapshot` optional;目标 operation 只依赖 Content 本域 Work / PlanningSection / Command / Audit,不依赖 AI candidate、style-check、Meta 或 FileService;V9 schema gate 已覆盖 tenant_id、JSONB、默认值、unique/index/trigger、CAS update_time;`savePlanningItem` matrix 已覆盖 create/update/replay、三类 command conflict no-write、validation、revision/schema/projection conflict、unique conflict、owner/tenant no-write;P1R mixed gates、XML 防空跑和 protected diff 要求齐备。该双 PASS 只代表审阅版可进入执行版编写;不代表 `getPlanning` / `savePlanningItem` completed,也不得直接修改 scanner/report/gate 或提交/push。
|
||||
|
||||
P1R Content Planning Completed Approval 执行版已新增:`docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md`。执行版只锁定 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level approval;不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51,不修改 OpenAPI、Content 业务实现或 SQL migration。目标 summary 仅作为获批后执行目标:`233/141/92/0/0/0`,Content 目标为 `10 completed / 41 needs_verification`。执行版明确要求用户再次批准 4 项后才可实施:2 ops 清单、operation-level approval、allowed diff、mixed gate summary / Content / 非目标域防回退断言同步。当前只写执行方案和 `.agent`,未修改 scanner、coverage report、P1R gate、OpenAPI、Content main/java 或 SQL migration。下一步必须 fresh execution spec/scope review + fresh execution quality/feasibility/testing review;双 PASS 前不得实现、提交、push 或推进 Content completed。
|
||||
|
||||
P1R Content Planning Completed Approval 执行版首轮 review:Kant execution spec/scope review PASS;Russell execution quality/feasibility/testing review FAIL,两个 P1 有效并已按 `superpowers:receiving-code-review` 只读验证后修订。第一,执行版 Task 4 的 HTTP+DB `_test` 上下文漏写 `MuseWebAutoConfiguration` 所需的 `ApiErrorLogCommonApi` stub 与真实 tenant SQL 拦截器要求,已修订为复用 `P1rContentCoreCompletedApprovalIT` 的 Web/MyBatis/DataSource 模式,明确注册 `TenantLineInnerInterceptor + TenantDatabaseInterceptor(new TenantProperties())` 和 `ApiErrorLogCommonApi` test-local stub。第二,普通顺序 HTTP 请求无法稳定触发 `savePlanningItem` insert unique catch;已有 row 会先进入 existing 分支,因此不能把 existing-row revision conflict 冒充 duplicate unique conflict。执行版已修订为:DB unique 约束证据放入 V9 schema gate,由直接 JDBC insert 验证 duplicate `(tenant_id, work_id, section_key)` 被 PostgreSQL 拒绝;服务层 `DuplicateKeyException` 转 `CONTENT_REVISION_CONFLICT` 由 focused `ContentPlanningServiceTest` 覆盖;HTTP matrix 覆盖 existing-row revision/schema/projection conflict 与 no-write。由于执行版已在 review 后修改,Kant PASS 失效;下一步必须重新派发 fresh execution spec/scope review + fresh execution quality/feasibility/testing review,双 PASS 前不得实现、提交、push 或推进 Content completed。
|
||||
|
||||
P1R Content Planning Completed Approval 执行版第二轮 fresh review 已双 PASS:Hypatia execution spec/scope review PASS、Ramanujan execution quality/feasibility/testing review PASS,均无 P0/P1/P2/P3 blocker。Hypatia 已验证执行版严格锁定 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level completed approval,不加入 `content` domain allowlist,不推进 Content 51/51,不修改 OpenAPI、Content main/java 或 SQL migration;目标值 `233/141/92` 与 Content `10/41` 可由当前 `233/139/94`、Content `8/43` 加 N=2 机械推导,41 个保留 `needs_verification` operation 与审阅版一致,Russell P1 修订未扩大 scope。Ramanujan 已验证 Task 4 最小 Web/MyBatis/DataSource 测试上下文可落地,`ApiErrorLogCommonApi` stub、`TenantLineInnerInterceptor + TenantDatabaseInterceptor(new TenantProperties())`、facade stubs、tenant context、credential redaction、system property restore 与 XML 防空跑要求齐备;unique conflict 证据已改为 V9 schema gate 直接 JDBC duplicate 约束验证 + focused service `DuplicateKeyException -> CONTENT_REVISION_CONFLICT`,HTTP matrix 不再假装触发 insert unique catch。该双 PASS 只代表执行版可进入用户批准点;不代表 `getPlanning` / `savePlanningItem` completed,也不允许未获批准前修改 scanner、coverage report、P1R gate、OpenAPI、Content main/java 或 SQL migration。下一步必须由用户明确批准四项:1) 2 ops 清单;2) operation-level approval 且不加 `content` domain allowlist;3) 执行版 allowed diff;4) mixed gate 的 summary / Content / 非目标域防回退断言同步。
|
||||
|
||||
P1R Content Planning Completed Approval implementation 当前状态:用户已批准执行版四项前置条件,本轮只把 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation 加入 operation-level completed approval,未把 `content` 加入 domain-level allowlist,未修改 OpenAPI、Content 业务实现或 SQL migration。scanner 仍保持 `APPROVED_COMPLETED_DOMAINS={"ai","knowledge"}`,只追加两个 `content:*` operation key;coverage summary 已推进为 `233/141/92/0/0/0`,Content 为 `10 completed / 41 needs_verification`,`savePlanningItem.requiresCommandId=true`。TDD RED 已验证旧 report 下 focused P1R gates 因目标 summary 仍为 139、`content/getPlanning` 仍为 `needs_verification` 而失败;GREEN 后 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 通过并重生成 report。新增 `P1rContentPlanningCompletedApprovalIT` 使用 MockMvc HTTP 入口、真实 mapper/service 与 PostgreSQL `_test` 库,单类验证 `11/11 pass`,覆盖 V9 planning schema 的 JSONB/default/tenant/unique/index/trigger/duplicate/CAS update_time 证据、`getPlanning` happy/empty/missing/cross-owner/cross-tenant/no-write,以及 `savePlanningItem` create/update/replay、三类 command conflict(不同 content/sectionKey/workId)、validation、revision/schema/projection conflict、missing/cross-owner/cross-tenant no-write 与全租户 command/outbox 污染断言。Ohm fresh correctness/spec review FAIL,3 个 P1 有效:缺 `commandId`/缺 `content` validation 未做全租户 command/outbox no-write;command/audit snapshot 断言不足,未覆盖 tenant_id、request_hash、result_snapshot 与 audit requestSummary/operationId/status/reason 等关键事实;执行版 Task 3 mandatory focused `ContentPlanningServiceTest` 与 `AppContentPlanningControllerTest` 未运行、XML 缺失。已修复并验证:缺 `commandId`/缺 `content` 改为全租户 command/outbox no-write;command/audit snapshot 增加 `tenantId`、`requestHash` 长度、`result.newRevision/sectionKey`、audit `side/operationId/reason/status/resultSummary`、`requestSummary.operationId/sectionKey/expectedRevision/saveMode` 断言;Task 3 focused tests fresh 运行 `ContentPlanningServiceTest 15/15` 与 `AppContentPlanningControllerTest 7/7`,两个 XML mtime 均在运行窗口内。组合验证 `P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT` 使用独立 `_test` 库 `muse_p1r_content_planning_combined_test`,首次因只设置 `P1R_CONTENT_COMPLETED_PASSWORD` 而缺 `P1R_FLYWAY_PASSWORD` 失败,确认根因为既有 Flyway IT 环境变量要求;仅调整运行环境同时设置 `P1R_CONTENT_COMPLETED_PASSWORD` 与 `P1R_FLYWAY_PASSWORD` 后重跑通过,合计 `27/27 pass`。P1R mixed gates 已重跑并 XML 防空,`49/49 pass`;scanner `--check` 已重跑;protected OpenAPI diff 为空,`git diff --check` 通过。此前 fresh testing/data-integrity review Curie PASS 已被 Ohm 后续修复推进为过期证据,只保留为历史记录;Kepler 超时后关闭,无可用结论。新增 memory:`docs/memorys/2026-06-13-P1RContentPlanning状态推进.md`。当前必须重新派发 fresh implementation spec/correctness review + fresh quality/data-integrity/testing review;双 PASS 前不得提交、push 或宣称 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
P1R Content Planning Completed Approval implementation fresh re-review 进展:Locke correctness/spec review PASS,确认 scope/diff、scanner operation-level approval、coverage `233/141/92/0/0/0`、Content `10/41`、非目标 operation 仍 `needs_verification`、Planning IT 与 Ohm 3 个 P1 修复均符合执行版;Locke 残余风险为执行版 allowed diff memory 文件日期与实际 `2026-06-13` 不一致,已修订执行版 allowed diff。Hilbert testing/data-integrity review FAIL,2 个 P2 有效:V9 schema gate 未显式断言 `schema_version`、`projection_version`、`revision` 三列 `NOT NULL`;savePlanning missing/cross-owner/cross-tenant no-write 未逐一断言目标 planning row 未创建。已最小修复:补三列 `columnNullable(...)=NO` 断言;补 missing work、cross-owner、cross-tenant 当前 tenant 与目标 tenant planning row count 为 0 的断言。修复后已 fresh 重跑 `P1rContentPlanningCompletedApprovalIT`,tests=11、failures=0、errors=0、skipped=0,XML mtime 为 `2026-06-13 01:53:08 +0800`。当前需要重新派发 fresh correctness/spec re-review + testing/data-integrity re-review;双 PASS 前不得提交、push 或宣称 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
P1R Content Planning Completed Approval implementation fresh re-review 已双 PASS:Epicurus correctness/spec re-review PASS,确认 protected diff 为空、scope 未扩大、scanner 仍只追加 `content:getPlanning`/`content:savePlanningItem`、执行版 allowed diff 已改为实际 memory 路径、Hilbert 两个 P2 已关闭、coverage report 为 `233/141/92/0/0/0` 且 Content 为 `10 completed / 41 needs_verification`;Epicurus 未运行 Maven 测试,只做只读 diff/source/docs/report 复核。Nietzsche testing/data-integrity re-review PASS,确认 schema NOT NULL、missing/cross-owner/cross-tenant 目标 row no-write、缺 commandId/缺 content 全租户 command/outbox no-write、command/audit snapshot 解析断言、fresh XML `P1rContentPlanningCompletedApprovalIT 11/11`、`.agent`/memory 留痕和 `git diff --check` 均无新 blocker;Nietzsche 未运行 Maven 测试,只解析现有 XML 并运行只读 diff check。残余风险:Surefire XML 含本地 classpath、user.dir、测试 DB URL 等本机信息,外发前需清洗;新增 IT 与 memory 当前仍未提交,提交前必须确认纳入正确 commit。该双 PASS 只代表 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level completed approval 具备收口条件,不代表 Content 51/51、Account remaining、Market remaining 或总 P1R completed;提交或 push 仍需用户明确指令。
|
||||
|
||||
P1R Content Planning Completed Approval implementation final verification:fresh 双 PASS 后已重跑 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 并重新生成 report;Task 3 focused `ContentPlanningServiceTest,AppContentPlanningControllerTest` fresh 运行 `22/22 pass` 且 XML fresh;P1R mixed gates fresh 运行 `49/49 pass`;Content combined DB gate `P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT` fresh 运行 `27/27 pass`;`git diff --check` 无输出;protected `docs/api-contracts`、Content main/java、SQL diff 为空;正确 jq 验证 summary 为 `233/141/92/0/0/0`,Content 为 `10 completed / 41 needs_verification`,`getPlanning` 与 `savePlanningItem` 为 `dedicated/completed`,`listPlanningCandidates`、`createStyleCheck`、`getStyleCheckResult` 仍为 `dedicated/needs_verification`。当前状态具备提交候选条件,但仍未提交、未 push。
|
||||
|
||||
@ -0,0 +1,291 @@
|
||||
# P1R Content Planning Completed Approval 审阅版
|
||||
|
||||
日期:2026-06-12
|
||||
|
||||
## 结论
|
||||
|
||||
推荐 Content 下一批只推进 formal planning section 两个 operation 的 operation-level completed approval:
|
||||
|
||||
```text
|
||||
content:getPlanning
|
||||
content:savePlanningItem
|
||||
```
|
||||
|
||||
不推荐把剩余 43 个 Content operation 一次性推进 completed,也不推荐把 `content` 加入 domain-level completed allowlist。
|
||||
|
||||
这两个 operation 属于 Content 本域正式规划数据,不依赖 AI candidate / style check 外部 owner,不依赖 FileService,不依赖 Meta projection 运行时,也不需要修 OpenAPI 合同。它们的 completed approval 应独立成一个小切片,用真实 PostgreSQL `_test`、MockMvc HTTP 入口、V9 planning schema、CAS / command replay / audit / tenant-owner 隔离证据闭合。
|
||||
|
||||
本审阅版只冻结下一批候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Current["当前 Content<br/>8 completed / 43 needs_verification"] --> Choose["选择下一批 evidence slice"]
|
||||
Choose --> Planning["推荐:formal planning section<br/>getPlanning + savePlanningItem"]
|
||||
Choose --> Wider["不推荐:剩余 Content 43 全量推进"]
|
||||
Planning --> Boundary{"是否依赖外部 owner?"}
|
||||
Boundary -->|否| Evidence["Content 本域 DB evidence<br/>muse_content_planning_section"]
|
||||
Boundary -->|AI/File/Meta| Defer["保持 needs_verification"]
|
||||
Evidence --> Review["fresh spec/scope review<br/>fresh quality/feasibility review"]
|
||||
Review --> Exec["双 PASS 后写执行版"]
|
||||
Exec --> Approval{"用户明确批准后才实现"}
|
||||
Approval -->|否| Stay["保持 233/139/94"]
|
||||
Approval -->|是| Target["目标 233/141/92<br/>Content 10 completed / 41 needs_verification"]
|
||||
```
|
||||
|
||||
## 当前事实状态
|
||||
|
||||
工作区:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
```
|
||||
|
||||
当前 HEAD:
|
||||
|
||||
```text
|
||||
3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=139
|
||||
needsVerification=94
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
当前 Content 状态:
|
||||
|
||||
```text
|
||||
content total=51
|
||||
completed=8
|
||||
needsVerification=43
|
||||
```
|
||||
|
||||
已 completed 的 Content 第一批 operation:
|
||||
|
||||
```text
|
||||
content:listWorks
|
||||
content:getWork
|
||||
content:listChapters
|
||||
content:getChapter
|
||||
content:listBlocks
|
||||
content:getBlock
|
||||
content:saveBlock
|
||||
content:getBlockSourceAttribution
|
||||
```
|
||||
|
||||
本审阅版推荐的下一批目标值只是后续获批后的执行目标:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=141
|
||||
needsVerification=92
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
|
||||
content total=51
|
||||
completed=10
|
||||
needsVerification=41
|
||||
```
|
||||
|
||||
## 推荐候选
|
||||
|
||||
| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence |
|
||||
|---|---|---|---|---|
|
||||
| `content:getPlanning` | `GET` | `/app-api/muse/works/{workId}/planning` | Content 本域正式 planning section 读模型;只依赖 work owner guard 与 `muse_content_planning_section` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖有 section、空 section、latest source snapshot、missing work、cross owner、cross tenant、纯读 no-write |
|
||||
| `content:savePlanningItem` | `PUT` | `/app-api/muse/works/{workId}/planning/{sectionKey}` | Content 本域正式 planning section 写路径;OpenAPI / VO required 口径一致;已有 command/audit/CAS 实现 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 create expectedRevision=0、update expectedRevision match、command replay、missing command/content/expectedRevision、revision/schema/projection conflict、unique conflict、cross owner/tenant no-write、command/audit snapshot |
|
||||
|
||||
推荐理由:
|
||||
|
||||
- 两个 operation 只处理正式 planning section,所有事实落在 Content owner 自有表 `muse_content_planning_section` 与 Content command/audit 事实内。
|
||||
- OpenAPI `SavePlanningItemRequest` 已要求 `commandId`、`content`、`expectedRevision`,与 `SavePlanningItemReqVO` 的 `@NotBlank` / `@NotNull` 一致;本切片不需要修改 OpenAPI。
|
||||
- `sourceSnapshot` 在 OpenAPI 与 VO 中都是 optional;执行版不得把 optional 改成 required,也不得借此夹带合同修正。
|
||||
- `getPlanning` / `savePlanningItem` 可与 AI planning candidate / style check 完全切开,避免把 AI owner runtime closure 混进本轮 completed approval。
|
||||
- V9 SQL 已提供 `muse_content_planning_section`、唯一键 `(tenant_id, work_id, section_key)`、work 索引和 `update_time` trigger;执行版可以用真实 `_test` 证明 schema 仍随 V1-V21 clean migrate 生效。
|
||||
|
||||
## 不纳入本批的 operation
|
||||
|
||||
以下 41 个 Content operation 必须继续 `dedicated / needs_verification`:
|
||||
|
||||
```text
|
||||
content:adminListExportTasks
|
||||
content:adminListImportTasks
|
||||
content:adminListWorks
|
||||
content:adminGetWork
|
||||
content:adminListChapters
|
||||
content:adminRiskAction
|
||||
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
|
||||
```
|
||||
|
||||
排除原因:
|
||||
|
||||
- `createWork`、`updateWork`、`deleteWork`、`createChapter`、`updateChapter`、`deleteChapter`、`createBlock`、`reorderChapters`、`splitBlock`、`mergeBlocks`、`deleteBlock` 等结构写命令仍有独立 OpenAPI / commandId / revision 合同闭合风险,应作为后续写命令合同一致性切片。
|
||||
- `mergeBlockSuggestion`、`createPlanningCandidate`、`listPlanningCandidates`、`getPlanningCandidate`、`confirmPlanningCandidate`、`discardPlanningCandidate`、`createStyleCheck`、`getStyleCheckResult` 依赖 AI owner projection、quality result、candidate state 或 style-check runtime,不应借 formal planning section 证据推进。
|
||||
- `validateDynamicFields`、`listMetaProjections`、`getMetaProjection` 依赖 Meta owner schema / projection 解释与动态字段校验,不应夹带。
|
||||
- `createImportTask`、`getImportTask`、`createParseJob`、`getParseJob`、`listParseJobChapters`、`confirmChapterParseResult`、`rejectChapterParseResult`、`batchConfirmChapters`、`retryParseJob`、`exportWork`、`createExportTask`、`getExportTask`、`downloadExportPackage` 依赖 FileService、文件产物、解析任务和真实下载闭环。
|
||||
- admin read 与 `adminRiskAction` 需要独立覆盖 admin RBAC、治理摘要、risk flag 聚合、治理动作幂等和审计,不应混入用户端 planning section。
|
||||
|
||||
## 已有实现证据
|
||||
|
||||
### OpenAPI / VO 口径
|
||||
|
||||
OpenAPI 当前路径:
|
||||
|
||||
- `GET /app-api/muse/works/{workId}/planning`,operationId 为 `getPlanning`。
|
||||
- `PUT /app-api/muse/works/{workId}/planning/{sectionKey}`,operationId 为 `savePlanningItem`。
|
||||
|
||||
`SavePlanningItemRequest` 当前 required:
|
||||
|
||||
```text
|
||||
commandId
|
||||
content
|
||||
expectedRevision
|
||||
```
|
||||
|
||||
服务端 `SavePlanningItemReqVO` 当前 required:
|
||||
|
||||
```text
|
||||
commandId: @NotBlank
|
||||
content: @NotNull
|
||||
expectedRevision: @NotNull
|
||||
```
|
||||
|
||||
因此本切片不需要修改 `docs/api-contracts/content/openapi.yaml`。
|
||||
|
||||
### 服务实现
|
||||
|
||||
`ContentPlanningServiceImpl.getPlanning`:
|
||||
|
||||
- 调用 `requireOwnedWork(userId, workId)`。
|
||||
- 查询 `PlanningSectionMapper.selectListByWorkId(workId)`。
|
||||
- 返回 sections、最大 revision 和 latest `sourceSnapshot`。
|
||||
|
||||
`ContentPlanningServiceImpl.savePlanningItem`:
|
||||
|
||||
- 强校验 `commandId` 和 `expectedRevision`。
|
||||
- request hash 绑定 `workId` 与 `sectionKey`。
|
||||
- 通过 `ContentCommandService.reserveCommand` 支持幂等 replay。
|
||||
- `requireOwnedWork(userId, workId)` 后读写 `muse_content_planning_section`。
|
||||
- 新建 section 要求 `expectedRevision=0`。
|
||||
- 更新 section 走 `revision` CAS,并校验 optional `expectedSchemaVersion` / `expectedProjectionVersion`。
|
||||
- 成功后通过 `ContentAuditService.recordSucceededWithAudit` 写 command/audit result snapshot。
|
||||
|
||||
### SQL schema
|
||||
|
||||
V9 `muse_content_planning_section` 当前包含:
|
||||
|
||||
- `tenant_id`
|
||||
- `work_id`
|
||||
- `section_key`
|
||||
- `content_payload JSONB`
|
||||
- `schema_version`
|
||||
- `projection_version`
|
||||
- `source_snapshot JSONB`
|
||||
- `revision`
|
||||
- unique `(tenant_id, work_id, section_key)`
|
||||
- index `(tenant_id, work_id)`
|
||||
- `trg_muse_content_planning_section_updated_at`
|
||||
|
||||
执行版必须证明 V1-V21 clean migrate 后 V9 planning schema 仍存在且约束有效。
|
||||
|
||||
### 既有测试
|
||||
|
||||
已有单元 / controller 测试覆盖了部分行为:
|
||||
|
||||
- `ContentPlanningServiceTest` 覆盖 `getPlanning` 读 sections、owner mismatch、missing command、expectedRevision 冲突、新建、更新、unique conflict、command replay、command/audit result snapshot。
|
||||
- `AppContentPlanningControllerTest` 覆盖 `X-API-Version`、`savePlanningItem` 缺 commandId、owner guard 错误响应、`getPlanning` / `savePlanningItem` CommonResult route。
|
||||
|
||||
这些是候选证据,不等同 completed。执行版仍必须补 HTTP + real mapper/service + PostgreSQL `_test` 的 completed-grade evidence。
|
||||
|
||||
## 后续执行版必须包含的验证
|
||||
|
||||
执行版至少要定义以下验证,不得只依赖已有 Mockito 单测:
|
||||
|
||||
1. 新增或独立扩展 `P1rContentPlanningCompletedApprovalIT`,使用 MockMvc HTTP 入口、真实 mapper/service、真实 PostgreSQL `_test`,只导入 planning section 必需 controller/service/mapper。
|
||||
2. Flyway target 使用当前 V1-V21,验证 `migrations_executed=21`,并 mandatory 断言 V9 planning schema:
|
||||
- `muse_content_planning_section` 表存在。
|
||||
- `tenant_id` 非空且具备默认值,真实写入后能按 tenant 隔离。
|
||||
- `content_payload` 为 JSONB、非空、默认 `'{}'::jsonb`;`source_snapshot` 为 JSONB 且允许为空。
|
||||
- `schema_version`、`projection_version`、`revision` 非空且默认值为 `1`。
|
||||
- unique `(tenant_id, work_id, section_key)`、index `(tenant_id, work_id)` 与 `trg_muse_content_planning_section_updated_at` 存在。
|
||||
- 真实 `_test` 写入必须验证 JSONB 存储、默认 revision/schema/projection、duplicate `(tenant_id, work_id, section_key)` 被拒绝、CAS 更新后 revision+1 且 `update_time` 推进。
|
||||
3. `getPlanning` matrix:
|
||||
- 有 sections,返回 sections、revision、latest sourceSnapshot。
|
||||
- 空 sections,返回空数组、revision=0、sourceSnapshot=null。
|
||||
- missing work、cross owner、cross tenant。
|
||||
- 纯读路径不写 Content command / planning section / event outbox。
|
||||
4. `savePlanningItem` matrix:
|
||||
- 新建 section:`expectedRevision=0`,写入 content/sourceSnapshot/schemaVersion/projectionVersion、tenant_id、command/audit snapshot。
|
||||
- 更新 section:`expectedRevision` 匹配,revision+1,CAS 更新生效。
|
||||
- command replay:同 commandId / 同 request hash 返回首次 result,不重复 insert/update。
|
||||
- command conflict:同 commandId 但不同 request hash 必须返回 `CONTENT_COMMAND_ID_CONFLICT`,并分别覆盖三个独立 HTTP+real DB 子用例:同 commandId 不同 content、同 commandId 不同 sectionKey、同 commandId 不同 workId;三者都必须断言 planning row count/content/revision、command count、audit/result snapshot 均保持首次成功状态。
|
||||
- 缺 `commandId`、缺 `content`、缺 `expectedRevision`。
|
||||
- `expectedRevision` 冲突、schemaVersion 冲突、projectionVersion 冲突。
|
||||
- duplicate `(tenant_id, work_id, section_key)` 转为 revision conflict。
|
||||
- missing work、cross owner、cross tenant no-write。
|
||||
5. P1R gates:
|
||||
- `P1rApiCoverageReportTest`
|
||||
- `P1rContentRealApiGateTest`
|
||||
- `P1rEventsRealApiGateTest`
|
||||
- `P1rAiRealApiGateTest`
|
||||
- `P1rKnowledgeRealApiGateTest`
|
||||
- `P1rMarketRealApiGateTest`
|
||||
- `P1rAccountRealApiGateTest`
|
||||
- `P1rMetaRealApiGateTest`
|
||||
6. XML 防空跑必须逐个读取 Surefire XML,检查 tests、mtime、failures、errors、skipped,不能只看 Maven BUILD SUCCESS。
|
||||
7. protected diff:
|
||||
- OpenAPI diff 必须为空。
|
||||
- Content main/java 业务实现 diff 必须为空。
|
||||
- SQL migration diff 必须为空。
|
||||
|
||||
## 用户批准点
|
||||
|
||||
本审阅版通过 fresh review 后,只能进入执行版编写。
|
||||
|
||||
执行版通过 fresh review 后,真正实现前仍必须由用户明确批准:
|
||||
|
||||
1. 只审批 `content:getPlanning` 和 `content:savePlanningItem` 两个 operation。
|
||||
2. 继续 operation-level approval,不把 `content` 加入 domain-level allowlist。
|
||||
3. 允许按执行版修改 scanner、coverage report、P1R gates、新增 planning HTTP+DB `_test`、memory 和 `.agent`。
|
||||
4. 允许同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为 `233/141/92/0/0/0` 与 Content `10 completed / 41 needs_verification`。
|
||||
|
||||
未获得上述批准前,不得修改 scanner/report/gate,不得实现,不得提交,不得 push,不得宣称 Content 10/51 或 51/51 completed。
|
||||
@ -0,0 +1,690 @@
|
||||
# P1R Content Planning 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-12
|
||||
|
||||
## 结论
|
||||
|
||||
本执行版只锁定 Content formal planning section 的两个 operation-level completed approval:
|
||||
|
||||
```text
|
||||
content:getPlanning
|
||||
content:savePlanningItem
|
||||
```
|
||||
|
||||
本轮不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51 completed,不修改 OpenAPI,不修改 Content 业务实现,不修改 SQL migration。
|
||||
|
||||
获批后目标 coverage summary 为:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=141
|
||||
needsVerification=92
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
Content 域目标状态为:
|
||||
|
||||
```text
|
||||
content total=51
|
||||
completed=10
|
||||
needsVerification=41
|
||||
```
|
||||
|
||||
当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Current["当前 Content<br/>8 completed / 43 needs_verification"] --> Exec["执行版<br/>锁定 Planning 2 ops"]
|
||||
Exec --> Review["fresh execution spec/scope review<br/>fresh execution quality/feasibility review"]
|
||||
Review --> Approval{"用户明确批准<br/>2 ops / operation-level / allowed diff / mixed gates"}
|
||||
Approval -->|否| Stay["保持当前 coverage<br/>233/139/94"]
|
||||
Approval -->|是| Red["TDD RED<br/>先改 gate 期望<br/>旧 report 必须失败"]
|
||||
Red --> Green["TDD GREEN<br/>scanner operation allowlist<br/>regenerate report"]
|
||||
Green --> Runtime["Planning HTTP + real DB _test<br/>V9 schema + command/audit/CAS"]
|
||||
Runtime --> Verify["focused gates + 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
|
||||
3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁
|
||||
```
|
||||
|
||||
当前 coverage summary:
|
||||
|
||||
```text
|
||||
233 139 94 0 0 0
|
||||
```
|
||||
|
||||
当前 Content 状态:
|
||||
|
||||
- Content total 为 51。
|
||||
- 已有 8 个 operation 为 `dedicated / completed`:`listWorks`、`getWork`、`listChapters`、`getChapter`、`listBlocks`、`getBlock`、`saveBlock`、`getBlockSourceAttribution`。
|
||||
- 仍有 43 个 operation 为 `dedicated / needs_verification`,其中包括本执行版目标 `getPlanning` 与 `savePlanningItem`。
|
||||
- `APPROVED_COMPLETED_DOMAINS` 当前只允许 `ai`、`knowledge`。
|
||||
- `APPROVED_COMPLETED_OPERATIONS` 当前包含 Events streamEvents、Meta 16、Account 第一批 10、Market 第一批 4、Content 第一批 8。
|
||||
- `P1rContentRealApiGateTest` 当前断言 Content 为 `8 completed / 43 needs_verification`,并把 `getPlanning`、`savePlanningItem` 视为未批准。
|
||||
- `P1rApiCoverageReportTest` 当前断言 summary `completed=139`、`needsVerification=94`,并断言 `content:getPlanning` 为 `needs_verification`。
|
||||
|
||||
已验证的只读实现事实:
|
||||
|
||||
- OpenAPI `SavePlanningItemRequest` required 为 `commandId`、`content`、`expectedRevision`。
|
||||
- 服务端 `SavePlanningItemReqVO` required 为 `commandId`、`content`、`expectedRevision`。
|
||||
- `sourceSnapshot` 在 OpenAPI 与 VO 中均为 optional,本轮不得改成 required。
|
||||
- `ContentPlanningServiceImpl.getPlanning` 通过 `requireOwnedWork(userId, workId)` 做 owner guard,读取 `muse_content_planning_section` 并返回 sections、最大 revision 与 latest sourceSnapshot。
|
||||
- `ContentPlanningServiceImpl.savePlanningItem` 使用 `commandId`、scoped request hash、`ContentCommandService.reserveCommand`、`requireOwnedWork`、planning section upsert、revision CAS、schema/projection version guard 和 `ContentAuditService.recordSucceededWithAudit`。
|
||||
- V9 SQL 已有 `muse_content_planning_section`、JSONB payload/source snapshot、默认 revision/schema/projection、唯一键 `(tenant_id, work_id, section_key)`、索引 `(tenant_id, work_id)` 和 `update_time` trigger。
|
||||
|
||||
## 执行边界
|
||||
|
||||
### 必须先获用户明确批准
|
||||
|
||||
实现前必须同时获得以下 4 项批准:
|
||||
|
||||
1. 批准本轮只审批 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation。
|
||||
2. 批准继续使用 operation-level approval,不把 `content` 加入 domain-level completed allowlist。
|
||||
3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Planning HTTP+DB `_test`、memory 和 `.agent`。
|
||||
4. 批准同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为 `233/141/92/0/0/0` 与 Content `10 completed / 41 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/P1rContentPlanningCompletedApprovalIT.java`
|
||||
- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentPlanningServiceTest.java`,仅限补充本执行版缺失的 focused behavior 证据。
|
||||
- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/app/AppContentPlanningControllerTest.java`,仅限补充本执行版缺失的 controller contract 证据。
|
||||
- `docs/agent-specs/.agent`
|
||||
- `docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md`
|
||||
- `docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md`
|
||||
- `docs/memorys/2026-06-13-P1RContentPlanning状态推进.md`
|
||||
|
||||
如 implementation 发现必须修改 Content 业务实现、OpenAPI、SQL migration、AI/Meta/FileService owner 实现、非批准 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 41 或总 P1R completed。
|
||||
- 不推进 AI planning candidate、style check、Meta projection、dynamic fields、FileService、import/export/parse/admin governance operation。
|
||||
- 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。
|
||||
|
||||
## 审批清单
|
||||
|
||||
| operation key | Method | Path | 目标状态 | 完成证据边界 |
|
||||
|---|---|---|---|---|
|
||||
| `content:getPlanning` | `GET` | `/app-api/muse/works/{workId}/planning` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖有 sections、空 sections、latest sourceSnapshot、missing work、cross owner、cross tenant、纯读 no-write |
|
||||
| `content:savePlanningItem` | `PUT` | `/app-api/muse/works/{workId}/planning/{sectionKey}` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 create、update CAS、command replay、三类 command conflict、validation、schema/projection/revision conflict、unique conflict、owner/tenant no-write、command/audit snapshot |
|
||||
|
||||
## 保持 needs_verification 的 Content operation
|
||||
|
||||
以下 41 个 operation 必须保持 `dedicated / needs_verification`:
|
||||
|
||||
```text
|
||||
content:adminListExportTasks
|
||||
content:adminListImportTasks
|
||||
content:adminListWorks
|
||||
content:adminGetWork
|
||||
content:adminListChapters
|
||||
content:adminRiskAction
|
||||
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
|
||||
```
|
||||
|
||||
保留原因:
|
||||
|
||||
- 结构写命令仍有独立 OpenAPI / commandId / revision 合同闭合风险。
|
||||
- 导入、导出、下载、解析确认依赖 FileService、AI、Knowledge owner 闭环。
|
||||
- Meta projection、dynamic fields 依赖 Meta owner schema / projection 解释与校验。
|
||||
- AI planning candidate、style check、suggestion merge 依赖 AI owner runtime / projection / quality facts。
|
||||
- admin read / governance 需要单独覆盖 admin RBAC、治理摘要、risk flags、治理动作幂等和审计。
|
||||
|
||||
## 证据矩阵
|
||||
|
||||
| 证据类型 | 必跑或必查内容 | 目的 |
|
||||
|---|---|---|
|
||||
| TDD RED | 先改 P1R gate 期望,不改 scanner/report | 证明旧 report 下会失败,避免直接改 scanner 制造假绿 |
|
||||
| Scanner/report GREEN | `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` | 证明 operation-level allowlist 与 report 一致,summary 为 `233/141/92` |
|
||||
| Planning HTTP+DB `_test` | 新增 `P1rContentPlanningCompletedApprovalIT` | 证明两个 operation 经 MockMvc HTTP 入口访问真实 PostgreSQL `_test` 数据 |
|
||||
| V9 schema gate | `P1rContentPlanningCompletedApprovalIT` 内验证 V1-V21 clean migrate 与 V9 planning schema | 证明 planning section schema、JSONB、默认值、unique/index/trigger、CAS/update_time 仍真实可用 |
|
||||
| Focused unit/controller tests | `ContentPlanningServiceTest`、`AppContentPlanningControllerTest` | 支撑 service/controller contract,不替代 HTTP+DB `_test` |
|
||||
| P1R Content gates | `P1rContentRealApiGateTest`、`P1rApiCoverageReportTest` | 证明 only 10 Content operation completed,41 个继续 needs_verification |
|
||||
| P1R mixed gates | Events、AI、Knowledge、Market、Account、Meta gates | 防止非目标 domain 被连带推进或回退 |
|
||||
| XML 防空跑 | 逐个读取 Surefire XML,检查 tests、mtime、failures、errors、skipped | 防止 Maven 未跑目标类或旧 XML 假绿 |
|
||||
| Protected diff | OpenAPI、Content main/java、SQL migration 必须无 diff | 证明没有通过改合同、实现或迁移掩盖缺口 |
|
||||
| Allowed diff | unstaged、staged、untracked 均必须落在允许清单 | 证明没有扩大 scope |
|
||||
|
||||
## TDD 执行步骤
|
||||
|
||||
### Task 0:实现前现场确认
|
||||
|
||||
- [ ] 确认 worktree、HEAD 和 coverage:
|
||||
|
||||
```bash
|
||||
pwd
|
||||
git -c core.quotePath=false status --short --branch
|
||||
git log --oneline -5
|
||||
jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
|
||||
233 139 94 0 0 0
|
||||
```
|
||||
|
||||
- [ ] 确认 `getPlanning` 与 `savePlanningItem` 当前仍为 `needs_verification`:
|
||||
|
||||
```bash
|
||||
jq -r '.operations[] | select(.domain=="content" and (.operationId=="getPlanning" or .operationId=="savePlanningItem")) | [.operationId,.implementationStatus,.completionStatus,.requiresCommandId] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
getPlanning dedicated needs_verification false
|
||||
savePlanningItem dedicated needs_verification true
|
||||
```
|
||||
|
||||
- [ ] 确认实施前只有文档 diff,没有 OpenAPI、scanner、coverage report、业务实现、SQL migration、P1R gate test diff:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false status --short --branch
|
||||
git -c core.quotePath=false diff -- docs/api-contracts
|
||||
git -c core.quotePath=false diff -- muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports
|
||||
git -c core.quotePath=false diff -- muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse
|
||||
git -c core.quotePath=false diff -- muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api
|
||||
```
|
||||
|
||||
### XML 防空跑通用规则
|
||||
|
||||
除 TDD RED 失败验证外,所有 Maven test 验证命令都必须执行以下规则,不能只读取历史 Surefire XML:
|
||||
|
||||
- 命令前记录 `export RUN_START_EPOCH=$(date +%s)`。
|
||||
- 命令前删除本任务要求检查的 `target/surefire-reports/TEST-*.xml`。
|
||||
- 命令后逐个确认 XML 存在、`mtime >= RUN_START_EPOCH`、`tests` 不低于本执行版最低值、`failures=0`、`errors=0`、`skipped=0`。
|
||||
- 任一 XML 缺失、mtime 早于运行开始时间、tests 低于最低值或存在失败/跳过,都必须视为验证失败。
|
||||
|
||||
TDD RED 失败验证必须同样删除目标 XML 并记录 `RUN_START_EPOCH`,但 RED 的预期是 Maven 失败;此时只校验失败发生在本次运行窗口内,并人工确认失败点来自旧 report mismatch。
|
||||
|
||||
推荐校验脚本:
|
||||
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
|
||||
run_start = int(os.environ["RUN_START_EPOCH"])
|
||||
checks = {
|
||||
"muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml": 6,
|
||||
}
|
||||
failed = False
|
||||
for path_text, min_tests in checks.items():
|
||||
path = Path(path_text)
|
||||
if not path.exists():
|
||||
print(f"MISSING_XML {path}")
|
||||
failed = True
|
||||
continue
|
||||
if int(path.stat().st_mtime) < run_start:
|
||||
print(f"STALE_XML {path}")
|
||||
failed = True
|
||||
continue
|
||||
root = ET.parse(path).getroot()
|
||||
tests = int(root.attrib.get("tests", "0"))
|
||||
failures = int(root.attrib.get("failures", "0"))
|
||||
errors = int(root.attrib.get("errors", "0"))
|
||||
skipped = int(root.attrib.get("skipped", "0"))
|
||||
print(path, tests, failures, errors, skipped)
|
||||
if tests < min_tests or failures or errors or skipped:
|
||||
failed = True
|
||||
if failed:
|
||||
sys.exit(1)
|
||||
PY
|
||||
```
|
||||
|
||||
执行具体任务时必须把 `checks` 替换成该任务表格内列出的 XML 路径和最低 tests。
|
||||
|
||||
### Task 1:TDD RED,先改 gate 期望
|
||||
|
||||
获批后先修改:
|
||||
|
||||
- `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`
|
||||
|
||||
RED 期望:
|
||||
|
||||
- summary 期望 `completed=141`、`needsVerification=92`,但旧 report 仍是 `139/94`,必须失败。
|
||||
- Content 10 个 approved operation 期望 `completed`,但 `getPlanning` 与 `savePlanningItem` 在旧 report 仍是 `needs_verification`,必须失败。
|
||||
- Content 41 个未批准 operation 继续期望 `needs_verification`,其中必须包含 `createWork`、`updateWork`、`createChapter`、`mergeBlockSuggestion`、`exportWork`、`listPlanningCandidates`、`createStyleCheck`、`adminRiskAction`。
|
||||
- Account 仍为 `10 completed / 23 needs_verification`,Market 仍为 `4 completed / 28 needs_verification`,Meta 仍为 `16 completed / 0 needs_verification`,AI/Knowledge/Events 不回退。
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd muse-cloud
|
||||
export RUN_START_EPOCH=$(date +%s)
|
||||
rm -f \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMetaRealApiGateTest.xml
|
||||
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
|
||||
```
|
||||
|
||||
预期:至少一个失败点来自旧 report 的 `139/94` 或 `getPlanning` / `savePlanningItem` 尚未 completed。若直接通过,必须停止并排查假绿。
|
||||
|
||||
### Task 2:TDD GREEN,最小 scanner/report 状态推进
|
||||
|
||||
只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`:
|
||||
|
||||
- 不修改 `APPROVED_COMPLETED_DOMAINS`。
|
||||
- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加两个 `content:*` operation key。
|
||||
- 保持 completed 必须仍是 `dedicated` 的校验。
|
||||
|
||||
生成 report:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
预期 summary:
|
||||
|
||||
```text
|
||||
total=233
|
||||
completed=141
|
||||
needsVerification=92
|
||||
incomplete=0
|
||||
genericPersistence=0
|
||||
ssePlaceholder=0
|
||||
```
|
||||
|
||||
预期 Content:
|
||||
|
||||
```text
|
||||
completed=10
|
||||
needsVerification=41
|
||||
```
|
||||
|
||||
预期 scanner 仍不包含 `content` domain-level allowlist:
|
||||
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
source = Path("muse-cloud/scripts/p1r-audit-api-coverage.py").read_text()
|
||||
assert 'APPROVED_COMPLETED_DOMAINS = {"ai", "knowledge"}' in source
|
||||
for key in ["content:getPlanning", "content:savePlanningItem"]:
|
||||
assert key in source
|
||||
print("content_planning_operation_level_allowlist=PASS")
|
||||
PY
|
||||
```
|
||||
|
||||
### Task 3:Focused Planning unit/controller verification
|
||||
|
||||
运行 Content planning focused tests:
|
||||
|
||||
```bash
|
||||
cd muse-cloud
|
||||
export RUN_START_EPOCH=$(date +%s)
|
||||
rm -f \
|
||||
muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.application.ContentPlanningServiceTest.xml \
|
||||
muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.controller.app.AppContentPlanningControllerTest.xml
|
||||
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
|
||||
mvn -o test -pl muse-module-content/muse-module-content-server -am \
|
||||
-Dtest=ContentPlanningServiceTest,AppContentPlanningControllerTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
必须读取以下 XML 并检查 `mtime >= RUN_START_EPOCH`、`failures=0`、`errors=0`、`skipped=0`:
|
||||
|
||||
| Test class | 最低 tests |
|
||||
|---|---:|
|
||||
| `ContentPlanningServiceTest` | 15 |
|
||||
| `AppContentPlanningControllerTest` | 7 |
|
||||
|
||||
Focused tests 必须至少支撑:
|
||||
|
||||
- `getPlanning` 有 sections 与 owner mismatch。
|
||||
- `savePlanningItem` 缺 `commandId`、缺 `expectedRevision`、create expectedRevision=0、update revision match、mapper `DuplicateKeyException` 转 `CONTENT_REVISION_CONFLICT`、command replay、command/audit snapshot。
|
||||
- Controller 层 `X-API-Version`、参数校验、CommonResult 路由与错误响应。
|
||||
|
||||
Focused tests 只作为补充证据,不替代 Task 4 的 HTTP + real DB `_test`。
|
||||
|
||||
### Task 4:Planning HTTP + 真实 PostgreSQL `_test` completed gate
|
||||
|
||||
新增 `P1rContentPlanningCompletedApprovalIT`,使用独立 `_test` 数据库、MockMvc HTTP 入口、真实 mapper/service、真实 PostgreSQL 数据。
|
||||
|
||||
测试上下文要求:
|
||||
|
||||
- 只导入 planning 必需 controller/service/mapper 与基础 Web/MyBatis/DataSource 自动配置,不做 Content 全包扫描。
|
||||
- 最小 Spring 配置必须复用 `P1rContentCoreCompletedApprovalIT` 的 Web/MyBatis/DataSource 模式:`JacksonAutoConfiguration`、`HttpMessageConvertersAutoConfiguration`、`DataSourceAutoConfiguration`、`DataSourceTransactionManagerAutoConfiguration`、`JdbcTemplateAutoConfiguration`、`TransactionAutoConfiguration`、`RestTemplateAutoConfiguration`、`WebMvcAutoConfiguration`、`MuseDataSourceAutoConfiguration`、`MuseMybatisAutoConfiguration`、`MybatisPlusAutoConfiguration`、`MybatisPlusJoinAutoConfiguration`、`MuseWebAutoConfiguration`。
|
||||
- 导入 `AppContentPlanningController`、`ContentPlanningServiceImpl`、`ContentCommandServiceImpl`、`ContentAuditServiceImpl`。
|
||||
- 提供 test-local `ApiErrorLogCommonApi` stub,返回 `CommonResult.success(true)`,满足 `MuseWebAutoConfiguration` / `GlobalExceptionHandler` 依赖。
|
||||
- 注册 `TenantLineInnerInterceptor`,通过 `MyBatisUtils.addInterceptor(interceptor, new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(new TenantProperties())), 0)` 接入 MyBatis Plus interceptor,确保 cross-tenant 断言是真实 SQL tenant 隔离,不只是 seed 数据碰巧不可见。
|
||||
- 提供 test-local `ContentPlanningCandidateFacade` 与 `ContentStyleCheckFacade` bean,返回 unavailable;目标两个 operation 不得调用这些 facade。
|
||||
- 使用 `WebApplicationContext + MockMvc` 访问 `/app-api/muse/works/{workId}/planning` 与 `/app-api/muse/works/{workId}/planning/{sectionKey}`。
|
||||
- 每个测试启动时设置 login user、tenant context,并在测试结束清理。
|
||||
- 每个测试前 `TRUNCATE muse_content_event_publish_outbox, muse_content_command_log, muse_content_planning_section, muse_content_work RESTART IDENTITY CASCADE`,再 seed 必需数据。
|
||||
|
||||
建库与运行:
|
||||
|
||||
```bash
|
||||
source ~/.config/muse-repo/infra.env
|
||||
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
|
||||
export P1R_CONTENT_PLANNING_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_planning_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_PLANNING_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_PLANNING_COMPLETED_APPROVAL_TEST_DB"
|
||||
|
||||
cd muse-cloud
|
||||
export RUN_START_EPOCH=$(date +%s)
|
||||
rm -f muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentPlanningCompletedApprovalIT.xml
|
||||
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
|
||||
mvn -o test -pl muse-server -am \
|
||||
-Dtest=P1rContentPlanningCompletedApprovalIT \
|
||||
-Dflyway.postgresql.transactional.lock=false \
|
||||
-Dp1r.flyway.locations=filesystem:sql/muse \
|
||||
-Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_PLANNING_COMPLETED_APPROVAL_TEST_DB" \
|
||||
-Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
|
||||
-Djava.net.useSystemProxies=false \
|
||||
-DsocksProxyHost= -DsocksProxyPort= \
|
||||
-Dhttp.proxyHost= -Dhttp.proxyPort= \
|
||||
-Dhttps.proxyHost= -Dhttps.proxyPort= \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
必须检查 XML:
|
||||
|
||||
| Test class | 最低 tests |
|
||||
|---|---:|
|
||||
| `P1rContentPlanningCompletedApprovalIT` | 11 |
|
||||
|
||||
该 IT 必须覆盖以下 11 个测试:
|
||||
|
||||
1. 拒绝 JVM password system property 和 JDBC credential query,库名必须以 `_test` 结尾。
|
||||
2. V1-V21 clean migrate 后验证 V9 planning schema:表、列类型、nullable/default、JSONB、unique/index/trigger。
|
||||
3. `getPlanning` 有 sections,返回 sections、最大 revision 与 latest sourceSnapshot,且不写 command/outbox。
|
||||
4. `getPlanning` 空 sections,返回空数组、revision=0、sourceSnapshot=null,且不写 command/outbox。
|
||||
5. `getPlanning` missing work、cross owner、cross tenant,错误响应不泄露其它 tenant/owner 数据,且 no-write。
|
||||
6. `savePlanningItem` create:`expectedRevision=0`,写入 tenant_id、content、sourceSnapshot、schemaVersion、projectionVersion、revision=1、command/audit result snapshot。
|
||||
7. `savePlanningItem` update:`expectedRevision` 匹配,revision+1,content/sourceSnapshot 更新,`update_time` 推进。
|
||||
8. `savePlanningItem` command replay:同 commandId / 同 request hash 返回首次 result,不重复 insert/update,不重复 command/audit。
|
||||
9. `savePlanningItem` command conflict:同 commandId 不同 content、同 commandId 不同 sectionKey、同 commandId 不同 workId 三个独立 HTTP+real DB 子用例都返回 `CONTENT_COMMAND_ID_CONFLICT`,并断言 planning row count/content/revision、command count、audit/result snapshot 均保持首次成功状态。
|
||||
10. `savePlanningItem` validation/revision/schema/projection conflict:缺 `commandId`、缺 `content`、缺 `expectedRevision`、existing row + `expectedRevision=0` 冲突、stale expectedRevision 冲突、schemaVersion 冲突、projectionVersion 冲突均 no-write。
|
||||
11. `savePlanningItem` missing work、cross owner、cross tenant 均 no-write,不写 planning row、command result 或 outbox。
|
||||
|
||||
Unique conflict 证据边界:
|
||||
|
||||
- 普通顺序 HTTP 请求如果已有 `(tenant_id, work_id, section_key)` row,真实实现会先 `selectByWorkIdAndSectionKey` 进入 existing 分支,不会触发 insert unique catch;不得把这种路径冒充 duplicate unique conflict。
|
||||
- DB unique 约束证据必须放在 V9 schema gate:用直接 JDBC insert 验证 duplicate `(tenant_id, work_id, section_key)` 被 PostgreSQL 拒绝,且既有 row 不变。
|
||||
- 服务层 `DuplicateKeyException` 转 `CONTENT_REVISION_CONFLICT` 的转换证据由 `ContentPlanningServiceTest` focused test 覆盖。
|
||||
- 如 implementation 额外选择 HTTP+real DB 并发 race 证明服务 catch 分支,必须使用独立 JDBC transaction 和受控提交协调,确保 HTTP PUT 的 select 看不到未提交 row、insert 在唯一键上等待、对端 commit 后返回 `CONTENT_REVISION_CONFLICT`;该 race 证据不是本轮 completed approval 的最低必需项。
|
||||
|
||||
数据库与安全要求:
|
||||
|
||||
- 密码只能从 `P1R_CONTENT_PLANNING_PASSWORD`、`P1R_FLYWAY_PASSWORD` 或 `MUSE_POSTGRES_PASSWORD` 环境变量读取,不能通过 JVM system property 传入。
|
||||
- JDBC URL 不能携带 `user`、`username`、`password`、`pass`、`pwd`、`token`、`secret`、`api_key`、`bearer`、`access_token`、`refresh_token` 等凭据 query。
|
||||
- 如果测试读取并脱敏 `p1r.flyway.url/user`,必须保存原始 system property 并在 `@AfterAll` 恢复。
|
||||
- 测试输出和断言不得打印明文数据库密码;raw Surefire XML 只能作为本地证据,外发前需清洗 DB host/user。
|
||||
|
||||
V9 schema assertion 必须包含:
|
||||
|
||||
- `muse_content_planning_section` 表存在。
|
||||
- `tenant_id BIGINT NOT NULL DEFAULT 0`。
|
||||
- `content_payload JSONB NOT NULL DEFAULT '{}'::jsonb`。
|
||||
- `source_snapshot JSONB` 允许为空。
|
||||
- `schema_version INT NOT NULL DEFAULT 1`。
|
||||
- `projection_version INT NOT NULL DEFAULT 1`。
|
||||
- `revision INT NOT NULL DEFAULT 1`。
|
||||
- unique `(tenant_id, work_id, section_key)` 存在。
|
||||
- index `(tenant_id, work_id)` 存在。
|
||||
- `trg_muse_content_planning_section_updated_at` 存在。
|
||||
- 真实 insert 验证 JSONB 存储、默认 revision/schema/projection、duplicate unique 被拒绝、CAS update 后 revision+1 且 `update_time` 推进。
|
||||
|
||||
Seed 数据至少包含:
|
||||
|
||||
- tenant `100` / login owner `9001` 的 owned work。
|
||||
- tenant `100` / login owner `9001` 的 empty planning work。
|
||||
- tenant `100` / other owner `9002` 的 other owner work。
|
||||
- tenant `200` / login owner `9001` 的 other tenant work。
|
||||
|
||||
必须查询和断言的 DB fact:
|
||||
|
||||
- `muse_content_planning_section`:`tenant_id`、`work_id`、`section_key`、`content_payload`、`schema_version`、`projection_version`、`source_snapshot`、`revision`、`update_time`。
|
||||
- `muse_content_command_log`:`tenant_id`、`command_id`、`command_type`、`owner_user_id`、`target_type`、`target_id`、`request_hash`、`result_snapshot`。
|
||||
- `muse_content_event_publish_outbox`:planning operation 不应写入 outbox,所有目标路径 outbox count 必须保持 0。
|
||||
|
||||
错误码至少覆盖:
|
||||
|
||||
- `CONTENT_COMMAND_ID_CONFLICT`
|
||||
- `CONTENT_FORBIDDEN`
|
||||
- `CONTENT_NOT_FOUND`
|
||||
- `CONTENT_EXPECTED_REVISION_REQUIRED`
|
||||
- `CONTENT_REVISION_CONFLICT`
|
||||
- Controller `@Valid` 参数缺失的 CommonResult 400 响应
|
||||
|
||||
### Task 5:P1R mixed gates
|
||||
|
||||
运行 P1R focused mixed gates:
|
||||
|
||||
```bash
|
||||
cd muse-cloud
|
||||
export RUN_START_EPOCH=$(date +%s)
|
||||
rm -f \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml \
|
||||
muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMetaRealApiGateTest.xml
|
||||
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 \
|
||||
-DfailIfNoTests=false \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
必须读取以下 XML:
|
||||
|
||||
| Test class | 最低 tests |
|
||||
|---|---:|
|
||||
| `P1rApiCoverageReportTest` | 6 |
|
||||
| `P1rContentRealApiGateTest` | 5 |
|
||||
| `P1rEventsRealApiGateTest` | 7 |
|
||||
| `P1rAiRealApiGateTest` | 7 |
|
||||
| `P1rKnowledgeRealApiGateTest` | 8 |
|
||||
| `P1rMarketRealApiGateTest` | 6 |
|
||||
| `P1rAccountRealApiGateTest` | 5 |
|
||||
| `P1rMetaRealApiGateTest` | 5 |
|
||||
|
||||
总计最低 tests 为 49;如新增断言导致 tests 增加,XML 最低值可同步提高,但不能低于上表。
|
||||
|
||||
Gate 更新要求:
|
||||
|
||||
- `P1rApiCoverageReportTest`:expected summary 改为 `completed=141`、`needsVerification=92`;approved Content completed set 改为 10;断言 `getPlanning` 与 `savePlanningItem` completed;仍断言 `createWork`、`updateWork`、`createChapter`、`mergeBlockSuggestion`、`exportWork`、`adminRiskAction` 为 `needs_verification`。
|
||||
- `P1rContentRealApiGateTest`:approved Content completed set 改为 10;Content count 改为 `10 completed / 41 needs_verification`;代表性未批准 operation 必须包含 AI planning candidate / style check 相关 operation。
|
||||
- 6 个 mixed gate:同步 summary 文案和 Content count 到 `10/41`,并继续校验 Account `10/23`、Market `4/28`、Meta `16/0`、AI/Knowledge/Events 不回退。
|
||||
|
||||
### Task 6:Coverage scanner final check
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
jq -r '.operations[] | select(.domain=="content") | [.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
233 141 92 0 0 0
|
||||
```
|
||||
|
||||
Content 必须精确为 10 completed / 41 needs_verification,且 `APPROVED_COMPLETED_DOMAINS` 仍不包含 `content`。
|
||||
|
||||
### Task 7:Diff gate 与文档留痕
|
||||
|
||||
- [ ] 更新 `.agent`,记录 implementation evidence、测试命令、XML 计数、protected diff、review 状态和 remaining boundary。
|
||||
- [ ] 新增或更新 `docs/memorys/2026-06-13-P1RContentPlanning状态推进.md`,只记录已验证事实,不把 review PASS 或 gate PASS 写成 Content 51/51 completed。
|
||||
- [ ] 运行 whitespace gate:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- [ ] 检查 OpenAPI protected diff 为空:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false diff -- docs/api-contracts
|
||||
```
|
||||
|
||||
- [ ] 检查 Content main/java 与 SQL migration diff 为空:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false diff -- muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse
|
||||
```
|
||||
|
||||
- [ ] 检查 scanner/report/gate/test/doc diff 只落在 allowed list:
|
||||
|
||||
```bash
|
||||
git -c core.quotePath=false diff --name-only
|
||||
git -c core.quotePath=false ls-files --others --exclude-standard
|
||||
```
|
||||
|
||||
允许路径必须是本执行版“本轮允许变更”列出的路径。出现其它路径时必须停止并说明。
|
||||
|
||||
### Task 8:Fresh implementation review gate
|
||||
|
||||
实现与验证完成后,必须重新派发两个 fresh reviewer:
|
||||
|
||||
- fresh implementation spec/correctness reviewer。
|
||||
- fresh implementation quality/data-integrity/testing reviewer。
|
||||
|
||||
两个 reviewer 都必须只读复核:
|
||||
|
||||
- 正确 worktree 与 HEAD。
|
||||
- coverage summary `233/141/92/0/0/0`。
|
||||
- Content 精确为 `10 completed / 41 needs_verification`。
|
||||
- `APPROVED_COMPLETED_DOMAINS` 未加入 `content`。
|
||||
- scanner 只追加 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level allowlist。
|
||||
- OpenAPI、Content main/java、SQL migration diff 为空。
|
||||
- `P1rContentPlanningCompletedApprovalIT` HTTP + real DB `_test` 证据覆盖 V9 schema、getPlanning、savePlanningItem、三类 command conflict 和 no-write。
|
||||
- P1R mixed gates XML 防空跑成立。
|
||||
|
||||
fresh implementation review 双 PASS 前,不得提交、push 或宣称 `getPlanning` / `savePlanningItem` completed approval 已收口。
|
||||
|
||||
## 回滚策略
|
||||
|
||||
如任一 gate 失败:
|
||||
|
||||
- 保留失败输出和 XML 事实,先定位是否是执行版遗漏、测试假设错误、实现缺口或环境问题。
|
||||
- 不通过修改 OpenAPI、Content main/java 或 SQL migration 来绕过本轮 coverage 缺口。
|
||||
- 如缺口证明当前实现不足以支撑 completed approval,回退本轮 scanner/report/gate 状态推进,只保留审阅版和执行版文档事实,向用户汇报阻塞。
|
||||
|
||||
如需要回退已实施的 operation-level approval:
|
||||
|
||||
- 从 `APPROVED_COMPLETED_OPERATIONS` 移除 `content:getPlanning` 和 `content:savePlanningItem`。
|
||||
- 重新运行 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check`,恢复 summary `233/139/94/0/0/0`。
|
||||
- 恢复 P1R gate 对 Content `8 completed / 43 needs_verification` 的断言。
|
||||
- 保留 memory 中的失败事实和回退原因,避免后续重复推进。
|
||||
|
||||
## 自检清单
|
||||
|
||||
- [ ] 本执行版只覆盖 `content:getPlanning` 与 `content:savePlanningItem`。
|
||||
- [ ] 本执行版未要求把 `content` 加入 domain-level completed allowlist。
|
||||
- [ ] 本执行版未要求修改 OpenAPI、Content main/java 或 SQL migration。
|
||||
- [ ] 41 个保留 `needs_verification` operation 与审阅版一致。
|
||||
- [ ] TDD RED 在 scanner/report 修改前发生。
|
||||
- [ ] HTTP+DB `_test` 覆盖 V9 schema、tenant/owner、getPlanning、savePlanningItem、CAS、command replay、三类 command conflict 和 no-write。
|
||||
- [ ] Mixed gates 包含 Account、Meta 防回退。
|
||||
- [ ] XML 防空跑有明确 mtime、tests、failures、errors、skipped 规则。
|
||||
- [ ] 实现完成后仍需 fresh implementation review 双 PASS,才允许请求提交/push。
|
||||
369
docs/memorys/2026-06-13-P1RContentPlanning状态推进.md
Normal file
369
docs/memorys/2026-06-13-P1RContentPlanning状态推进.md
Normal file
@ -0,0 +1,369 @@
|
||||
# P1R Content Planning 状态推进
|
||||
|
||||
日期:2026-06-13
|
||||
|
||||
## 结论
|
||||
|
||||
本轮只推进 Content Planning 2 个 operation-level completed approval:
|
||||
|
||||
- `content:getPlanning`
|
||||
- `content:savePlanningItem`
|
||||
|
||||
Content 仍不是整域 completed。其余 41 个 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
|
||||
3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁
|
||||
```
|
||||
|
||||
实施前 coverage summary:
|
||||
|
||||
```text
|
||||
233 139 94 0 0 0
|
||||
```
|
||||
|
||||
实施后 coverage summary:
|
||||
|
||||
```text
|
||||
233 141 92 0 0 0
|
||||
```
|
||||
|
||||
实施后 Content 状态:
|
||||
|
||||
```text
|
||||
completed=10
|
||||
needs_verification=41
|
||||
```
|
||||
|
||||
目标 operation 状态:
|
||||
|
||||
```text
|
||||
getPlanning dedicated completed false
|
||||
savePlanningItem dedicated completed true
|
||||
```
|
||||
|
||||
## 继续 needs_verification 的 Content operation
|
||||
|
||||
```text
|
||||
content:adminListExportTasks
|
||||
content:adminListImportTasks
|
||||
content:adminListWorks
|
||||
content:adminGetWork
|
||||
content:adminListChapters
|
||||
content:adminRiskAction
|
||||
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
|
||||
```
|
||||
|
||||
## 修改范围
|
||||
|
||||
本轮修改:
|
||||
|
||||
- `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/P1rContentPlanningCompletedApprovalIT.java`
|
||||
- `docs/agent-specs/.agent`
|
||||
- `docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md`
|
||||
- `docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md`
|
||||
- `docs/memorys/2026-06-13-P1RContentPlanning状态推进.md`
|
||||
|
||||
未修改:
|
||||
|
||||
- 7 个 OpenAPI。
|
||||
- Content 业务实现。
|
||||
- SQL migration。
|
||||
- Content domain-level completed allowlist。
|
||||
|
||||
## TDD evidence
|
||||
|
||||
RED 阶段先修改 P1R gate 期望值和 Content Planning 2 个 operation 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现:
|
||||
|
||||
- 失败点来自旧 report 仍为 `completed=139 / needsVerification=94`。
|
||||
- 失败点来自 `content/getPlanning` 仍为 `needs_verification`。
|
||||
- 失败点包含 summary 断言 `expected 141 but was 139`。
|
||||
|
||||
GREEN 阶段完成 scanner operation-level allowlist、report 重新生成、Planning HTTP+DB `_test` evidence 和 focused gates 后,目标验证通过。
|
||||
|
||||
## MockMvc HTTP 入口 + DB 证据
|
||||
|
||||
新增:
|
||||
|
||||
- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentPlanningCompletedApprovalIT.java`
|
||||
|
||||
验证方式:
|
||||
|
||||
- 使用 Spring Mock web context + `MockMvc`。
|
||||
- 使用真实 mapper / service。
|
||||
- 使用真实 PostgreSQL `_test` 数据库。
|
||||
- 使用 Flyway clean/migrate V1-V21。
|
||||
- 密码只从环境变量读取,禁止通过 JVM system property 或 JDBC query 传入。
|
||||
|
||||
说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。
|
||||
|
||||
单类验证数据库:
|
||||
|
||||
```text
|
||||
muse_p1r_content_planning_completed_approval_test
|
||||
```
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
P1rContentPlanningCompletedApprovalIT: tests=11, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
覆盖内容:
|
||||
|
||||
- 拒绝 JVM password system property、JDBC credential query 和非 `_test` 数据库。
|
||||
- V9 planning schema 门禁:JSONB 类型、nullable/default、`tenant_id` BIGINT NOT NULL DEFAULT 0、`schema_version/projection_version/revision` NOT NULL + default、unique、index、trigger。
|
||||
- 直接 JDBC 行为门禁:默认 revision/schema/projection、JSONB 存储、duplicate `(tenant_id, work_id, section_key)` 被 PostgreSQL 以 `23505` 拒绝、不同 tenant 可并存、CAS update 后 revision+1 且 `update_time` 推进。
|
||||
- `getPlanning` happy、empty、missing work、cross owner、cross tenant,查询路径不写 command/outbox。
|
||||
- `savePlanningItem` create,断言 planning section、sourceSnapshot、command audit snapshot 与 outbox no-write。
|
||||
- command/audit snapshot 明细断言覆盖 `tenantId`、`requestHash` 长度、`result.newRevision`、`result.sectionKey`、audit `side/operationId/reason/status/resultSummary`,以及 `requestSummary.operationId/sectionKey/expectedRevision/saveMode`。
|
||||
- `savePlanningItem` update,断言 CAS revision、schemaVersion、projectionVersion、sourceSnapshot。
|
||||
- `savePlanningItem` replay,不重复推进 revision、不重复写 command。
|
||||
- 三类 command conflict no-write:同 `commandId` 不同 content、不同 `sectionKey`、不同 `workId` 均返回 `CONTENT_COMMAND_ID_CONFLICT`,首次 planning row 与 command audit/result snapshot 不变,目标 section/work 不产生脏 row。
|
||||
- 缺 `commandId`、缺 `content`、缺 `expectedRevision`、revision conflict、schemaVersion conflict、projectionVersion conflict 均断言 no-write;其中缺 `commandId` 与缺 `content` 已补全租户 command/outbox no-write。
|
||||
- missing work、cross owner、cross tenant 均断言事务回滚后无残留 command,并补充全租户 command/outbox 污染断言。
|
||||
- missing work、cross owner、cross tenant 均补充目标 planning row no-write:不存在的 work 不创建 row,其他 owner work 不在当前 tenant 创建 row,cross tenant 不污染当前 tenant 或目标 tenant planning row。
|
||||
|
||||
## 组合验证
|
||||
|
||||
组合验证使用真实 PostgreSQL `_test` 库:
|
||||
|
||||
```text
|
||||
muse_p1r_content_planning_combined_test
|
||||
```
|
||||
|
||||
首次组合运行失败原因:
|
||||
|
||||
```text
|
||||
P1rContentEventsPublishFlywayMigrationIT.should_migrateV1ToV21OnRealPostgresqlAndVerifyContentPublishOutboxSchema
|
||||
缺少必需环境变量: P1R_FLYWAY_PASSWORD 或 MUSE_POSTGRES_PASSWORD
|
||||
```
|
||||
|
||||
根因:
|
||||
|
||||
- 新增 Planning IT 与 Core IT 接受 `P1R_CONTENT_COMPLETED_PASSWORD`。
|
||||
- 既有 Content Events Flyway IT 接受 `P1R_FLYWAY_PASSWORD` 或 `MUSE_POSTGRES_PASSWORD`。
|
||||
- 首次组合命令只设置了 `P1R_CONTENT_COMPLETED_PASSWORD`。
|
||||
|
||||
修正:
|
||||
|
||||
- 仅调整运行环境,同时设置 `P1R_CONTENT_COMPLETED_PASSWORD` 与 `P1R_FLYWAY_PASSWORD`。
|
||||
- 未修改代码。
|
||||
|
||||
通过结果:
|
||||
|
||||
```text
|
||||
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=27, failures=0, errors=0, skipped=0
|
||||
```
|
||||
|
||||
## focused Content tests
|
||||
|
||||
执行版 Task 3 focused tests 已单独运行,并确认 XML 为本轮 fresh 产物:
|
||||
|
||||
```text
|
||||
ContentPlanningServiceTest: tests=15, failures=0, errors=0, skipped=0, fresh_xml=yes
|
||||
AppContentPlanningControllerTest: tests=7, failures=0, errors=0, skipped=0, fresh_xml=yes
|
||||
combined focused tests: 22/22 pass
|
||||
```
|
||||
|
||||
## focused gates
|
||||
|
||||
P1R mixed gates 通过,并已做 XML 防空跑:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
合计:
|
||||
|
||||
```text
|
||||
P1R mixed gates: 49/49 pass
|
||||
```
|
||||
|
||||
## scanner 与 diff 证据
|
||||
|
||||
scanner:
|
||||
|
||||
```bash
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```text
|
||||
Generated docs/superpowers/reports/p1r-api-coverage.json
|
||||
Generated docs/superpowers/reports/p1r-api-coverage.md
|
||||
233 141 92 0 0 0
|
||||
```
|
||||
|
||||
Content operation 状态确认:
|
||||
|
||||
```text
|
||||
completed=10
|
||||
needs_verification=41
|
||||
```
|
||||
|
||||
本轮新增 completed 清单:
|
||||
|
||||
```text
|
||||
getPlanning
|
||||
savePlanningItem
|
||||
```
|
||||
|
||||
diff check:
|
||||
|
||||
```text
|
||||
git diff --check: pass
|
||||
```
|
||||
|
||||
protected OpenAPI diff:
|
||||
|
||||
```text
|
||||
empty
|
||||
```
|
||||
|
||||
## fresh review 状态
|
||||
|
||||
历史 review:
|
||||
|
||||
- Curie testing/data-integrity review PASS,无 blocker;该结论发生在 Ohm 后续修复之前,当前只作为历史记录。
|
||||
- Kepler correctness reviewer 超时后关闭,无可用结论。
|
||||
|
||||
Ohm fresh correctness/spec review FAIL,3 个 P1 有效:
|
||||
|
||||
- 缺 `commandId` 与缺 `content` validation 路径未做全租户 command/outbox no-write。
|
||||
- command/audit snapshot 断言不足,缺 tenant_id、request_hash、result_snapshot、audit requestSummary/operationId/status/reason 等关键事实。
|
||||
- 执行版 Task 3 mandatory focused `ContentPlanningServiceTest` 与 `AppContentPlanningControllerTest` 未运行,XML 证据缺失。
|
||||
|
||||
已修复并验证:
|
||||
|
||||
- 缺 `commandId` 与缺 `content` validation 改为全租户 command/outbox no-write。
|
||||
- command/audit snapshot 增加 `tenantId`、`requestHash`、`result.newRevision/sectionKey`、audit `side/operationId/reason/status/resultSummary`、`requestSummary.operationId/sectionKey/expectedRevision/saveMode` 断言。
|
||||
- Task 3 focused tests fresh 运行,`ContentPlanningServiceTest 15/15` 与 `AppContentPlanningControllerTest 7/7`,XML mtime 均在运行窗口内。
|
||||
|
||||
本轮 fresh implementation review:
|
||||
|
||||
- Locke correctness/spec review PASS,确认 scope、scanner、coverage、非目标 operation、Planning IT 和 Ohm 3 个 P1 修复均符合执行版。
|
||||
- Locke 残余风险:执行版 allowed diff memory 文件日期写成 `2026-06-12`,实际为 `2026-06-13`;已修订执行版 allowed diff。
|
||||
- Hilbert testing/data-integrity review FAIL,2 个 P2 有效:V9 schema gate 未显式断言 `schema_version/projection_version/revision` NOT NULL;missing/cross-owner/cross-tenant no-write 未逐一断言目标 planning row 未创建。
|
||||
|
||||
Hilbert P2 已修复并验证:
|
||||
|
||||
- 补 `schema_version`、`projection_version`、`revision` 三列 `columnNullable(...)=NO` 断言。
|
||||
- 补 missing work、cross-owner、cross-tenant 当前 tenant 与目标 tenant planning row count 为 0 的断言。
|
||||
- 修复后 fresh 重跑 `P1rContentPlanningCompletedApprovalIT`,tests=11、failures=0、errors=0、skipped=0,XML mtime 为 `2026-06-13 01:53:08 +0800`。
|
||||
|
||||
当前仍需重新派发 fresh implementation spec/correctness re-review + fresh quality/data-integrity/testing re-review;双 PASS 前不得提交或 push。
|
||||
|
||||
Fresh re-review 已双 PASS:
|
||||
|
||||
- Epicurus correctness/spec re-review PASS,确认 protected diff 为空、scope 未扩大、scanner 仍只追加 `content:getPlanning` / `content:savePlanningItem`、执行版 allowed diff 已改为实际 memory 路径、Hilbert 两个 P2 已关闭、coverage report 为 `233/141/92/0/0/0` 且 Content 为 `10 completed / 41 needs_verification`。Epicurus 未运行 Maven 测试,只做只读 diff/source/docs/report 复核。
|
||||
- Nietzsche testing/data-integrity re-review PASS,确认 schema NOT NULL、missing/cross-owner/cross-tenant 目标 row no-write、缺 `commandId` / 缺 `content` 全租户 command/outbox no-write、command/audit snapshot 解析断言、fresh XML `P1rContentPlanningCompletedApprovalIT 11/11`、`.agent` / memory 留痕和 `git diff --check` 均无新 blocker。Nietzsche 未运行 Maven 测试,只解析现有 XML 并运行只读 diff check。
|
||||
|
||||
残余风险:
|
||||
|
||||
- Surefire XML 含本地 classpath、`user.dir`、测试 DB URL 等本机信息,外发前需清洗。
|
||||
- 新增 IT 与 memory 当前仍未提交,提交前必须确认纳入正确 commit。
|
||||
- 双 PASS 只代表 `content:getPlanning` 与 `content:savePlanningItem` 两个 operation-level completed approval 具备收口条件,不代表 Content 51/51、Account remaining、Market remaining 或总 P1R completed。
|
||||
|
||||
## final verification
|
||||
|
||||
Fresh 双 PASS 后已重跑:
|
||||
|
||||
```text
|
||||
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check: pass
|
||||
ContentPlanningServiceTest + AppContentPlanningControllerTest: 22/22 pass, fresh XML yes
|
||||
P1R mixed gates: 49/49 pass
|
||||
Content combined DB gate: 27/27 pass
|
||||
git diff --check: pass
|
||||
protected OpenAPI / Content main/java / SQL diff: empty
|
||||
```
|
||||
|
||||
正确 jq 验证:
|
||||
|
||||
```text
|
||||
summary: 233 141 92 0 0 0
|
||||
Content completed: 10
|
||||
Content needs_verification: 41
|
||||
getPlanning dedicated completed false
|
||||
savePlanningItem dedicated completed true
|
||||
listPlanningCandidates dedicated needs_verification false
|
||||
createStyleCheck dedicated needs_verification true
|
||||
getStyleCheckResult dedicated needs_verification false
|
||||
```
|
||||
|
||||
## 当前边界
|
||||
|
||||
- 本轮只代表 Content Planning 2 个 operation-level completed approval。
|
||||
- 不代表 Content 51/51 completed。
|
||||
- 不代表 Account remaining completed。
|
||||
- 不代表 Market remaining completed。
|
||||
- 不代表总 P1R completed。
|
||||
- raw Surefire XML 可能包含 DB host/user;外发前需清洗。
|
||||
- 当前具备提交候选条件;提交或 push 仍需用户明确指令。
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-05-25T15:49:45+00:00",
|
||||
"generatedAt": "2026-05-25T10:28:57+00:00",
|
||||
"sourceContracts": [
|
||||
"docs/api-contracts/account/openapi.yaml",
|
||||
"docs/api-contracts/ai/openapi.yaml",
|
||||
@ -11,9 +11,9 @@
|
||||
],
|
||||
"summary": {
|
||||
"totalOperations": 233,
|
||||
"completedOperations": 139,
|
||||
"completedOperations": 141,
|
||||
"incompleteOperations": 0,
|
||||
"needsVerificationOperations": 94,
|
||||
"needsVerificationOperations": 92,
|
||||
"catchAllOperations": 0,
|
||||
"genericPersistenceOperations": 0,
|
||||
"ssePlaceholderOperations": 0,
|
||||
@ -9674,7 +9674,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/app/AppContentPlanningController.java"
|
||||
],
|
||||
@ -9711,7 +9711,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",
|
||||
@ -10077,7 +10077,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/app/AppContentPlanningController.java"
|
||||
],
|
||||
@ -10114,7 +10114,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": "events",
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# P1R API Coverage Report
|
||||
|
||||
- Generated at: `2026-05-25T15:49:45+00:00`
|
||||
- Generated at: `2026-05-25T10:28:57+00:00`
|
||||
- Total operations: `233`
|
||||
- Completed: `139`
|
||||
- Needs verification: `94`
|
||||
- Completed: `141`
|
||||
- Needs verification: `92`
|
||||
- Incomplete: `0`
|
||||
- Catch-all: `0`
|
||||
- Generic persistence: `0`
|
||||
@ -130,7 +130,7 @@
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/meta-projections` | `listMetaProjections` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/meta-projections/{projectionKey}` | `getMetaProjection` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | POST | `/app-api/muse/works/{workId}/parse-jobs` | `createParseJob` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/planning` | `getPlanning` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/planning` | `getPlanning` | dedicated | completed | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/planning/candidates` | `listPlanningCandidates` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | POST | `/app-api/muse/works/{workId}/planning/candidates` | `createPlanningCandidate` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}` | `getPlanningCandidate` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
@ -138,7 +138,7 @@
|
||||
| content | app | POST | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}/discard` | `discardPlanningCandidate` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | POST | `/app-api/muse/works/{workId}/planning/style-checks` | `createStyleCheck` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | GET | `/app-api/muse/works/{workId}/planning/style-checks/{jobId}` | `getStyleCheckResult` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | PUT | `/app-api/muse/works/{workId}/planning/{sectionKey}` | `savePlanningItem` | dedicated | needs_verification | P1R-1 Content Real API |
|
||||
| content | app | PUT | `/app-api/muse/works/{workId}/planning/{sectionKey}` | `savePlanningItem` | dedicated | completed | P1R-1 Content Real API |
|
||||
| events | app | GET | `/app-api/muse/events` | `streamEvents` | dedicated | completed | P1R-7 End-to-End Acceptance |
|
||||
| knowledge | admin | GET | `/admin-api/muse/knowledge/drafts` | `listKnowledgeDraftsGovernance` | dedicated | completed | P1R-5 Knowledge Real API |
|
||||
| knowledge | admin | GET | `/admin-api/muse/knowledge/global-kbs` | `listGlobalKnowledgeBases` | dedicated | completed | P1R-5 Knowledge Real API |
|
||||
|
||||
@ -54,7 +54,7 @@ class P1rAccountRealApiGateTest {
|
||||
"adminListQuotaAdjustments"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -63,7 +63,9 @@ class P1rAccountRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
/** P1R-3 计划第 1 节冻结的 Account operation、方法和路径清单。 */
|
||||
@ -188,12 +190,12 @@ class P1rAccountRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Content 只推进第一批 8 个 operation,防止 Account gate 漏掉 Content 未批准项回退检查。
|
||||
* 验证 Content 只推进当前已批准的 10 个 operation,防止 Account gate 漏掉 Content 未批准项回退检查。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_8_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_10_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -213,8 +215,8 @@ class P1rAccountRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(8, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(43, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(10, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(41, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readAccountOperations() throws IOException {
|
||||
|
||||
@ -85,7 +85,7 @@ class P1rAiRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -94,7 +94,9 @@ class P1rAiRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -139,8 +141,8 @@ class P1rAiRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(139, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 8");
|
||||
assertEquals(141, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -165,8 +167,8 @@ class P1rAiRealApiGateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_content_meta_account_dedicated_needs_verification() throws IOException {
|
||||
assertContentStatusCount(8, 43);
|
||||
void should_keep_content_meta_account_dedicated_statuses() throws IOException {
|
||||
assertContentStatusCount(10, 41);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertMarketStatusCount(4, 28);
|
||||
@ -195,8 +197,8 @@ class P1rAiRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private void assertAccountStatusCount(int expectedCompleted, int expectedNeedsVerification) throws IOException {
|
||||
|
||||
@ -92,7 +92,9 @@ class P1rApiCoverageReportTest {
|
||||
"content:listBlocks",
|
||||
"content:getBlock",
|
||||
"content:saveBlock",
|
||||
"content:getBlockSourceAttribution"
|
||||
"content:getBlockSourceAttribution",
|
||||
"content:getPlanning",
|
||||
"content:savePlanningItem"
|
||||
);
|
||||
|
||||
/** 已批准的 MetaSchema 管理接口。 */
|
||||
@ -150,7 +152,9 @@ class P1rApiCoverageReportTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -268,7 +272,7 @@ class P1rApiCoverageReportTest {
|
||||
completed++;
|
||||
}
|
||||
}
|
||||
assertEquals(139, completed, "Content 第一批 8 个 operation-level approval 后 completed 总数只能从 131 增至 139");
|
||||
assertEquals(141, completed, "Content Planning 2 个 operation-level approval 后 completed 总数只能从 139 增至 141");
|
||||
|
||||
assertOperationStatus("events", "streamEvents", "dedicated", "completed");
|
||||
for (String operationId : APPROVED_META_SCHEMA_COMPLETED_OPERATIONS) {
|
||||
@ -295,11 +299,12 @@ class P1rApiCoverageReportTest {
|
||||
assertOperationStatus("content", "createChapter", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "mergeBlockSuggestion", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "exportWork", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "getPlanning", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "listPlanningCandidates", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "createStyleCheck", "dedicated", "needs_verification");
|
||||
assertOperationStatus("content", "adminRiskAction", "dedicated", "needs_verification");
|
||||
assertDomainStatusCount("market", "dedicated", "needs_verification", 28);
|
||||
assertDomainStatusCount("account", "dedicated", "needs_verification", 23);
|
||||
assertDomainStatusCount("content", "dedicated", "needs_verification", 43);
|
||||
assertDomainStatusCount("content", "dedicated", "needs_verification", 41);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -93,7 +93,7 @@ class P1rContentRealApiGateTest {
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
/** 已批准的 Content 第一批用户端核心作品、章节、Block 与来源归因接口,仍保持 operation-level approval。 */
|
||||
/** 已批准的 Content 用户端核心与 formal planning section 接口,仍保持 operation-level approval。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -102,17 +102,20 @@ class P1rContentRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
/** 必须继续保持 needs_verification 的代表性未批准 Content operation,防止 CRUD、导出、规划和 admin 误推进。 */
|
||||
/** 必须继续保持 needs_verification 的代表性未批准 Content operation,防止 CRUD、导出、AI planning/style 和 admin 误推进。 */
|
||||
private static final Set<String> REPRESENTATIVE_UNAPPROVED_CONTENT_OPERATIONS = Set.of(
|
||||
"createWork",
|
||||
"updateWork",
|
||||
"createChapter",
|
||||
"mergeBlockSuggestion",
|
||||
"exportWork",
|
||||
"getPlanning",
|
||||
"listPlanningCandidates",
|
||||
"createStyleCheck",
|
||||
"adminRiskAction"
|
||||
);
|
||||
|
||||
@ -196,7 +199,7 @@ class P1rContentRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前 completed approval 只推进第一批 8 个 Content operation,其余 43 个仍保持 needs_verification。
|
||||
* 验证当前 completed approval 只推进已批准的 10 个 Content operation,其余 41 个仍保持 needs_verification。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@ -220,8 +223,8 @@ class P1rContentRealApiGateTest {
|
||||
operationId + " 未获 Content 第一批批准,必须继续保持 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(8, completed, "Content 第一批 operation-level completed approval 只能推进 8 个 operation");
|
||||
assertEquals(43, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(10, completed, "Content Planning operation-level completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(41, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
for (String operationId : REPRESENTATIVE_UNAPPROVED_CONTENT_OPERATIONS) {
|
||||
assertContentOperationStatus(operationId, "needs_verification");
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class P1rEventsRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -54,7 +54,9 @@ class P1rEventsRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -100,10 +102,10 @@ class P1rEventsRealApiGateTest {
|
||||
void should_keep_completed_approval_summary_at_approved_operation_boundary() throws IOException {
|
||||
JsonNode summary = readReport().path("summary");
|
||||
|
||||
assertEquals(139, summary.path("completedOperations").asInt(),
|
||||
"completedOperations 只能来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 8");
|
||||
assertEquals(94, summary.path("needsVerificationOperations").asInt(),
|
||||
"needsVerificationOperations 必须扣除已批准的 Events streamEvents、Meta 16、Account 10、Market 4 和 Content 8");
|
||||
assertEquals(141, summary.path("completedOperations").asInt(),
|
||||
"completedOperations 只能来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 10");
|
||||
assertEquals(92, summary.path("needsVerificationOperations").asInt(),
|
||||
"needsVerificationOperations 必须扣除已批准的 Events streamEvents、Meta 16、Account 10、Market 4 和 Content 10");
|
||||
assertEquals(0, summary.path("incompleteOperations").asInt(),
|
||||
"Events 退出 placeholder 后不应再留下 incomplete operation");
|
||||
assertEquals(0, summary.path("genericPersistenceOperations").asInt(),
|
||||
@ -167,7 +169,7 @@ class P1rEventsRealApiGateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_8_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_10_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -187,8 +189,8 @@ class P1rEventsRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(8, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(43, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(10, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(41, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readSingleEventsOperation() throws IOException {
|
||||
|
||||
@ -107,7 +107,7 @@ class P1rKnowledgeRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -116,7 +116,9 @@ class P1rKnowledgeRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -178,14 +180,14 @@ class P1rKnowledgeRealApiGateTest {
|
||||
void should_count_ai_and_knowledge_operations_as_completed() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(139, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 8");
|
||||
assertEquals(141, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_existing_dedicated_domains_unchanged() throws IOException {
|
||||
assertDomainStatusCount("ai", 41, "completed");
|
||||
assertContentStatusCount(8, 43);
|
||||
assertContentStatusCount(10, 41);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
assertMarketStatusCount(4, 28);
|
||||
@ -214,8 +216,8 @@ class P1rKnowledgeRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -78,7 +78,7 @@ class P1rMarketRealApiGateTest {
|
||||
"unfavoriteAsset"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -87,7 +87,9 @@ class P1rMarketRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -145,15 +147,15 @@ class P1rMarketRealApiGateTest {
|
||||
void should_keep_completion_summary_owned_by_approved_scopes_only() throws IOException {
|
||||
JsonNode report = readReport();
|
||||
|
||||
assertEquals(139, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 8");
|
||||
assertEquals(141, report.path("summary").path("completedOperations").asInt(),
|
||||
"completedOperations 必须只来自 AI 41 + Knowledge 59 + Events streamEvents 1 + Meta 16 + Account 10 + Market 4 + Content 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_keep_existing_domain_statuses_unchanged() throws IOException {
|
||||
assertDomainStatusCount("ai", 41, "completed");
|
||||
assertDomainStatusCount("knowledge", 59, "completed");
|
||||
assertContentStatusCount(8, 43);
|
||||
assertContentStatusCount(10, 41);
|
||||
assertMetaStatusCount(16, 0);
|
||||
assertAccountStatusCount(10, 23);
|
||||
}
|
||||
@ -181,8 +183,8 @@ class P1rMarketRealApiGateTest {
|
||||
}
|
||||
}
|
||||
assertEquals(51, total, "Content operation 数量必须保持 51");
|
||||
assertEquals(expectedCompleted, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(expectedCompleted, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(expectedNeedsVerification, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -76,7 +76,7 @@ class P1rMetaRealApiGateTest {
|
||||
"activateFunctionChainVersion"
|
||||
);
|
||||
|
||||
/** Content completed approval 第一批只允许推进的 8 个 operation。 */
|
||||
/** Content completed approval 当前只允许推进 10 个 operation。 */
|
||||
private static final Set<String> APPROVED_CONTENT_COMPLETED_OPERATIONS = Set.of(
|
||||
"listWorks",
|
||||
"getWork",
|
||||
@ -85,7 +85,9 @@ class P1rMetaRealApiGateTest {
|
||||
"listBlocks",
|
||||
"getBlock",
|
||||
"saveBlock",
|
||||
"getBlockSourceAttribution"
|
||||
"getBlockSourceAttribution",
|
||||
"getPlanning",
|
||||
"savePlanningItem"
|
||||
);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@ -168,12 +170,12 @@ class P1rMetaRealApiGateTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Content 只推进第一批 8 个 operation,防止 Meta gate 漏掉 Content 未批准项回退检查。
|
||||
* 验证 Content 只推进当前已批准的 10 个 operation,防止 Meta gate 漏掉 Content 未批准项回退检查。
|
||||
*
|
||||
* @throws IOException 读取覆盖报告失败时抛出
|
||||
*/
|
||||
@Test
|
||||
void should_keep_content_partial_approval_at_8_completed_operations() throws IOException {
|
||||
void should_keep_content_partial_approval_at_10_completed_operations() throws IOException {
|
||||
JsonNode contentOperations = readContentOperations();
|
||||
int completed = 0;
|
||||
int needsVerification = 0;
|
||||
@ -193,8 +195,8 @@ class P1rMetaRealApiGateTest {
|
||||
"Content/" + operationId + " 未获本轮批准,必须继续 needs_verification");
|
||||
}
|
||||
}
|
||||
assertEquals(8, completed, "Content 第一批 completed approval 只能推进 8 个 operation");
|
||||
assertEquals(43, needsVerification, "Content 剩余 43 个 operation 必须继续 needs_verification");
|
||||
assertEquals(10, completed, "Content Planning completed approval 后只能推进 10 个 operation");
|
||||
assertEquals(41, needsVerification, "Content 剩余 41 个 operation 必须继续 needs_verification");
|
||||
}
|
||||
|
||||
private JsonNode readMetaOperations() throws IOException {
|
||||
|
||||
@ -88,6 +88,8 @@ APPROVED_COMPLETED_OPERATIONS = {
|
||||
"content:getBlock",
|
||||
"content:saveBlock",
|
||||
"content:getBlockSourceAttribution",
|
||||
"content:getPlanning",
|
||||
"content:savePlanningItem",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user