oh-my-muse/docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md

42 KiB
Raw Blame History

P1R Content Completed Approval 执行版

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task after the explicit approval gate. Steps use checkbox syntax for tracking.

日期2026-06-12

结论

本执行版推荐第一轮只做 Content operation-level completed approval不做 Content domain-level 51/51 completed。

第一轮精确审批 8 个用户端核心作品 / 章节 / Block / 来源归因 operation

content:listWorks
content:getWork
content:listChapters
content:getChapter
content:listBlocks
content:getBlock
content:saveBlock
content:getBlockSourceAttribution

审批后目标 coverage summary 为:

total=233
completed=139
needsVerification=94
incomplete=0
genericPersistence=0
ssePlaceholder=0

Content 域目标状态为:

content total=51
completed=8
needsVerification=43

本执行版只定义实现前置批准项、允许变更范围、TDD 顺序、验证命令、XML 防空跑和回滚策略。当前不得直接实现,不得修改 OpenAPI、scanner、coverage report、业务实现或 gate test。

flowchart TB
    Current["当前 Content<br/>51 dedicated / needs_verification"] --> Exec["执行版<br/>锁定第一批 8 个 operation"]
    Exec --> Review["fresh execution spec/scope review<br/>fresh execution quality/feasibility review"]
    Review --> Approval{"用户明确批准<br/>8 ops / operation-level / allowed diff / legacy gates"}
    Approval -->|否| Stay["保持当前 coverage<br/>233/131/102"]
    Approval -->|是| Red["TDD RED<br/>先改 Content/Coverage/legacy gates<br/>旧 report 必须失败"]
    Red --> Green["TDD GREEN<br/>scanner operation allowlist + regenerate report"]
    Green --> Runtime["Content HTTP+DB _test<br/>V21 Flyway + source/event gates"]
    Runtime --> Verify["focused + P1R mixed + XML 防空跑<br/>allowed/protected diff"]
    Verify --> FreshReview["fresh implementation review"]

当前事实

正确 worktree

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

当前 HEAD

0032774 test(p1r): 收口 Market 第一批 completed approval 门禁

当前 coverage summary

233	131	102	0	0	0

当前 Content 事实:

  • Content 51 个 operation 全部为 dedicated / needs_verification
  • APPROVED_COMPLETED_DOMAINS 当前只包含 aiknowledge
  • APPROVED_COMPLETED_OPERATIONS 当前包含 events:streamEvents、Meta 16 个 operation、Account 第一批 10 个 operation、Market 第一批 4 个 operation。
  • P1rContentRealApiGateTest 当前明确禁止 Content operation 标记为 completed
  • P1rApiCoverageReportTest 当前断言 summary completed=131,并断言 Content 51 个 operation 继续 needs_verification
  • P1rEventsRealApiGateTestP1rAiRealApiGateTestP1rKnowledgeRealApiGateTestP1rMarketRealApiGateTestP1rAccountRealApiGateTestP1rMetaRealApiGateTest 读取全局 summary 或非目标域状态;实现前必须把这些 mixed gate 同步列入单独批准项。
  • P1R-7f 已证明 saveBlock -> source attribution active -> Content outbox -> Events -> SSE owner visibleneeds_verification evidence但该 evidence 不等同于 saveBlock API operation completed。

执行边界

必须先获用户明确批准

实现前必须同时获得以下 4 项批准:

  1. 批准第一轮 Content completed operation 精确清单为本执行版列出的 8 个 operation。
  2. 批准只走 operation-level approval不把 content 加入 domain-level completed allowlist。
  3. 批准按本执行版修改 scanner、coverage report、Content/Coverage gate、必要 legacy mixed gate、Content HTTP+DB _test IT、ContentEventsPublish gate 安全边界、memory 和 .agent
  4. 批准同步 6 个 mixed gate 文件的 summary / Content / 非目标域状态防回退断言:
    • 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

未获 4 项批准前,不得实施本执行版。

本轮允许变更

获批后只允许修改以下路径:

  • 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/P1rContentCoreCompletedApprovalIT.java,新增 Content 8 个 operation 的 MockMvc HTTP + 真实 PostgreSQL _test 证据。
  • muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishFlywayMigrationIT.java,仅限保存/恢复 p1r.flyway.url/user system property 与组合顺序验证所需的安全边界增强;不得弱化 V21 schema 断言。
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAppServiceTest.java
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxServiceTest.java
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishWorkerTest.java
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/app/AppContentControllerTest.java
  • muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/app/AppContentSourceControllerTest.java
  • docs/agent-specs/.agent
  • docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md
  • docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md
  • docs/memorys/2026-06-12-P1RContent状态推进.md

