oh-my-muse/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md
zizi 2692af32e7 test(p1r): 收口 Content Planning completed approval 门禁
将 content:getPlanning 与 content:savePlanningItem 纳入 operation-level completed approval,并保留 Content 其余 operation 的 needs_verification 边界。
2026-06-13 02:24:29 +08:00

36 KiB
Raw Blame History

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

content:getPlanning
content:savePlanningItem

本轮不把 content 加入 domain-level completed allowlist不推进 Content 51/51 completed不修改 OpenAPI不修改 Content 业务实现,不修改 SQL migration。

获批后目标 coverage summary 为:

total=233
completed=141
needsVerification=92
incomplete=0
genericPersistence=0
ssePlaceholder=0

Content 域目标状态为:

content total=51
completed=10
needsVerification=41

当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。

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

/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0

当前 HEAD

3ced553 test(p1r): 收口 Content 第一批 completed approval 门禁

当前 coverage summary

233	139	94	0	0	0

当前 Content 状态:

  • Content total 为 51。
  • 已有 8 个 operation 为 dedicated / completedlistWorksgetWorklistChaptersgetChapterlistBlocksgetBlocksaveBlockgetBlockSourceAttribution
  • 仍有 43 个 operation 为 dedicated / needs_verification,其中包括本执行版目标 getPlanningsavePlanningItem
  • APPROVED_COMPLETED_DOMAINS 当前只允许 aiknowledge
  • APPROVED_COMPLETED_OPERATIONS 当前包含 Events streamEvents、Meta 16、Account 第一批 10、Market 第一批 4、Content 第一批 8。
  • P1rContentRealApiGateTest 当前断言 Content 为 8 completed / 43 needs_verification,并把 getPlanningsavePlanningItem 视为未批准。
  • P1rApiCoverageReportTest 当前断言 summary completed=139needsVerification=94,并断言 content:getPlanningneeds_verification

已验证的只读实现事实:

  • OpenAPI SavePlanningItemRequest required 为 commandIdcontentexpectedRevision
  • 服务端 SavePlanningItemReqVO required 为 commandIdcontentexpectedRevision
  • 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.reserveCommandrequireOwnedWork、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:getPlanningcontent: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

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 ContentPlanningServiceTestAppContentPlanningControllerTest 支撑 service/controller contract不替代 HTTP+DB _test
P1R Content gates P1rContentRealApiGateTestP1rApiCoverageReportTest 证明 only 10 Content operation completed41 个继续 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
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

预期:

/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
233	139	94	0	0	0
  • 确认 getPlanningsavePlanningItem 当前仍为 needs_verification
jq -r '.operations[] | select(.domain=="content" and (.operationId=="getPlanning" or .operationId=="savePlanningItem")) | [.operationId,.implementationStatus,.completionStatus,.requiresCommandId] | @tsv' docs/superpowers/reports/p1r-api-coverage.json

预期:

getPlanning	dedicated	needs_verification	false
savePlanningItem	dedicated	needs_verification	true
  • 确认实施前只有文档 diff没有 OpenAPI、scanner、coverage report、业务实现、SQL migration、P1R gate test diff
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_EPOCHtests 不低于本执行版最低值、failures=0errors=0skipped=0
  • 任一 XML 缺失、mtime 早于运行开始时间、tests 低于最低值或存在失败/跳过,都必须视为验证失败。

TDD RED 失败验证必须同样删除目标 XML 并记录 RUN_START_EPOCH,但 RED 的预期是 Maven 失败;此时只校验失败发生在本次运行窗口内,并人工确认失败点来自旧 report mismatch。

推荐校验脚本:

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 1TDD 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=141needsVerification=92,但旧 report 仍是 139/94,必须失败。
  • Content 10 个 approved operation 期望 completed,但 getPlanningsavePlanningItem 在旧 report 仍是 needs_verification,必须失败。
  • Content 41 个未批准 operation 继续期望 needs_verification,其中必须包含 createWorkupdateWorkcreateChaptermergeBlockSuggestionexportWorklistPlanningCandidatescreateStyleCheckadminRiskAction
  • Account 仍为 10 completed / 23 needs_verificationMarket 仍为 4 completed / 28 needs_verificationMeta 仍为 16 completed / 0 needs_verificationAI/Knowledge/Events 不回退。

运行:

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/94getPlanning / savePlanningItem 尚未 completed。若直接通过必须停止并排查假绿。

