将 content:getPlanning 与 content:savePlanningItem 纳入 operation-level completed approval,并保留 Content 其余 operation 的 needs_verification 边界。
13 KiB
P1R Content Planning Completed Approval 审阅版
日期:2026-06-12
结论
推荐 Content 下一批只推进 formal planning section 两个 operation 的 operation-level completed approval:
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。
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"]
当前事实状态
工作区:
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
当前 HEAD:
3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁
当前 coverage summary:
total=233
completed=139
needsVerification=94
incomplete=0
genericPersistence=0
ssePlaceholder=0
当前 Content 状态:
content total=51
completed=8
needsVerification=43
已 completed 的 Content 第一批 operation:
content:listWorks
content:getWork
content:listChapters
content:getChapter
content:listBlocks
content:getBlock
content:saveBlock
content:getBlockSourceAttribution
本审阅版推荐的下一批目标值只是后续获批后的执行目标:
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_timetrigger;执行版可以用真实_test证明 schema 仍随 V1-V21 clean migrate 生效。
不纳入本批的 operation
以下 41 个 Content operation 必须继续 dedicated / needs_verification:
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:
commandId
content
expectedRevision
服务端 SavePlanningItemReqVO 当前 required:
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 走
revisionCAS,并校验 optionalexpectedSchemaVersion/expectedProjectionVersion。 - 成功后通过
ContentAuditService.recordSucceededWithAudit写 command/audit result snapshot。
SQL schema
V9 muse_content_planning_section 当前包含:
tenant_idwork_idsection_keycontent_payload JSONBschema_versionprojection_versionsource_snapshot JSONBrevision- 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/savePlanningItemCommonResult route。
这些是候选证据,不等同 completed。执行版仍必须补 HTTP + real mapper/service + PostgreSQL _test 的 completed-grade evidence。
后续执行版必须包含的验证
执行版至少要定义以下验证,不得只依赖已有 Mockito 单测:
- 新增或独立扩展
P1rContentPlanningCompletedApprovalIT,使用 MockMvc HTTP 入口、真实 mapper/service、真实 PostgreSQL_test,只导入 planning section 必需 controller/service/mapper。 - 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推进。
getPlanningmatrix:- 有 sections,返回 sections、revision、latest sourceSnapshot。
- 空 sections,返回空数组、revision=0、sourceSnapshot=null。
- missing work、cross owner、cross tenant。
- 纯读路径不写 Content command / planning section / event outbox。
savePlanningItemmatrix:- 新建 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。
- 新建 section:
- P1R gates:
P1rApiCoverageReportTestP1rContentRealApiGateTestP1rEventsRealApiGateTestP1rAiRealApiGateTestP1rKnowledgeRealApiGateTestP1rMarketRealApiGateTestP1rAccountRealApiGateTestP1rMetaRealApiGateTest
- XML 防空跑必须逐个读取 Surefire XML,检查 tests、mtime、failures、errors、skipped,不能只看 Maven BUILD SUCCESS。
- protected diff:
- OpenAPI diff 必须为空。
- Content main/java 业务实现 diff 必须为空。
- SQL migration diff 必须为空。
用户批准点
本审阅版通过 fresh review 后,只能进入执行版编写。
执行版通过 fresh review 后,真正实现前仍必须由用户明确批准:
- 只审批
content:getPlanning和content:savePlanningItem两个 operation。 - 继续 operation-level approval,不把
content加入 domain-level allowlist。 - 允许按执行版修改 scanner、coverage report、P1R gates、新增 planning HTTP+DB
_test、memory 和.agent。 - 允许同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为
233/141/92/0/0/0与 Content10 completed / 41 needs_verification。
未获得上述批准前,不得修改 scanner/report/gate,不得实现,不得提交,不得 push,不得宣称 Content 10/51 或 51/51 completed。