如 implementation 发现必须修改 Content 业务实现、OpenAPI、SQL migration、AI/Knowledge/Meta/FileService owner 实现、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。

本轮禁止变更

  • 不修改 7 个 OpenAPI。
  • 不修改 Content 业务实现来掩盖 coverage 缺口。
  • 不新增或修改 SQL migration。
  • 不新增或修改 Content domain-level completed allowlist。
  • 不推进 create/update/delete/structure edit operation。
  • 不推进 import/export/parse/FileService operation。
  • 不推进 Meta projection / dynamic fields operation。
  • 不推进 AI planning candidate / style check / suggestion merge operation。
  • 不推进 admin governance / admin read operation。
  • 不推进 planning section operation。
  • 不推进 Account remaining 23、Market remaining 28 或总 P1R completed。

第一轮审批清单

operation key Method Path 目标状态 完成证据边界
content:listWorks GET /app-api/muse/works dedicated / completed App 当前 owner真实 DB 分页,有数据页、空页、同 owner 跨 tenant 不可见
content:getWork GET /app-api/muse/works/{workId} dedicated / completed App 当前 owner存在、missing、跨 owner、跨 tenantDTO 字段完整
content:listChapters GET /app-api/muse/works/{workId}/chapters dedicated / completed work owner guard章节排序有章节、空章节、work missing、跨 owner/tenant
content:getChapter GET /app-api/muse/works/{workId}/chapters/{chapterId} dedicated / completed chapter 属于 path work详情含 blocksmissing、wrong work、跨 owner/tenant
content:listBlocks GET /app-api/muse/works/{workId}/chapters/{chapterId}/blocks dedicated / completed chapter 属于 path workBlock 排序有数据、空列表、wrong work/chapter、跨 owner/tenant
content:getBlock GET /app-api/muse/works/{workId}/blocks/{blockId} dedicated / completed block 属于 path work存在、missing、wrong work、跨 owner/tenant
content:saveBlock PUT /app-api/muse/works/{workId}/blocks/{blockId} dedicated / completed commandId、expectedRevision、sourceSnapshot、revision CAS、source attribution、Content outbox、replay、conflict/no-write、wrong work、跨 owner/tenant
content:getBlockSourceAttribution GET /app-api/muse/works/{workId}/blocks/{blockId}/source-attribution dedicated / completed 当前 Block revision 来源读模型有来源、空来源、missing、wrong work、跨 owner/tenant不泄露 attribution

说明:本轮使用 MockMvc HTTP 入口 + real mapper/service + PostgreSQL _test 作为执行版要求的 HTTP 入口证据,不声明 socket 级 live servlet container 证据。

保持 needs_verification 的 Content operation

以下 43 个 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:getPlanning
content:listPlanningCandidates
content:createPlanningCandidate
content:getPlanningCandidate
content:confirmPlanningCandidate
content:discardPlanningCandidate
content:createStyleCheck
content:getStyleCheckResult
content:savePlanningItem

保留原因:

  • 写命令存在 OpenAPI / coverage requiresCommandId 与服务端 VO 口径不一致风险,不能夹带合同修正。
  • 导入、导出、下载、解析确认依赖 FileService、AI、Knowledge owner 闭环。
  • Meta projection、dynamic fields 依赖 Meta owner 解释与校验。
  • AI planning candidate、style check、suggestion merge 依赖 AI owner runtime / projection / quality facts。
  • admin read / governance 需要单独覆盖 RBAC、治理摘要、risk flags 和 admin DTO。
  • formal planning section 虽属 Content 本域,但有 schemaVersion、projectionVersion、upsert CAS、command replay应作为后续独立切片。

证据矩阵