Task 2TDD GREEN最小 scanner/report 状态推进

只修改 muse-cloud/scripts/p1r-audit-api-coverage.py

  • 不修改 APPROVED_COMPLETED_DOMAINS
  • 只向 APPROVED_COMPLETED_OPERATIONS 追加两个 content:* operation key。
  • 保持 completed 必须仍是 dedicated 的校验。

生成 report

python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check

预期 summary

total=233
completed=141
needsVerification=92
incomplete=0
genericPersistence=0
ssePlaceholder=0

预期 Content

completed=10
needsVerification=41

预期 scanner 仍不包含 content domain-level allowlist

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 3Focused Planning unit/controller verification

运行 Content planning focused tests

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_EPOCHfailures=0errors=0skipped=0

Test class 最低 tests
ContentPlanningServiceTest 15
AppContentPlanningControllerTest 7

Focused tests 必须至少支撑:

  • getPlanning 有 sections 与 owner mismatch。
  • savePlanningItemcommandId、缺 expectedRevision、create expectedRevision=0、update revision match、mapper DuplicateKeyExceptionCONTENT_REVISION_CONFLICT、command replay、command/audit snapshot。
  • Controller 层 X-API-Version、参数校验、CommonResult 路由与错误响应。

Focused tests 只作为补充证据,不替代 Task 4 的 HTTP + real DB _test

Task 4Planning 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 模式:JacksonAutoConfigurationHttpMessageConvertersAutoConfigurationDataSourceAutoConfigurationDataSourceTransactionManagerAutoConfigurationJdbcTemplateAutoConfigurationTransactionAutoConfigurationRestTemplateAutoConfigurationWebMvcAutoConfigurationMuseDataSourceAutoConfigurationMuseMybatisAutoConfigurationMybatisPlusAutoConfigurationMybatisPlusJoinAutoConfigurationMuseWebAutoConfiguration
  • 导入 AppContentPlanningControllerContentPlanningServiceImplContentCommandServiceImplContentAuditServiceImpl
  • 提供 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 ContentPlanningCandidateFacadeContentStyleCheckFacade 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 必需数据。

建库与运行:

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 createexpectedRevision=0,写入 tenant_id、content、sourceSnapshot、schemaVersion、projectionVersion、revision=1、command/audit result snapshot。
  7. savePlanningItem updateexpectedRevision 匹配revision+1content/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 conflictcommandId、缺 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 不变。
  • 服务层 DuplicateKeyExceptionCONTENT_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_PASSWORDP1R_FLYWAY_PASSWORDMUSE_POSTGRES_PASSWORD 环境变量读取,不能通过 JVM system property 传入。
  • JDBC URL 不能携带 userusernamepasswordpasspwdtokensecretapi_keybeareraccess_tokenrefresh_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_sectiontenant_idwork_idsection_keycontent_payloadschema_versionprojection_versionsource_snapshotrevisionupdate_time
  • muse_content_command_logtenant_idcommand_idcommand_typeowner_user_idtarget_typetarget_idrequest_hashresult_snapshot
  • muse_content_event_publish_outboxplanning 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 5P1R mixed gates

运行 P1R focused mixed gates

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 更新要求:

  • P1rApiCoverageReportTestexpected summary 改为 completed=141needsVerification=92approved Content completed set 改为 10断言 getPlanningsavePlanningItem completed仍断言 createWorkupdateWorkcreateChaptermergeBlockSuggestionexportWorkadminRiskActionneeds_verification
  • P1rContentRealApiGateTestapproved Content completed set 改为 10Content 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 6Coverage scanner final check

运行:

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

预期:

233	141	92	0	0	0

Content 必须精确为 10 completed / 41 needs_verificationAPPROVED_COMPLETED_DOMAINS 仍不包含 content

Task 7Diff 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
git diff --check
  • 检查 OpenAPI protected diff 为空:
git -c core.quotePath=false diff -- docs/api-contracts
  • 检查 Content main/java 与 SQL migration diff 为空:
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
git -c core.quotePath=false diff --name-only
git -c core.quotePath=false ls-files --others --exclude-standard

允许路径必须是本执行版“本轮允许变更”列出的路径。出现其它路径时必须停止并说明。

Task 8Fresh 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:getPlanningcontent: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:getPlanningcontent: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:getPlanningcontent: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。