证据类型 必跑或必查内容 目的
Content focused tests ContentAppServiceTestContentSourceServiceTestContentEventPublishOutboxServiceTestContentEventPublishWorkerTestAppContentControllerTestAppContentSourceControllerTest 支撑本域读写、source attribution、outbox、controller contract 与错误码
Content HTTP+DB _test 新增 P1rContentCoreCompletedApprovalIT 证明 8 个 operation 经 MockMvc HTTP 入口访问真实 PostgreSQL _test 数据,覆盖 shadow-path matrix
Content source/event gates P1rContentEventsPublishMigrationSqlTestP1rContentEventsPublishDependencyTestP1rContentEventsPublishEndToEndTestP1rContentEventsPublishFlywayMigrationIT 证明 saveBlock 的 source attribution / Content outbox / Events 前置证据仍成立
P1R Content gates P1rContentRealApiGateTestP1rApiCoverageReportTest 证明 only 8 Content operation completed43 个继续 needs_verification
P1R mixed gates Coverage、Content、Events、AI、Knowledge、Market、Account、Meta gates 防止非目标 domain 被连带推进,并同步 legacy summary / Content 状态断言
Flyway _test P1rContentEventsPublishFlywayMigrationIT fresh rerun 证明 V1-V21 clean migrate、V21 outbox 表/索引/约束/trigger、invalid insert 拒绝
Coverage scanner python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check 证明 scanner/report 与 operation-level allowlist 口径一致
XML 防空跑 逐个读取 Surefire XML检查 tests、mtime、failures、errors、skipped 防止 Maven 未跑目标类或旧 XML 假绿
Protected diff 7 个 OpenAPI、Content 业务实现、SQL migration 必须无 diff 证明没有通过改合同、实现或迁移掩盖缺口
Allowed diff unstaged、staged、untracked 均必须落在允许清单 证明没有顺手扩大 scope

TDD 执行步骤

Task 0实现前现场确认

  • 确认 worktree
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	131	102	0	0	0
  • 确认 Content 51 个 operation 仍全部是 dedicated / needs_verification
jq -r '.operations[] | select(.domain=="content") | [.operationId,.implementationStatus,.completionStatus,.requiresCommandId] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
  • 确认实施前只有文档 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不要求 failures=0

推荐校验脚本:

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 期望

获批后先修改:

  • P1rApiCoverageReportTest.java
  • P1rContentRealApiGateTest.java
  • 必要的 6 个 mixed gate
    • P1rEventsRealApiGateTest.java
    • P1rAiRealApiGateTest.java
    • P1rKnowledgeRealApiGateTest.java
    • P1rMarketRealApiGateTest.java
    • P1rAccountRealApiGateTest.java
    • P1rMetaRealApiGateTest.java

RED 期望:

  • summary 期望 completed=139needsVerification=94,但旧 report 仍是 131/102,必须失败。
  • Content 8 个 approved operation 期望 completed,但旧 report 仍是 needs_verification,必须失败。
  • Content 43 个未批准 operation 继续期望 needs_verification,其中必须包含 createWorkupdateWorkcreateChaptermergeBlockSuggestionexportWorkgetPlanningadminRiskAction
  • 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 的 131/102 或 Content 8 个 operation 尚未 completed。若直接通过必须停下排查假绿。

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

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

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

生成 report

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

预期 summary

total=233
completed=139
needsVerification=94
incomplete=0
genericPersistence=0
ssePlaceholder=0

预期 Content

completed=8
needsVerification=43

预期 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:listWorks", "content:getWork", "content:listChapters", "content:getChapter",
    "content:listBlocks", "content:getBlock", "content:saveBlock", "content:getBlockSourceAttribution",
]:
    assert key in source
print("content_operation_level_allowlist=PASS")
PY

Task 3Content focused verification

运行 Content 本域 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.ContentAppServiceTest.xml \
  muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.application.ContentSourceServiceTest.xml \
  muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.application.ContentEventPublishOutboxServiceTest.xml \
  muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.application.ContentEventPublishWorkerTest.xml \
  muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.controller.app.AppContentControllerTest.xml \
  muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.controller.app.AppContentSourceControllerTest.xml \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishMigrationSqlTest.xml \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishDependencyTest.xml \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishEndToEndTest.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.P1rApiCoverageReportTest.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,muse-server -am \
  -Dtest=ContentAppServiceTest,ContentSourceServiceTest,ContentEventPublishOutboxServiceTest,ContentEventPublishWorkerTest,AppContentControllerTest,AppContentSourceControllerTest,P1rContentEventsPublishMigrationSqlTest,P1rContentEventsPublishDependencyTest,P1rContentEventsPublishEndToEndTest,P1rContentRealApiGateTest,P1rApiCoverageReportTest \
  -Dsurefire.failIfNoSpecifiedTests=false

必须读取以下 XML 并检查 mtime >= RUN_START_EPOCHfailures=0errors=0skipped=0

Test class 最低 tests
ContentAppServiceTest 13
ContentSourceServiceTest 23
ContentEventPublishOutboxServiceTest 6
ContentEventPublishWorkerTest 10
AppContentControllerTest 5
AppContentSourceControllerTest 5
P1rContentEventsPublishMigrationSqlTest 3
P1rContentEventsPublishDependencyTest 2
P1rContentEventsPublishEndToEndTest 5
P1rContentRealApiGateTest 5
P1rApiCoverageReportTest 6

如实现修改测试数量XML 检查的最低 tests 可高于上表,但不能低于上表。

Task 4Content HTTP + 真实 PostgreSQL _test completed gate

新增 P1rContentCoreCompletedApprovalIT,使用独立 _test 数据库和真实 mapper/service 数据,不能使用生产库或非 _test 库:

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_CONTENT_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_core_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_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_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.P1rContentCoreCompletedApprovalIT.xml
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rContentCoreCompletedApprovalIT \
  -Dflyway.postgresql.transactional.lock=false \
  -Dp1r.flyway.locations=filesystem:sql/muse \
  -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_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

该 IT 必须至少覆盖以下 12 个测试XML 最低 tests 为 12

Test class 最低 tests
P1rContentCoreCompletedApprovalIT 12

必须证明:

  • GET /app-api/muse/works 覆盖有数据分页、空作品页、同 owner 不同 tenant 不可见。
  • GET /app-api/muse/works/{workId} 覆盖存在、ID 不存在、跨 owner、跨 tenant。
  • GET /app-api/muse/works/{workId}/chapters 覆盖有章节、空章节、work missing、跨 owner、跨 tenant。
  • GET /app-api/muse/works/{workId}/chapters/{chapterId} 覆盖存在并返回 blocks、chapter missing、chapter 属于同 owner 另一个 work、跨 owner、跨 tenant。
  • GET /app-api/muse/works/{workId}/chapters/{chapterId}/blocks 覆盖有 Block、空列表、chapter 属于同 owner 另一个 work、跨 owner、跨 tenant。
  • GET /app-api/muse/works/{workId}/blocks/{blockId} 覆盖存在、block missing、block 属于同 owner 另一个 work、跨 owner、跨 tenant。
  • PUT /app-api/muse/works/{workId}/blocks/{blockId} 覆盖 happy、command replay、revision conflict、缺 commandId、缺 expectedRevision、缺 sourceSnapshot、block 属于同 owner 另一个 work、跨 owner、跨 tenant。
  • GET /app-api/muse/works/{workId}/blocks/{blockId}/source-attribution 覆盖当前 revision 有来源、当前 revision 空来源、block missing、block 属于同 owner 另一个 work、跨 owner、跨 tenant且 path mismatch 不泄露 attribution。
  • 每个路径必须断言 HTTP 状态、CommonResult 体、关键 DTO 字段和 DB 行数。
  • 所有失败路径必须断言 block/source/outbox/command no-write 或事务回滚后的行数不变。
  • 成功 saveBlock 必须断言 muse_content_block_source_attribution(source_status=active)muse_content_event_publish_outboxtenant_id、owner、blockRevision、sourceStatus、notificationType 和 commandId/outbox 唯一事实。
  • 该 IT 必须在测试启动时校验库名以 _test 结尾;密码只能从 P1R_CONTENT_COMPLETED_PASSWORDP1R_FLYWAY_PASSWORDMUSE_POSTGRES_PASSWORD 环境变量读取,不能通过 JVM system property 传入;日志和断言不得输出明文数据库密码。
  • 该 IT 如果读取并脱敏 p1r.flyway.url/user,必须保存原始 system property 并在 @AfterAll 恢复。

Task 5真实 PostgreSQL _test Flyway gate

fresh rerun 并增强 P1rContentEventsPublishFlywayMigrationIT。使用独立 _test 库,不能使用生产库或非 _test 库:

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_CONTENT_EVENTS_TEST_DB=muse_p1r_content_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_EVENTS_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_EVENTS_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.P1rContentEventsPublishFlywayMigrationIT.xml
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rContentEventsPublishFlywayMigrationIT \
  -Dflyway.postgresql.transactional.lock=false \
  -Dp1r.flyway.locations=filesystem:sql/muse \
  -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_EVENTS_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
P1rContentEventsPublishFlywayMigrationIT 4

必须保持或补齐以下 assertion

  • V1-V21 clean migrate。
  • target_schema_version=21
  • successful_migration_count=21
  • V21 muse_content_event_publish_outbox 表、索引、唯一约束、CHECK 约束、trigger 存在。
  • invalid outbox insert 被 PostgreSQL 拒绝,至少覆盖非法 event_type、非法 notification_typesource_status=deleted、非法 publish_status、非法 owner/block/resource/attempt。
  • 保存原始 p1r.flyway.url/user,脱敏后在 @AfterAll 恢复,避免同一 Surefire JVM 中污染其它 Flyway IT。

Task 6Content completed IT 与 Content events Flyway IT 组合顺序验证

为证明 system property 脱敏不会污染同 JVM 后续测试,必须用同一个 _test 库做普通顺序与反序组合验证。

source ~/.config/muse-repo/infra.env
export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD"
export P1R_CONTENT_COMBINED_TEST_DB=muse_p1r_content_combined_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_COMBINED_TEST_DB'" | grep -q 1 || \
PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \
  -h "$MUSE_POSTGRES_HOST" \
  -p "$MUSE_POSTGRES_PORT" \
  -U "$MUSE_POSTGRES_USERNAME" \
  -d postgres \
  -c "CREATE DATABASE $P1R_CONTENT_COMBINED_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.P1rContentCoreCompletedApprovalIT.xml \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishFlywayMigrationIT.xml
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT \
  -Dflyway.postgresql.transactional.lock=false \
  -Dp1r.flyway.locations=filesystem:sql/muse \
  -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_COMBINED_TEST_DB" \
  -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
  -Djava.net.useSystemProxies=false \
  -DsocksProxyHost= -DsocksProxyPort= \
  -Dhttp.proxyHost= -Dhttp.proxyPort= \
  -Dhttps.proxyHost= -Dhttps.proxyPort= \
  -Dsurefire.failIfNoSpecifiedTests=false

export RUN_START_EPOCH=$(date +%s)
rm -f \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentCoreCompletedApprovalIT.xml \
  muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishFlywayMigrationIT.xml
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
  -Dtest=P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT \
  -Dsurefire.runOrder=reversealphabetical \
  -Dflyway.postgresql.transactional.lock=false \
  -Dp1r.flyway.locations=filesystem:sql/muse \
  -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_COMBINED_TEST_DB" \
  -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \
  -Djava.net.useSystemProxies=false \
  -DsocksProxyHost= -DsocksProxyPort= \
  -Dhttp.proxyHost= -Dhttp.proxyPort= \
  -Dhttps.proxyHost= -Dhttps.proxyPort= \
  -Dsurefire.failIfNoSpecifiedTests=false

两次组合验证均必须满足:

Test class 最低 tests
P1rContentCoreCompletedApprovalIT 12
P1rContentEventsPublishFlywayMigrationIT 4

Task 7P1R mixed gate

运行 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 \
  -Dsurefire.failIfNoSpecifiedTests=false

必须读取以下 XML

Test class 最低 tests
P1rApiCoverageReportTest 6
P1rContentRealApiGateTest 5
P1rEventsRealApiGateTest 6
P1rAiRealApiGateTest 7
P1rKnowledgeRealApiGateTest 8
P1rMarketRealApiGateTest 6
P1rAccountRealApiGateTest 4
P1rMetaRealApiGateTest 4

每个 XML 均必须 mtime >= RUN_START_EPOCHfailures=0errors=0skipped=0

这些 gate 必须共同证明:

  • summary 只从 131/102 推进到 139/94
  • Content 只从 0/51 推进到 8/43
  • Account 仍保持 10 completed / 23 needs_verification
  • Market 仍保持 4 completed / 28 needs_verification
  • Meta 仍保持 16 completed / 0 needs_verification
  • AI 41、Knowledge 59、Events 1 仍为 completed不回退。

Task 8报告与 diff gate

检查 summary

jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json

预期:

233	139	94	0	0	0

检查 Content 8/43

python3 - <<'PY'
import json
from collections import Counter
from pathlib import Path

approved = {
    "listWorks", "getWork", "listChapters", "getChapter",
    "listBlocks", "getBlock", "saveBlock", "getBlockSourceAttribution",
}
data = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text())
content_ops = [op for op in data["operations"] if op["domain"] == "content"]
counter = Counter(op["completionStatus"] for op in content_ops)
print(counter)
assert counter == Counter({"needs_verification": 43, "completed": 8})
for op in content_ops:
    expected = "completed" if op["operationId"] in approved else "needs_verification"
    assert op["implementationStatus"] == "dedicated", op["operationId"]
    assert op["completionStatus"] == expected, op["operationId"]
print("content_8_43=PASS")
PY

检查 protected diff

git -c core.quotePath=false diff --name-only -- docs/api-contracts
git -c core.quotePath=false diff --cached --name-only -- docs/api-contracts
git -c core.quotePath=false diff --name-only -- muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse
git -c core.quotePath=false diff --cached --name-only -- muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse

输出必须为空。

检查 whitespace

git diff --check

检查 allowed diff必须覆盖 unstaged / staged / untracked

python3 - <<'PY'
import subprocess
import sys

allowed = {
    "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/P1rContentCoreCompletedApprovalIT.java",
    "muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishFlywayMigrationIT.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAppServiceTest.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxServiceTest.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishWorkerTest.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/app/AppContentControllerTest.java",
    "muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/app/AppContentSourceControllerTest.java",
    "docs/agent-specs/.agent",
    "docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md",
    "docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md",
    "docs/memorys/2026-06-12-P1RContent状态推进.md",
}

commands = [
    ["git", "-c", "core.quotePath=false", "diff", "--name-only"],
    ["git", "-c", "core.quotePath=false", "diff", "--cached", "--name-only"],
    ["git", "-c", "core.quotePath=false", "ls-files", "--others", "--exclude-standard"],
]
actual = set()
for command in commands:
    output = subprocess.check_output(command, text=True)
    actual.update(line for line in output.splitlines() if line)
unexpected = sorted(actual - allowed)
if unexpected:
    print("UNEXPECTED_DIFF")
    print("\n".join(unexpected))
    sys.exit(1)
print("allowed_diff_gate=PASS")
PY

输出只能包含本执行版允许路径。若出现 OpenAPI、业务实现、SQL migration 或非批准 gate 文件,必须停止。

Task 9memory 与 .agent

实现通过后新增:

  • docs/memorys/2026-06-12-P1RContent状态推进.md

内容必须记录:

  • 批准的 8 个 Content operation。
  • 保持 needs_verification 的 43 个 Content operation 及原因。
  • scanner/report/gate 修改范围。
  • Content HTTP+DB _test 的数据库名、验证命令、XML 计数和 shadow-path matrix 覆盖点。
  • ContentEventsPublish gate、V21 Flyway _test、组合顺序验证的命令和结果。
  • focused tests、P1R mixed gates、XML 防空跑、protected diff 和 allowed diff 结果。
  • 明确说明不代表 Content 51/51 completed不代表 Account remaining / Market remaining / 总 P1R completed。

同时更新 docs/agent-specs/.agent,但只能记录事实,不写未验证结论。

Task 10fresh implementation review

实现、验证和文档留痕后,必须派发两类 fresh reviewer

  • spec/correctness reviewer检查 8 个 operation 是否严格匹配本执行版43 个 operation 是否仍 needs_verificationContent domain allowlist 是否未新增。
  • quality/data-integrity/testing reviewer检查 scanner/report/gate、legacy mixed gate 同步、HTTP+DB _test、XML 防空跑、V21 Flyway _test、system property restore、allowed/protected diff、rollback 是否可靠。

双 PASS 前不得提交、push 或宣称 Content completed approval 已收口。

回滚策略

如实现后需要撤回:

  1. APPROVED_COMPLETED_OPERATIONS 移除 8 个 content:* key。
  2. 重新运行:
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
  1. 恢复 gate 期望:
summary completed=131
summary needsVerification=102
content completed=0
content needsVerification=51
  1. 删除或修订本轮 memory保留失败原因。
  2. 重新运行 Content/P1R focused gates、git diff --check、protected diff 和 allowed diff。

验收标准

执行版本身可以进入实现前批准点的条件:

  1. 本文件已写入 docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md
  2. .agent 记录执行版当前状态。
  3. git diff --check 通过。
  4. OpenAPI、scanner、coverage report、业务实现、SQL migration、P1R gate test 当前无新增 diff。
  5. fresh execution spec/scope review PASS。
  6. fresh execution quality/feasibility review PASS。

实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。