From e181669197a112914d3a762da2386ddbd4ae9306 Mon Sep 17 00:00:00 2001 From: lili Date: Sun, 14 Jun 2026 04:35:00 -0700 Subject: [PATCH] =?UTF-8?q?chore(agent-infra):=20=E5=BB=BA=E7=AB=8B=20agen?= =?UTF-8?q?t=20=E5=BC=80=E5=8F=91=E5=9F=BA=E5=BB=BA=E3=80=81=E6=B8=85?= =?UTF-8?q?=E7=90=86=E5=8E=86=E5=8F=B2=20churn=20=E5=B9=B6=E4=BB=A5=20BC?= =?UTF-8?q?=20=E8=BF=9D=E4=BE=8B=E6=95=B4=E6=94=B9=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本会话三部分交付,均经 JDK21 真实构建验证(非退出码,读 BUILD SUCCESS + Tests run): 1) Agent 开发基建(机械门禁优先) - 入口与中枢:AGENTS.md、.agents/{knowledge,rules,skills,workflows}、CLAUDE.md 订正 - 订正 .gitignore:移除对 .agent/.agents 的忽略——它们是版本化 agent 基建,须入库(此前被忽略致克隆即缺) - 机械门禁:CI 真跑测试(maven.yml JDK21、去 -Dmaven.test.skip)、覆盖台账去硬编码、 BC 边界 ArchUnit 门(BcBoundaryArchTest)、契约先行门(ContractFirstGateTest:Flyway 卫生 + OpenAPI 结构) - 单一进度源 docs/mvp/进度总账.md + 7 个 BC per-module .agent + mise.toml(锁 JDK21) - P1 增量:AiSuggestionMergeProjectionFacade(Gap A)、ContentSourceServiceImpl 事务化 outbox 回流(Gap B) 2) 过期历史文档清理(97 份 churn,git 可恢复) - 删 docs/memorys(34)、agent-specs 审阅/执行版+迁移review(34)、superpowers/plans+specs(25)、 design-docs/临时+memorys(4);保留 superpowers/reports/coverage(门禁依赖) - 唯一干货蒸馏入 .agents/knowledge/external-deps-and-gotchas.md;订正大纲/映射表/基线悬空引用 3) P1 harness 验证:消除已登记 BC 违例 ContentMuseWorkOwnerFacade - content-api 新增只读端口 MuseContentWorkOwnerApi + content-server 实现(读自有 DAL); AI 适配器改消费该端口、移除全部 content.dal 依赖,AI 业务规则与 4 消费者不变 - 删除 ArchUnit 豁免 → 门禁收紧(反向红 31 例 / 正向绿;适配器单测 13/0F、端口实现 7/0F) 注:muse-studio/src(SSE 相关 4 文件)与 muse-module-ai/pom.xml(移除孤儿 contract-server) 为本会话之前已存在的未提交改动,非本次工作,未纳入本提交。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .agents/README.md | 68 + .../knowledge/external-deps-and-gotchas.md | 33 + .agents/knowledge/module-reality-baseline.md | 19 + .agents/knowledge/project-and-architecture.md | 33 + .agents/knowledge/tech-decisions.md | 22 + .agents/rules/bc-boundaries.md | 43 + .agents/rules/contract-first.md | 67 + .../verification-and-anti-false-green.md | 49 + .agents/skills/add-business-module.md | 26 + .../skills/golden-journey-vertical-slice.md | 37 + .agents/workflows/ai-development-protocol.md | 49 + .gitignore | 3 +- AGENTS.md | 100 +- CLAUDE.md | 12 +- design-docs/00-文档大纲.md | 4 +- .../memorys/2026-05-22-产品文档管理步骤.md | 136 - .../memorys/2026-05-24-整体文档一致性修复.md | 60 - design-docs/临时-产品形态阶段化重设计计划.md | 174 - .../临时-新产品形态设计与文档改造说明.md | 691 ---- design-docs/内容映射表.md | 2 +- ...06-P1R7SourceOwnerPropagation全局审阅版.md | 309 -- ...06-P1R7SourceOwnerPropagation全局执行版.md | 353 --- ...06-06-P1R7bSourceOwnerPropagation审阅版.md | 148 - ...06-06-P1R7bSourceOwnerPropagation执行版.md | 1141 ------- ...7cKnowledgeSourceOwnerPropagation审阅版.md | 373 --- ...7cKnowledgeSourceOwnerPropagation执行版.md | 591 ---- ...6-06-07-P1R7CompletedApproval预检审阅版.md | 234 -- ...P1R7dMarketSourceOwnerPropagation审阅版.md | 204 -- ...P1R7dMarketSourceOwnerPropagation执行版.md | 650 ---- ...1R7eAccountSourceOwnerPropagation审阅版.md | 265 -- ...1R7eAccountSourceOwnerPropagation执行版.md | 786 ----- ...1R7fContentSourceOwnerPropagation审阅版.md | 260 -- ...1R7fContentSourceOwnerPropagation执行版.md | 822 ----- .../2026-06-09-P1R7CompletedApproval审阅版.md | 264 -- .../2026-06-09-P1R7CompletedApproval执行版.md | 657 ---- ...26-06-10-P1RMetaCompletedApproval审阅版.md | 311 -- ...1RMetaRemaining5CompletedApproval审阅版.md | 333 -- ...1RMetaRemaining5CompletedApproval执行版.md | 590 ---- ...RRemainingDomainCompletedApproval审阅版.md | 427 --- ...06-11-P1RAccountCompletedApproval审阅版.md | 410 --- ...06-11-P1RAccountCompletedApproval执行版.md | 506 --- ...06-11-P1RContentCompletedApproval审阅版.md | 450 --- ...-06-11-P1RMarketCompletedApproval审阅版.md | 311 -- ...-06-11-P1RMarketCompletedApproval执行版.md | 738 ----- ...06-12-P1RContentCompletedApproval执行版.md | 857 ----- ...RContentPlanningCompletedApproval审阅版.md | 291 -- ...RContentPlanningCompletedApproval执行版.md | 690 ---- docs/agent-specs/2026-06-13-P0-止血冻结令.md | 66 + ...ntSecurityEventsCompletedApproval审阅版.md | 258 -- ...ntSecurityEventsCompletedApproval执行版.md | 700 ----- ...ContentAdminReadCompletedApproval审阅版.md | 350 --- ...ContentAdminReadCompletedApproval执行版.md | 690 ---- ...tAdminRiskActionCompletedApproval审阅版.md | 417 --- ...tAdminRiskActionCompletedApproval执行版.md | 615 ---- .../2026-06-13-目标达成对抗复盘.md | 204 ++ .../2026-06-13-项目目标与模块现状基线.md | 459 +++ .../2026-06-14-agent基建六砖交付.md | 108 + docs/dev-baseline/muse-design-docs/CLAUDE.md | 1 - ...026-05-24-muse-studio写作台与AI协作实现.md | 38 - ...6-05-24-muse-studio脚手架与基础设施搭建.md | 76 - .../2026-05-24-作品与工作区章节管理.md | 77 - .../2026-05-25-muse-admin管理端搭建.md | 263 -- .../2026-05-25-muse-cloud后端P1收口.md | 252 -- ...026-05-25-muse-studio智能体市场个人中心.md | 192 -- docs/memorys/2026-05-26-P1R0基线门禁收口.md | 235 -- .../memorys/2026-05-27-P1R1内容真实API收口.md | 145 - docs/memorys/2026-05-27-P1R2元治理规格计划.md | 96 - .../2026-05-28-P1R2元治理真实API收口.md | 106 - .../2026-05-28-P1R3账户真实API规格计划.md | 293 -- .../2026-05-30-P1R4AI真实API规格计划.md | 465 --- ...2026-06-01-P1R5KnowledgeRAGFlow规格计划.md | 239 -- .../memorys/2026-06-02-P1R外部验收依赖留痕.md | 331 -- .../2026-06-03-P1R4P1R5完成状态推进.md | 178 -- docs/memorys/2026-06-03-P1R6Market规格计划.md | 221 -- .../2026-06-04-P1R6Market真实API收口.md | 290 -- .../2026-06-05-P1R7aEventsSSE审阅版.md | 137 - .../2026-06-05-P1R7aEventsSSE真实API收口.md | 265 -- ...-06-P1R7bSourceOwnerPropagation真实链路.md | 218 -- ...KnowledgeSourceOwnerPropagation真实链路.md | 209 -- ...R7dMarketSourceOwnerPropagation真实链路.md | 289 -- ...7eAccountSourceOwnerPropagation真实链路.md | 302 -- ...7fContentSourceOwnerPropagation真实链路.md | 328 -- ...026-06-09-P1R7CompletedApproval状态推进.md | 203 -- .../2026-06-10-P1RMetaRemaining5状态推进.md | 183 -- docs/memorys/2026-06-10-P1RMeta状态推进.md | 143 - docs/memorys/2026-06-11-P1RAccount状态推进.md | 253 -- docs/memorys/2026-06-11-P1RMarket状态推进.md | 262 -- docs/memorys/2026-06-12-P1RContent状态推进.md | 378 --- ...-06-13-P1RAccountSecurityEvents状态推进.md | 274 -- .../2026-06-13-P1RContentAdminRead状态推进.md | 307 -- ...06-13-P1RContentAdminRiskAction状态推进.md | 261 -- .../2026-06-13-P1RContentPlanning状态推进.md | 369 --- docs/memorys/README.md | 5 + docs/mvp/进度总账.md | 70 + .../plans/2026-05-24-Muse四仓执行计划.md | 2791 ----------------- .../plans/2026-05-24-P1-muse-cloud.md | 1695 ---------- .../plans/2026-05-24-P2-muse-studio.md | 1128 ------- .../plans/2026-05-24-P3-muse-admin.md | 436 --- .../plans/2026-05-24-P4-集成切换.md | 295 -- .../plans/2026-05-25-P1R-0-baseline-gate.md | 669 ---- .../plans/2026-05-25-muse-cloud-explainer.md | 439 --- .../2026-05-26-P1R-1-content-real-api.md | 709 ----- .../plans/2026-05-27-P1R-2-meta-real-api.md | 787 ----- .../2026-05-28-P1R-3-account-real-api.md | 895 ------ .../plans/2026-05-30-P1R-4-ai-real-api.md | 600 ---- ...-06-01-P1R-5-knowledge-ragflow-real-api.md | 514 --- .../plans/2026-06-03-P1R-6-market-real-api.md | 534 ---- .../plans/2026-06-05-P1R-7a-events-sse.md | 796 ----- .../2026-05-24-Muse四仓开发计划-design.md | 295 -- .../2026-05-25-P1R-0-baseline-gate-design.md | 195 -- ...26-05-25-P1R-muse-cloud-real-api-design.md | 438 --- .../2026-05-25-muse-cloud-explainer-design.md | 333 -- ...026-05-26-P1R-1-content-real-api-design.md | 441 --- .../2026-05-27-P1R-2-meta-real-api-design.md | 498 --- ...026-05-28-P1R-3-account-real-api-design.md | 564 ---- .../2026-05-30-P1R-4-ai-real-api-design.md | 519 --- ...P1R-5-knowledge-ragflow-real-api-design.md | 363 --- ...2026-06-03-P1R-6-market-real-api-design.md | 365 --- .../2026-06-05-P1R-7-events-sse-review.md | 318 -- mise.toml | 2 + muse-cloud/.github/workflows/maven.yml | 12 +- muse-cloud/muse-module-ai/.agent | 9 + .../AiSuggestionMergeProjectionFacade.java | 162 + .../facade/ContentMuseWorkOwnerFacade.java | 103 +- ...AiSuggestionMergeProjectionFacadeTest.java | 115 + .../ContentMuseWorkOwnerFacadeTest.java | 147 +- muse-cloud/muse-module-content/.agent | 9 + .../content/api/MuseContentWorkOwnerApi.java | 48 + .../api/MuseContentWorkOwnerApiImpl.java | 64 + .../application/ContentSourceServiceImpl.java | 7 + .../api/MuseContentWorkOwnerApiImplTest.java | 113 + .../application/ContentSourceServiceTest.java | 7 + muse-cloud/muse-module-events/.agent | 9 + muse-cloud/muse-module-knowledge/.agent | 9 + muse-cloud/muse-module-market/.agent | 9 + muse-cloud/muse-module-member/.agent | 9 + muse-cloud/muse-module-meta/.agent | 9 + muse-cloud/muse-server/pom.xml | 7 + .../api/P1rApiCoverageReportTest.java | 54 +- .../framework/arch/BcBoundaryArchTest.java | 47 + .../contract/ContractFirstGateTest.java | 158 + 141 files changed, 2415 insertions(+), 41731 deletions(-) create mode 100644 .agents/README.md create mode 100644 .agents/knowledge/external-deps-and-gotchas.md create mode 100644 .agents/knowledge/module-reality-baseline.md create mode 100644 .agents/knowledge/project-and-architecture.md create mode 100644 .agents/knowledge/tech-decisions.md create mode 100644 .agents/rules/bc-boundaries.md create mode 100644 .agents/rules/contract-first.md create mode 100644 .agents/rules/verification-and-anti-false-green.md create mode 100644 .agents/skills/add-business-module.md create mode 100644 .agents/skills/golden-journey-vertical-slice.md create mode 100644 .agents/workflows/ai-development-protocol.md delete mode 100644 design-docs/memorys/2026-05-22-产品文档管理步骤.md delete mode 100644 design-docs/memorys/2026-05-24-整体文档一致性修复.md delete mode 100644 design-docs/临时-产品形态阶段化重设计计划.md delete mode 100644 design-docs/临时-新产品形态设计与文档改造说明.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局审阅版.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局执行版.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation审阅版.md delete mode 100644 docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation执行版.md delete mode 100644 docs/agent-specs/2026-06-07-P1R7CompletedApproval预检审阅版.md delete mode 100644 docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md delete mode 100644 docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation执行版.md delete mode 100644 docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation审阅版.md delete mode 100644 docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation执行版.md delete mode 100644 docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation审阅版.md delete mode 100644 docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation执行版.md delete mode 100644 docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-10-P1RMetaCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-10-P1RRemainingDomainCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md create mode 100644 docs/agent-specs/2026-06-13-P0-止血冻结令.md delete mode 100644 docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md delete mode 100644 docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md delete mode 100644 docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md create mode 100644 docs/agent-specs/2026-06-13-目标达成对抗复盘.md create mode 100644 docs/agent-specs/2026-06-13-项目目标与模块现状基线.md create mode 100644 docs/agent-specs/2026-06-14-agent基建六砖交付.md delete mode 100644 docs/memorys/2026-05-24-muse-studio写作台与AI协作实现.md delete mode 100644 docs/memorys/2026-05-24-muse-studio脚手架与基础设施搭建.md delete mode 100644 docs/memorys/2026-05-24-作品与工作区章节管理.md delete mode 100644 docs/memorys/2026-05-25-muse-admin管理端搭建.md delete mode 100644 docs/memorys/2026-05-25-muse-cloud后端P1收口.md delete mode 100644 docs/memorys/2026-05-25-muse-studio智能体市场个人中心.md delete mode 100644 docs/memorys/2026-05-26-P1R0基线门禁收口.md delete mode 100644 docs/memorys/2026-05-27-P1R1内容真实API收口.md delete mode 100644 docs/memorys/2026-05-27-P1R2元治理规格计划.md delete mode 100644 docs/memorys/2026-05-28-P1R2元治理真实API收口.md delete mode 100644 docs/memorys/2026-05-28-P1R3账户真实API规格计划.md delete mode 100644 docs/memorys/2026-05-30-P1R4AI真实API规格计划.md delete mode 100644 docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md delete mode 100644 docs/memorys/2026-06-02-P1R外部验收依赖留痕.md delete mode 100644 docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md delete mode 100644 docs/memorys/2026-06-03-P1R6Market规格计划.md delete mode 100644 docs/memorys/2026-06-04-P1R6Market真实API收口.md delete mode 100644 docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md delete mode 100644 docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md delete mode 100644 docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路.md delete mode 100644 docs/memorys/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation真实链路.md delete mode 100644 docs/memorys/2026-06-07-P1R7dMarketSourceOwnerPropagation真实链路.md delete mode 100644 docs/memorys/2026-06-07-P1R7eAccountSourceOwnerPropagation真实链路.md delete mode 100644 docs/memorys/2026-06-08-P1R7fContentSourceOwnerPropagation真实链路.md delete mode 100644 docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md delete mode 100644 docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md delete mode 100644 docs/memorys/2026-06-10-P1RMeta状态推进.md delete mode 100644 docs/memorys/2026-06-11-P1RAccount状态推进.md delete mode 100644 docs/memorys/2026-06-11-P1RMarket状态推进.md delete mode 100644 docs/memorys/2026-06-12-P1RContent状态推进.md delete mode 100644 docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md delete mode 100644 docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md delete mode 100644 docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md delete mode 100644 docs/memorys/2026-06-13-P1RContentPlanning状态推进.md create mode 100644 docs/memorys/README.md create mode 100644 docs/mvp/进度总账.md delete mode 100644 docs/superpowers/plans/2026-05-24-Muse四仓执行计划.md delete mode 100644 docs/superpowers/plans/2026-05-24-P1-muse-cloud.md delete mode 100644 docs/superpowers/plans/2026-05-24-P2-muse-studio.md delete mode 100644 docs/superpowers/plans/2026-05-24-P3-muse-admin.md delete mode 100644 docs/superpowers/plans/2026-05-24-P4-集成切换.md delete mode 100644 docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md delete mode 100644 docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md delete mode 100644 docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md delete mode 100644 docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md delete mode 100644 docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md delete mode 100644 docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md delete mode 100644 docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md delete mode 100644 docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md delete mode 100644 docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md delete mode 100644 docs/superpowers/specs/2026-05-24-Muse四仓开发计划-design.md delete mode 100644 docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md delete mode 100644 docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-05-25-muse-cloud-explainer-design.md delete mode 100644 docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md delete mode 100644 docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md create mode 100644 mise.toml create mode 100644 muse-cloud/muse-module-ai/.agent create mode 100644 muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacade.java create mode 100644 muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacadeTest.java create mode 100644 muse-cloud/muse-module-content/.agent create mode 100644 muse-cloud/muse-module-content/muse-module-content-api/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApi.java create mode 100644 muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImpl.java create mode 100644 muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImplTest.java create mode 100644 muse-cloud/muse-module-events/.agent create mode 100644 muse-cloud/muse-module-knowledge/.agent create mode 100644 muse-cloud/muse-module-market/.agent create mode 100644 muse-cloud/muse-module-member/.agent create mode 100644 muse-cloud/muse-module-meta/.agent create mode 100644 muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java create mode 100644 muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 00000000..e9b04ca2 --- /dev/null +++ b/.agents/README.md @@ -0,0 +1,68 @@ +# .agents/ —— oh-my-muse 的 Agent 能力中枢 + +> 本目录沉淀项目的**全部 Agent 能力、知识、规则与流程**,让团队在长期 AI 驱动开发中**复利式积累**,持续提升 agent 编码的**能力、准确率与稳定性**。 +> 项目工作入口、定位、目录见根 [`../AGENTS.md`](../AGENTS.md)(项目唯一入口);`CLAUDE.md` 是 design-docs(设计 SSOT)的写作规范,亦从属于 AGENTS.md。 +> +> **设计原则(本项目特有,来自对抗复盘)**:**机械门禁优先**。本项目的失控根因是"假绿"——软约束被绕过。故 `.agents/` 的规则不止是文档纪律,**必须配套机械门禁**(CI/ArchUnit/契约校验),见 [`rules/verification-and-anti-false-green.md`](rules/verification-and-anti-false-green.md)。 + +--- + +## 一、目录结构与职责 + +| 子目录 | 职责 | 回答的问题 | +|---|---|---| +| `knowledge/` | 事实与蓝图的蒸馏(产品、架构、模块现状基线、关键决策) | **是什么** | +| `rules/` | 必须遵守的硬约束 + 其机械门禁 | **必须怎样** | +| `skills/` | 可复用操作手册 playbook | **怎么做某一类事** | +| `workflows/` | 元流程(如何承接→分析→评审→执行→验证→沉淀一个任务) | **如何承接任务** | + +通用顺序:**先 knowledge 对齐事实 → 看 rules 划红线(且红线有机械门禁兜底)→ 用 skills 落地 → 按 workflows 推进与收尾。** + +--- + +## 二、文件清单(✅ 已建 / ⏳ 建设中) + +**rules/** + +| 文件 | 状态 | 一句话 | +|---|---|---| +| [`rules/verification-and-anti-false-green.md`](rules/verification-and-anti-false-green.md) | ✅ | **脊柱规则**:完成=机械验证、机械门禁优先、反假绿(P0 已落地首批门禁) | +| [`rules/bc-boundaries.md`](rules/bc-boundaries.md) | ✅ | BC 边界:禁跨域 .dal 直连,ArchUnit 机械约束(已知违例 ContentMuseWorkOwnerFacade 单点登记整改) | +| [`rules/contract-first.md`](rules/contract-first.md) | ✅ | 契约先行:API=`docs/api-contracts/*`、DB=`sql/muse/V*` **原地 SSOT**;Flyway 卫生 + OpenAPI 结构 机械门禁(openapi-diff CI 待启用) | +| `rules/engineering-conventions.md` | ⏳ | 命名/分层/错误码/提交/PR(收敛 `docs/dev-baseline/global/01,02`) | +| `rules/security-and-reliability.md` | ⏳ | 安全/幂等/超时重试/可观测(收敛 `docs/dev-baseline/global/05,06`) | + +**knowledge/** + +| 文件 | 状态 | 一句话 | +|---|---|---| +| [`knowledge/project-and-architecture.md`](knowledge/project-and-architecture.md) | ✅ | Muse 定位 / 意图 / 非目标、最高不变式、工程底座、7 BC 职责与约束(蒸馏 design-docs) | +| [`knowledge/module-reality-baseline.md`](knowledge/module-reality-baseline.md) | ✅ | 模块**真实**现状指针(指向现状基线 spec + 对抗复盘)+ 基建复利进展 | +| [`knowledge/tech-decisions.md`](knowledge/tech-decisions.md) | ✅ | 已确认架构决策蒸馏(指向 `架构-03-ADR` + CLAUDE.md 决策表) | +| [`knowledge/external-deps-and-gotchas.md`](knowledge/external-deps-and-gotchas.md) | ✅ | 外部依赖(New-API/RAGFlow 地址·凭据来源)+ 集成兼容坑 + 前端/构建坑(蒸馏自已清理 memorys) | + +**skills/** + +| 文件 | 状态 | 一句话 | +|---|---|---| +| [`skills/golden-journey-vertical-slice.md`](skills/golden-journey-vertical-slice.md) | ✅ | "完成"的样板:三指标 + 关 mock(MSW dev-only)+ 真后端真库 + IT/Playwright 端到端 | +| [`skills/add-business-module.md`](skills/add-business-module.md) | ✅ | 新增 muse-module(BC)标准步骤:两子模块 + 契约 + 迁移 + 注册 + 守 BC 门禁 | + +**workflows/** + +| 文件 | 状态 | 一句话 | +|---|---|---| +| [`workflows/ai-development-protocol.md`](workflows/ai-development-protocol.md) | ✅ | 承接→分析→评审→执行→验证→沉淀;简单/全流程分流 + 证据门 + 构建纪律 | + +--- + +## 三、维护规则(关键) + +1. **何时新增 vs 更新**:全新主题→新增文件;已有主题补充/修正→更新现有文件(先查重,不造重复)。 +2. **过时即处理**:信息失效立即修正或删除;与代码/文档冲突时以**已验证事实**为准。 +3. **单一主题、精炼、可检索**:每文件聚焦一主题,多用表格;做蒸馏与索引,不照搬源文档大段内容。 +4. **同步索引**:任何结构性变更(增/删/改名)同步更新本 README 清单 + [`../AGENTS.md`](../AGENTS.md) 导航。 +5. **rules 必配门禁**:新增 rule 时必须说明其**机械门禁**落点(CI/ArchUnit/测试),否则它只是会被绕过的软约束(本项目教训)。 +6. **语言**:一律简体中文;交叉引用用相对路径。 + +> 维护本身是任务收尾的一部分——见 [`workflows/ai-development-protocol.md`](workflows/ai-development-protocol.md) 的"沉淀"环节。 diff --git a/.agents/knowledge/external-deps-and-gotchas.md b/.agents/knowledge/external-deps-and-gotchas.md new file mode 100644 index 00000000..f885260d --- /dev/null +++ b/.agents/knowledge/external-deps-and-gotchas.md @@ -0,0 +1,33 @@ +# knowledge:外部依赖、验收环境与工程坑(蒸馏自历史 memorys) + +> 蒸馏自 2026-06-14 已清理的过程文档(外部验收留痕、P1 收口、studio/admin 搭建),只保留"删了会真丢"的操作事实与踩坑。**凭据只记来源文件,不记明文**。过时即更。 + +## 一、外部依赖与验收环境 +| 依赖 | 地址 | 关键事实 | +|---|---|---| +| New-API(LLM 网关) | `http://100.64.0.8:3000`,OpenAI 兼容 `/v1/chat/completions` | 已验收模型 `MiniMax-M2.5`;容器 `new-api` 端口 3000;其 DB 在 `infra-postgres`(宿主 `100.64.0.8:5433`) | +| RAGFlow(知识运行时) | `http://100.64.0.8`(及 `:9380`) | `/api/v1/datasets`、`/api/v1/retrieval`;健康检查 `/v1/system/healthz` | +| 开发 PG / Redis | 见下方凭据来源 | 远端 PG **15**(用户确认可用,不强制 PG16);Redis `100.64.0.8:6379` | + +**凭据来源(明文不入库,只记位置)**: +- 外部验收:`muse-cloud/scripts/dev/p1r-external-acceptance.env`(`set -a; . 该文件; set +a` 加载;含 New-API base/token、`MUSE_AI_NEW_API_DEFAULT_MODEL_KEY=MiniMax-M2.5`、RAGFlow base/key、GraphRAG 开关)。 +- 开发基础设施:`~/.config/muse-repo/infra.env`(PG/Redis 连接 + 真实凭据)。 +- ⚠️ **安全**:上述令牌仅限当前内网验收上下文;**仓库若同步到更大范围,必须先轮换 New-API 令牌**。 + +**Live 验收 harness(opt-in,默认跳过,`MUSE_P1R_EXTERNAL_ACCEPTANCE=true` 才真跑)**: +- `muse-module-ai/.../application/muse/facade/P1rNewApiLiveAcceptanceIT.java`(New-API) +- `muse-module-knowledge/.../application/muse/facade/P1rRagFlowLiveAcceptanceIT.java`(RAGFlow) +- 另:`muse-server/.../framework/api/P1rAiRuntimeEndToEndLiveAcceptanceIT.java`、`P1rKnowledgeRuntimeEndToEndLiveAcceptanceIT.java` +- 输出脱敏(只打 endpoint/模型/id/状态/耗时/usage/key 长度 + sha256 前 12);RAGFlow smoke 会留存 `p1r-live-<时间戳>` dataset 不删。 +- **runtime client live ≠ coverage completed**:仍需 Muse API 触发 + 落库 + 审计 + 失败路径证据(见 [golden-journey](../skills/golden-journey-vertical-slice.md))。 + +## 二、外部集成兼容坑(实证) +- **New-API 系统管理令牌 ≠ RAGFlow key**:用前者打 RAGFlow `/api/v1/datasets` 返回 `code=109 Authentication error`,必须用独立 `ragflow-*` key。 +- **RAGFlow 建数据集**:`POST /api/v1/datasets` 带 `config:{}` 返回 `code=101 Extra inputs are not permitted`;**只发 `name`** 才 `code=0`。adapter 已改为 createDataset 只发 name,config 走独立 `updateDatasetConfig`。 +- **RAGFlow 文档状态轮询**:用真实支持的 `?id=`(单个);多 id 在 adapter 内 fail-closed 为 `VALIDATION_ERROR`。 +- **GraphRAG attribution**:默认 fail-closed(`ATTRIBUTION_NOT_CONFIGURED`);真跑需 `MUSE_KNOWLEDGE_RAGFLOW_GRAPHRAG_ATTRIBUTION_READY=true` + `..._GRAPHRAG_DATASET_ID`。 + +## 三、前端 / 构建坑速查 +- **studio(Vite + React + TS6.0)**:`erasableSyntaxOnly` 禁用 constructor 参数属性;`exactOptionalPropertyTypes` 下可选属性需规避写法;采纳/Diff 用 **Coarse-to-Fine 分级 Diff** 规避 3000+ 字正文上 O(N×M) LCS 性能爆炸;MSW 仅 `import.meta.env.DEV` 启用。 +- **admin(Vben)**:`museAdminApi` 只拼 `/muse/**`(baseURL 已是 `/admin-api`,否则双前缀 `/admin-api/admin-api`);路由测试**不能**放 `src/router/routes/modules/**`(会被 Vite 动态路由 glob 当业务路由);Opsera pre-commit 缺失时跳过用 `touch /tmp/.opsera-pre-commit-scan-passed`。 +- **git 边界(未决)**:`muse-admin/` 经 `.gitignore` 作"独立子仓库边界",源码靠 `git add -f` 纳入——长期纳管方式待定(见进度总账 TODO)。 diff --git a/.agents/knowledge/module-reality-baseline.md b/.agents/knowledge/module-reality-baseline.md new file mode 100644 index 00000000..625f4637 --- /dev/null +++ b/.agents/knowledge/module-reality-baseline.md @@ -0,0 +1,19 @@ +# knowledge:模块真实现状基线(指针) + +> **真实现状的权威 SSOT**:[`docs/agent-specs/2026-06-13-项目目标与模块现状基线.md`](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md)(逐模块证据)+ [`2026-06-13-目标达成对抗复盘.md`](../../docs/agent-specs/2026-06-13-目标达成对抗复盘.md)(失控根因)。本文件只做导航 + 关键结论蒸馏;**冲突时以代码与覆盖率台账为准**。 + +## 关键结论(蒸馏,2026-06-13 只读判断,未实跑 mvn/npm) +- **后端不是空壳**:9 BC 后端普遍是真实业务逻辑(幂等 / 乐观锁 / 审计 / Shadow→Canonical 硬闸门 / 真实 HTTP 外部集成)。覆盖率台账 233 operation:**147 completed / 86 needs_verification;catch_all / generic_persistence / sse_placeholder / missing / blocked 全为 0**。逐域 operation:account 33 / ai 41 / content 51 / events 1 / knowledge 59 / market 32 / meta 16。 +- **“completed” = 覆盖率门禁批准状态**(需 HTTP 入口 + 真实 PostgreSQL + Flyway + 证据),**≠ 端到端可用**;历史“完成”声称是低报而非高报。 +- **三大真实缺口**: + 1. **前端 muse-studio 严重滞后**:AI 候选闭环断链、知识库 / 市场生产侧 UI 缺失、个人中心约 21% 端面且跑 MSW mock——objective“先审后入”在用户可见层当前不可用。 + 2. **跨 BC facade 多为 `Unavailable` 占位**:meta impact-preview、account 投影等仅 fail-closed 占位 → 运行期返回 `*_UNAVAILABLE`(meta 写链路、account 约 21/33 端面运行期降级)。 + 3. **机械门禁缺口**:原 ADR“AI 授权包级隔离 + ArchUnit”全仓 0 规则;gateway 路由未接任何 Muse BC 且留死路由。 +- **整体只读评估 ≈ 76%**(后端高 / 内部验证门禁中 / 前端低 / 跨 BC 集成与机械门禁低)。 + +## 订正与进展(随基建推进更新 —— 复利留痕) +- **A1/R6 订正**:基线 §1.1“本仓不承载运行时代码”与磁盘事实矛盾——本仓是 **monorepo**,物理承载 `muse-cloud/muse-admin/muse-studio` 代码 + CI;以 [`../../AGENTS.md`](../../AGENTS.md) 为准。 +- **缺口 3 已部分补齐(基建复利)**: + - BC 边界 ArchUnit 门 → [`../rules/bc-boundaries.md`](../rules/bc-boundaries.md)(已验证绿)。 + - 契约先行门(Flyway 卫生 + OpenAPI 结构)→ [`../rules/contract-first.md`](../rules/contract-first.md)(已验证绿)。 + - CI 真跑测试 + JDK21 + 门禁去硬编码 → P0(见 [`../rules/verification-and-anti-false-green.md`](../rules/verification-and-anti-false-green.md))。 diff --git a/.agents/knowledge/project-and-architecture.md b/.agents/knowledge/project-and-architecture.md new file mode 100644 index 00000000..f02869dc --- /dev/null +++ b/.agents/knowledge/project-and-architecture.md @@ -0,0 +1,33 @@ +# knowledge:项目定位与架构(蒸馏) + +> 蒸馏自 design-docs(架构-01/02、后端-01/02、产品-01/03)与现状基线 spec。**本文件是索引 + 一句话蒸馏,权威细节看被指向文档**;过时即修正。 + +## 一、是什么 / 为什么 +- **定位**:把 Muse 从“设定提取助手 + 提示词面板”升级为面向长篇小说的**多角色 AI 创作与资产流通系统**——管理员治理系统能力;用户在低认知负担工作台完成 写作/规划/生成/知识治理/质量感知/导入导出;**作品 / 智能体 / 知识库**作为可授权、可流通资产。 +- **意图**:让创作者获得“可控、可解释、越用越懂当前作品”的 AI 协作;核心是**用户主权——AI 不黑盒改稿,先审后入**。 +- **非目标**(明确排除):一键全自动黑盒写作机;通用智能体平台 / 通用 RAG SaaS / 阅读分发社区;市场本阶段不做完整电商支付结算 DRM。 +- 详见 `design-docs/产品-01-产品定位与核心价值.md`、`产品-03-用户旅程与操作流程.md`。 + +## 二、最高不变式(任何改动不得违反) +- **Shadow→Canonical 双轨主权**:AI / 市场 / 外部知识默认不可信,只产候选 / 草稿 / 快照 / 任务结果;正式作品事实只能由**用户确认、用户保存,或目标 owner 显式规则**写入;**正文保存 ≠ 知识入库**。 +- **逻辑 owner 优先**于物理表 / 代码模块 / 页面入口(ADR-015);跨 BC 写入走 owner facade。 +- 详见 `架构-02-核心数据结构与双轨模型.md`、`架构-04-状态机与约束清单.md`;决策见 [`tech-decisions.md`](tech-decisions.md)。 + +## 三、工程底座 +- Java 21 + Spring Boot 3 + PostgreSQL(JSONB)+ yudao-cloud fork + Sa-Token;**模块化单体优先**(ADR-001),痛点可量化才拆服务。 +- monorepo:`muse-cloud`(后端)/ `muse-admin`(Vben 管理端)/ `muse-studio`(React 用户端);同一 git 工作树物理承载三仓代码 + CI(见 [`module-reality-baseline.md`](module-reality-baseline.md) 的 A1/R6 订正)。 +- 详见 `后端-02-工程结构与模块职责.md`。 + +## 四、业务域 BC(逻辑 owner;台账口径 7 个对外 operation 域) +| BC | owner 模块 | 一句话职责 + 关键约束 | +|---|---|---| +| **ai** | muse-module-ai | 链路编排 / 生成 / 分析 / 质量门控 / AI 候选;**只产 Shadow,绝不直写 Canonical**;grant 与 runtime 包级隔离 | +| **content** | muse-module-content | Work/Chapter/Block/正文版本;**正文 Canonical 唯一写入方**;Block 写入带 expectedRevision 且 revision 单调递增;**Accept Suggestion 是候选进正文的唯一合法入口** | +| **knowledge** | muse-module-knowledge | Local/User/全局知识、Draft、绑定、投影;进 Local KB 唯一入口=用户确认草稿;依赖 RAGFlow GraphRAG | +| **market** | muse-module-market | 市场资产 / 发布 / 审核 / 授权 / 安装 / 跳转;**授权 ≠ 所有权转移**,市场非源事实 owner,目标事实回目标 owner | +| **meta** | muse-module-meta(独立) | MetaSchema 元结构定义;admin 写全局、用户作品级覆盖;业务模块经 facade-api **只读**消费投影(ADR-016) | +| **events** | 各 owner + outbox(**非独立模块**) | Source/Authorization 横切契约;事件驱动各模块自治决策 active/disabled,**无 needs_recheck**(ADR-017/018) | +| **account** | muse-module-member(承载) | Profile/Entitlement/Quota/Usage/Security/Audit;用户可见权益**读模型**,**不反写**他域事实;高危审计 append-only | + +- 平台底座(gateway/system/infra/pay/bpm/mp/report)= yudao 继承,无“完成”声称。设计文档以“9 BC”口径表述,完整边界见 `架构-01-系统全貌与边界上下文.md`。 +- BC 边界由 [`../rules/bc-boundaries.md`](../rules/bc-boundaries.md) 的 ArchUnit 机械约束兜底;对外契约见 [`../rules/contract-first.md`](../rules/contract-first.md)。 diff --git a/.agents/knowledge/tech-decisions.md b/.agents/knowledge/tech-decisions.md new file mode 100644 index 00000000..4ae1f60c --- /dev/null +++ b/.agents/knowledge/tech-decisions.md @@ -0,0 +1,22 @@ +# knowledge:已确认架构决策(蒸馏) + +> 权威 ADR 在 [`design-docs/架构-03-关键决策与原则(ADR).md`](../../design-docs/架构-03-关键决策与原则(ADR).md);另见根 `CLAUDE.md` 决策表。本文件只蒸馏“结论”供快速对齐,背景 / 选项 / 后果看 ADR 原文;过时即修正。 + +| 决策 | 结论(一句话) | +|---|---| +| **ADR-001** 模块化单体优先 | 默认模块化单体(清晰边界 / 本地事务 / 低成本);痛点可量化才拆服务,事实源与用户确认边界必须回 Muse | +| **ADR-003** 数据库 | PostgreSQL + JSONB | +| **ADR-006** 知识运行时 | RAGFlow GraphRAG;客户端不可用时 `Unavailable` 优雅降级(fail-closed) | +| **ADR-013** 鉴权 | Sa-Token | +| **ADR-014/015** yudao fork + 逻辑 owner 优先 | yudao-cloud 作工程基座 / 过渡物理落点;逻辑 owner / facade / 状态机 / 写入边界以 BC 为准;跨 BC 写入走 owner facade | +| **ADR-016** Governance 拆散 / MetaSchema 独立 | MetaSchema→独立 BC;Protection Node + Quality Policy→AI grant 包;市场治理→Marketplace admin 包 | +| **ADR-017/018** Source 传播 | 事件驱动各模块自治;无独立 source 模块、无 Candidate Decision Envelope、**去 needs_recheck**;接受候选时实时对比来源版本;`source_snapshot` 独立表 DDL 归 infra | +| Account 物理落 member | member = Account BC 物理承载,扩 Entitlement/Quota/Usage/Security/Binding;唯一写入 authority,不反写他域事实 | +| AI 授权隔离 | grant 与 runtime **包级隔离 + ArchUnit**;机械门禁落点见 [`../rules/bc-boundaries.md`](../rules/bc-boundaries.md) | +| API 版本 | `X-API-Version` Header | +| 实时通信 | SSE(AI stream 独立 + 事件统一) | +| 正文保护 | 自动保存 + IndexedDB 安全网 | +| Block 粒度 / 知识确认 | 场景/小节级;知识默认自动确认,冲突时人工 | +| Entitlement / Archive | 可变表 + 审计日志;Archive 先同表,后续按量分离 | + +> 上述为“已确认”决策的稳定结论;若 ADR 原文与本表冲突,以 `架构-03` 为准并回写本表。 diff --git a/.agents/rules/bc-boundaries.md b/.agents/rules/bc-boundaries.md new file mode 100644 index 00000000..95c37c71 --- /dev/null +++ b/.agents/rules/bc-boundaries.md @@ -0,0 +1,43 @@ +# 规则:BC 边界 —— 禁跨域 DAL 直连(ArchUnit 机械门禁) + +> **类型**:硬约束(rules) · **状态**:生效 2026-06-14 · **门禁**:ArchUnit(机械阻断,非自觉) +> **上位**:[`verification-and-anti-false-green.md`](verification-and-anti-false-green.md) §二.6 +> **由来**:对抗复盘 A4——软约束下 AI 模块 `ContentMuseWorkOwnerFacade` 直连 content 的 `WorkMapper/ChapterMapper/BlockMapper` + `WorkDO/ChapterDO/BlockDO`,实质破了"模块化单体 + 清晰 BC 边界"(ADR)的根基,且会随时间蔓延、拆服务时代价爆炸。 + +--- + +## 一、规则 + +- 一个业务域(BC)模块**不得 import / 依赖他域的 DAL**(`.dal..`,含 dataobject 与 mysql Mapper)。 +- 跨 BC 读写**只能走对外 API 或 facade-api 契约**(本域定义端口,他域提供适配器;或经对外 API)。 +- 本域读自己的 DAL 不受限。 + +> 正面范例:P1 增量 2 的 `AiSuggestionMergeProjectionFacade` —— AI 实现 content 的 `ContentAiSuggestionFacade` 端口、只读 **AI 自有** suggestion 库,**不碰 content DAL**,即合规的跨 BC 接缝。 + +--- + +## 二、机械门禁(不是自觉) + +- 测试:`muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java`(ArchUnit 1.3.0)。 +- CI:随 `muse-cloud/.github/workflows/maven.yml`(P0 已打开测试门、JDK21)在 PR/push 阻断。 +- 任何**新增**跨域 DAL 依赖 → 测试红 → 阻断合入。 + +--- + +## 三、违例登记与整改 + +**当前无任何豁免**——`BcBoundaryArchTest` 已不含 `haveNameNotMatching`,任何 AI→他域 DAL 依赖一律红。 + +### 已整改(历史) +| 违例类 | 曾经内容 | 整改 | 验收(2026-06-14) | +|---|---|---|---| +| `module.ai.application.muse.facade.ContentMuseWorkOwnerFacade` | 直连 content `WorkMapper/ChapterMapper/BlockMapper` + `WorkDO/ChapterDO/BlockDO`(字节码 31 处依赖) | content 暴露对外只读端口 `MuseContentWorkOwnerApi`(content-api 定义、content-server 实现读自有 DAL);AI 适配器改消费该端口,移除全部 content.dal 依赖,AI 业务规则不变 | 反向:删豁免+旧码 → 门禁红、精确报 31 例;正向:整改+删豁免 → 门禁**绿**(收紧),适配器单测 13/0F、端口实现单测 7/0F | + +> 豁免一律视为**临时**:违例消除后必须从 `haveNameNotMatching` 删除该类,使门禁收紧——**收紧即整改验收**(本条已完成,豁免已清零)。新违例不在任何豁免内,一律红。 + +--- + +## 四、扩展方式 + +- 新增 BC 间约束(如禁 content→ai.dal、禁前端契约外的耦合)按同样方式在 `BcBoundaryArchTest` 加 `@Test` 规则。 +- 违例消除后,从对应 `haveNameNotMatching` 豁免里删掉该类,门禁随即收紧——**收紧动作本身就是整改完成的验收**。 diff --git a/.agents/rules/contract-first.md b/.agents/rules/contract-first.md new file mode 100644 index 00000000..c204bd8b --- /dev/null +++ b/.agents/rules/contract-first.md @@ -0,0 +1,67 @@ +# 规则:契约先行 —— API/DB 契约为 SSOT(机械门禁) + +> **类型**:硬约束(rules) · **状态**:生效 2026-06-14 · **门禁**:JUnit/surefire(本地+CI 机械阻断) +> **上位**:[`verification-and-anti-false-green.md`](verification-and-anti-false-green.md) §二 +> **由来**:契约与 DB schema 此前靠自觉维护、无机械校验,易漂移、易被误删/写坏而不被发现——又一处“假绿”温床。 + +--- + +## 一、SSOT 原地认定(不另起 contracts/ 副本) + +本项目已有两套契约源,**原地**认定为 SSOT,**禁止复制到新目录**形成双源漂移(否则即犯“孤儿/拼接设计”): + +| 契约类别 | SSOT 位置 | 运行时关系 | +|---|---|---| +| API 契约 | `docs/api-contracts/<域>/openapi.yaml` + `docs/api-contracts/openapi-base.yaml` | 前后端/跨 BC 协作契约;`docs/api-contracts/generated/` 为产物,非 SSOT | +| DB schema 契约 | `muse-cloud/sql/muse/V<版本>__<描述>.sql` | Flyway `spring.flyway.locations` 含 `filesystem:sql/muse`,**运行时真实执行** | + +> 参考项目 `games-development-ai` 把契约集中到根 `contracts/`;本项目因 Flyway 已绑定 `sql/muse`、OpenAPI 已成体系,**原地认定**比物理搬迁更小风险、零重复。需要统一入口时由 [`../../AGENTS.md`](../../AGENTS.md) 与本规则**索引**指向,不复制内容。 + +--- + +## 二、规则 + +- **契约先行**:任何对外 API / DB 结构变更,先改契约(OpenAPI / 新增 Flyway 迁移),再改实现;实现反向对齐契约。 +- **DB 迁移只增不改**:已合入的 `V*.sql` 不得编辑(改其内容会破坏 Flyway 校验和与他人已应用的历史);变更一律**新增**更高版本号迁移。 +- **跨 BC / 前后端**只认契约,不靠口头约定;契约缺失即视为接口未定义。 + +--- + +## 三、机械门禁(不是自觉) + +- 测试:`muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java`,随 CI([`maven.yml`](../../muse-cloud/.github/workflows/maven.yml),P0 已打开测试门 + JDK21)在 PR/push 阻断。两条: + 1. **Flyway 迁移卫生**:版本号唯一、从 1 连续无缺口、命名 `V<版本>__<描述>.sql`。重复/缺口/乱名 → 红。 + 2. **OpenAPI 契约存在性+结构**:必备业务域(account/ai/content/events/knowledge/market/meta)各有可解析的 `openapi.yaml`,含 `openapi/info/paths` 顶层键且为 OAS3。被删/写坏 → 红。 +- 已验证:2026-06-14 JDK21 全 reactor `clean test`,`ContractFirstGateTest` Tests run: 2, Failures: 0(证据见交付报告)。 + +--- + +## 四、待启用加固:openapi-diff 破坏性变更检测(CI 级) + +存在性/结构门禁挡不住“悄悄改坏既有契约语义”(删字段、改必填、改类型)。**破坏性变更检测**用 `oasdiff` 在 PR 上比对 base 分支与 PR 的 OpenAPI,发现破坏性差异即红。就绪片段(待接入 `maven.yml` 或独立 workflow,接入后需一次 CI 运行验证): + +```yaml + openapi-breaking-change: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + - name: 检出 base 分支契约 + run: git worktree add /tmp/base "${{ github.event.pull_request.base.sha }}" + - name: oasdiff 破坏性变更检测(逐域) + run: | + for d in account ai content events knowledge market meta; do + docker run --rm -v "$PWD:/pr" -v /tmp/base:/base tufin/oasdiff breaking \ + "/base/docs/api-contracts/$d/openapi.yaml" "/pr/docs/api-contracts/$d/openapi.yaml" + done +``` + +> 未接入前,本项不得在报告中宣称“破坏性变更已被门禁拦截”——只声明存在性/结构与迁移卫生已机械保证。 + +--- + +## 五、扩展方式 + +- 新增业务域:在 `docs/api-contracts/<域>/openapi.yaml` 建契约,并把 `<域>` 加入 `ContractFirstGateTest.REQUIRED_API_DOMAINS`(机械要求该域契约长存)。 +- DB 结构演进:新增 `V<下一版本>__<描述>.sql`,切勿改历史迁移;门禁自动校验连续与唯一。 +- 需要“契约↔实现不漂移”的更强门禁(OpenAPI operationId 与控制器/服务交叉核对)时,与 P0 覆盖台账(`P1rApiCoverageReportTest`)合并设计,避免重复台账。 diff --git a/.agents/rules/verification-and-anti-false-green.md b/.agents/rules/verification-and-anti-false-green.md new file mode 100644 index 00000000..88512fe0 --- /dev/null +++ b/.agents/rules/verification-and-anti-false-green.md @@ -0,0 +1,49 @@ +# 规则:验证优先 · 反"假绿"(机械门禁优先) + +> **类型**:硬约束(rules) · **状态**:生效 2026-06-14 · **优先级**:最高(与本规则冲突的其它约定一律让位) +> **由来**:对抗复盘 `docs/agent-specs/2026-06-13-目标达成对抗复盘.md` 裁定本项目最深根因是 **「假绿」**——完成度信号可手工拨、CI 跳过测试、标签自填、前端 mock 假绿。现状基线见 `docs/agent-specs/2026-06-13-项目目标与模块现状基线.md`。 +> **一句话**:**完成 = 机械验证通过,不是台账数字、不是自觉。** 软约束在本项目已被实证绕过,故一切约束以机械门禁为先。 + +--- + +## 一、为什么需要这条规则(已核实的"假绿"证据) + +| 假绿形态 | 证据 | +|---|---| +| 完成度手工可拨 | 门禁测试曾硬编码 `assertEquals(147, completed)`,与 `p1r-api-coverage.json` 互相对着改(已于 P0 拆除) | +| CI 不跑测试 | `maven.yml` 曾用 `-Dmaven.test.skip=true` 跳过全部测试(已于 P0 移除);JDK 矩阵 8/11/17 ≠ 项目 21,主代码根本编不过(已订正为 21) | +| 标签自填 | 233 条 operation 的 `implementationStatus` 全人工填 `dedicated`,从未与代码机械核对 | +| live 验收自跳 | `*LiveAcceptanceIT` 全 `assumeTrue` 默认跳过 = 绿 | +| 前端 mock 假绿 | `muse-studio` DEV 无条件挂 MSW + `sse.ts` 契约漂移被 mock 喂成假绿,无人通过真实 UI 跑通后端 | + +> 结论:这套环境**不缺文档纪律,缺机械校验**。所以本规则不写"应当",写"机械上做不到就不算数"。 + +--- + +## 二、硬规则(违反即缺陷) + +1. **完成的定义**:一个 operation / 功能 / 旅程标记 `completed` 必须有**自动化绿证据**(CI 实跑通过的单测/集成/端到端)。无证据只能 `needs_verification`。**禁止以手工台账数字作为完成的事实源。** +2. **CI 必须真跑测试**:门禁 job 禁用 `-Dmaven.test.skip` / `skipTests`;必须在项目真实 JDK(**21**)下编译+跑测试。新增模块的测试必须纳入 CI。 +3. **门禁断言"计算而非硬编码"**:禁止 `assertEquals(<魔法数>, completed)` 这类被人工拨动的常量;完成数/状态必须**从真实判据重新计算/扫描**得出(参见 `muse-server/.../P1rApiCoverageReportTest`,P0 已改造为 summary 自洽 + completed 必须引用磁盘真实存在的源码文件)。 +4. **三口径报完成,禁单一 %**:对内对外只报 ① 代码实现度 ② 自动化验证度(CI 实跑通过数)③ 端到端可用度(真实形态跑通的旅程数)。**禁止合并成一个"X% 完成"**(合并数是高估来源)。 +5. **契约变更挂 CI 校验**:接口/DB 契约改动必须有机械校验(`openapi-diff` / `flyway validate`)在 CI 阻断,不能只靠"契约先行"的文档纪律。契约 SSOT = `contracts/`(建设中)。 +6. **BC 边界由 ArchUnit 机械约束**:模块不得 `import` 他域 `.dal..`(DO/Mapper)。跨 BC 只能走对外 API / facade-api 契约。已存在的违例(如 AI 模块直连 content DAL)登记为"已知违例 + 整改期",新违例一律 CI 阻断。(ArchUnit 规则建设中,见 [[rules/bc-boundaries]]) +7. **前端 mock 不算"已实现"**:MSW/mock 下的通过**不构成**可用证据。"可用"= 关该旅程 mock、对真后端、自动化端到端跑通(见 [[skills/golden-journey-vertical-slice]])。 +8. **证据规则**:区分"已验证事实 / 推断 / 假设";**没有验证证据,绝不声称"完成/修复/通过/无问题"**。能只读核实的先核实。 + +--- + +## 三、红线(立即视为缺陷,必须停下处理) + +- 发现 verification theater:跳测试、硬编码门禁、手工拨完成度、`assumeTrue` 默认吞掉验收、mock 假绿当真。 +- 为"让数字好看"而非"让用户能用"投入工时(如反复 `收口 X completed approval`)——已被 `docs/agent-specs/2026-06-13-P0-止血冻结令.md` 冻结。 + +--- + +## 四、已落地(本规则的首批机械门禁) + +- ✅ P0:`maven.yml` 删 `skip` + JDK 矩阵→21 + 开 PR 门;`P1rApiCoverageReportTest` 去硬编码 147 → summary 自洽 + 文件存在性牙齿(23/23 绿,JDK21 实跑)。 +- ✅ P0-2:收口刷分回路冻结令落档。 +- ⏳ 待建:ArchUnit BC 边界门 [[rules/bc-boundaries]];`contracts/` SSOT + `openapi-diff` CI;进度总账(done=自动化绿旅程)。 + +> 维护:本规则随机械门禁逐条落地而更新"已落地"清单;新发现的假绿形态追加进第一节并补对应门禁。 diff --git a/.agents/skills/add-business-module.md b/.agents/skills/add-business-module.md new file mode 100644 index 00000000..cfd6aa9d --- /dev/null +++ b/.agents/skills/add-business-module.md @@ -0,0 +1,26 @@ +# skill:新增 muse-module 业务模块(BC) + +> **类型**:操作手册(skills) · **关联门禁**:[`../rules/bc-boundaries.md`](../rules/bc-boundaries.md)、[`../rules/contract-first.md`](../rules/contract-first.md) +> **何时用**:新增一个**业务域 BC(逻辑 owner)**。先确认确属新 BC(逻辑 owner 优先,勿与既有 7 BC 重叠,见 [`../knowledge/project-and-architecture.md`](../knowledge/project-and-architecture.md));复杂域先走评审版 spec。 + +--- + +## 标准步骤(参照既有 `muse-module-meta` 布局) + +1. **建两子模块** `muse-cloud/muse-module-/`: + - `muse-module--api`:对外契约(DTO / ErrorCode / facade-api 接口),供他域**只读**消费。 + - `muse-module--server`:实现。父 pom 聚合两者(参照 `muse-module-meta/pom.xml`)。 +2. **server 包结构对齐既有**(`cn/iocoder/muse/module//` 下):`controller`(admin/app)、`application`(service + facade 实现)、`dal`(dataobject + mysql Mapper)、`domain`、`convert`、`enums`。 +3. **DB 迁移**:新增 `muse-cloud/sql/muse/V<下一版本>__<描述>.sql`;**切勿编辑历史迁移**。`ContractFirstGateTest` 会校验版本唯一 / 连续 / 命名 `V<版本>__<描述>.sql`。 +4. **API 契约(契约先行)**:建 `docs/api-contracts//openapi.yaml`(OAS3,含 `openapi/info/paths`),并把 `""` 加入 [`ContractFirstGateTest.REQUIRED_API_DOMAINS`](../../muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java)(机械要求该域契约长存)。 +5. **注册依赖**:在 [`muse-server/pom.xml`](../../muse-cloud/muse-server/pom.xml) 加 `muse-module--server`(参照既有 dependency 块)。 +6. **守 BC 边界**:**不得 import 他域 `.dal`**;跨 BC 只走对外 API / facade-api。[`BcBoundaryArchTest`](../../muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java) 机械兜底(新违例=红)。明确本域只产 Shadow 还是可写 Canonical,遵守双轨主权与 owner 写入边界。 +7. **测试**:Mockito 单测(`BaseMockitoUnitTest`)覆盖业务分支;真库 / 真外部场景加 `*IT.java`(见 [`golden-journey-vertical-slice.md`](golden-journey-vertical-slice.md))。 +8. **本地验证(JDK21)**:`mise exec -- mvn -B -pl muse-server -am test`(单构建,勿并发——并发写同一 `target` 会致 `NoSuchFileException` 假错);随 CI 阻断。 + +--- + +## 收尾沉淀 +- 更新 [`../knowledge/project-and-architecture.md`](../knowledge/project-and-architecture.md) 的 BC 表(新增一行)。 +- 维护该模块 `.agent`(目标 / 边界 / out-of-scope / 现状 / TODO;机制建设中)。 +- 如该域需保持 completed 台账纪律,纳入 `P1rApiCoverageReportTest` 口径。 diff --git a/.agents/skills/golden-journey-vertical-slice.md b/.agents/skills/golden-journey-vertical-slice.md new file mode 100644 index 00000000..b070d54b --- /dev/null +++ b/.agents/skills/golden-journey-vertical-slice.md @@ -0,0 +1,37 @@ +# skill:黄金旅程纵切 —— “完成”的样板 + +> **类型**:操作手册(skills) · **上位**:[`../rules/verification-and-anti-false-green.md`](../rules/verification-and-anti-false-green.md) +> **何时用**:声明任何**用户可见功能**“完成”之前;或要把某条旅程从“后端 completed 台账”推进到“真实可用”。 + +--- + +## 一、“完成”的定义(三指标,缺一不可,禁止单一百分比) + +| 指标 | 含义 | 证据 | +|---|---|---| +| 代码存在 | 控制器 / 服务 / 前端组件 / DB 迁移 / 契约 在位 | 文件 + 台账引用 | +| 自动化验证绿 | 单测 + IT + e2e 真跑通过 | `Tests run / Failures` 行、Playwright 报告 | +| 端到端可用 | **关 mock**、真后端、真库,一条用户旅程从入口走到 Canonical 落库 | IT / e2e 跑绿截图或日志 | + +> 后端覆盖台账(`P1rApiCoverageReportTest`)里的 `completed` **只是门禁批准状态**(需 HTTP 入口 + 真 PostgreSQL + Flyway + 证据),**不等于本旅程端到端可用**。不要用它冒充指标三。 + +--- + +## 二、步骤 + +1. **选一条最小纵切旅程**:从 studio 用户入口 → muse-server 接口 → 业务逻辑 → Canonical 落库(例:写正文 → AI 生成候选 → 用户 Accept Suggestion → content 正文写入 + 候选归档)。 +2. **关 mock**:studio 的 MSW 仅在 `import.meta.env.DEV` 启用(见 [`../../muse-studio/src/main.tsx`](../../muse-studio/src/main.tsx) 第 9 行;handlers 在 `muse-studio/src/api/mocks/handlers/*`)。真实验证须 **MSW OFF**——用 `pnpm build && pnpm preview`(生产构建即不启 worker),并让前端指向真实 `muse-server`(**确认点**:`muse-studio/vite.config.ts` 的 proxy / API baseURL 指向真后端)。 +3. **起真实后端**:`muse-server` + 真实 PostgreSQL + Flyway 迁移(`muse-cloud/sql/muse/V*`)到位;JDK21(`mise exec -- ...`)。 +4. **自动化端到端**: + - 后端:沿用既有 IT 模式 [`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/`](../../muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/) —— `*FlywayMigrationIT`(真库迁移)、`*EndToEndLiveAcceptanceIT`(真外部依赖)。IT 由 failsafe 运行(非 surefire),用 `mvn verify`(**确认点**:本仓 failsafe 绑定与 PG/外部依赖凭据)。 + - 前端:Playwright [`muse-studio/e2e/*.spec.ts`](../../muse-studio/e2e/) + [`playwright.config.ts`](../../muse-studio/playwright.config.ts),`pnpm test:e2e` 对真实后端。 +5. **跑绿 + 留证据**:保存 IT/Playwright 报告与 `Tests run` 行;无绿证据不得声称“完成”。 +6. **如实三指标报告**:分别给“代码 / 自动化验证 / 端到端可用”三态,而非一个百分比。 + +--- + +## 三、反模式(本项目实证教训) +- **MSW 假绿**:前端只对 mock handler 通过,误当真实可用(基线:个人中心约 21% 面跑 MSW)。 +- **台账冒充可用**:把 `completed`(门禁批准)说成端到端可用。 +- **只跑 surefire 不跑 IT**:跳过真库 / 真外部就宣称端到端。 +- **跨 BC facade 仍是 `Unavailable` 占位**却标完成(基线:meta 写链路、account 多端面运行期 `*_UNAVAILABLE`)。 diff --git a/.agents/workflows/ai-development-protocol.md b/.agents/workflows/ai-development-protocol.md new file mode 100644 index 00000000..7e9a04ee --- /dev/null +++ b/.agents/workflows/ai-development-protocol.md @@ -0,0 +1,49 @@ +# workflow:AI 开发协议(承接→分析→评审→执行→验证→沉淀) + +> **类型**:元流程(workflows) · **简版规约**:[`../../AGENTS.md`](../../AGENTS.md) §5 工作协议(本文件是其操作化展开)。 +> **何时用**:承接**任何** oh-my-muse 任务时先走本流程分流。核心立场:**机械门禁优先、完成=验证、反假绿**。 + +--- + +## 〇、分流(先判复杂度) + +| 路径 | 判据 | 流程 | +|---|---|---| +| **直通** | 简单 / 低危 / 局部 / 可清晰验证 | 目标 → 最小改动 → 验证 → 报告 | +| **全流程** | 复杂 / 高危 / 跨模块 / 改用户可见行为 / 触外部服务·支付·数据·模型 | 一~六全程 | + +--- + +## 一、承接 +- 读 [`../../AGENTS.md`](../../AGENTS.md) → [`../README.md`](../README.md);用 [`../knowledge/`](../knowledge/) 对齐事实(定位/架构/真实现状/决策),用 [`../rules/`](../rules/) 划红线。 +- 涉及历史现状先查 [`../knowledge/module-reality-baseline.md`](../knowledge/module-reality-baseline.md),**避免重复探索**。 +- 复杂任务:先**复述理解**(目标 / 边界 / 非目标)。 + +## 二、分析 +- 第一性原理 + 金字塔:问题、边界、假设、风险、推荐路径。 +- 区分**已验证事实 / 推断 / 假设**;只读可得的先查代码·文档·配置·测试·日志,再问人。 + +## 三、评审(仅全流程) +- 评审版 spec:`docs/agent-specs/YYYY-MM-DD-主题-review.md`(**结论先行**,只留背景/目标/非目标/推荐方案/权衡/影响面/风险/验收/待确认)→ **两轮评审** → 执行版 `...-execution.md`(目标边界/前置/涉及文件/数据流/契约/步骤/失败路径/验证法/完成条件/回滚)。 + +## 四、执行 +- **最小改动**:只动相关代码,复用既有模式,不顺手重构。 +- **无孤儿/拼接**:任何新结构/接口/模型须带入口、使用路径、失败路径、验收标准。 +- 全简体中文注释;外部交互/核心实现/错误路径留可追溯日志。 +- 契约先行(见 [`../rules/contract-first.md`](../rules/contract-first.md));守 BC 边界(见 [`../rules/bc-boundaries.md`](../rules/bc-boundaries.md))。 + +## 五、验证(证据门 —— 不可跳过) +- **完成 = 机械验证**;无自动化绿证据**不得**声称“完成/修复/通过”(见 [`../rules/verification-and-anti-false-green.md`](../rules/verification-and-anti-false-green.md))。 +- 用户可见功能按 [`../skills/golden-journey-vertical-slice.md`](../skills/golden-journey-vertical-slice.md) 的**三指标**报告(代码 / 自动化验证 / 端到端可用),不给单一百分比。 +- 关键门禁须绿:BC(`BcBoundaryArchTest`)、契约(`ContractFirstGateTest`)、CI 真跑测试(JDK21)。 +- **构建纪律(实证教训)**:① 勿并发跑多个 `mvn` 写同一 `target`(会致 `NoSuchFileException` 假错);② 勿信退出码 / 末尾 `grep` 掩盖——读 `BUILD SUCCESS` + `Tests run / Failures` 真实行;③ JDK21 经 `mise exec` 锁定。 + +## 六、沉淀(复利来源 —— 也是硬约束) +- 把可复用产出回写 [`../`](../) 对应层:新事实→`knowledge/`;新硬约束(**必配机械门禁**)→`rules/`;新操作手册→`skills/`;流程改进→本 `workflows/`。 +- 先查重再新增;过时即修正/删除;结构性变更**同步 [`../README.md`](../README.md) 与 [`../../AGENTS.md`](../../AGENTS.md) 索引**。 +- 进度进总账 + per-module `.agent`(建设中),**不再新增“状态推进”过程文档**(churn 是失控来源之一)。 +- 交付后按需询问:是否将关键信息持久化到 `docs/memorys/` 或 `docs/agent-specs/`。 + +--- + +> 一句话:**先对齐事实、划红线,再动手;动手必最小、必留证据;完成必机械验证、必沉淀。** diff --git a/.gitignore b/.gitignore index 135eba5f..b6ea6192 100644 --- a/.gitignore +++ b/.gitignore @@ -153,8 +153,7 @@ vite.config.ts.* # ==================== # AI Agent # ==================== -.agent -.agents +# 注:.agent / .agents 是本项目版本化的 agent 开发基建(见 AGENTS.md),必须入库,故不在此忽略 .cursor skills-lock.json precommit-scan-*.md diff --git a/AGENTS.md b/AGENTS.md index 61769c98..872c27d9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1,99 @@ -@CLAUDE.md +# AGENTS.md —— oh-my-muse(Muse)· Agent 工作入口 + +> **本项目以 AI 驱动开发。** 无论你是 AI agent 还是工程师,**每个任务都从这里开始**。 +> 本文件是**项目唯一事实源**:既答"这是什么项目"(定位/目录),也答"怎么在这里干活"(先读什么、守什么规则、怎么把经验沉淀回去)。 +> 能力细节在 [`.agents/`](.agents/);`CLAUDE.md` 是 design-docs(设计 SSOT)的写作规范,从属本文件。 + +--- + +## 1. 项目定位 + +**Muse = AI 驱动的长篇创作工具**(Shadow→Canonical 主权:AI 产出经"先审后入"才进入正式正文)。四仓协作,采用 monorepo 承载。 + +| 仓 | 技术栈 | 职责 | +|---|---|---| +| muse-cloud | Java 21 + Spring Boot 3 + PostgreSQL + Yudao 框架 | 后端(9 业务域 muse-module-*) | +| muse-admin | Vue 3 + Vben Admin + TS | 管理端前端 | +| muse-studio | React + Vite + TS | 用户端前端 | +| design-docs / docs | Markdown + SQL | 设计 SSOT / agent 规格与基建 | + +业务域:`account(member)` / `ai` / `content` / `knowledge` / `market` / `meta` / `events` + 平台底座。 + +--- + +## 2. ⚠️ 仓库物理事实(2026-06-14 订正,对抗复盘 A1/R6) + +**本仓是 monorepo,物理承载实现代码,不是"纯设计文档仓"。** + +- `git ls-files muse-cloud` = 4468 文件、无 `.gitmodules` → `muse-cloud/`、`muse-admin/`、`muse-studio/` 三个实现仓代码**就在本工作树里、被本仓版本控制**;CI 在 `muse-cloud/.github/workflows/`。 +- 因此:**agent 可以也应当修改这些实现代码**(遵循各自规范 + [`.agents/rules/`](.agents/rules/))。此前"本仓不承载代码/CI"的描述与磁盘矛盾,会让 agent 误判改动边界、绕路产生文档 churn,已订正。 +- `design-docs/`、`docs/` 是设计/规格 SSOT;实现代码须反向对齐其正式文档。 + +--- + +## 3. 目录导航 + +``` +oh-my-muse/ +├── AGENTS.md # 本文件:项目入口(唯一事实源) +├── CLAUDE.md # design-docs 写作规范(从属本文件) +├── .agents/ # Agent 能力中枢(knowledge/rules/skills/workflows;见 .agents/README.md) +├── muse-cloud/ muse-admin/ muse-studio/ # 三实现仓代码(可改) +├── design-docs/ # 产品/架构/前后端/流程/专题 设计 SSOT + 内容映射表/大纲 +└── docs/ + ├── agent-specs/ # 现状基线 + 对抗复盘 + P0 冻结令 + 六砖交付(过程 churn 已清理) + ├── mvp/进度总账.md # 进度单一事实源(+ 历史交付时间线) + ├── api-contracts/ # 各域 OpenAPI = API 契约 SSOT(原地;见 .agents/rules/contract-first.md) + ├── dev-baseline/ # 既有全局/各仓规范(待收敛进 .agents/rules) + └── memorys/ # 已归档清理(见 README);进度改用 mvp/进度总账.md +``` + +--- + +## 4. 任务前必读 + +| 序 | 文档 | 作用 | +|---|---|---| +| 1 | [`.agents/rules/verification-and-anti-false-green.md`](.agents/rules/verification-and-anti-false-green.md) | **脊柱规则**:完成=机械验证、机械门禁优先、反假绿 | +| 2 | `docs/agent-specs/2026-06-13-项目目标与模块现状基线.md` | 项目目标 + 模块**真实**现状(避免重复探索) | +| 3 | `docs/agent-specs/2026-06-13-目标达成对抗复盘.md` | 失控根因(假绿)与重定路径 | +| 4 | [`.agents/README.md`](.agents/README.md) | 能力中枢导航 | +| 5 | design-docs(`00-文档大纲.md` 入口 + `架构-01/02`、`后端-01`) | 设计事实 | + +--- + +## 5. 工作协议(硬约束) + +1. **读后动手**:复杂任务先读 [`.agents/`](.agents/) 与相关 `docs/`,对齐事实再开工。 +2. **机械门禁优先 / 完成=验证**:遵守 [`verification-and-anti-false-green`](.agents/rules/verification-and-anti-false-green.md)——无自动化绿证据不得声称完成;新规则必配机械门禁,不靠自觉。 +3. **复杂/高危先评审**:跨模块 / 改用户可见行为 / 触外部服务·支付·数据 → 评审版 → 两轮评审 → 执行版,再写代码。 +4. **契约先行**:改接口/数据结构先改契约(API=`docs/api-contracts/*`、DB=新增 `sql/muse/V*.sql`,均为原地 SSOT)再实现;遵守 [`contract-first`](.agents/rules/contract-first.md),已有 Flyway 卫生 + OpenAPI 结构机械门禁(openapi-diff 待启用)。 +5. **最小改动 + 中文注释**:只动相关代码,复用既有模式,不顺手重构;代码全简体中文注释,关键路径可追溯日志。 +6. **无孤儿/拼接设计**:任何新结构/接口/模型须带入口、使用路径、失败路径、验收标准。 +7. **证据规则**:区分已验证事实/推断/假设;无证据不声称"完成/修复/通过"。 + +--- + +## 6. 知识沉淀机制(也是硬约束 —— 复利的来源) + +`.agents/` 的价值在于**复利**:让"越往后开发越快越准"。因此: + +- **每次有价值的交付后,把可复用产出回写对应层**:新事实→`knowledge/`;新硬约束(必配门禁)→`rules/`;新操作手册→`skills/`;流程改进→`workflows/`。 +- **先查重再新增**;过时即修正/删除;结构性变更**同步 [`.agents/README.md`](.agents/README.md) 索引与本文导航**。 +- **进度只进 [`总账`](docs/mvp/进度总账.md) + per-module `.agent`,不再新增“状态推进”过程文档**(过程文档 churn 是失控来源之一)。 + +> 无沉淀的任务是"一次性消耗";有沉淀,下一个同类任务在已有成果上更快更准地推进。 + +--- + +## 7. 当前基建建设状态(随建随更) + +- ✅ 机械门禁地基:CI 真跑测试(JDK21)、门禁去硬编码、P0 冻结令(见脊柱规则"已落地")。 +- ✅ 入口与中枢骨架:本文件 + `.agents/README.md` + 脊柱规则。 +- ✅ BC 边界 ArchUnit 门([`bc-boundaries`](.agents/rules/bc-boundaries.md)):AI 不得直连他域 DAL,已验证绿。 +- ✅ 契约先行机械门禁([`contract-first`](.agents/rules/contract-first.md)):Flyway 迁移卫生 + OpenAPI 结构,原地 SSOT,已验证绿。 +- ✅ knowledge 蒸馏 / skills(黄金旅程“完成”定义 + 加 BC 模块)/ workflow(AI 开发协议):见 [`.agents/README.md`](.agents/README.md)。 +- ✅ 进度总账(单一进度源)+ 7 BC per-module `.agent`:见 [`docs/mvp/进度总账.md`](docs/mvp/进度总账.md)。 +- ⏳ 后续(非阻塞):openapi-diff 破坏性变更 CI、`docs/dev-baseline/global/*` 收敛进 `.agents/rules/`(engineering-conventions / security-and-reliability)、BC 边界门扩到其它方向。 + +> 进度只进 [`docs/mvp/进度总账.md`](docs/mvp/进度总账.md) + 各模块 `.agent`,不新增状态过程文档。 diff --git a/CLAUDE.md b/CLAUDE.md index 48624020..a7ecc22a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,14 +11,14 @@ Muse 是一个 AI 驱动的长篇创作工具,采用四仓架构: | muse-studio | React + Vite + TypeScript | 用户端前端 | | **muse-design-docs(本仓)** | Markdown + SQL | 设计文档 SSOT | -## 本仓职责 +## 本仓职责与项目入口(2026-06-14 订正) -本仓是 Muse 四仓架构中的设计文档仓,承担产品、架构、流程、前端、后端、专题设计的**单一事实源(SSOT)**。 +> **项目级工作入口 = 根 [`AGENTS.md`](AGENTS.md)**。本文件聚焦 **design-docs(设计文档 SSOT)的写作规范**;项目定位/目录/工作协议以 AGENTS.md 为准。 +> ⚠️ **订正(对抗复盘 A1/R6)**:本仓**不是**“纯设计文档仓”。它是 monorepo,同一 git 工作树内**物理承载** `muse-cloud/`、`muse-admin/`、`muse-studio/` 实现代码(`git ls-files muse-cloud` 4468 文件、无 submodule)+ CI(`muse-cloud/.github/workflows/`)。此前“本仓不承载代码/CI”与磁盘事实矛盾,会让 agent 误判可改动边界、绕路产生文档 churn,故订正。 -- 不承载运行时代码 -- 不承载 CI/CD 配置或部署脚本 -- 只定义目标设计和 owner 归属 -- 实现仓的代码、迁移和测试必须反向对齐本仓的正式文档 +- `design-docs/` 与 `docs/`:产品、架构、流程、前端、后端、专题设计的**单一事实源(SSOT)**;实现代码须反向对齐这些正式文档。 +- `muse-cloud/`、`muse-admin/`、`muse-studio/`:**实现仓代码,可被 agent 修改**(遵循各自规范 + `.agents/rules/`)。 +- 设计文档的 owner 归属与写作规范见下文各节。 ## 开发模式 diff --git a/design-docs/00-文档大纲.md b/design-docs/00-文档大纲.md index 38313b76..35a57541 100644 --- a/design-docs/00-文档大纲.md +++ b/design-docs/00-文档大纲.md @@ -27,7 +27,7 @@ | `muse-cloud/` | fork `YunaiV/yudao-cloud` | 后端主仓;承接网关、system、infra、framework、任务、文件、权限等 Yudao 基础能力,并补 Muse 的 content、knowledge、ai、market、account 业务模块 | `后端-01`、`后端-02`、`后端-03`、`后端-04`、`后端-05` | | `muse-admin/` | fork `yudao-ui-admin-vben` | 管理端;承载 Admin Console 的系统治理、元结构、Prompt / Agent、全局知识、市场治理、用户、权限、审计和质量观察 | `产品-02B`、`流程-01A`、`流程-02A`、`前端-01`、`后端-05` | | `muse-studio/` | 自研用户端,推荐 Next.js / React | 普通用户创作端;承载我的作品、作品工作台、写作台、智能体工作台、知识库工作台、市场和个人中心 | `产品-02C`、`产品-02D`、`产品-02E`、`产品-02F`、`产品-02G`、`前端-01`、`前端-02`、`前端-03` | -| `muse-design-docs/` | 当前 `design-docs/` 独立化后的设计文档仓 | 产品、架构、流程、前端、后端、专题和阶段计划的设计 SSOT;不承载运行时代码 | `00-文档大纲`、`内容映射表`、`临时-产品形态阶段化重设计计划` | +| `muse-design-docs/` | 当前 `design-docs/` 独立化后的设计文档仓 | 产品、架构、流程、前端、后端、专题的设计 SSOT | `00-文档大纲`、`内容映射表`、各正式分册 | 协作约束: @@ -147,4 +147,4 @@ - 映射表:`内容映射表.md` - 历史文档源:`doc/new-design/`(仅保留参考,不再作为当前设计单一事实源) -- 临时改造文档:`临时-新产品形态设计与文档改造说明.md` 只作为本轮改造依据;正式文档更新后应可删除,删除后不保留为长期归属入口。 +- 临时改造文档:已于 2026-06-14 完成改造并删除(内容已落入 `产品-*`/`架构-*`/`流程-*` 等正式分册;完整历史见 git)。 diff --git a/design-docs/memorys/2026-05-22-产品文档管理步骤.md b/design-docs/memorys/2026-05-22-产品文档管理步骤.md deleted file mode 100644 index e58d44f4..00000000 --- a/design-docs/memorys/2026-05-22-产品文档管理步骤.md +++ /dev/null @@ -1,136 +0,0 @@ -# 产品文档管理步骤 - -- 日期:2026-05-22 -- 适用范围:本轮 Muse 产品形态阶段化重设计,以及后续 `产品-02D~02G`、`产品-03`、`流程-*`、`架构-*`、`前端-*`、`后端-*` 的文档更新。 -- 文档性质:项目内 memorys 留痕,用于下次继续设计时快速恢复流程约束和产品文档质量要求。 - -## 1. 阶段化文档管理步骤 - -本轮产品文档不能一次性全量缝合,必须按阶段、按顺序推进。 - -1. 先确认当前阶段。 -2. 只修改当前阶段 owner 文档。 -3. 已修改并确认过的前序文档作为当前阶段事实来源。 -4. 后续阶段文档只能作为参考,用来发现冲突和待承接事项,不能反向覆盖前序结论。 -5. 发现冲突时,先记录冲突和决策问题,不擅自混合新旧口径。 -6. 每个阶段完成后,需要用户确认,再进入下一阶段。 -7. 不跨阶段顺手修改后续文档。 -8. 原型、review、修复都必须回到当前阶段文档闭环。 - -当前阶段顺序以 `design-docs/临时-产品形态阶段化重设计计划.md` 为准。 - -## 2. 产品文档内容要求 - -产品详细规格必须能支撑前端 UI 原型、后端产品接口拆解、测试验收和后续旅程文档。 - -每个 `产品-02B~02G` 分册至少要回答: - -1. 有哪些功能模块。 -2. 有哪些页面,页面 ID 是什么。 -3. 每个页面的目标、角色、入口、返回规则是什么。 -4. 页面有哪些区域。 -5. 每个区域展示什么信息。 -6. 列表、表单、详情、弹窗、抽屉分别展示哪些字段。 -7. 查询条件是什么字段,用什么查询方式。 -8. 用户可以做什么主操作、次操作和危险操作。 -9. 操作前置条件、确认方式、成功结果、失败反馈和后续处理是什么。 -10. 页面有哪些正常态、空态、错误态、无权限态、只读态、禁用态。 -11. 需要哪些产品接口。 -12. 产品接口的输入上下文、返回内容、权限、状态变化、副作用、失败类型、用户反馈、审计要求和后续承接是什么。 -13. 哪些能力明确不做,哪些能力移交后续阶段。 - -不能只写“支持管理”“展示列表”“提供配置”“查看详情”这类概念描述。 - -## 3. 产品接口要求 - -产品接口不是后端 endpoint,也不是数据库字段,而是产品层合同。 - -接口表必须使用 `产品-02A` 的统一结构: - -| 字段 | 要求 | -|---|---| -| 接口名称 | 产品层稳定名称 | -| 类型 | 查询 / 命令 / 决策 / 任务 / 授权 / 来源 / 质量 / 用量 / 审计 / 选项 | -| 使用页面 | 哪些页面或区域调用 | -| 用户意图 | 用户为什么需要它 | -| 输入上下文 | 账户、角色、对象、筛选、分页、授权快照、来源版本、幂等键等 | -| 返回内容 | 页面需要展示的数据,不写数据库字段 | -| 权限规则 | 谁能调用,哪些内容脱敏,哪些状态禁用 | -| 状态变化 / 副作用 | 是否改变状态,是否产生任务、审计、用量、授权快照或来源状态 | -| 失败类型 / 用户反馈 | 失败原因和页面如何提示 | -| 审计要求 | 是否记录操作者、理由、范围、时间、前后状态 | -| 后续承接 | 由哪个产品、流程、架构或后端文档继续细化 | - -新增页面或操作时,必须同步补产品接口;新增接口时,必须能在页面、操作和覆盖表中找到使用位置。 - -## 4. 原型约束 - -HTML 原型必须严格来自当前阶段文档。 - -1. 文档没有描述的功能、字段、状态、按钮、模块,不允许出现在原型中。 -2. 原型不是文档摘要,而是可参考的真实产品 UI 稿。 -3. 列表必须有贴近业务的假数据。 -4. 表单必须展示紧凑字段、输入控件、默认值或示例值。 -5. 多组件页面必须按字段密度、组件大小和真实工作流布局。 -6. 该有按钮、筛选、状态、禁用原因、错误反馈的地方要在原型中体现。 -7. UI 风格要符合产品文档中的后台/工作台风格,不做营销页式设计。 - -如果原型暴露文档缺失,优先修文档,再按修复后的文档改原型。 - -## 5. Review 和修复闭环 - -对关键产品分册执行 review 时,默认按大厂 PRD 标准检查: - -1. 文档边界是否正确。 -2. 内容是否完整。 -3. 页面、字段、操作、状态、接口是否闭合。 -4. 是否存在没有业务发起人的孤儿功能。 -5. 权限、审计、安全、数据边界是否可信。 -6. 是否违反前序阶段已确认设计。 -7. 是否引入后续阶段职责。 -8. 是否有隐含架构边界或领域边界冲突。 -9. 是否能直接支撑 UI 原型和后续后端接口拆解。 - -修复时按严重性处理: - -1. P0:边界错误、越权、职责错位、无法实现、与前序结论冲突。 -2. P1:字段、接口、状态、异常、审批、幂等、补偿、审计不完整。 -3. P2:表达、索引、后续承接、原型便利性和长期治理项。 - -修复后必须做至少三类检查: - -1. 当前文档 diff 检查。 -2. 关键词扫描旧口径残留。 -3. 接口表、页面表、覆盖表闭合检查。 - -## 6. 已确认的关键产品文档口径 - -以下口径已经在当前轮次反复确认,后续阶段必须承接。 - -1. 管理员控制台是治理面,不是用户创作工作台。 -2. 普通用户默认从我的作品进入,作品工作台默认落在写作台。 -3. 智能体工作台、知识库工作台、市场、个人中心是独立产品空间。 -4. 用户也有自己的知识库管理模块,知识库也能上架市场。 -5. 管理员元结构管理必须保留。 -6. 大量 AI 能力是系统预编排功能链路,用户配置的是开放槽位中的子智能体替换。 -7. 输入输出合规、拆分切块、入 RAG、语义安全围栏、静态检查、权限、审计、Shadow -> Canonical 等系统保护节点不可替换。 -8. AI 候选、知识草稿、规划候选必须先进入待审层,用户确认后才能进入正式事实。 -9. 市场购买、安装或绑定资产不等于自动写入用户作品事实。 -10. New-API 是外部 LLM Gateway,Muse 直接调用其接口管理网关用户、订阅额度、余额查询和调用日志归属,不同步其路由、成本和调用日志权威数据。 -11. 接口调用日志和业务审计日志要区分;所有系统接口调用要有轻量日志,高危业务动作进入 append-only 审计。 -12. 权限设计要类似 yudao 的角色、权限组、菜单、页面、操作、数据范围模型,但不能形成单一超级权限。 -13. 通用私有正文查看入口不进入当前管理员控制台设计。 -14. 来源失效不做独立管理模块,只在引用来源处展示来源已失效、已下架、授权已撤销或需重验。 - -## 7. 下次继续时的执行提示 - -继续做下一个产品分册时,先读取: - -1. `design-docs/临时-产品形态阶段化重设计计划.md` -2. `design-docs/产品-01-产品定位与核心价值.md` -3. `design-docs/产品-02-核心功能与交互边界.md` -4. `design-docs/产品-02A-统一功能规格模板与产品接口约定.md` -5. 已确认的前序分册,例如 `产品-02B`、`产品-02C` -6. 本文件 - -然后只修改当前阶段目标文档。若用户要求 review,则先 review 当前阶段文档;若用户要求修复,则按 review 结论修当前阶段文档;若用户要求原型,则严格按当前阶段文档生成 HTML 原型。 diff --git a/design-docs/memorys/2026-05-24-整体文档一致性修复.md b/design-docs/memorys/2026-05-24-整体文档一致性修复.md deleted file mode 100644 index 82b1d9af..00000000 --- a/design-docs/memorys/2026-05-24-整体文档一致性修复.md +++ /dev/null @@ -1,60 +0,0 @@ -# 整体文档一致性修复 - -- 日期:2026-05-24 -- 适用范围:Muse 设计文档整体 review 后的 P0/P1 跨分册一致性修复。 -- 文档性质:项目内 memorys 留痕,用于后续继续设计、review 或进入实现阶段时快速恢复关键边界。 - -## 1. 本次修复背景 - -在阶段 7 前后端架构文档完成后,对 `design-docs/` 做了多角度整体 review,发现部分已完成分册之间仍存在 owner、状态、事实写入和产品能力 gate 的 P0 冲突。 - -本次修复是一次明确的跨阶段一致性修复例外:不是重新设计所有阶段,也不是进入阶段 8 索引整理,而是在用户确认后,按独立 owner 分组修复已经发现的阻断级问题。 - -## 2. 本次修复方式 - -修复任务拆为五个互不重叠的子代理任务: - -1. 架构与领域边界:修 `架构-01/02/03/04`。 -2. 后端 Schema/API/模块契约:修 `后端-01/02/03/04/05`。 -3. 产品 02 与市场原型:修 `产品-02/02C/02F` 和 02F 原型。 -4. 流程与专题:修 `流程-01A/01B/02A/02B` 与 `专题-01/02/03/04`。 -5. 前端文档:修 `前端-01/02/03`。 - -主代理最后做跨文档整合,补了 `产品-03`、来源状态命名和用户可见技术词残留,并更新阶段计划。 - -## 3. 已收口的关键口径 - -后续设计和实现必须继续承接以下结论。 - -1. MetaSchema 的逻辑 owner 固定为 Admin/Governance;物理表即使暂落 Content,也只能通过治理 facade 写入。Content 只承载或消费投影。 -2. Agent、Tool Grant、Agent Runtime Permission Envelope 必须分层。AI runtime 只能消费服务端签发的权限包,不能自授权、扩权或绕过 owner 校验。 -3. Market 只负责市场资产、授权、安装记录、来源侧 handoff、授权摘要和跳转审计。目标 owner 自己创建和消费 target precheck/session,Market 不写目标事实。 -4. Import task/context 归 Work/Content;Parse Job、Chapter Parse Result、Chapter Review 归 AI Orchestration;Knowledge 只在章节审阅确认后创建或更新 Knowledge Draft。 -5. 动态字段没有跨 owner 的通用写接口。`/dynamic-fields/validate` 只做校验和路由建议,正式写入必须回到 Content、Planning、Knowledge、Agent 等 owner action。 -6. Accept / Merge 写 Canonical 前必须消费或重算 Candidate Decision Envelope。硬闸门必须覆盖输出合规、静态检查、质量结果版本、来源状态、授权快照、作品资产 feature gate、`expectedRevision` 和幂等结果。(ADR-018 已决策去掉独立 Candidate Decision Envelope 概念封装,改为接受时实时对比来源状态;Agent Runtime Permission Envelope 仍保留,两者是不同概念。专题文档已同步更新。) -7. 作品资产当前只允许阅读、收藏和授权记录。模板化、参考来源写入、AI 上下文绑定必须等待后续 feature gate、作品 owner 入口、使用检查、来源谱系、API/Schema 和失败回退闭合。 -8. SourceStatus、SourceEventType、SourceActionPolicy 必须分层表达。SourceStatus 包含 `delisted`;SourceActionPolicy 包含 `read_only`,不能把 `allowed/blocked/needs_recheck` 当成来源事实状态。 -9. 来源撤权、召回、下架、阻断、owner 缺失和授权失效会影响候选接受、Local KB 来源型确认、生成上下文、绑定、导出任务和已签发下载凭证,但不能自动回滚已确认 Canonical。 -10. 用户可见原型和前端文案不能暴露 `handoff token`、`precheckId`、`Tool Grant`、`Runtime Permission Envelope` 等技术合同名;界面应使用跳转授权、使用检查、工具权限摘要、运行权限摘要等产品语言。 - -## 4. 后续继续时先检查 - -继续阶段 8 或进入实现阶段前,先检查: - -1. `design-docs/临时-产品形态阶段化重设计计划.md` 中 D-045 和 C-006。 -2. `产品-02C/02F/03` 中作品资产是否仍保持只读/收藏/授权记录的默认 gate。 -3. `后端-05` 中动态字段是否仍没有跨 owner 泛写接口。 -4. `专题-01`、`流程-02B` 和 `前端-02` 是否仍要求 Candidate Decision Envelope 硬闸门。(ADR-018 已决策去掉独立 Candidate Decision Envelope,改为实时对比;Agent Runtime Permission Envelope 仍保留。) -5. `架构-01/02/04`、`后端-03/04/05`、`专题-03` 是否仍统一 SourceStatus / SourceEventType / SourceActionPolicy。 -6. 阶段 8 已在 D-046 开始补 `00-文档大纲.md` 和 `内容映射表.md` 的四仓工程架构索引;后续仍需继续复核其他过期引用和 owner 映射。 - -## 5. 验证方式 - -本次修复完成后已执行: - -1. `git diff --check` -2. 阶段 8 文件未修改检查。 -3. 作品资产误开放关键词扫描。 -4. 动态字段泛写入口扫描。 -5. SourceStatus / SourceEventType / SourceActionPolicy 旧口径扫描。 -6. 原型用户可见技术词扫描。 diff --git a/design-docs/临时-产品形态阶段化重设计计划.md b/design-docs/临时-产品形态阶段化重设计计划.md deleted file mode 100644 index b71f0fe3..00000000 --- a/design-docs/临时-产品形态阶段化重设计计划.md +++ /dev/null @@ -1,174 +0,0 @@ -# 临时-产品形态阶段化重设计计划 - -- 版本:v1 -- 更新日期:2026-05-23 -- 目标读者:产品 / 架构 / 前端 / 后端 / 文档维护者 -- 文档性质:本轮设计变更的流程约束和阶段管理文档,不替代正式产品、架构、流程或实现文档。 - -## 0. 本次讨论的流程约束 - -本轮目标是按阶段、按顺序重新设计 Muse 的产品形态和后续设计文档,不做一次性全量缝合。 - -必须遵守以下要求: - -1. 当前阶段只修改当前阶段文档,不跨阶段顺手修改后续文档。 -2. 讨论设计时,以已经修改并确认过的文档为准;后面阶段文档只能作为参考,不能反向压当前阶段。 -3. 每进入下一阶段,必须承接前面阶段已经确定的产品形态、边界、术语、非目标和硬约束,不能遗漏前面阶段设计。 -4. 如果后续参考文档和前面已确认设计冲突,先记录冲突并提出决策问题,不擅自缝合。 -5. 每个阶段结束时,必须形成可追溯的阶段结论,再进入下一个阶段。 -6. 本计划文档负责管理阶段、约束、决策记录和冲突记录;正式设计内容仍落在对应 owner 文档中。 - -## 1. 工作目标 - -通过分阶段设计,重新收口 Muse 的产品形态、功能边界、用户旅程、流程、架构和后续实现文档,使整套设计文档从上到下保持一致。 - -本轮变更的核心不是局部润色,而是先确定产品形态,再让后续文档按顺序继承,不允许后续文档各自维护一套旧口径。 - -## 2. 阶段顺序 - -| 阶段 | 目标文档 | 阶段目标 | 状态 | -|---|---|---|---| -| 阶段 0 | `design-docs/临时-产品形态阶段化重设计计划.md` | 建立本轮设计变更流程约束、阶段顺序和记录方式 | 已完成 | -| 阶段 1 | `design-docs/产品-01-产品定位与核心价值.md` | 确认产品定位、产品空间、目标用户、核心价值、非目标和成功指标 | 已完成 | -| 阶段 2.0 | `design-docs/产品-02-核心功能与交互边界.md` | 承接产品形态,重写功能边界、角色边界、交互规则和非谈判项,作为阶段 2 总纲 | 已完成 | -| 阶段 2.1 | `design-docs/产品-02A-统一功能规格模板与产品接口约定.md` | 建立详细功能规格、页面规格和产品接口的统一写法 | 已完成 | -| 阶段 2.2 | `design-docs/产品-02B-管理员控制台功能规格.md` | 明确管理员控制台的页面、数据、操作、权限和产品接口 | 已完成 | -| 阶段 2.3 | `design-docs/产品-02C-用户工作区与作品工作台功能规格.md` | 明确我的作品、作品工作台、写作台、规划、知识、导入导出等页面规格 | 已完成 | -| 阶段 2.4 | `design-docs/产品-02D-智能体工作台功能规格.md` | 明确智能体创建、配置、安装、替换槽位、试用、发布等功能规格 | 已完成 | -| 阶段 2.5 | `design-docs/产品-02E-知识库工作台功能规格.md` | 明确全局知识库、用户知识库、账户可用知识库、安装、绑定、处理状态和发布规格 | 已完成并提交 | -| 阶段 2.6 | `design-docs/产品-02F-市场功能规格.md` | 明确作品、智能体、知识库市场资产的发现、授权、安装、绑定、发布和治理规格 | 已完成并提交 | -| 阶段 2.7 | `design-docs/产品-02G-个人中心功能规格.md` | 明确个人资料、偏好、用量、权益、授权记录和发布记录总览规格 | 已完成并提交 | -| 阶段 3 | `design-docs/产品-03-用户旅程与操作流程.md` | 承接产品空间和功能边界,重写管理员、普通用户、智能体、市场等旅程 | 已完成并提交 | -| 阶段 4 | `design-docs/流程-01A-管理员操作流程(操作视角).md` / `design-docs/流程-01B-普通用户操作流程(操作视角).md` / `design-docs/流程-02A-管理员系统处理流程(系统视角).md` / `design-docs/流程-02B-普通用户系统处理流程(系统视角).md` | 按角色拆分操作流程和系统处理流程 | 已完成并提交 | -| 阶段 5 | `design-docs/架构-01-系统全貌与边界上下文.md` / `design-docs/架构-02-核心数据结构与双轨模型.md` / `design-docs/架构-04-状态机与约束清单.md` | 承接产品与流程,调整有界上下文、核心模型、状态机和约束清单 | 已完成并提交 | -| 阶段 6 | `design-docs/专题-*` | 按需要调整 AI 编排、质量门控、竞品取舍、正文建议接受等专题合同 | 已完成并提交 | -| 阶段 7 | `design-docs/前端-*` / `design-docs/后端-*` | 承接产品、流程、架构结论,更新工程结构、Schema、API 和实现约束 | 整体一致性修复完成,待复核确认 | -| 阶段 8 | `design-docs/00-文档大纲.md` / `design-docs/内容映射表.md` | 最后统一索引、文档 owner 和内容映射,清理过期引用 | 已开始;四仓工程架构索引已补 | - -阶段 2 当前基线状态: - -- `产品-02A~02G` 已按阶段完成、修复并提交,其中 `产品-02E~02G` 已通过本轮用户确认进入阶段 3 的输入基线。 -- 阶段 3 以 `产品-01`、`产品-02`、`产品-02A~02G` 为前序事实来源,重写 `产品-03` 的用户旅程和操作流程。 -- 后续流程、架构、专题、前端和后端文档只能承接阶段 1~3 已确认结论,不能反向修改当前阶段已确认边界。 - -| 分册 | 当前状态 | 是否纳入局部冻结基线 | 未闭合事项 | -|---|---|---|---| -| `产品-02A` | 已完成 | 是 | 作为统一模板继续约束 02E~02G | -| `产品-02B` | 已完成 | 是 | 市场分类/推荐/曝光具体 surface 由 02F 承接 | -| `产品-02C` | 已完成 | 是 | 知识库工作台、市场和个人中心跳转仍保留 pending 目标 | -| `产品-02D` | 已完成 | 是 | 市场审核结果、上架、下架和申诉由 02F/02B 承接 | -| `产品-02E` | 已完成并提交 | 是 | 作为知识库工作台旅程、绑定、处理状态和发布准备的输入基线;全局知识库治理 authority 仍归 02B | -| `产品-02F` | 已完成并提交 | 是 | 作为市场发现、授权、安装、handoff、发布提交和治理结果的输入基线;作品资产 owner 缺口仍由后续 02C/流程阶段承接 | -| `产品-02G` | 已完成并提交 | 是 | 作为个人中心账户总览、用量、权益、授权、发布记录和安全事件的输入基线;个人中心仍只是 read model 和跳转空间 | - -## 3. 阶段门禁 - -每个阶段进入前,需要确认: - -1. 上一阶段文档已经完成修改并被确认。 -2. 本阶段要修改的文档 owner 明确。 -3. 本阶段只处理当前 owner 文档的职责,不抢后续文档的精确归属。 -4. 参考后续文档时,只抽取冲突和约束,不直接按后续文档反改当前阶段。 - -每个阶段结束前,需要确认: - -1. 当前文档是否完整承接了前面阶段的已确认结论。 -2. 是否留下与后续阶段有关但暂不修改的待承接事项。 -3. 是否存在需要用户决策的冲突项。 -4. 是否需要把阶段结论补充到本计划文档的决策记录中。 - -## 4. 设计讨论规则 - -讨论设计时,所有结论按以下优先级判断: - -1. 已确认并已修改的前序阶段文档。 -2. 当前阶段正在讨论和准备修改的目标文档。 -3. 后续阶段文档中的参考信息。 -4. 代码实现、旧文档、历史计划和外部竞品信息。 - -如果不同来源发生冲突: - -- 前序阶段已确认内容优先。 -- 后续阶段文档只能登记为待承接或待修正,不能反向否定前序阶段。 -- 如果冲突会改变产品形态、权限边界、资产边界或数据模型,必须先提问确认。 - -## 5. 冲突记录 - -| 编号 | 发现阶段 | 冲突来源 | 冲突描述 | 处理方式 | 状态 | -|---|---|---|---|---|---| -| C-001 | 阶段 1 准备 | 新版 `产品-01` 与旧 `产品-02/03` | 新版引入智能体工作台、知识库工作台和市场空间,旧后续文档尚未承接 | 阶段 2 已由 `产品-02` 承接;阶段 3 继续由 `产品-03` 承接,不在阶段 2 跨文档修改 | 部分承接 | -| C-002 | 阶段 2.5~2.7 二轮复核 | `产品-02C` 局部冻结基线与 02F/02G 作品资产发布/使用需求 | 02F/02G 需要作品资产发布准备包和作品资产使用 owner,但当前不能跨阶段修改已冻结的 02C | 在 02F/02G 中标为后续阻断:02C 后续补齐 `work-publish-prep` 和作品资产使用预检前,作品资产不得提交发布、模板化、参考写入或进入 AI 上下文 | 待后续阶段承接 | -| C-003 | 阶段 3 review | `产品-03` 初稿与前序安全/资产边界 | 子代理 review 发现外部知识 lineage、保护节点不可开放、handoff 生命周期、知识库类型、市场授权/安装/绑定状态、管理员分权等边界需要补强 | 阶段 3 只修 `产品-03`,不反改 02 系列;将确认为流程/架构必须承接的不变式 | 已修复并确认 | -| C-004 | 阶段 4 review / 阶段 5 复核 | `产品-03` 与阶段 4 四分册 | 初次 review 认为 `产品-03` 中存在“已确认作品事实自动回滚”的旧口径残留;阶段 5 复核确认该短语位于“不能被解释为”列,不是旧口径 | 不需要跨阶段修改 `产品-03`;阶段 4 和阶段 5 均继续按“已确认事实不自动回滚,但受限来源不得继续进入新生成、新绑定、新导出许可范围或新的知识确认”处理 | 已复核,无需修改 | -| C-005 | 阶段 7 准备 | 旧 `前端-*` / `后端-*` 与当前 Muse 工程口径 | 旧前端文档把管理员后台和用户端都写成 Next.js 同一前端;旧后端文档按 `muse-*` 自研模块化单体描述,未承接 Yudao Cloud fork、Vben Admin 管理后台和独立 `muse-studio` 用户端的工程基线 | 阶段 7 已按新工程口径重写前后端架构:后端以 `muse-cloud/` fork 为底座,管理后台走 `muse-admin/`,用户端单独自研 `muse-studio/`;阶段 8 已补四仓工程架构索引 | 已承接,待确认 | -| C-006 | 阶段 7 整体 review 修复 | 已完成的产品、流程、架构、专题、前端、后端分册 | 多角度整体 review 发现 P0 跨文档冲突:MetaSchema logical owner 与物理落表混淆、Agent/Tool Grant/Runtime Permission 可被 AI runtime 自授权、Market handoff/precheck 抢目标 owner、Parse Job owner 冲突、动态字段泛写入口绕过事实 owner、SourceStatus/SourceEventType/Action Policy 命名不统一、Accept/Merge 缺 Candidate Decision Envelope 硬闸门、作品资产被提前写成可模板化/参考/进 AI 上下文 | 用户明确要求拆分独立任务并用子代理修复,因此本次作为 P0 一致性修复例外,打开受影响 owner 文档定点修复;阶段 8 已开始补索引,当前先补四仓工程架构 | 已修复,待复核 | - -## 6. 决策记录 - -| 编号 | 日期 | 阶段 | 决策 | 影响范围 | -|---|---|---|---|---| -| D-001 | 2026-05-22 | 阶段 0 | 本轮设计变更采用按阶段、按顺序推进;当前阶段只改当前阶段文档;后续文档只作为参考 | 全部设计文档 | -| D-002 | 2026-05-22 | 阶段 1 | `产品-01` 锁定六个产品空间:管理员控制台、用户工作区、智能体工作台、知识库工作台、市场、个人中心;知识库作为可管理、可复用、可上架市场的一级资产 | `产品-01` 已修改;后续 `产品-02/03`、流程、架构、前后端文档必须按阶段承接 | -| D-003 | 2026-05-22 | 阶段 1 | 管理员元数据治理必须保留;大量 AI 能力是系统预编排功能链路,用户只能替换开放槽位中的子智能体;输入输出合规、拆分切块、入 RAG、语义安全围栏、静态检查等系统保护节点不可替换 | `产品-01` 已修改;阶段 2 需要在 `产品-02` 中细化可替换槽位和不可替换系统节点 | -| D-004 | 2026-05-22 | 阶段 1 | 子代理评审后补强 `产品-01` 的 P0/P1 边界:主产品楔子与六空间优先级、市场授权不等于所有权转移、第三方市场资产默认不可信、正文保存与知识入库分层、替换槽位合同、管理员治理分权、信任与合规指标 | `产品-01` 已修改;阶段 2 起必须继续承接这些边界,不能把后续功能设计成越权、自动污染作品事实或单点超级权限 | -| D-005 | 2026-05-22 | 阶段 2.0 | `产品-02` 按六个产品空间重构功能与交互边界,并明确系统功能编排、替换槽位合同、三类知识库、市场授权、Shadow/Canonical、stale guard、全书解析章节边界和非谈判项 | `产品-02` 已修改;阶段 2.1~2.7 和阶段 3 需要按这些边界继续细化,不反向修改阶段 1/2.0 结论 | -| D-006 | 2026-05-22 | 阶段 2.0 | 子代理评审后修复 `产品-02` 的 P1/P2 边界:no-config 主路径、authority/surface、管理员分权、工作流智能体工具授权、市场授权生命周期、作品资产使用入口、上下文外发、来源状态校验、全书解析两步确认、候选知识变更状态和渐进展示 | `产品-02` 已修改;阶段 2.1~2.7 和阶段 3/4/5 必须继续承接这些交互、流程、状态机和权限边界 | -| D-007 | 2026-05-22 | 阶段 2.1 | `产品-02` 不继续膨胀为单一大 PRD;阶段 2 拆为总纲 + `产品-02A~02G` 详细功能规格。详细规格必须能支撑前端 UI 原型,覆盖页面、可见数据、用户操作、状态、权限、异常和产品接口 | 新增 `产品-02A` 统一模板;后续按六个产品空间继续补 `产品-02B~02G` | -| D-008 | 2026-05-22 | 阶段 2.2 | 子代理评审后修复 `产品-02B` 的 P0/P1 缺口:补齐管理员控制台页面 ID、产品接口闭环、系统智能体配置、质量评估集与结果、质量效果观测、市场申诉、来源状态展示、分权防自批、上下文外发门禁、任务重验矩阵、New-API 调用日志归属和审计不可篡改边界 | `产品-02B` 已修改;阶段 2.2 仍待用户确认,确认后再进入 `产品-02C`,不跨阶段修改用户工作区、智能体工作台、知识库工作台、市场或个人中心分册 | -| D-009 | 2026-05-22 | 阶段 2.2 | 原型暴露 `产品-02B` 的 PRD 粒度和孤儿功能问题:删除管理员角色变更申请、New-API 复制式队列/差异/补偿、市场推荐分类等无当前业务发起人的功能;New-API 改为直接调用网关用户、订阅额度、余额和调用日志接口;日志拆为接口调用日志和业务审计日志;新增页面字段与查询条件规格和功能点合法性清单 | `产品-02B` 已修改;后续 `产品-02C~02G` 也必须按“页面字段 + 查询条件 + 操作反馈 + 业务发起人”粒度编写,避免只描述组件或制造孤儿功能 | -| D-010 | 2026-05-22 | 阶段 2.2 | 根据原型复审继续收敛 `产品-02B`:补充类似 yudao 的角色、权限组、菜单页面权限、操作权限和数据范围模型;新增“权限组与菜单页面权限”页面;删除通用私有正文查看入口;删除独立来源失效管理页,改为在引用来源处展示“来源已失效/已下架/授权已撤销/需重验” | `产品-02`、`产品-02A`、`产品-02B` 已做当前阶段一致性修正;后续分册只能承接来源状态展示和权限管控模型,不再恢复独立来源管理后台或通用私有正文访问入口 | -| D-011 | 2026-05-22 | 阶段 2.3 | `产品-02B` 本轮复核未发现阻断问题,仅修正当前文档内来源状态旧口径残留;阶段 2.3 进入 `产品-02C`,按 `产品-02A` 模板补齐用户工作区与作品工作台的页面、字段、操作、状态和产品接口 | 新增 `产品-02C`;阶段 2.3 待确认,确认后再进入 `产品-02D`,不跨阶段修改智能体工作台、知识库工作台、市场或个人中心分册 | -| D-012 | 2026-05-22 | 阶段 2.4 | 用户确认 `产品-02C` 通过后,阶段 2.4 进入 `产品-02D`;`产品-02D` 按 `产品-02A` 模板补齐智能体工作台的智能体列表、创建、配置型编辑、工作流编辑、试用、槽位兼容、作品槽位候选选择、已安装授权、发布准备、运行用量和产品接口 | 新增 `产品-02D`;已由 D-014 提交通过,不跨阶段修改知识库工作台、市场或个人中心分册 | -| D-013 | 2026-05-22 | 阶段 2.4 | 子代理评审后修复 `产品-02D` 的 P0/P1 缺口:补齐 handoff session 与 `precheckId` 原子消费、市场/第三方智能体信任模型、Prompt 注入与敏感信息治理、Tool Grant 合同、试用异步状态、幂等和快照消费、授权/版本/绑定传播矩阵、运行记录视图拆分、发布草稿到市场审核申请边界、页面状态矩阵和字段级规格 | `产品-02D` 与 02D 原型已修改;已由 D-014 提交通过,不跨阶段修改知识库工作台、市场或个人中心分册 | -| D-014 | 2026-05-22 | 阶段 2.4 -> 2.5 | 用户要求提交 `产品-02A~02D` 相关文档并进入下个阶段;`产品-02D` 视为当前阶段通过,阶段 2.5 开始处理知识库工作台规格 | `产品-02A~02D` 作为局部冻结基线;完整 02 系列仍需 02E~02G 完成后成立 | -| D-015 | 2026-05-22 | 阶段 2.4 修复 | 大厂级 PRD review 后修复 `产品-02A~02D` 的基线和原型问题:明确 02A~02D 只是局部冻结基线,补 HTML 原型验收合同、横切安全与数据治理合同、02C 页面/操作/接口覆盖表、02C 决策接口幂等与授权快照、02B/02C 指向 02D 的精确页面 ID,并修正 02C/02D 原型中失效来源可确认、功能平铺、暂缓备份和普通作者默认暴露资产作者入口等误导 | 只修复已提交的 02A~02D、阶段计划和既有原型;不推进 02E,不修改 02F~02G | -| D-016 | 2026-05-22 | 阶段 2.5 | 根据用户要求开始 `产品-02E`,新增知识库工作台详细规格初稿,覆盖知识库列表、创建、资料管理、处理状态、已安装授权、作品绑定、发布准备、使用记录、全局知识库视图和产品接口 | 当前只新增 `产品-02E` 并更新本计划记录;不修改 02F~02G、产品-03、流程、架构、前端或后端文档 | -| D-017 | 2026-05-22 | 阶段 2.5~2.7 并行初稿 | 用户纠正本轮并行目标为 `产品-02E/02F/02G`;启动三个子代理分别完善知识库工作台、市场、个人中心文档和对应 HTML 原型 | 新增或完善 `产品-02E/02F/02G` 与对应原型;仍不修改 `产品-03`、流程、架构、专题、前端或后端文档;后续需要对 02E~02G 做统一 review 和确认 | -| D-018 | 2026-05-23 | 阶段 2.5~2.7 review | 子代理 review 发现 02E/02F/02G PRD 仍有闭合缺口:02E 全局知识库 owner、删除/导出/处理检查/handoff;02F handoff、三类资产分型、发布材料、召回影响、申诉状态、PRD 验收混入执行项;02G AssetSummary、作品发布路径、安全导出、偏好操作、收藏摘要 | 进入定点修复;只改 02E/02F/02G PRD 和本计划,不改 HTML、产品-03、流程、架构、专题、前端或后端文档 | -| D-019 | 2026-05-23 | 阶段 2.5~2.7 修复 | 按 review 结果修复 02E/02F/02G PRD:收紧全局知识库 authority 到 02B;补处理检查、删除、导出和绑定 handoff;补市场三类资产可信信息、发布材料、治理影响、申诉状态和 handoff 不变式;补个人中心 AssetSummary、作品发布跳转、安全事件导出、偏好动作接口和收藏摘要边界 | 修复完成后待复核确认;确认前 02E~02G 不纳入局部冻结基线,不进入产品-03/流程/架构阶段 | -| D-020 | 2026-05-23 | 阶段 2.5~2.7 二轮 review | 子代理复核未发现 P0,但发现 P1/P2:02E 删除/导出任务闭合不足;02F 市场 owner 写入冲突、预检快照、发布检查快照、申诉撤回、治理处理 handoff 和作品资产 owner 缺失;02G 安全事件确认、测试通知口径、账户 ledger authority 和 02F/交易/02B owner 文案过宽 | 进入二轮定点修复;仍只改 02E/02F/02G PRD 和本计划,不改 HTML、产品-03、流程、架构、专题、前端或后端文档 | -| D-021 | 2026-05-23 | 阶段 2.5~2.7 二轮修复 | 修复二轮 findings:02E 补导出预检/任务/下载、取消删除和绑定 handoff 查询;02F 改为市场只生成安装/关联/绑定/治理 handoff,补 `agentSlotPrecheckId`、`kbBindPrecheckId`、`workAssetUsePrecheckId`、`marketPublishCheckId`、申诉撤回和作品资产 owner 阻断;02G 补安全事件本人确认、测试通知只面向已验证渠道、账户记录 read-model authority、02F/02B/交易分册边界和作品发布阻断 | 修复完成,待再次复核确认;后续架构阶段需承接 Marketplace/Asset、License/Installation、User Knowledge Asset、Account Ledger/Handoff 的 BC、authority、读模型和状态机边界 | -| D-022 | 2026-05-23 | 阶段 2.5~2.7 -> 3 | 用户要求提交 `产品-02E~02G` 并提交到 main 后进入 `产品-03`;`产品-02A~02G` 作为阶段 3 输入基线 | 阶段 3 可以重写 `产品-03`;仍不跨阶段修改流程、架构、专题、前端或后端文档 | -| D-023 | 2026-05-23 | 阶段 3 review | 子代理 review `产品-03` 初稿后确认大框架成立,但必须修复外部知识 lineage、保护节点注册表、handoff 生命周期、三类知识库口径、市场授权状态、管理员分权、no-config 长篇主路径、取消/任务/失败反馈等问题 | 阶段 3 进入定点修复;只改 `产品-03` 和本计划 | -| D-024 | 2026-05-23 | 阶段 3 修复 | 按 review 结果修复 `产品-03`:补外部来源 lineage、防知识洗白、保护节点注册表与 allowlist、管理员分权、New-API 最小权限和账务 owner 边界、智能体版本/工具授权快照、三类知识库 owner、市场授权/安装/绑定/使用/召回状态、作品资产 owner 阻断、handoff 生命周期、异步任务取消和失败反馈 | 修复完成,等待用户确认;确认前不进入阶段 4 的流程文档重写 | -| D-025 | 2026-05-23 | 阶段 3 提交 | 用户确认阶段 3 修复结果,要求更新阶段计划并提交 `产品-03` 与阶段计划 | 阶段 3 纳入已确认基线;下一阶段进入流程文档阶段,继续遵守不跨阶段修改约束 | -| D-026 | 2026-05-23 | 阶段 4 启动 | 用户要求进入下个阶段;阶段 4 处理流程文档,承接 `产品-01`、`产品-02`、`产品-02A~02G`、`产品-03` 的已确认基线 | 旧流程文档结构仅作为参考;本阶段不修改产品、架构、专题、前端或后端文档 | -| D-027 | 2026-05-23 | 阶段 4 拆分 | 用户要求阶段 4 拆成四个流程文档:管理员操作视角、普通用户操作视角、管理员系统处理流程、普通用户系统处理流程 | 阶段 4 最终交付四个分册;旧流程文档作为旧稿参考,不再作为最终承重文档 | -| D-028 | 2026-05-23 | 阶段 4 四分册初稿 | 创建 `流程-01A`、`流程-01B`、`流程-02A`、`流程-02B` 四个新分册,并将旧流程文档改为拆分导航页 | 四分册进入初稿校验和 review;本阶段仍不修改产品、架构、专题、前端或后端文档 | -| D-029 | 2026-05-23 | 阶段 4 review | 启动新子代理 review 四个分册,发现必须修复的问题:全书解析章节确认不能写正式知识、作品资产模板化/参考来源/AI 上下文当前必须阻断、外部来源不能通过“改写自有事实”洗白、handoff 绑定条件不足、来源传播目标不全、导出/下载和账户安全链路缺失、系统智能体发布与市场恢复缺高危门禁、New-API 和任务治理流程过粗 | 进入阶段 4 定点修复;只改四个流程分册和本计划,不修改产品、架构、专题、前端或后端文档 | -| D-030 | 2026-05-23 | 阶段 4 修复 | 按 review 结果修复四个分册:章节解析确认只产生待确认知识草稿;作品资产当前只允许阅读、收藏和授权记录;改写自有事实保留 lineage、授权快照和召回状态;补 handoff 绑定字段、导出安全链路、账户安全链路、来源传播对象全集、管理员脱敏边界、系统智能体影响预览/灰度/复核、市场恢复重检、New-API 最小权限与幂等补偿、任务类型治理矩阵 | 修复完成后进入校验和用户确认;确认前不进入阶段 5 架构文档 | -| D-031 | 2026-05-23 | 阶段 4 引用迁移 | 用户确认删除旧流程导航页,活跃文档引用全部迁移到 `流程-01A/01B/02A/02B` 四个分册 | 删除旧导航页;四个分册成为唯一流程入口;本次只做引用迁移和导航归属调整,不重写产品、架构、专题、前端或后端设计内容 | -| D-032 | 2026-05-23 | 阶段 5 启动 | 用户确认进入架构阶段;阶段 5 只修改 `架构-01`、`架构-02`、`架构-04` 和本计划,承接阶段 1~4 已确认的六产品空间、系统保护节点、用户知识库、市场授权、handoff、来源 lineage、全书解析章节确认和 New-API 边界 | `架构-01/02/04` 进入重写;产品、流程、专题、前端和后端文档不在本阶段顺手修改 | -| D-033 | 2026-05-23 | 阶段 5 初稿 | 重写 `架构-01/02/04`:`架构-01` 改为六产品空间与 BC owner 的架构边界,`架构-02` 补齐用户知识库、市场资产、智能体槽位、handoff、来源 lineage、授权快照、导出和账户用量模型,`架构-04` 重写候选、知识草稿、全书解析章节审阅、知识库、智能体、市场授权、handoff、来源传播、导出下载、New-API 和审计状态机 | 阶段 5 初稿已完成并进入校验;待 review 和用户确认后再提交,不修改专题、前端或后端文档 | -| D-034 | 2026-05-23 | 阶段 5 review | 子代理 review 未发现 P0,但发现需要修复的 P1/P2:正文 Canonical 来源承载、New-API 凭据安全、智能体运行时权限包、导出包安全、个人中心模型、作品/章节/规划正式态生命周期、市场发布检查快照、BC 落地矩阵、handoff owner/precheck、授权快照与来源状态事件合同、导出 owner 拆分、New-API 执行链与归属链拆分、状态命名一致性 | 进入阶段 5 定点修复;只改 `架构-01/02/04` 和本计划,不修改产品、流程、专题、前端或后端文档 | -| D-035 | 2026-05-23 | 阶段 5 修复 | 按 review 结果修复 `架构-01/02/04`:补 BC 落地矩阵、关键 authority 表、Handoff owner 合同、导出 owner 拆分、New-API 凭据和执行/归属链边界、文件导出安全;补 Block Source Attribution、Parse/Chapter Review、Source Status Event、Authorization Snapshot、Agent Runtime Permission Envelope、发布检查快照、个人中心与导出模型;补 Work/Chapter/Planning 生命周期、章节审阅状态、发布检查状态、来源传播任务、导出下载和审计约束 | 阶段 5 review 修复完成,进入校验和用户确认;确认前不进入专题、前端或后端阶段 | -| D-036 | 2026-05-23 | 阶段 5 -> 6 | 用户确认提交阶段 5 并进入下一个阶段;阶段 5 架构文档纳入已确认基线,阶段 6 开始处理 `专题-*` 文档 | 阶段 6 只能承接阶段 1~5 已确认的产品、流程和架构结论;本次提交不修改专题、前端或后端文档 | -| D-037 | 2026-05-23 | 阶段 6 初稿修复 | 检查专题文档后确认不能跳过:`专题-01` 仍有接受候选同步确认知识草稿的旧口径,`专题-03` 夹带旧 Proposal、默认超级管理员和 doc/dev 门禁口径,`专题-04` 为空文件且承接质量门控/创作健康度,`专题-02` 需要从实现态对标改为取舍参考 | 阶段 6 只修改 `专题-01/02/03/04` 和本计划;不修改产品、流程、架构、前端或后端文档 | -| D-038 | 2026-05-23 | 阶段 6 修复 | 修复专题文档初稿:`专题-01` 改为接受候选只写正文和候选归档、不自动确认知识草稿,并补 Block Source Attribution;`专题-03` 重写为 AI 编排、运行时权限包、上下文组装、检索、风险路由、质量评测输入和 New-API 边界合同;`专题-04` 补齐质量门控、创作健康度、关键维度、有限重写、质量策略、来源变化和验收清单;`专题-02` 轻量刷新为竞品取舍参考,不再承载旧实现态口径 | 初稿修复完成后进入校验和 review;确认前不进入前端/后端阶段 | -| D-039 | 2026-05-23 | 阶段 6 review | 子代理严格 review 发现阶段 6 专题仍有 P0/P1 闭合缺口:Accept 对 revoked/recalled/blocked/owner_missing 等正文来源未硬阻断;市场作品资产 feature gate 和 `workAssetUsePrecheckId` 缺失;离线质量评估缺私有内容边界;接口样例过细、幂等不足、`contentOverride` 可能洗白来源;AI 编排和质量门控的候选状态、风险合并、New-API 脱敏、Source Status Event 传播和 Writing Health owner 需要统一 | 进入阶段 6 定点修复;仍只改 `专题-01/02/03/04` 和本计划,不修改产品、流程、架构、前端或后端文档 | -| D-040 | 2026-05-23 | 阶段 6 review 修复 | 按 review 结果修复专题文档:`专题-01` 收回到决策命令语义合同,补幂等、接受前置条件、作品资产预检、正文来源硬阻断、修改后合并 lineage 继承和失败矩阵;`专题-03` 补 Provisional Shadow Candidate、Tool Grant、Authorization Snapshot、检索结果合同、Candidate Decision Envelope、质量状态、New-API 脱敏和来源传播矩阵;`专题-04` 拆硬阻断/叙事关键/非关键维度,补决策优先级、离线评估私有内容边界、线上质量观测和下游承接;`专题-02` 去除旧实现态断言并改为目标取舍语言 | 阶段 6 review 修复完成,待复核确认;确认前不进入阶段 7 前端/后端文档 | -| D-041 | 2026-05-23 | 阶段 6 -> 7 | 用户要求提交阶段 6 并进入下个阶段;`专题-01/02/03/04` 和阶段计划纳入已确认基线,阶段 7 准备处理前端/后端文档 | 阶段 7 只能承接阶段 1~6 已确认的产品、流程、架构和专题结论;索引与内容映射仍属于阶段 8,不在阶段 7 顺手修改 | -| D-042 | 2026-05-23 | 阶段 7 工程基线 | 阶段 7 前后端工程口径调整为:后端主仓为 `muse-cloud/`,fork `YunaiV/yudao-cloud`;管理后台为 `muse-admin/`,fork `yudao-ui-admin-vben` 并使用 `apps/web-antd`,基于 Vue 3 + Vite + Ant Design Vue + TypeScript,接口走 `/admin-api/**`;用户端单独自研 `muse-studio/`,建议 Next.js / React / TypeScript,接口走 `/app-api/**`;设计文档后续可独立为 `muse-design-docs/` | 阶段 7 重写 `前端-*` / `后端-*` 必须以此为工程基线;保留 `system/infra/ai/member/pay/bpm/report/mp` 等 Yudao 能力口径,已裁剪 `mall/crm/erp/mes/iot` 和空壳 `yudao-ui`;`mp` 明确保留 | -| D-043 | 2026-05-23 | 阶段 7 初稿 | 按 D-042 重写阶段 7 前后端文档:前端拆为 `muse-admin/` 管理后台和独立 `muse-studio/` 用户端;后端主仓 `muse-cloud/` 拆为 Yudao 原生模块复用和 Muse `content/knowledge/ai/market/account` 业务模块;API 改为 `/admin-api/**` 与 `/app-api/**`;Schema 改为 Yudao fork 下的模块级业务 Schema,`后端-04a` 降级为旧 SQL 历史快照且禁止执行 | 阶段 7 初稿已覆盖 `前端-01/02/03`、`后端-01/02/03/04/04a/05` 和本计划;确认前不进入阶段 8,不修改 `00-文档大纲.md` 与 `内容映射表.md` | -| D-044 | 2026-05-23 | 阶段 7 review 修复 | 用户确认阶段 7 可拆分给 worker 并行修订;review 发现的 New-API owner、管理员私有内容边界、MetaSchema 投影兼容、planning owner API、用户知识库工作台依赖、Source/Authorization owner、保护节点注册表、handoff owner、Schema/API 缺口已按分册修复 | 阶段 7 review 修复已完成,待主代理复核和用户确认;阶段 8 索引与内容映射继续保持未修改,确认前不进入阶段 8 | -| D-045 | 2026-05-24 | 阶段 7 整体一致性修复 | 用户要求对整体设计文档中“不对、不一致、不闭环”的内容拆成独立可修复任务并用子代理修复;本轮按架构 owner、后端 Schema/API、产品/原型、流程/专题、前端分册并行修复,并由主代理补齐残留:作品资产当前只允许阅读/收藏/授权记录;MetaSchema 逻辑 owner 固定为 Admin/Governance;Agent/Tool Grant/Runtime Permission 不能由 AI runtime 自授权;Market 只发起来源侧 handoff,目标 owner 自己创建和消费 precheck;Parse Job / Chapter Parse Result 归 AI Orchestration;动态字段无跨 owner 泛写接口;Accept/Merge 必须消费或重算 Candidate Decision Envelope;SourceStatus / SourceEventType / SourceActionPolicy 统一包含 delisted/read_only 语义 | 受影响的产品、流程、架构、专题、前端、后端文档已定点修复;阶段 8 在 D-046 开始补索引;本轮修复待复核确认后再进入更完整的阶段 8 清理 | -| D-046 | 2026-05-24 | 阶段 8 工程仓库索引 | 用户确认开始补齐四仓工程架构索引;本次将 `muse-cloud/`、`muse-admin/`、`muse-studio/`、`muse-design-docs/` 写入 `00-文档大纲.md` 和 `内容映射表.md`,并同步 `前端-01`、`后端-02` 的仓库命名映射 | 阶段 8 仅刷新工程仓库索引和 owner 映射,不改产品功能边界、Schema、API 或状态机 | - -## 7. 阶段交付格式 - -每个阶段交付时,默认输出以下内容: - -1. 结论:当前阶段文档是否可以进入修改或是否已经收口。 -2. 本阶段修改范围:明确只改哪些文件。 -3. 已承接的前序结论:列出来自前序阶段的关键约束。 -4. 暂不处理的后续事项:列出需要后续阶段承接的内容。 -5. 冲突和待确认问题:只保留会影响决策的问题。 -6. 验证状态:说明已检查哪些引用、术语、边界或文档 owner。 - -## 8. 当前下一步 - -当前处于阶段 8 工程仓库索引补齐:阶段 7 整体一致性修复已完成但仍待复核确认;本轮先把四仓工程架构写入入口索引和映射表,并同步前后端工程文档的目标仓库命名。 - -下一步是统一复核:P0 是否已清空,Yudao 原生模块复用边界、`account` 与 `member` 的后续物理落地决策、Schema 与 API 的 owner 闭合、`muse-admin/` 与 `muse-studio/` 的入口边界、MetaSchema 与 Source/Authorization 横切模型落点、`后端-04a` 降级为历史 SQL 快照后是否需要在后续实现阶段重新生成模块 migration,以及阶段 8 的索引/映射文档是否还有其他过期引用需要清理。 - -阶段 8 已开始处理 `00-文档大纲.md` 与 `内容映射表.md`。本次只补四仓工程架构和仓库 owner 映射;其他索引、过期引用和内容映射清理仍需后续复核。 diff --git a/design-docs/临时-新产品形态设计与文档改造说明.md b/design-docs/临时-新产品形态设计与文档改造说明.md deleted file mode 100644 index 9b9b3c2d..00000000 --- a/design-docs/临时-新产品形态设计与文档改造说明.md +++ /dev/null @@ -1,691 +0,0 @@ -# 临时:新产品形态设计与文档改造说明 - -- 版本:v1 -- 更新日期:2026-05-10 -- 状态:临时过渡文档 -- 目标读者:产品 / 架构 / 前端 / 后端 / 文档维护者 -- 删除条件:当本文件中的产品形态与文档改造要求已经落入 `产品-*`、`流程-*`、`架构-*`、`前端-*`、`后端-*`、`doc/dev/*` 等正式文档后,删除本文件。 -- 边界说明:本文只定义“新产品形态”和“如何改造现有设计文档”。它不是长期 SSOT,不替代正式设计文档。 - -## 1. 新产品形态定义 - -### 1.1 产品定位修正 - -Muse 不应该被定义成“带 AI 能力的写作后台”,而应该定义成: - -> 面向长篇小说创作的双角色 AI 创作系统:管理员管理系统能力,普通用户在低认知负担的作品工作台里完成创作、规划、生成、知识治理和使用追踪。 - -这个定义包含两个核心变化: - -1. 产品不再只有“作者用户”一个视角,而是明确拆成 管理员(Admin) 与 普通用户(User) 两类角色。 -2. 普通用户看到的是创作工作台,不是系统后台;管理员看到的是系统控制台,不是作品创作界面。 - -### 1.2 角色与边界 - -| 角色 | 主要目标 | 不能混入的内容 | -|---|---|---| -| 管理员(Admin) | 管理系统能力、模型能力、智能体能力、系统知识和用户 | 不直接参与普通用户单个作品的创作决策 | -| 普通用户(User) | 管理自己的作品、写作、规划、生成、导入解析、导出交付、查看使用情况 | 不直接看到系统 Prompt、智能体配置、底层元数据配置和系统日志 | - -管理员负责“系统能做什么、怎么做、用什么做”。 - -普通用户负责“我的作品是什么、我要怎么写、哪些内容进入我的作品事实”。 - -### 1.3 管理员控制台 - -管理员控制台是系统能力配置后台,默认只对管理员开放。 - -| 模块 | 范围 | -|---|---| -| 元数据与创作模板管理 | MetaSchema / MetaField、作品模板、实体 / 关系 / 事件 / 叙事字段、继承、版本、字段是否 UI 可见、是否进入 AI 上下文 | -| 全局知识库与访问策略 | 全局资料集、写作方法、体裁规则、平台规范、默认绑定用户或用户组、是否可见、是否可检索、是否可用于生成 | -| AI Agent 配置 | 生成 Agent、提取 Agent、校验 Agent、规划 Agent、检索 Agent、启停、超时、重试、fallback | -| Prompt 与生成策略 | 系统 Prompt、任务 Prompt、体裁 Prompt、文风 Prompt、场景策略、版本管理、灰度、回滚 | -| New-API 用户与套餐同步 | 创建 Muse 用户时同步创建 New-API 用户;调用 New-API 接口修改用户分组、用户配额、用户套餐和绑定状态 | -| 上下文组装策略 | Layer 0-3、场景拼装、Token 预算、缓存策略、Watermark Overlay、上下文来源解释 | -| 检索与投影治理 | RAGFlow dataset、索引同步、projection_outbox、projection_watermarks、重建索引、Graph Query Provider / Neo4j 条件能力 | -| Pipeline 与任务治理 | 生成、提取、校验、风险路由、全书解析、任务重试、取消、重放、失败恢复 | -| 用户与权限管理 | 用户、角色、管理员权限、普通用户权限、资源访问边界、状态、封禁或删除流程 | -| 日志、审计与可观测性 | Muse 业务审计、任务失败、异常、后台作业状态、New-API 同步失败;模型消耗日志以 New-API 为准 | -| 质量评估与评测集 | 固定评测场景、固定数据集、固定评分标准、多模型评审、算法汇总评分、评测报告和回归对比 | - -New-API 已经负责模型供应商、模型路由、用户分组限流、Token 成本策略和消耗日志记录,Muse 管理员控制台不重复实现这些复杂控制。Muse 只负责和系统用户生命周期相关的同步动作,以及必要的分组、配额、套餐调整入口。 - -质量评估与评测集不是自由表单。它应该是固定场景、固定数据集、固定评分标准的评测系统。针对一次被测输出,系统使用多个大模型按同一评分标准评审,再用算法计算最终评分、置信度和风险结论。 - -首批评测场景至少包括: - -- 续写生成:是否承接前文、推进情节、保持角色目标。 -- 改写与文风:是否保留原意、贴合作品声音、避免同质化 AI 文风。 -- 角色一致性:角色声音、目标、动机、弧光是否稳定。 -- 世界事实一致性:实体属性、关系、规则、时间线是否冲突。 -- 场景推进:章节目标、场景节拍、冲突、结果和下一步钩子是否成立。 -- 张力与悬念:张力曲线、悬念维护、信息释放是否符合章节意图。 -- 知识抽取:实体、关系、事件、叙事状态提取是否准确、可追溯。 -- 冲突检测:风险标记、去重、消歧、冲突摘要是否可靠。 -- 全书解析:章节级汇总、批量确认边界、冲突分布是否可用。 -- 长程连载:连续 5-10 章后,世界保真度和叙事质量是否稳定。 - -管理员侧可以使用系统术语,例如 `MetaSchema`、`Agent`、`Prompt`、`Pipeline`、`Job`、`Knowledge Base`、`Evaluation Dataset`。 - -### 1.4 普通用户产品结构 - -普通用户登录后的默认入口应该是“我的作品”,而不是系统管理页。 - -这里需要明确:“我的作品”和“作品工作台”不是同一个产品空间。 - -- 我的作品:用户自己的作品列表和入口页。 -- 作品工作台:用户进入某一个作品后的创作空间。 - -普通用户侧建议拆成三块,并按职责消除重叠: - -| 区域 | 产品职责 | 不负责什么 | -|---|---|---| -| 我的作品 | 展示用户自己的作品列表;支持搜索、筛选、排序、最近编辑、作品状态、新建作品、打开作品、继续写作;可以提供“从文稿创建作品”的入口 | 不承载正文编辑、作品规划、知识治理、解析确认、导出配置等单作品深层流程 | -| 作品工作台 | 围绕单个作品完成写作、规划、生成、知识确认、一致性检查、导入解析、导出交付、生成记录查看 | 不承担跨作品列表管理,也不展示系统后台配置 | -| 个人中心 | 个人信息、Token 使用、生成记录总览、配额、套餐、偏好 | 不承担单个作品的创作决策 | - -如果用户在“我的作品”里点击“导入”或“导出”,产品语义也只是进入某个作品的导入 / 导出流程,不应该在列表页里展开完整操作。 - -普通用户不应该默认看到 `Schema / Proposal / Job / Pipeline / Agent / Prompt` 这些词。 - -### 1.5 单个作品的工作台结构 - -每个作品应该成为一个完整创作空间,而不是多个后台页面的拼接。 - -工作台还有优化空间:它不应该把“写作、规划、知识、检查、导入、导出、记录”平铺成一排后台导航,而应该默认让用户停留在写作流里,再按需要打开规划、知识和检查能力。 - -建议采用“默认写作台 + 少量工作区 + 上下文面板”的结构: - -| 工作区 | 作用 | 用户语言 | -|---|---|---| -| 写作台 | 正文编辑、章节导航、续写、改写、描写、候选卡、版本历史、AI 参考来源说明 | 继续写、改写、描写、插入、丢弃、查看参考 | -| 作品规划台 | 作品方向、世界设定、角色关系、章节大纲、文风检查;所有入口都支持 AI 生成、补全、整理和检查 | 规划、补全、检查、生成方案 | -| 知识与一致性 | 当前作品已确认知识、待确认知识、来源追溯、风险标记、一致性检查、问题定位 | 作品知识、待确认、冲突、定位修复 | -| 导入解析 | 上传旧文稿、导入状态、全书解析、章节确认、批量选择、一键确认全部可确认章节 | 导入、解析、确认章节 | -| 导出交付 | 导出正文、导出知识、选择范围、格式、导出记录 | 导出正文、导出设定、备份 | -| 记录与用量 | 单作品生成历史、任务历史、失败原因、Token 使用、成本 | 使用记录、生成记录、失败原因 | - -默认进入“写作台”。规划台是高质量小说生成的承重入口,但不应该盖过日常写作入口。 - -更具体的界面组织可以采用四区结构: - -- 左侧:章节、大纲、情节节拍、作品设定锚点。 -- 中间:当前章节正文编辑器。 -- 右侧:AI 候选、建议历史、待确认设定、任务反馈。 -- 下方或抽屉:故事圣经(Story Bible)、作品知识、上下文命中、风险说明。 - -高频 AI 操作应贴近光标和文本块(Block),用作者能理解的动作词,例如续写、改写、描写、头脑风暴、场景推进、换口吻、收紧、放慢。不要让用户为了写一段正文,先理解待审层(Shadow)、规范数据(Canonical)、提案(Proposal)、任务(Job)。 - -工作台交互原则: - -1. AI 辅助能力不单独做成一级工作区,而是出现在写作、规划、知识、导入解析等具体场景里。 -2. “检查”不应该变成强制审核关卡;它是用户主动查看的质量工具,问题可以定位到正文、规划或知识。 -3. 作品健康状态可以作为轻量提示存在,例如世界事实风险、叙事推进风险、风格漂移风险、解析待确认数量。 -4. 每次生成都应该能解释“本次参考了哪些作品知识、规划项和上下文”,但默认只展示摘要,避免增加认知负担。 - -### 1.6 作品规划台的高质量小说维度 - -高质量小说的关键维度都应该有创作规划入口,但不能做成后台表单,也不能把所有文学术语平铺成一堆导航。 - -本地文档和外部产品资料共同指向一个结论:长篇小说质量不是单点能力,而是“作品方向 -> 世界事实 -> 角色变化 -> 剧情结构 -> 场景推进 -> 文风表达 -> 质量检查”的分层系统。 - -整理依据: - -- 本地 `doc/dev/01-总体路线图与阶段依赖.md` 已经把能力拆成世界状态平面(World State Plane) 与叙事状态平面(Narrative State Plane)。 -- 本地 Sudowrite 逆向文档显示,故事圣经(Story Bible)至少拆成文风(Style)、梗概(Synopsis)、角色(Characters)、世界构建(Worldbuilding)、大纲(Outline),且大纲(Outline)、叙事视角(Point of View, POV)、时态(Tense)、场景(Scenes) 会影响生成。 -- Sudowrite 官方文档显示,故事圣经(Story Bible)用类型(Genre)、文风(Style)、梗概(Synopsis)、角色(Characters)、世界构建(Worldbuilding)、大纲(Outline)、场景(Scenes) 共同约束后续写作。 -- Novelcrafter 的知识库(Codex) / 计划视图(Plan) 设计也把角色、地点、物品、传说设定(Lore)、章节、场景、叙事视角(Point of View, POV)、支线(Subplot) 放在同一创作空间里联动。 -- 通用写作方法通常把小说拆成情节(Plot)、人物(Characters)、背景(Setting)、冲突(Conflict)、叙事视角(Point of View)、文风(Style)、主题(Theme) 等核心要素。 - -外部参考链接: - -- [Sudowrite: 什么是故事圣经(Story Bible)](https://docs.sudowrite.com/using-sudowrite/1ow1qkGqof9rtcyGnrWUBS/what-is-story-bible/jmWepHcQdJetNrE991fjJC) -- [Novelcrafter](https://www.novelcrafter.com/) - -项目内补充设计思路: - -| 来源 | 对高质量生成有意义的设计思路 | 产品归类 | -|---|---|---| -| `doc/dev/04-S2-上下文组装引擎.md` | 上下文组装引擎(Context Assembly Engine)已经把作品、实体、章节、目标文本拆成四层上下文,并要求按场景注入叙事弧线、悬念、角色目标、章节意图和张力 | 用户侧需要看到“作品规划台”入口;具体组装、令牌预算(Token Budget)、缓存和截断属于系统支撑能力 | -| `doc/dev/04-S2-上下文组装引擎.md` | 元结构定义(MetaSchema)按领域(Domain)、范围(Scope)、目标类型(Target Type)拆分,包含世界(World)和叙事(Narrative)两类领域 | 用户侧应形成“世界设定 / 角色关系 / 章节大纲”的规划入口;字段继承和运行态载体属于后台能力 | -| `doc/dev/06-S4-统一Pipeline.md` | 生成流水线(Generation Pipeline)和文本块提取流水线(BlockExtraction Pipeline)要求从正文和 AI 建议里持续提取世界要素与叙事要素,并通过风险标记(Risk Markers)提示问题 | 用户侧需要“知识与一致性”和“检查”入口;流水线步骤、重试、风险路由属于系统支撑能力 | -| `doc/dev/07-S5-全链路闭环验证.md` | 质量评估明确拆成世界保真度(World Fidelity)与叙事质量(Narrative Quality),叙事质量包含弧线推进、悬念管理、张力曲线、角色声音、情节推进和段落节奏 | 这些维度应反向约束作品规划台和检查项;具体评分、长程评测(Long-horizon Evaluation)、消融评测(Ablation Evaluation)属于管理员和系统评测能力 | -| `doc/design-docs/后端-04-统一数据库Schema-v1.md` | 叙事状态(Narrative State)有真实运行态载体,保存弧线进度、章节意图、角色目标和动机 | 用户侧不直接操作底层载体,但需要能在规划台看到可理解的角色弧光、章节目标和剧情推进状态 | -| `doc/design-docs/架构-02-核心数据结构与双轨模型.md`、`doc/design-docs/后端-04-统一数据库Schema-v1.md` | 当前稳定内容骨架是作品(Work)、章节(Chapter)、文本块(Block),叙事状态只按作品、章节、实体三个范围保存 | 作品规划台需要作品结构、章节计划和情节节拍;不应先把小说场景(Scene)贸然升成独立一级数据模型 | -| `doc/design-docs/产品-02-核心功能与交互边界.md` | 一致性检查(Consistency Check)被定义为主动查看工具,不是强制审核关卡 | 用户侧应把检查做成轻量工具,不应把每次写作都变成审批流程 | - -因此,项目内文档还能补充的高质量生成维度不是“再加更多表单”,而是把已有底层设计翻译成用户可理解的规划和检查入口: - -- 上下文命中与可解释性:生成前后说明参考了哪些作品知识、规划项和上下文;这是系统透明度,不是用户手填维度。 -- 作品结构与进度:章节顺序、章节摘要、当前字数、完成状态和近期目标要能被规划和检查;这是用户规划维度。 -- 章节意图与场景节拍:每章每场要有目标、冲突、结果和下一步钩子;这是用户规划维度。 -- 长程记忆与演变轨迹:角色、关系、事件和世界规则要能按章节演变;这是世界设定与角色关系的一部分。 -- 事件时间线与因果链:重大事件需要绑定章节、参与角色、因果效果和后续影响;这是世界状态与剧情结构之间的桥。 -- 用户编辑后的再提取:用户改正文后,系统要重新提取并让旧草稿失效;这是质量闭环,不是用户一级入口。 -- 风险标记与质量闸门:实体冲突、叙事停滞、悬念遗忘、风格漂移要被提示;这是检查入口。 -- 评测闭环:长程评测、消融评测和多模型评审用于系统改进;普通用户侧只看到必要的质量提示。 - -维度是否应该进入 Muse,要满足至少一个条件: - -1. 会影响后续生成上下文。 -2. 能从正文或规划中提取、保存、追踪或校验。 -3. 用户确实需要在创作时做选择,而不是只给系统内部评分。 -4. 能帮助判断“故事是否更好看”,而不只是“设定是否不打架”。 - -每个规划入口都必须同时支持: - -- 用户手写 -- AI 生成 -- AI 补全 -- AI 整理 -- AI 检查 -- AI 给多组选项 -- 从正文提取 -- 和后续生成上下文绑定 - -建议采用下面的分层结构: - -| 层级 | 普通用户看到的入口 | 子维度 | 意义 | AI 辅助方式 | -|---|---|---|---|---| -| 作品方向 | 作品设定 | 题材、类型、主题、读者承诺、基调、禁区、结局方向 | 给全书生成一个稳定北极星,避免 AI 只会局部续写 | 生成故事前提、整理梗概、检查偏题、给多版方向 | -| 作品结构 | 章节与进度 | 章节顺序、章节摘要、当前字数、完成状态、近期目标、断点位置 | 给长期创作一个可管理骨架,避免写作流和规划流脱节 | 生成章节摘要、整理进度、提示断点、建议下一章目标 | -| 世界与实体 | 角色 / 地点 / 物品 / 组织 / 规则 | 实体属性、关系、时间线、世界规则、能力体系、重要物品 | 解决“世界是什么样的”,保证长篇一致性 | 从正文提取、补全属性、发现冲突、生成关系图 | -| 角色与关系 | 角色弧光 | 目标、动机、弱点、秘密、变化阶段、关系变化、对白特征 | 解决“人物为什么行动、如何变化”,避免人物工具化 | 生成角色弧线、检查章节推进、提示关系停滞 | -| 事件与时间线 | 时间线 / 因果链 | 重大事件、发生章节、参与角色、前因后果、长期影响 | 把世界状态和剧情推进连起来,避免事件只发生一次就被遗忘 | 从正文提取事件、整理时间线、检查因果断裂、提示后续影响 | -| 剧情结构 | 大纲 / 主线 / 支线 | 卷、幕、章节、转折点、支线、伏笔与回收 | 解决“故事往哪里走”,避免中段散掉 | 生成大纲、补齐缺口、检查主线偏移、追踪伏笔兑现 | -| 章节叙事规划 | 章节目标 / 情节节拍 | 章节意图、叙事视角、时态、地点、出场人物、冲突、结果、下一步钩子 | 这是生成正文前最关键的近程控制面,避免原地打转 | 生成情节节拍、检查目标是否完成、建议推进动作 | -| 张力与节奏 | 冲突 / 悬念 / 节奏 | 利害关系、阻力、反转、悬念问题、信息释放、高潮密度 | 解决“读者为什么继续看”,但不应做成独立大表单 | 评估张力不足、建议升级冲突、检查章节节奏 | -| 叙事策略 | 叙事视角 | 叙事视角、时态、叙事距离、多视角切换、信息遮蔽 | 保证讲述方式一致,避免 AI 生成时口吻和视角漂移 | 设置章节策略、检查视角偏移、提示信息泄露 | -| 文风与文本质量 | 文风 | 作者声音、句式、节奏、描写密度、对白密度、禁用风格 | 解决“写出来像不像这本书”,承接文风(Style) / 匹配我的文风(Match My Style)类能力 | 分析样本、生成风格说明、检查风格偏移、给改写建议 | -| 质量检查 | 一致性检查 | 实体一致性、大纲一致性、细纲一致性、进度推进、风格漂移、伏笔遗漏 | 这是检查层,不是用户手填层;把上面各层合成可操作问题 | 自动扫描、列出风险、定位正文、给修复方案 | - -产品上不建议把每一行都做成一级导航。普通用户侧可以收敛为 5 个一级入口: - -1. 作品设定:作品方向、主题、题材、读者承诺。 -2. 章节大纲:作品结构、章节进度、主线、支线、伏笔。 -3. 世界设定:实体、关系、规则、事件、时间线。 -4. 角色关系:角色档案、角色弧光、关系变化。 -5. 文风检查:文风、叙事策略、张力节奏、质量诊断。 - -原来的“文笔 / 文风”应合并为“文风与文本质量”;“大纲一致性 / 细纲一致性 / 进度推进”应分别落到剧情结构、章节叙事规划和质量检查;“张力”保留,但作为跨大纲和章节的检查维度,不单独做成重表单。 - -当前正式模型更稳定的结构是作品、章节、文本块和叙事状态,不建议在这个阶段把小说场景(Scene)做成独立一级模型。产品上可以使用“情节节拍 / 场景推进”这样的作者语言,但落文档时应先归入章节叙事规划,后续确认需要独立模型时再扩展。 - -这些入口共同组成 Muse 的“作品规划台”,不是独立的系统管理模块。 - -### 1.7 知识库分层 - -知识库必须按归属拆成两大类,避免普通用户误操作系统级知识,也避免把单个作品的知识和全局资料混在一起。 - -| 类型 | 归属 | 创建与维护 | 普通用户可见 / 可用策略 | 用途 | -|---|---|---|---|---| -| 全局知识库(Global Knowledge Base) | 管理员 | 管理员导入、维护、授权、停用 | 管理员控制谁默认绑定、谁可见、谁可检索、谁可用于生成、用户是否可解绑 | 写作方法、体裁规则、平台规范、公共资料、可授权资料集 | -| 局域知识库(Local Knowledge Base) | 单个作品 | 系统根据用户作品自动创建、自动维护;来自正文、导入解析、规划、用户确认 | 用户只看到元数据允许展示的实体、关系、字段和摘要;系统内部可保留更多生成上下文 | 当前作品的正式事实、叙事状态、实体关系、来源追溯、生成上下文 | - -全局知识库是管理员能力,不是用户作品内容。管理员需要能配置: - -- 默认绑定用户或用户组。 -- 用户是否能在 UI 里看见该知识库。 -- 用户是否能主动检索该知识库。 -- 用户是否能在生成时使用该知识库。 -- 用户是否能绑定、解绑或只读使用。 -- 是否参与默认上下文、仅按需检索,还是完全不可用。 - -局域知识库是作品能力,系统按作品自动创建,不要求用户手动建库。用户可见的是“作品知识 / 角色设定 / 世界设定 / 引用来源”,不是数据库或索引后台。 - -局域知识库需要有“内部完整知识”和“用户可见投影”两个层面: - -- 系统内部完整知识:用于生成、检查、检索、审计,包含 AI 提取到但不适合直接展示给用户的中间事实。 -- 用户可见投影:只展示元数据允许展示的实体类型、字段、关系和摘要。 - -元数据对象需要补充可见性字段。建议至少区分: - -| 字段 | 含义 | -|---|---| -| `uiVisible` | 是否在普通用户 UI 展示 | -| `aiContext` | 是否进入 AI 上下文 | -| `userEditable` | 普通用户是否可编辑 | -| `userSearchable` | 普通用户是否可检索 | -| `exportable` | 是否允许随作品导出 | - -抽取出的实体、关系和字段根据所属元数据的 `uiVisible` 决定是否出现在普通用户 UI。`uiVisible=false` 不代表不能参与 AI 生成;是否进入 AI 上下文由 `aiContext` 单独控制。 - -### 1.8 普通用户侧术语替换 - -底层技术术语可以保留在架构和后端文档,但普通用户产品文案必须换成创作语言。 - -| 系统术语 | 普通用户语言 | -|---|---| -| MetaSchema | 创作规则 / 作品模板 / 字段模板 | -| Agent | AI 助手 / AI 能力 | -| Prompt | 指令模板 / 生成规则 | -| Proposal | 待确认知识 / 知识变更 | -| Shadow | AI 候选 / 待确认内容 | -| Canonical | 已确认内容 / 正式设定 | -| Job | 生成记录 / 处理记录 | -| Narrative State | 叙事状态 / 剧情推进状态 | -| Pipeline | 处理流程 / 生成流程 | - -### 1.9 必须继续守住的底层边界 - -新产品形态不能牺牲现有设计里已经定下的硬边界: - -- AI 输出必须先进待审层(Shadow),不能绕过用户直接写正文或知识库。 -- 用户确认后才进入规范数据(Canonical)。 -- 作品知识必须可追溯来源。 -- 修改后合并必须让旧知识草稿失效,并重新提取。 -- 全书解析必须保留章节确认边界;可以提供“一键确认全部可确认章节”,但落库、校验、失败回滚必须按 `parse_job_id + chapter_id` 逐章隔离,不能变成一个不可解释的全局写入。 -- 未确认草稿不得进入正式检索。 -- 普通用户低认知负担不等于系统黑盒;需要在必要时解释“本次 AI 参考了什么”。 - -## 2. 现有文档修改方式 - -### 2.1 修改总原则 - -这次不是给现有文档补几段说明,而是要把产品形态重新落进正式文档。 - -修改时遵守: - -1. `产品-*` 负责用户视角与产品边界。 -2. `流程-*` 负责用户步骤和系统处理链路。 -3. `架构-*` 负责角色边界、数据边界、上下文边界和不变式。 -4. `前端-*` 负责信息架构、交互结构和前端状态拥有。 -5. `后端-*` 负责权限、接口、数据结构、后台能力和系统管理能力。 -6. `doc/dev/*` 负责实现路线图和阶段验收。 -7. 单一归属不能被打破:表结构以 `后端-04` 为准,接口以 `后端-05` 为准,状态机以 `架构-04` 为准,ADR 以 `架构-03` 为准,模块职责以 `后端-02` 为准。 -8. 本临时文档只做改造依据,改完删除。 - -修改时禁止: - -- 把同一个字段、状态、接口、表结构在多个正式文档里重复定义。 -- 因为产品入口增加,就为每个 UI 面板新增一张表。 -- 把 IA 阶段写成必须先完成的工程实现大阶段;IA 只代表产品形态与文档对齐 gate。 -- 把管理员配置能力塞进普通用户工作台,或把普通用户创作流程写成后台管理流程。 - -### 2.2 推荐修改顺序 - -1. 先改产品文档:`产品-01`、`产品-02`、`产品-03`。 -2. 再改流程文档:`流程-01A/01B`、`流程-02A/02B`。 -3. 再改架构文档:`架构-01`、`架构-02`、`架构-03`、`架构-04`。 -4. 再改前端文档:`前端-01`、`前端-02`、`前端-03`。 -5. 再改后端与 API 文档:`后端-01`、`后端-02`、`后端-03`、`后端-04`、`后端-05`;如果 `后端-04` 的目标表结构变化,再同步检查 `后端-04a-完整建表SQL.sql` 是否需要更新。 -6. 再改 dev 路线图:`doc/dev/00` 到 `doc/dev/09`,只把新产品形态作为阶段影响面和验收 gate 纳入,不重排已有 S0-S5 的技术依赖。 -7. 最后改入口与映射:`00-文档大纲.md`、`内容映射表.md`。 -8. 全部正式文档对齐后,删除本文件。 - -### 2.3 产品文档修改要求 - -#### `产品-01-产品定位与核心价值.md` - -需要修改: - -- 一句话定义改成“双角色 AI 创作系统”。 -- 目标用户拆成 管理员(Admin) 与 普通用户(User)。 -- 核心价值新增: - - 管理员可控的系统能力 - - 普通用户低认知负担的创作工作台 - - 高质量小说规划入口 -- 非目标新增: - - 普通用户直接操作系统后台 - - 把作品规划台做成知识管理后台 - - 只做 prose 生成而不管叙事质量 - -#### `产品-02-核心功能与交互边界.md` - -需要修改: - -- 功能清单按角色拆分: - - 管理员控制台 - - 普通用户作品工作台 - - 普通用户个人中心 -- 普通用户功能改为用户语言: - - 我的作品(作品列表) - - 写作台 - - 作品规划台 - - 知识与一致性 - - 导入解析 - - 导出交付 - - 记录与用量 -- 增加“普通用户不可见系统概念”边界。 -- 明确“我的作品”只负责作品列表和入口,不负责单作品深层创作流程。 -- 增加“作品规划台分层维度”,避免把高质量小说能力做成平铺表单。 -- 增加“全局知识库 / 局域知识库”产品边界。 -- 保留 Shadow / Canonical / Source Snapshot 等底层边界,但表达为用户可理解反馈。 - -#### `产品-03-用户旅程与操作流程.md` - -需要修改: - -- 拆成两类旅程: - - 管理员配置系统能力 - - 普通用户创作作品 -- 普通用户默认旅程改为: - 1. 登录进入我的作品 - 2. 新建或导入作品 - 3. 进入写作台 - 4. 在需要时进入作品规划台 - 5. 用 AI 生成 / 改写 / 描写 / 检查 - 6. 确认候选与知识 - 7. 检索作品知识或被授权的全局知识库 - 8. 导出或继续创作 -- 管理员旅程不进入单个作品写作,只配置系统能力。 - -### 2.4 流程文档修改要求 - -#### `流程-01A-管理员操作流程(操作视角).md` / `流程-01B-普通用户操作流程(操作视角).md` - -需要修改: - -- 只写普通用户视角,不承载管理员后台配置流程。 -- 普通用户流程第一屏必须是“我的作品”,不是系统后台。 -- 单个作品流程要体现: - - 写作台 - - 作品规划台 - - 知识与一致性 - - 导入解析 - - 导出交付 - - 记录与用量 -- 作品规划台每个规划入口都要支持 AI 辅助。 -- 全书解析要支持逐章确认、批量选择和一键确认全部可确认章节;系统处理边界仍按章节隔离。 -- 保留候选卡、确认、修改后合并、丢弃当前建议等已有生命周期语义。 - -#### `流程-02A-管理员系统处理流程(系统视角).md` / `流程-02B-普通用户系统处理流程(系统视角).md` - -需要修改: - -- 增加管理员配置链路: - - 元数据配置 - - 智能体配置 - - Prompt 配置 - - 全局知识库配置 - - 全局知识库访问策略配置 - - New-API 用户 / 分组 / 配额 / 套餐同步配置 - - 上下文组装策略配置 - - Pipeline 与任务治理配置 - - 质量评估与评测集配置 -- 增加普通用户作品链路: - - 作品写作链路 - - 作品规划链路 - - 作品知识确认链路 - - 全局知识库授权使用链路 - - 局域知识库自动维护链路 - - 使用记录链路 -- 明确管理员配置影响普通用户生成链路,但普通用户不能直接修改系统配置。 -- 管理员链路在这里写系统处理关系,不写成普通用户操作流程。 - -### 2.5 架构文档修改要求 - -#### `架构-01-系统全貌与边界上下文.md` - -需要修改: - -- 系统边界改为双角色边界。 -- 增加 Admin Console 与 User Workspace 两个上层入口。 -- BC 划分需要覆盖: - - Admin BC:系统模板、Prompt、Agent、用户、New-API 同步、全局知识库、访问策略、评测集 - - Content BC:作品、章节、文本块 - - Knowledge BC:全局知识库、局域知识库、用户可见投影 - - AI BC:生成、提取、校验、任务、候选 - - Usage / Audit BC:Token 使用、生成记录、系统日志 -- 明确全局知识库、局域知识库、用户可见投影的边界。 - -#### `架构-02-核心数据结构与双轨模型.md` - -需要修改: - -- 在模型层区分: - - 系统级模板与配置 - - 用户级作品数据 - - 作品级知识数据 - - 全局知识库与访问策略 - - 局域知识库与用户可见投影 -- 普通用户侧“创作规则 / 作品模板”映射到底层 MetaSchema。 -- 作品规划台中的作品方向、作品结构、世界实体、事件时间线、角色弧光、剧情结构、章节叙事规划、张力节奏、叙事策略、文风与质量检查,需要分别落到 World State / Narrative State / MetaSchema 或相关模型边界。 -- 当前阶段不要把小说场景(Scene)升成独立一级模型;产品语言可以写“情节节拍 / 场景推进”,模型归入章节叙事规划。 -- 元数据对象需要支持 `uiVisible`、`aiContext`、`userEditable`、`userSearchable`、`exportable` 等控制字段。 -- 保留 Shadow / Canonical / Archive 三层模型。 - -#### `架构-03-关键决策与原则(ADR).md` - -需要修改: - -- 如果新增或改变下面任一架构决策,必须补 ADR,而不是只在产品或后端文档里散写: - - 管理员控制台成为独立系统入口。 - - New-API 职责边界:New-API 管模型供应商、路由、分组、限流、成本和消耗日志;Muse 只同步用户、分组、配额和套餐。 - - 全局知识库 / 局域知识库分层。 - - 作品规划台只映射到已有 MetaSchema / Narrative State / Knowledge 模型,不为每个 UI 面板新增孤立模型。 - - 小说场景(Scene)暂不作为独立一级模型。 - -#### `架构-04-状态机与约束清单.md` - -需要修改: - -- 增加系统配置类对象的状态或版本约束: - - Prompt 版本 - - Agent 配置版本 - - 全局知识库版本 - - 全局知识库访问策略版本 - - New-API 用户同步配置版本 - - 评测集版本 -- 增加全局知识库的启用 / 停用 / 授权边界。 -- 增加局域知识库自动维护、用户可见投影和元数据可见性变更的约束。 -- 普通用户作品知识确认仍沿用现有 Shadow -> Canonical 生命周期。 - -### 2.6 前端文档修改要求 - -#### `前端-01-工程结构与核心依赖.md` - -需要修改: - -- 前端信息架构拆成: - - Admin Console - - User Workspace - - Personal Center -- 路由和目录建议按角色边界组织,避免管理员页面和作品工作台混在一起。 - -#### `前端-02-编辑器与影子层交互.md` - -需要修改: - -- 编辑器不再只是正文 + Shadow Panel,而是作品写作台。 -- 引入右侧候选卡流和生成历史。 -- 引入作品规划台与写作台的互相跳转。 -- 普通用户看到“AI 候选 / 待确认知识 / 已确认设定”,不直接看到 Shadow / Proposal / Canonical。 -- 仍然保持 revision、冲突恢复、修改后合并、来源快照拦截。 - -#### `前端-03-元引擎与动态表单.md` - -需要修改: - -- 区分管理员配置 MetaSchema 与普通用户填写作品规划。 -- 管理员侧显示 `domain + scope + targetType` 等结构。 -- 普通用户侧显示为作品设定、章节大纲、世界设定、角色关系、文风检查等创作语言。 -- 所有规划入口都应该支持 AI 生成、补全、整理、检查。 -- 前端需要按元数据的 `uiVisible` 决定普通用户能看到哪些实体、字段和关系。 - -### 2.7 后端与 API 文档修改要求 - -#### `后端-01-领域模型与聚合设计.md` - -需要修改: - -- 增加或确认管理员配置相关聚合。这里先定义领域职责,不等于每一项都必须新增独立表: - - SystemPrompt / PromptVersion - - AgentConfig - - NewApiUserBinding - - NewApiPlanMapping - - GlobalKnowledgeBase - - GlobalKnowledgeAccessPolicy - - LocalKnowledgeBase - - KnowledgeVisibilityPolicy - - EvaluationDataset - - EvaluationRun - - UsageLog -- 区分系统级配置、用户级数据、作品级数据、全局知识库和局域知识库。 - -#### `后端-02-工程结构与模块职责.md` - -需要修改: - -- 重新检查模块职责是否覆盖新产品形态: - - `muse-admin`:管理员控制台、元数据、Prompt / Agent 配置、全局知识库授权、用户与 New-API 同步、评测集管理。 - - `muse-content`:作品、章节、文本块、导入导出、作品工作台所需内容能力。 - - `muse-knowledge`:全局知识库、局域知识库、作品知识、用户可见投影、知识检索边界。 - - `muse-ai`:上下文组装、生成 / 提取 / 校验编排、风险标记、任务状态,不承载模型供应商和成本路由。 - - `muse-shared`:跨模块基础类型、错误、领域事件、权限上下文。 -- 明确 New-API 的模型供应商、路由、成本、消耗日志不迁入 Muse 模块。 -- 明确跨模块调用仍走公开接口 / Facade,不因为管理员控制台增加就允许直接穿透 Repository。 - -#### `后端-03-关键流程实现与接口契约.md` - -需要修改: - -- 增加管理员配置变更如何影响生成链路。 -- 增加用户作品工作台所需后台能力: - - 作品规划数据保存 - - AI 辅助规划生成 - - 全局知识库授权检索 - - 局域知识库自动维护 - - 用户可见投影生成 - - 用户使用日志 -- 保持外部 AI 调用不进入正文合并主事务。 - -#### `后端-04-统一数据库Schema-v1.md` - -需要修改: - -- 按“复用 / 扩展 / 新增 / 待确认”四类整理表结构,避免过度建模: - -| 分类 | 对象 | 处理原则 | -|---|---|---| -| 优先复用 | `meta_schemas`、`narrative_states`、`knowledge_entities`、`knowledge_relations`、`generation_jobs`、`extraction_jobs`、`audit_logs` | 作品规划台、叙事状态、知识确认和任务记录优先复用这些目标模型 | -| 扩展字段 | 元数据可见性字段、知识库访问策略字段、任务来源和审计字段 | 只在已有模型确实承载不了时新增字段 | -| 倾向新增 | `prompt_versions`、`agent_configs`、`newapi_user_bindings`、`newapi_plan_mappings`、`global_knowledge_bases`、`global_knowledge_access_policies`、`evaluation_datasets`、`evaluation_runs` | 这些属于系统级配置或评测能力,适合独立建模,但仍需在 `后端-01` 先确认聚合边界 | -| 待确认 | `local_knowledge_bases`、`usage_logs` | 局域知识库可能由 `work_id` + 现有知识表隐式表达;Token 消耗日志以 New-API 为准,Muse 是否落本地汇总表需要再确认 | - -- 增加元数据可见性控制字段,例如 `ui_visible`、`ai_context`、`user_editable`、`user_searchable`、`exportable`。 -- 增加作品规划台需要的结构,优先复用 MetaSchema / narrative_states / knowledge_entities,不要为每个 UI 面板硬建孤立表。 -- 如果 `后端-04` 目标表结构更新,`后端-04a-完整建表SQL.sql` 必须同步检查;但不要先改 SQL 再反推设计文档。 - -#### `后端-05-统一API契约-v1.md` - -需要修改: - -- API 分组拆成: - - Admin APIs - - User Workspace APIs - - Personal Center APIs -- Admin APIs 包含元数据、Prompt、Agent、全局知识库、访问策略、New-API 用户同步、评测集、用户、系统日志。 -- User Workspace APIs 包含作品、写作、规划、局域知识库、全局知识库授权检索、导入解析、导出交付、生成候选。 -- Personal Center APIs 包含个人信息、Token 使用、生成记录。 -- 权限模型必须明确普通用户不能访问管理员接口。 - -### 2.8 dev 路线图修改要求 - -#### `doc/dev/00-项目真实现状与目标差距.md` - -需要修改: - -- 增加产品形态差距: - - 当前更像系统后台 - - 缺管理员控制台明确边界 - - 缺普通用户作品工作台信息架构 - - 缺作品规划台作为高质量小说生成入口 - - 缺全局知识库 / 局域知识库边界 - -#### `doc/dev/01-总体路线图与阶段依赖.md` - -需要修改: - -- 阶段路线里加入产品形态与文档对齐 gate。 -- 建议新增前置 gate: - - IA-0:角色边界与信息架构重构 - - IA-1:普通用户作品工作台 - - IA-2:管理员控制台 - - IA-3:作品规划台和高质量小说维度 -- IA-0 到 IA-3 是产品形态 / 信息架构 / 文档对齐 gate,不是要求在 S1 前完成全部工程实现。 -- 不要直接把所有管理员能力和作品规划能力塞进 S4/S5;S0-S5 仍保持 AI core 技术依赖链,只补角色影响面和验收口径。 - -#### `doc/dev/02` 到 `doc/dev/09` - -需要修改: - -- 每个阶段增加“管理员侧 / 普通用户侧 / 个人中心”影响面。 -- 验收口径要区分: - - 系统能力是否可配置 - - 普通用户是否低认知负担 - - 作品规划维度是否能被 AI 辅助 - - 高质量小说维度是否进入生成上下文或检查链路 - - 全局知识库授权和局域知识库自动维护是否按边界生效 - -### 2.9 入口与映射文档修改要求 - -#### `00-文档大纲.md` - -需要修改: - -- 增加“角色视角导航”: - - 管理员控制台 - - 普通用户工作台 - - 个人中心 -- 明确本临时文档删除后,正式归属由 `产品-* / 架构-* / 流程-* / 前后端文档` 承担。 - -#### `内容映射表.md` - -需要修改: - -- 增加新产品形态内容归属: - - 管理员控制台 -> 产品-02 / 架构-01 / 后端-05 / 前端-01 - - 我的作品 -> 产品-02 / 产品-03 / 流程-01B / 前端-01 - - 作品工作台 -> 产品-03 / 流程-01B / 前端-02 - - 作品规划台 -> 产品-02 / 产品-03 / 架构-02 / 前端-03 - - 知识与一致性 -> 产品-02 / 流程-01B / 架构-02 / 前端-02 / 后端-05 - - 全局知识库 / 局域知识库 -> 架构-01 / 架构-02 / 后端-04 / 后端-05 - - 使用日志 -> 产品-02 / 后端-05 / 前端-01 - -### 2.10 Sudowrite 对标文档修改要求 - -#### `专题-02-Sudowrite对标与Muse产品取舍.md` - -需要修改: - -- 保留“Sudowrite 强在作者前台体验,Muse 强在长期可控”的判断。 -- 增加新结论: - - Muse 借鉴 Sudowrite 的不是外观,而是作品工作台、候选卡流、故事圣经(Story Bible)、章节大纲 / 情节节拍(Outline / Beats)、风格入口。 - - Muse 要把这些能力放进普通用户作品工作台和作品规划台。 - - 管理员控制台是 Muse 区别于 Sudowrite 的系统可控能力。 -- 不要把插件市场列为当前 P0;插件和自定义工作流应放到系统成熟后的扩展能力。 - -### 2.11 横切风险与验收要求 - -正式文档改造时,下面这些风险不能只散落在单个文档里,必须在相关产品、架构、后端和前端文档中形成闭环: - -| 风险 | 必须写清的内容 | -|---|---| -| 权限边界 | 普通用户不能访问管理员接口;管理员不直接进入普通用户单个作品创作决策 | -| 配置版本 | Prompt、Agent、全局知识库、访问策略、评测集必须有版本、启停、回滚或至少变更记录 | -| New-API 同步 | Muse 创建用户时同步 New-API 用户;修改分组、配额、套餐必须幂等;失败要可重试和可审计 | -| 知识库授权 | 全局知识库默认不可越权使用;局域知识库只归属单个作品;用户可见投影由元数据控制 | -| 低认知负担 | 普通用户界面使用创作语言,不把 Schema / Pipeline / Agent / Prompt 暴露成默认导航 | -| 高质量生成 | 作品规划维度必须能进入上下文、提取、校验或检查链路;不能只做静态表单 | -| 数据一致性 | Shadow -> Canonical、Source Snapshot、章节级确认、修改后重新提取等既有硬边界不能被产品改造冲掉 | - -### 2.12 完成判定 - -只有满足下面条件,才能删除本文件: - -1. `产品-01` 已写清 Muse 是双角色 AI 创作系统。 -2. `产品-02` 已写清管理员控制台、普通用户作品工作台、个人中心的功能边界。 -3. `产品-03` 已写清管理员旅程与普通用户作品创作旅程。 -4. `流程-01A/01B` 已写清管理员操作和普通用户从“我的作品”进入单个作品工作台的流程。 -5. `流程-02A/02B` 已写清管理员配置如何影响普通用户生成链路,以及普通用户侧系统处理链路。 -6. `架构-01` 已写清 Admin Console / User Workspace / Personal Center 的系统边界。 -7. `架构-02` 已写清系统级配置、全局知识库、局域知识库、用户可见投影和作品规划数据的模型边界。 -8. `架构-03` 已补充必要 ADR,或明确本次没有新增 ADR。 -9. `前端-*` 已写清角色分区与普通用户低认知负担的信息架构。 -10. `后端-*` 已写清权限、接口、表结构、模块职责和配置链路。 -11. `doc/dev/*` 已把新产品形态作为路线图影响面与验收 gate 纳入,而不是误写成全新工程大阶段。 -12. `00-文档大纲.md` 和 `内容映射表.md` 已更新正式归属。 - -删除前必须再做一次全文检索,确认没有正式文档仍只描述单一作者视角,或把管理员配置能力混在普通用户作品工作台里。 diff --git a/design-docs/内容映射表.md b/design-docs/内容映射表.md index 22861ca7..dc54558b 100644 --- a/design-docs/内容映射表.md +++ b/design-docs/内容映射表.md @@ -57,7 +57,7 @@ | `muse-cloud/` | 后端主仓;fork `YunaiV/yudao-cloud`,承接 Yudao 平台能力和 Muse 业务模块 | `后端-01` / `后端-02` / `后端-03` / `后端-04` / `后端-05` | 以 `后端-02` 的模块 owner 和 `后端-05` 的 API 前缀为落地边界;旧 SQL 只能作为历史快照 | | `muse-admin/` | 管理端;fork `yudao-ui-admin-vben`,承接管理员控制台 | `产品-02B` / `流程-01A` / `流程-02A` / `前端-01` / `后端-05` | 只调用 `/admin-api/**`;复用 Vben 的菜单、权限、表格和表单体系,不复用用户端写作工作台 | | `muse-studio/` | 自研用户端;推荐 Next.js / React / TypeScript | `产品-02C` / `产品-02D` / `产品-02E` / `产品-02F` / `产品-02G` / `前端-01` / `前端-02` / `前端-03` | 只调用 `/app-api/**`;承载普通用户创作、资产、市场和个人中心,不复用 Vben 后台 layout | -| `muse-design-docs/` | 当前 `design-docs/` 独立化后的产品与架构 SSOT | `00-文档大纲` / `内容映射表` / `临时-产品形态阶段化重设计计划` / 各正式分册 | 继续按阶段和 owner 管理设计;实现仓必须对齐这里的正式 owner,不用临时文档反向覆盖正式分册 | +| `muse-design-docs/` | 当前 `design-docs/` 独立化后的产品与架构 SSOT | `00-文档大纲` / `内容映射表` / 各正式分册 | 继续按阶段和 owner 管理设计;实现仓必须对齐这里的正式 owner,不用临时文档反向覆盖正式分册 | ## 新产品形态内容归属(正式文档) diff --git a/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局审阅版.md b/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局审阅版.md deleted file mode 100644 index e27924cf..00000000 --- a/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局审阅版.md +++ /dev/null @@ -1,309 +0,0 @@ -# P1R7 Source Owner Propagation 全局审阅版 - -## 结论 - -推荐把 P1R-7 后续主线从“AI 单 owner 先做”提升为“全局 source owner propagation 分层方案”,但实现仍必须按 owner 分阶段切片推进。 - -全局推荐方案: - -1. Events owner 只负责统一事件投影、幂等、payload 校验、脱敏、可见性过滤和 SSE,不反向读取任何 source owner 业务表。 -2. 每个 source owner 只负责本域权威事实和本域 publish outbox;worker 通过 `muse-module-events-api` 调用 `EventsPublishApi`。 -3. 不建立一个跨 owner 的中心 source 模块,也不让 Events server 直接拥有 source propagation 业务事实。 -4. 当前 AI 第一切片实现草稿不能直接作为全局方案完成证据;它只能作为后续 P1R-7b 的候选实现素材,必须先按本全局方案复审和修订。 - -P1R-7 后续拆分建议: - -| 阶段 | 目标 | 状态口径 | -|---|---|---| -| P1R-7b | 全局 propagation 规则冻结 + AI terminal event 第一 owner 切片 | 只推进 `needs_verification` 证据 | -| P1R-7c | Knowledge source status / projection event propagation | 只推进 `needs_verification` 证据 | -| P1R-7d | Market lifecycle / account projection / governance event propagation | 只推进 `needs_verification` 证据 | -| P1R-7e | Account(Member) security / entitlement / usage notification propagation | 只推进 `needs_verification` 证据 | -| P1R-7f | Content canonical change / block saved / export task event propagation | 只推进 `needs_verification` 证据 | -| P1R-7 completed approval | 对 P1R-7 做单独完成审批 | 必须用户单独批准 | - -本审阅版不实现代码、不修改 OpenAPI、不修改 scanner、不修改 coverage JSON/Markdown,不把 Events、P1R-7、Market 或任何 owner 标为 `completed`。 - -## 已验证事实 - -### 工作区与状态 - -- 正确工作区为 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支为 `dev/1.0.0`,`git pull --ff-only origin dev/1.0.0` 输出 `Already up to date.`。 -- 当前 HEAD 为 `fa29753 test(p1r): 收口 Events SSE 门禁`。 -- 当前 worktree 有未提交的 P1R-7b AI outbox 草稿、V17 migration 草稿和 `docs/agent-specs/` 文档草稿;本审阅版不清理、不回退这些文件。 -- 受保护文件当前定向 `git status --short` 无输出: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### Coverage 与阶段边界 - -- 当前 coverage summary: - - `totalOperations = 233` - - `completedOperations = 100` - - `needsVerificationOperations = 133` - - `incompleteOperations = 0` - - `genericPersistenceOperations = 0` - - `ssePlaceholderOperations = 0` -- Events 当前唯一 operation 为 `streamEvents GET /app-api/muse/events = dedicated / needs_verification`。 -- Market 当前 32 operations 均为 `dedicated / needs_verification`。 -- `docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md` 明确 P1R-7a 未接入 source owner propagation,未运行 source owner publish tests。 - -### 已有全局设计约束 - -- `CLAUDE.md` 明确本仓是设计文档 SSOT,核心架构决策中 Source 传播采用“事件驱动 + 各模块自治”,统一实时通信为 SSE。 -- `design-docs/架构-02-核心数据结构与双轨模型.md` 明确:来源 owner 发出状态变化事件,各消费模块监听事件并自行处理反应逻辑,不需要独立 source 模块。 -- `design-docs/后端-03-关键流程实现与接口契约.md` 明确:AI 调用、投影、索引、导出、评估、来源传播、New-API 归属和通知走异步任务,异步失败不得回滚已提交 Canonical。 -- `docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md` 明确:跨 owner 发布模式固定为 source owner 本域 outbox + worker 调用 Events API;Events server 不允许反向依赖 source owner server,也不允许统一 SSE Controller 直接扫多域业务表。 - -### Events owner 当前能力 - -- `EventsPublishApi.publish(EventsPublishReqDTO)` 已存在于 `muse-module-events-api`。 -- `EventsPublishReqDTO` 已包含 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`eventType`、`resourceType`、`resourceId`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 已按 commandId 优先、source tuple 次级做幂等回查。 -- `EventsPublishServiceImpl` 使用 `insertIgnore`,避免用唯一约束异常作为正常幂等路径。 -- `UnifiedEventMapper.selectVisibleEventsForOwner` 只查询 `publish_status = accepted`、同租户、同 ownerUserId、未删除、`sequenceNo` 大于 cursor 的事件。 -- `EventsStreamServiceImpl` 将统一投影中的 `done` / `error` / `notification` 等事件转成 SSE。 - -### Source owner 当前能力差异 - -- AI 已有 `muse_ai_task_event` terminal fact、`MuseAiRuntimeJobDispatcher`、`MuseAiJobMapper.claimNextQueuedRuntimeJob()` 和 `FOR UPDATE SKIP LOCKED` worker 风格;当前未提交草稿新增了 AI event publish outbox,但尚未按全局方案完成复审。 -- Knowledge 已有 `muse_knowledge_source_event` 和 `muse_knowledge_projection_task`;当前 `MuseKnowledgeSourceEventService` 同步更新 projection 后把 task 记录为 `completed`,注释明确避免制造无执行者的 queued task。 -- Market 已有 `MarketAccountProjectionOutboxService`,但接口只有 `recordSynced` / `recordBlocked`,职责是 Market 到 Member Account projection 的同步状态,不是 Events publish worker。 -- Member(Account) 已有 `MemberSecurityEventDO`、`AccountIntegrationCallDO`、调用归因 job 和安全事件脱敏转换;当前 mapper 以查询/归因记录为主,未发现完整 Events publish outbox claim/retry/dead-letter 链路。 -- Content 正式设计中已有 `BlockSavedEvent` outbox 方向,但本次只读未验证当前 Content 已有可直接复用到 Events 的 publish worker。 - -## 推断 - -- 全局 source propagation 的核心不是新增一个“大 source 服务”,而是冻结所有 owner 都必须遵守的事件发布合同、幂等合同、失败补偿合同和审计合同。 -- 直接把当前 AI outbox 草稿继续推进,容易把“第一切片实现细节”误当成全局架构决策;应先用全局审阅版统一约束,再修订 AI 执行版。 -- Market 的 projection outbox 名称容易误导;它当前表达的是 Market -> Account 读模型同步状态,不适合直接复用为 Events publish outbox。 -- Knowledge 已有 source event,但目前 projection task 同步完成,缺少异步 publish worker;进入 P1R-7c 前需要先补本域发布补偿语义。 -- Account security event 适合做用户通知类 propagation,但必须先冻结安全 payload allowlist,避免把设备、IP、风控原始详情或内部判断泄露到统一 SSE。 -- Content 的 BlockSavedEvent 更接近内部 followup trigger,不一定都应该进入用户 SSE;需要先区分“内部领域事件”和“用户可见统一事件”。 - -## 假设 - -- P1R-7 后续允许在各 source owner 内新增 publish outbox 表或等价本域状态表。 -- P1R-7 后续允许在 source owner server 中新增 `muse-module-events-api` 依赖,但不允许依赖 `muse-module-events-server`。 -- 当前 `EventsPublishReqDTO` envelope 足够承载后续 owner 的用户可见事件;如果后续发现字段不足,应另起 OpenAPI / contract 变更审批,而不是在实现里绕过。 -- 当前 P1R-7 完成状态仍需用户单独批准;本方案只定义如何补证据链。 - -## 全局目标 - -1. 建立跨 owner 的统一 source propagation 规则,保证每个 owner 都按相同可审核链路发布事件。 -2. 保持 source owner 自治:事实归 owner,发布补偿归 owner,统一可见投影归 Events。 -3. 让 `/app-api/muse/events` SSE 成为用户可见事件的统一出口,而不是跨表扫描器。 -4. 为 P1R-7 completed approval 准备真实证据:成功路径、幂等、失败、重试、dead-letter、权限、审计、数据库落库和 SSE 可见性。 - -本文只定义“用户可见 Events 发布链路”。它不替代正式 Source / Authorization 内部传播链路;内部来源状态、授权快照、传播到目标 owner 的阻断和禁用仍由正式设计中的 Source Status Event / Source Propagation Job 或等价本域机制承担。 - -## 非目标 - -1. 不建立独立 source 模块。 -2. 不把 Events server 改造成 source owner 编排器。 -3. 不让 Events server 反向依赖 AI / Knowledge / Market / Member / Content server。 -4. 不把所有内部领域事件都暴露给 SSE。 -5. 不复用职责不匹配的 Market Account projection outbox。 -6. 不用修改 OpenAPI、scanner 或 coverage 报告掩盖缺口。 -7. 不把当前 P1R-7b AI 草稿视为已完成实现。 -8. 不推进 completed 状态。 - -## 推荐架构 - -```mermaid -flowchart LR - subgraph Owner["每个 Source Owner"] - Fact["本域权威事实
Canonical 或 Shadow terminal fact"] - Outbox["本域 publish outbox
queued/running/retryable/published/dead_letter"] - Worker["本域 worker
claim + lease + retry"] - Fact --> Outbox --> Worker - end - - Worker --> Api["EventsPublishApi
events-api only"] - - subgraph Events["Events Owner"] - Api --> Publish["幂等发布服务
commandId + source tuple"] - Publish --> Store[("muse_unified_event
accepted/rejected/blocked")] - Store --> Stream["SSE stream service
tenant + owner + sequence"] - end - - Stream --> Client["muse-studio fetch SSE"] -``` - -关键原则: - -1. Source owner 只发布已经落库的、可审计的本域事实,不发布临时内存状态。 -2. Source owner outbox 与本域事实绑定,但不能因为 Events 暂时不可用回滚已提交 Canonical。 -3. Worker 不拼接自由 payload,只从 outbox 固化的 allowlist payload 发布。 -4. Events owner 只校验和投影,不反查 source owner 表补上下文。 -5. SSE 只读 `muse_unified_event` accepted 事件,不直接查询 AI / Knowledge / Market / Account / Content 表。 - -## 全局发布合同 - -每个 owner 的发布请求必须满足: - -| 字段 | 全局规则 | -|---|---| -| `commandId` | 稳定、短、幂等;推荐 `_evt:`,长度不超过 128 | -| `tenantId` | 必填;worker 线程必须恢复租户上下文 | -| `ownerUserId` | 必填;决定 SSE 可见性 | -| `sourceOwner` | 固定 owner 枚举,如 `ai`、`knowledge`、`market`、`account`、`content` | -| `sourceType` | 固定业务事实类型,不使用表名泄露内部结构 | -| `sourceId` | 本域事实稳定 id 或业务 id | -| `sourceRevision` | 本域事实版本;没有版本时使用明确占位,但 owner 执行版必须解释 | -| `eventType` | 只能使用 Events OpenAPI 已声明类型;新增类型必须另起合同审批 | -| `resourceType/resourceId` | 用户界面定位对象;不得代替 source tuple 幂等 | -| `payloadSummary` | 只包含 OpenAPI schema 允许字段和安全摘要 | -| `emittedAt` | 本域事实发生时间,不是 worker 发布时间 | - -payload 全局禁令: - -1. 不发布 prompt、provider request/response raw body、完整正文、知识资料原文、授权头、token、apiKey、secret、password。 -2. 不发布内部风控原始规则、管理员备注原文、失败堆栈、数据库错误详情。 -3. 不发布未在 OpenAPI 中声明的扩展字段。 -4. 不把 `rejected` 或 `blocked` 事件作为 SSE 可见事件。 - -## 全局 outbox 状态机 - -```mermaid -stateDiagram-v2 - [*] --> queued: owner fact 已落库并创建 publish intent - queued --> running: worker claim + lease - retryable --> running: retry 到期 claim - running --> running: stale lease reclaim - running --> published: Events accepted 或 duplicate accepted - running --> retryable: temporary failure - running --> dead_letter: payload invalid / rejected / blocked / retry exhausted - retryable --> dead_letter: retry exhausted - published --> [*] - dead_letter --> [*] -``` - -全局要求: - -1. claim 必须是原子 SQL,优先使用 `FOR UPDATE SKIP LOCKED` 或等价安全领取机制。 -2. claim 必须包含 lease,避免 crash-after-claim 后永久 running。 -3. attempt 只在 claim 时递增,失败处理不得二次递增。 -4. 幂等插入必须优先使用 `ON CONFLICT DO NOTHING` / `insertIgnore` + 回查,不把唯一约束异常当正常路径;PostgreSQL 同事务异常可能让事务进入 aborted 状态。 -5. `dead_letter` 不能自动 replay;只有确认是临时故障的记录才能被重置为 `retryable`。 -6. 所有状态转换必须有中文日志,至少包含 owner、tenantId、ownerUserId、source tuple、outboxId、attempt 和错误摘要。 -7. owner 执行版必须显式冻结最小字段词汇:`max_attempt`、`next_retry_at`、`claim_expires_at`、固定退避策略、claim 索引和依赖树验证命令。 - -## 用户可见事件判定准则 - -只有同时满足以下条件的事件才进入统一 SSE: - -1. 用户需要实时感知该变化,且变化能影响当前用户下一步操作。 -2. 事件 payload 可以被压缩成 OpenAPI 已声明 schema 内的安全摘要。 -3. 事件不要求前端读取内部风控、授权、source propagation 或 worker 调度细节。 -4. 事件 owner 能提供稳定 source tuple、幂等 commandId 和 ownerUserId。 - -默认不进入统一 SSE 的事件: - -1. 只服务内部 followup trigger 的领域事件,例如 `BlockSavedEvent`。 -2. projection rebuild、source propagation target、retry job、worker heartbeat 等内部任务状态。 -3. 需要完整正文、知识资料、provider raw body、授权详情或安全风控原始证据才能解释的事件。 - -## 阶段化路线 - -### P1R-7b:全局规则冻结 + AI terminal event 第一切片 - -目标: - -- 修订现有 AI 第一切片执行版,使其服从本全局方案。 -- AI terminal `done/error` 通过本域 outbox + worker 发布到 Events。 -- 修正当前草稿中可能存在的同事务唯一冲突异常风险,采用 `ON CONFLICT DO NOTHING` / insert-ignore + 回查。 -- 证明 AI 只依赖 events-api,不依赖 events-server。 - -不做: - -- 不把 AI 以外 owner 混入 P1R-7b 实现。 -- 不标 completed。 - -### P1R-7c:Knowledge propagation - -目标: - -- 选择 Knowledge source status / projection summary 中最小用户可见事件。 -- 在 Knowledge owner 内新增或复用 publish outbox 语义,补 claim/retry/dead-letter。 -- 区分内部 projection task 和用户可见 Events notification。 - -不做: - -- 不把同步完成的 projection task 伪装成异步 worker。 -- 不发布知识资料原文。 - -### P1R-7d:Market propagation - -目标: - -- 覆盖 publish review、asset delist/recall、license/install/purchase 对用户可见状态的通知。 -- 保留 Market Account projection outbox 的原职责,另建或扩展专门 Events publish outbox。 - -不做: - -- 不把 Market 32 operations 标为 completed。 -- 不让 Market 事件接管源作品、源智能体或源知识库事实 owner。 - -### P1R-7e:Account(Member) propagation - -目标: - -- 覆盖 security event、entitlement/quota notification、New-API attribution summary 中适合用户可见的事件。 -- 复用既有安全事件脱敏转换,但新增统一 Events payload allowlist。 -- 如果后续目标偏“用户通知”而不是“source status propagation”,Account security event 可以作为 Knowledge 之后的低风险 second slice 备选。 - -不做: - -- 不发布设备风控原始细节、IP 内部判断、接口调用原始日志或成本原始 authority。 - -### P1R-7f:Content propagation - -目标: - -- 评估 BlockSavedEvent、export task、import task 中哪些应进入用户可见 SSE。 -- 明确内部 followup trigger 与用户通知的分界。 - -不做: - -- 不把正文保存的每次内部事件都推给 SSE。 -- 不发布正文全文或 block content。 - -## 完成审批门槛 - -P1R-7 completed approval 必须另起任务,并至少具备: - -1. Events owner 自身 gate:route ownership、OpenAPI schema、payload sanitizer、idempotency、SSE replay、frontend fetch SSE。 -2. 至少覆盖 AI、Knowledge、Market、Account(Member)、Content 中被声明为 P1R source owner 的真实发布链路。 -3. 每个 owner 都有成功、重复幂等、临时失败重试、retry exhausted、dead-letter、敏感 payload fail-closed、跨 owner 依赖方向测试。 -4. PostgreSQL `_test` Flyway / migration 证据覆盖新增 outbox 表和 `muse_unified_event`。 -5. HTTP / MockMvc / SSE 或等价 focused E2E 证据证明当前用户只能看到同 tenant + ownerUserId 的 accepted 事件。 -6. 审计或日志证据证明事件发布链路可追踪、可排查、可回放。 -7. 用户明确批准从 `needs_verification` 推进到 `completed`。 - -## 风险 - -- 抽象过早:如果现在先做共享 publish 库,可能在第二个 owner 前抽错接口;因此先冻结合同和测试模板,等 AI + Knowledge 两个 owner 通过后再抽小型 support module。 -- 事务风险:同事务内 outbox 插入如果靠捕获唯一约束异常处理,PostgreSQL 可能使事务不可继续;实现必须使用 insert-ignore 或先锁定事实行。 -- Payload 风险:不同 owner 的“摘要”含义不同,必须逐 owner allowlist,不允许泛化 Map 透传。 -- 完成状态风险:source propagation evidence 增加不等于 P1R-7 completed;completed 必须另有审批。 -- Dirty baseline 风险:当前 worktree 已有 AI 草稿,后续执行前必须决定是修订沿用、拆分提交,还是用户批准后重置对应草稿;本审阅版不处理。P1R-7b 执行版修订并双 review PASS 前,当前 AI outbox / V17 / docs 草稿不得继续测试、提交、推送或作为完成证据。 - -## 待确认项 - -1. 是否确认以本文作为新的 P1R-7 source owner propagation 全局主线,替代此前 AI-only 审阅版作为最高层方案。 -2. 是否确认 P1R-7b 仍从 AI terminal event 第一切片落地,但必须先修订执行版以符合本文全局规则。 -3. 是否确认 completed approval 另起任务,当前所有阶段只推进 `needs_verification` 证据。 - -## 下一步 - -1. 对本文进行 fresh spec compliance review。 -2. 对本文进行 fresh quality / feasibility review。 -3. 双 PASS 后修订 `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md`,把 AI 第一切片执行版降级为全局方案下的 P1R-7b 子计划。 -4. P1R-7b 子计划双 review PASS 后,才允许处理当前 AI 草稿实现和后续 worker 任务。 diff --git a/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局执行版.md b/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局执行版.md deleted file mode 100644 index 94295002..00000000 --- a/docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局执行版.md +++ /dev/null @@ -1,353 +0,0 @@ -# P1R7 Source Owner Propagation 全局执行版 - -## 结论 - -本执行版只定义 P1R-7 Source Owner Propagation 的全局落地顺序、子阶段拆分、review gate、验证门槛和当前 AI 草稿处理规则;不实现代码,不提交,不推送,不推进 completed。 - -总执行主线: - -```mermaid -flowchart TB - GlobalReview["全局审阅版
已双 review PASS"] --> GlobalPlan["全局执行版
本文件,待双 review"] - GlobalPlan --> Freeze["冻结全局合同
用户可见 Events 不替代内部 Source/Authorization"] - Freeze --> P7bPlan["P1R-7b 子计划
AI terminal event"] - P7bPlan --> P7bImpl["P1R-7b 实现
AI outbox + worker + Events evidence"] - P7bImpl --> NextDecision{"下一 owner 选择"} - NextDecision --> P7c["P1R-7c Knowledge
source status / projection notification"] - NextDecision --> P7e["P1R-7e Account
security / quota notification 备选"] - P7c --> P7d["P1R-7d Market
lifecycle / governance notification"] - P7e --> P7d - P7d --> P7f["P1R-7f Content
用户可见 content/task notification"] - P7f --> Approval["P1R-7 completed approval
用户单独批准"] -``` - -硬边界: - -1. 当前 AI outbox / V17 / docs 草稿在 P1R-7b 子计划修订并双 review PASS 前不得继续测试、提交、推送或作为完成证据。 -2. 全局方案只定义“用户可见 Events 发布链路”,不替代正式 Source / Authorization 内部传播链路。 -3. 每个 owner 的实现都必须 fresh implementer + fresh spec review + fresh quality / feasibility review。 -4. completed approval 必须另起任务,不能由任何 dedicated gate 或 owner evidence 自动推出。 - -## 范围 - -本执行版负责: - -- 固化 P1R-7 全局 source owner propagation 合同。 -- 定义后续 P1R-7b/c/d/e/f 子计划顺序和拆分条件。 -- 定义每个子计划必须继承的通用验证门槛。 -- 定义当前 dirty AI 草稿如何进入 P1R-7b 子计划复审。 - -本执行版不负责: - -- 不改业务代码、迁移、OpenAPI、scanner、coverage JSON/Markdown。 -- 不运行 Maven / pnpm / Flyway / coverage scanner full verification。 -- 不清理或回退当前 dirty/untracked baseline。 -- 不把 `streamEvents`、P1R-7、Market 或任何 owner 标为 `completed`。 - -## 已验证事实 - -### 工作区与 review gate - -1. 正确工作区为 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -2. 当前分支为 `dev/1.0.0`,远端 `origin/dev/1.0.0` 已 ff-only 对齐。 -3. 当前 HEAD 为 `fa29753 test(p1r): 收口 Events SSE 门禁`。 -4. 当前 `git status --short --branch` 显示未提交 AI P1R-7b 草稿、V17 migration 草稿和 `docs/agent-specs/` 文档草稿。 -5. 受保护文件定向 `git status --short` 无输出。 -6. `docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局审阅版.md` 已通过 fresh spec review: - - Kant / `019e9987-ca59-7370-bc49-9938eb93c70f` / PASS。 -7. 同一全局审阅版已通过 fresh quality / feasibility review: - - Hubble / `019e998b-2d82-7200-9289-c6c8dd37f744` / PASS。 - -### Coverage 与状态边界 - -1. 当前 coverage summary: - - `totalOperations = 233` - - `completedOperations = 100` - - `needsVerificationOperations = 133` - - `incompleteOperations = 0` - - `genericPersistenceOperations = 0` - - `ssePlaceholderOperations = 0` -2. Events 当前唯一 operation 为 `streamEvents GET /app-api/muse/events = dedicated / needs_verification`。 -3. Market 当前 32 operations 均为 `dedicated / needs_verification`。 -4. P1R-7a 收口文档明确:P1R-7a 未接入 source owner propagation,未运行 source owner publish tests。 - -### 全局设计事实 - -1. `CLAUDE.md` 将 Source 传播定为“事件驱动 + 各模块自治”。 -2. `design-docs/架构-02-核心数据结构与双轨模型.md` 明确不需要独立 source 模块。 -3. `design-docs/后端-03-关键流程实现与接口契约.md` 明确异步失败不得回滚已提交 Canonical。 -4. P1R-7a 执行版已固定 source owner 本域 outbox + worker 调用 Events API;Events server 不反向依赖 source owner server。 -5. Events owner 当前已有 `EventsPublishApi`、`EventsPublishReqDTO`、commandId/source tuple 幂等、payload sanitizer、accepted-only 可见查询和 SSE stream。 - -## 推断 - -1. 全局执行顺序应先冻结通用合同,再修订 P1R-7b AI 子计划;否则当前 AI 草稿可能继续携带单点设计里的事务和 dirty baseline 风险。 -2. AI 仍是第一实现切片,因为它已有 terminal fact、ownerUserId、runtime job 和 focused test 基础。 -3. Knowledge 适合作为第二个 source status propagation owner,但如果后续目标更偏用户通知而非来源状态传播,Account security event 可以作为更低风险 second slice 备选。 -4. Market 应排在 AI/Knowledge 之后,因为 Market Account projection outbox 与 Events publish outbox 职责不同,直接复用风险较高。 -5. Content 需要先界定内部领域事件与用户可见事件,否则容易把 BlockSavedEvent 等内部 trigger 过度推送到 SSE。 - -## 假设 - -1. 后续 owner 允许在本域新增 publish outbox 或等价状态表。 -2. 后续 owner server 允许新增 `muse-module-events-api` 依赖。 -3. 现有 Events OpenAPI 事件类型足够覆盖第一轮用户可见摘要;如果不够,应另起合同变更审批。 -4. 当前 AI 草稿可以作为 P1R-7b 子计划的候选素材,但必须经过执行版修订、双 review 和本地验证后才能继续。 - -## 全局合同 - -### 依赖合同 - -1. Source owner server 可以依赖 `muse-module-events-api`。 -2. Source owner server 不得依赖 `muse-module-events-server`。 -3. `muse-module-events-server` 不得依赖 AI / Knowledge / Market / Member / Content server。 -4. `muse-server` 可以装配 Events server 与各 source owner server。 -5. 每个 owner 子计划必须包含 Maven dependency tree 验证命令。 - -### 发布 envelope 合同 - -每个 owner 子计划必须固定: - -| 字段 | 要求 | -|---|---| -| `commandId` | 稳定短幂等键,长度 `<= 128`,推荐 `_evt:` | -| `tenantId` | 必填,worker 必须恢复租户上下文 | -| `ownerUserId` | 必填,决定 SSE 可见性 | -| `sourceOwner` | 固定 owner 枚举 | -| `sourceType` | 固定业务事实类型 | -| `sourceId` | 本域事实稳定 id 或业务 id | -| `sourceRevision` | 本域事实版本;缺失时必须在子计划解释占位语义 | -| `eventType` | 只能使用 Events OpenAPI 已声明类型 | -| `resourceType/resourceId` | UI 定位对象,不参与替代 source tuple 幂等 | -| `payloadSummary` | OpenAPI allowlist 安全摘要 | -| `emittedAt` | 本域事实发生时间 | - -### 用户可见事件判定 - -每个候选事件进入 SSE 前必须满足: - -1. 用户需要实时感知。 -2. 用户可据此采取动作或理解当前工作状态。 -3. payload 能压缩为 OpenAPI 已声明 schema 的安全摘要。 -4. 不需要暴露内部 source propagation、授权快照、worker、风控或 provider raw 细节。 -5. 能提供稳定 source tuple、commandId、ownerUserId 和 emittedAt。 - -默认不进入 SSE: - -1. 内部 followup trigger。 -2. projection rebuild / worker heartbeat / retry job 状态。 -3. 需要完整正文、知识资料、provider raw、授权详情或安全原始证据才能解释的事件。 - -### Outbox 合同 - -每个 owner 子计划必须包含: - -1. `queued/running/retryable/published/dead_letter` 或等价状态。 -2. `attempt_count` 或等价 claim 次数字段。 -3. `max_attempt` 配置。 -4. `next_retry_at`。 -5. `claimed_at`。 -6. `claim_expires_at`。 -7. 固定退避策略。 -8. claim 索引。 -9. `FOR UPDATE SKIP LOCKED` 或等价原子领取机制。 -10. `ON CONFLICT DO NOTHING` / `insertIgnore` + 回查,禁止把唯一约束异常作为正常幂等路径。 -11. `published_event_id` / `published_sequence_no` 或等价回写字段。 -12. `last_error_code` / `last_error_message` 安全错误摘要。 -13. 中文日志,包含 owner、tenantId、ownerUserId、source tuple、outboxId、attempt、状态转换和错误摘要。 - -### Payload 合同 - -每个 owner 子计划必须列出: - -1. 允许的 `eventType`。 -2. 每个 `eventType` 的 payload allowlist。 -3. 需要丢弃或归一化的敏感字段。 -4. payload invalid 时的 fail-closed 状态和错误码。 -5. rejected/blocked 后不得进入 SSE 可见查询的验证方式。 - -## 子阶段执行顺序 - -### Task 0:冻结总执行版 - -目标: - -- 本文件完成 fresh spec review + fresh quality / feasibility review 双 PASS。 -- 双 PASS 前不修订 P1R-7b 子计划,不继续 AI 草稿测试或实现。 - -验收: - -- Reviewer 明确 PASS。 -- 如有 FAIL,先修订本文件并重审。 -- protected files 仍无改动。 - -### Task 1:P1R-7b 子计划修订 - -目标: - -- 修订 `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md`。 -- 将它从“AI 单点执行版”改为“全局执行版下的 AI 第一切片子计划”。 -- 明确当前 AI 草稿的处理策略:修订沿用、拆分提交或用户批准后清理。 - -必须补充: - -1. 继承本执行版的依赖合同、envelope 合同、outbox 合同和 payload 合同。 -2. 明确 AI 只发布用户可见 terminal `done/error` 摘要。 -3. 明确 AI outbox 创建不得依赖捕获 `DuplicateKeyException` 作为正常幂等路径。 -4. 明确 P1R-7b 只处理 AI,不处理 Knowledge / Market / Account / Content。 -5. 明确 P1R-7b 不推进 completed。 - -Review gate: - -- Fresh spec compliance review。 -- Fresh quality / feasibility review。 -- 双 PASS 前不得继续当前 AI 草稿实现。 - -### Task 2:P1R-7b AI 实现收口 - -目标: - -- 在 P1R-7b 子计划双 PASS 后,处理当前 AI 草稿或由 fresh implementer 重做。 -- 实现 AI terminal event -> AI publish outbox -> AI worker -> EventsPublishApi -> `muse_unified_event` -> SSE 可见证据。 - -必须验证: - -1. AI focused tests。 -2. Outbox mapper/service/worker tests。 -3. Events publish accepted/rejected/blocked/duplicate tests。 -4. SSE visible query tests。 -5. Dependency tree:AI 只依赖 events-api,不依赖 events-server;Events server 不依赖 source owner server。 -6. Migration SQL test 和 Flyway `_test`。 -7. protected files 无改动。 - -Review gate: - -- 每个实现 Task fresh implementer。 -- 每个实现 Task fresh spec review。 -- 每个实现 Task fresh quality / feasibility review。 - -### Task 3:第二 owner 选择决策 - -目标: - -- 在 AI 第一切片通过后,只读评估第二 owner 是 Knowledge source status 还是 Account security notification。 - -选择规则: - -1. 如果目标是 source status propagation 证据优先,选择 Knowledge。 -2. 如果目标是用户可见通知链路低风险扩展优先,选择 Account security event。 -3. 不因 Market 有 outbox 命名而优先选择 Market。 -4. Content 进入前必须先冻结内部事件与用户可见事件判定。 -5. 如果 Account security event 先于 Market 落地,阶段编号仍保留 P1R-7e,不重命名 P1R-7d,避免阶段编号反向改写历史计划。 - -输出: - -- `docs/agent-specs/YYYY-MM-DD-P1R7c...审阅版.md`。 -- fresh spec review + fresh quality review。 - -### Task 4:P1R-7c / P1R-7e 子计划与实现 - -目标: - -- 按 Task 3 决策进入 Knowledge 或 Account。 -- 每个 owner 都必须先产出单独审阅版,再产出单独执行版;审阅版和执行版各自 fresh spec review + fresh quality / feasibility review 双 PASS 后,才允许进入该 owner 实现。 - -必须继承: - -- 本执行版的全局合同。 -- P1R-7b 的复用经验,但不得复制 AI payload / 状态机细节到不匹配 owner。 - -### Task 5:Market 与 Content 后续切片 - -目标: - -- Market 先拆清 Account projection outbox 与 Events publish outbox 职责。 -- Content 先拆清内部 followup trigger 与用户可见 Events notification。 - -进入条件: - -- 至少已有两个 owner 的 Events publish 链路通过验证,或者用户明确要求优先 Market / Content。 - -### Task 6:P1R-7 completed approval 预检 - -目标: - -- 汇总 Events owner + 各 source owner evidence。 -- 只读判断是否具备 completed approval 申请条件。 - -必须包含: - -1. coverage 当前状态。 -2. 所有 owner 的测试证据。 -3. HTTP / SSE / DB / Flyway / dependency / protected file evidence。 -4. 未覆盖 owner 和未覆盖失败路径清单。 - -边界: - -- 该 task 只做预检,不自动标 completed。 -- completed approval 必须用户单独批准。 - -## 验证命令模板 - -每个 owner 子计划必须按实际模块填充以下模板: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -o -pl -am -Dtest= -Dsurefire.failIfNoSpecifiedTests=false test -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -o -pl muse-server -am -Dtest= -Dsurefire.failIfNoSpecifiedTests=false test -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -o -pl -am dependency:tree -``` - -Flyway `_test` 必须使用独立测试库,且必须显式清空 JVM 代理参数或证明代理不会影响内网 PostgreSQL 连接。 - -## 当前 dirty baseline 处理规则 - -当前未提交 AI P1R-7b 草稿包括: - -- AI module POM、runtime projection service、job service 与相关测试改动。 -- AI publish outbox service / mapper / DO 新文件。 -- `muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql`。 -- P1R-7b migration SQL/Flyway test 草稿。 -- `docs/agent-specs/` 文档草稿。 - -处理规则: - -1. 本执行版双 PASS 前,不继续测试或实现这些草稿。 -2. P1R-7b 子计划修订时必须显式决定这些草稿是“修订沿用”还是“废弃重做”。 -3. 如果选择修订沿用,必须补证据证明草稿已符合本执行版全局合同。 -4. 如果选择废弃重做,必须先征得用户明确批准后才能清理相关 dirty/untracked 文件。 -5. 任何情况下不得回退用户或其他代理的无关改动。 - -## 完成条件 - -本总执行版完成条件: - -1. 本文件存在且内容覆盖全局合同、子阶段顺序、验证模板、dirty baseline 处理规则。 -2. Fresh spec compliance review PASS。 -3. Fresh quality / feasibility review PASS。 -4. protected files 无改动。 -5. 输出下一步:修订 P1R-7b 子计划,而不是直接继续 AI 实现。 -6. 本轮 reviewer 只读审查文档,不运行 full verification;review PASS 不能解释为实现验证 PASS。 - -P1R-7 全链路完成条件不在本文件完成范围内;它必须在 P1R-7 completed approval 任务中单独判断。 - -## 回滚策略 - -本执行版只新增文档;若 review FAIL: - -1. 只修订本文件和必要的 `.agent` 说明。 -2. 不回退代码草稿。 -3. 不修改 protected files。 -4. 不运行 full verification。 - -若后续实现阶段发现全局合同错误: - -1. 停止对应 owner 实现。 -2. 回到全局审阅版 / 执行版修订。 -3. 重新 fresh spec review + fresh quality review。 diff --git a/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md b/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md deleted file mode 100644 index 35461c1e..00000000 --- a/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md +++ /dev/null @@ -1,148 +0,0 @@ -# P1R7b Source Owner Propagation 审阅版 - -## 结论 - -推荐进入 P1R-7b,但只进入 source owner propagation 的第一切片,不进入 Events / P1R-7 / Market completed approval。 - -推荐拆分为: - -1. P1R-7b:只选择 AI task terminal event 作为第一切片,证明 AI owner 本域事件通过最小 publish outbox / worker 调用 `EventsPublishApi`,落入 `muse_unified_event`,并可被 `/app-api/muse/events` SSE 读到。 -2. P1R-7c 或后续:再评估 Knowledge、Market、Member(Account) 的 source owner propagation,不在 P1R-7b 同时改造多个 owner。 - -P1R-7b 最多补齐 source owner propagation 的 `needs_verification` 证据;不得自动把 Events、总 P1R-7 或 Market 32 operations 标为 `completed`。 - -## 已验证事实 - -- 正确工作区已核验为 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`,`git log -1 --oneline` 为 `fa29753 test(p1r): 收口 Events SSE 门禁`。 -- P1R-7a 推送后正确 worktree 曾核验为干净;本文档写入后当前 live `git status --short` 显示 `?? docs/agent-specs/`。后续执行版和实现前必须重新记录 live dirty baseline。受保护文件无改动: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` -- coverage 当前为 `completed=100`、`needsVerification=133`、`incomplete=0`、`genericPersistence=0`、`ssePlaceholder=0`。 -- Events 当前唯一 operation:`streamEvents GET /app-api/muse/events = dedicated / needs_verification`。 -- Market 当前 32 operations 均仍为 `dedicated / needs_verification`。 -- P1R-7a 收口文档明确:P1R-7a 未接入 source owner propagation,未运行 source owner publish tests。 -- `muse-module-events-api` 已存在 `EventsPublishApi.publish(EventsPublishReqDTO)`;Events server 已有 `muse_unified_event` 投影、幂等回查、payload 脱敏、`accepted/rejected/blocked` 状态和可见事件查询。 -- 当前 AI 有 `muse_ai_task_event`、task event replay mapper、`MuseAiRuntimeJobDispatcher`、`MuseAiJobMapper.claimNextQueuedRuntimeJob()`;但 AI runtime job 只领取 `ai_task_runtime`,AI source event retry 只提交 `source_event_retry` job,未验证存在 Events publish worker。 -- 当前 Knowledge 有 `muse_knowledge_source_event` 和 `muse_knowledge_projection_task`;`MuseKnowledgeSourceEventService` 同步更新 projection 后把 task 直接写为 `completed`,注释说明避免制造无执行者的 queued task。 -- 当前 Market 有 `MarketAccountProjectionOutboxService`,但接口只有 `recordSynced` / `recordBlocked`,用于 Market 写 member Account projection 的同步状态;未发现 claim/retry/dead-letter/replay worker。 -- 当前 Member(Account) 有 `AccountIntegrationCallDO`、`MemberSecurityEventDO` 和安全事件脱敏转换;mapper 只支持查询,不支持 publish outbox claim 或补偿状态转换。 - -## 推断 - -- AI 是 P1R-7b 第一切片的最小风险 owner:它已有 owner-visible task terminal event、runtime job、sequence/replay 和 focused test 基础,新增 Events publish outbox/worker 的范围最小。 -- Market 虽然有 outbox 命名和状态字段,但当前职责是 Account 投影同步记录,不是通用 Events publish 补偿队列;直接复用会混淆职责。 -- Knowledge 和 Member(Account) 要进入统一事件传播,都需要先补本域 publish outbox/worker 语义;放入 P1R-7b 会扩大 blast radius。 -- 如果 P1R-7b 同时拉 AI、Knowledge、Market、Member(Account),大概率会变成跨 owner 队列重构,不符合“真实最小”的阶段目标。 - -## 假设 - -- P1R-7b 允许在 AI owner 内新增最小 Events publish outbox/worker 结构,但仍不得修改 OpenAPI、scanner 或 coverage 口径。 -- P1R-7b 的 source owner publish tests 可以优先使用 focused unit / integration tests 证明发布、失败、重试、幂等和 SSE 可见性,不要求一次性覆盖所有业务 owner。 -- 当前代码结构中的 Events API 依赖方向会保持:source owner 可以依赖 `muse-module-events-api`,但 Events server 不反向依赖 AI / Knowledge / Market / Member server。 - -## 目标 - -- 证明至少一个真实 source owner 能把本域事实传播到统一 Events 投影。 -- 第一切片只覆盖 AI task terminal event 的 `done` / `error` 或等价最小可公开事件。 -- 建立 source owner 本域 outbox/job 到 `EventsPublishApi` 的可追踪链路。 -- 产出 publish tests 和最小 E2E evidence,证明 source owner -> Events -> `muse_unified_event` -> SSE stream 的链路可验证。 -- 保持 `streamEvents` 和相关 owner 状态在 `needs_verification` 证据推进层,不做 completed approval。 - -## 非目标 - -- 不实现代码;本文档只做人类审阅版设计。 -- 不修改任何 OpenAPI 合同。 -- 不修改 coverage scanner 或通过 scanner 改口径掩盖缺口。 -- 不修改 coverage JSON / Markdown,不标 `completed`。 -- 不把 P1R-7b 扩大成总 P1R-7 End-to-End Acceptance。 -- 不把 Market 32 operations 从 `needs_verification` 推进到 `completed`。 -- 不同时改造 AI、Knowledge、Market、Member(Account) 四个 owner。 -- 不清理、不回退、不修复错误 checkout `/Users/qingse/Sync/local-git/oh-my-muse` 的任何残留。 - -## 推荐方案 - -采用“AI first slice + 后续 owner 分批”的真实最小方案。 - -```mermaid -flowchart LR - Owner["AI source owner
task terminal fact"] --> Outbox["AI local publish outbox/job
queued/running/failed/dead_letter"] - Outbox --> Worker["AI publish worker
retry + idempotency"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Store[("muse_unified_event
accepted/rejected/blocked")] - Store --> Query["Events visible query
tenant + ownerUserId + sequence"] - Query --> SSE["/app-api/muse/events
SSE stream"] -``` - -推荐 P1R-7b 第一切片边界: - -- Source owner:AI。 -- Source fact:AI task terminal event,优先选择已可公开、已持久化、已有 ownerUserId 和 sequence 的 done/error 事实。 -- Publish mode:AI 本域 outbox/job 异步调用 `EventsPublishApi`,不在业务事务内阻塞等待 Events 成功。 -- Idempotency:使用 AI 本域 command/source tuple 映射 Events `commandId` 和 `(sourceOwner, sourceType, sourceId, sourceRevision, eventType)`。 -- Failure:Events 临时失败时 AI 业务事实仍保留,outbox 保持可重试;达到上限后进入阻塞或 dead-letter 等价状态。 -- SSE evidence:只证明 accepted 事件进入 `muse_unified_event` 后能被当前用户 SSE 可见查询读到。 - -后续切片: - -- P1R-7c:Knowledge source/projection event publish outbox。 -- P1R-7d:Market governance / Account projection 相关事件,需先拆清 Account 投影 outbox 与 Events publish outbox 职责。 -- P1R-7e:Member(Account) 安全/额度通知,需先冻结脱敏 payload contract。 - -## 候选 source owner 分级 - -| 分级 | Owner | 结论 | 主要证据 | -|---|---|---|---| -| 第一切片 | AI | 推荐 P1R-7b 先做 | 已有 task event、runtime job、ownerUserId、sequence/replay 基础;缺口集中在 Events publish outbox/worker | -| 后续高优先 | Knowledge | 放入 P1R-7c | 有 source event/projection 事实,但 projection task 当前同步完成,不是可执行补偿队列 | -| 后续中优先 | Market | 放入后续 | 有 Account projection outbox 状态记录,但职责是 Market -> Member Account 投影同步,不是 Events publish worker | -| 后续中优先 | Member(Account) | 放入后续 | 有安全事件和脱敏转换,但缺少 publish outbox/job/retry/dead-letter 入口 | - -## 关键取舍 - -- 选择 AI,不选择 Market:Market outbox 名称容易误导,但当前没有 claim/retry/dead-letter/replay worker;AI 的 task terminal event 更接近可公开事件源。 -- 做一个 owner,不做四个 owner:source owner propagation 的核心风险在失败补偿和幂等;先用单 owner 打穿链路,比并行堆多个半成品更可审核。 -- 新增本域 publish outbox,不把 runtime job 当 Events publish 队列:AI runtime job 职责是执行 New-API task;混用会污染任务语义和重试策略。 -- 保持 Events API 单向依赖:source owner 依赖 events-api;Events server 不读取 source owner 表,不反向依赖 source owner server。 -- 只推进证据,不推进 completed:P1R-7b 是 source propagation evidence 阶段,不是审批阶段。 - -## 影响范围 - -- 直接影响:AI owner 的 task terminal event 发布路径、Events publish API 调用、`muse_unified_event` 投影可见性、SSE focused evidence。 -- 间接影响:P1R-7 completed approval 的后续证据链更完整,但不会自动改变 completed 状态。 -- 不影响:OpenAPI 合同、scanner 规则、Market coverage 状态、错误 checkout `/Users/qingse/Sync/local-git/oh-my-muse`。 - -## 风险与兼容性 - -- 幂等风险:同一 AI task terminal event 重试不能生成多条 unified event;必须用 command/source tuple 双重幂等验证。 -- 事务风险:AI 业务事实不能因为 Events 暂时不可用而回滚;publish outbox 必须承载失败补偿。 -- 敏感信息风险:AI payload 只能发布 OpenAPI 允许的 done/error 摘要,不能透传 provider raw body、prompt、token、授权头或私有上下文。 -- 顺序风险:AI task sequence 与 Events 全局 sequence 是不同序列;文档和测试必须明确映射,不得混用 cursor。 -- 兼容性风险:新增 AI -> events-api 依赖可以接受;不得新增 AI -> events-server 依赖,也不得新增 Events server -> AI server 依赖。 -- 状态风险:P1R-7b evidence 可能让 Events 更接近 completion,但没有用户明确 approval 前不得改 coverage completed。 - -## 验收标准 - -- 有只读 preflight 证明 worktree、HEAD、coverage 和 protected files 状态。 -- AI 第一切片 source fact、outbox/job、publish worker、EventsPublishApi、`muse_unified_event` 和 SSE 可见查询形成闭环证据。 -- Publish tests 至少覆盖成功发布、重复发布幂等、EventsPublishApi 临时失败后 outbox 可重试、非法/敏感 payload 不进入可见 SSE、dead-letter 或等价阻塞状态。 -- E2E evidence 至少证明 AI terminal fact 发布后,`muse_unified_event` 有 accepted 记录,并能被 `/app-api/muse/events` 当前用户 SSE 读到。 -- 依赖方向测试证明 Events server 不依赖 AI / Knowledge / Market / Member server,AI 只依赖 `muse-module-events-api`。 -- coverage 仍不标 completed;若需要 completed approval,必须另起审批任务。 -- 受保护文件保持无改动。 - -## 待确认项 - -1. P1R-7b 第一切片是否确认只选 AI task terminal event,而不是 Market / Knowledge / Member。 -2. AI terminal event 的最小事件类型是否限定为 `done` / `error`,还是允许补一个 `notification` 摘要。 -3. P1R-7b 是否允许新增 AI 本域 publish outbox/job 表或等价状态字段;若不允许,只能做更弱的同步 publish 证明,不建议。 -4. P1R-7b 的 E2E evidence 是否需要真实 PostgreSQL `_test` + MockMvc/SSE,还是 focused integration test 足够进入下一轮 review。 - -## 下一步 - -1. 人类确认本文推荐方案和待确认项。 -2. 确认后再写执行版计划,路径建议为 `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md`。 -3. 执行版计划需先过 fresh spec review + fresh quality review;双 PASS 前不实现代码、不跑 full verification。 -4. 实现阶段只在 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` 进行,不处理 `/Users/qingse/Sync/local-git/oh-my-muse` 残留。 diff --git a/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md b/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md deleted file mode 100644 index e64ee64a..00000000 --- a/docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md +++ /dev/null @@ -1,1141 +0,0 @@ -# P1R7b Source Owner Propagation 执行版 - -## 结论 - -P1R-7b 是 `docs/agent-specs/2026-06-06-P1R7SourceOwnerPropagation全局执行版.md` 下的 AI terminal event 第一切片子计划。本文件只修订计划,不实现代码,不运行测试,不提交,不推送,不推进 Events / P1R-7 / Market completed approval。 - -当前 AI 草稿实现只能作为“修订沿用候选”,不能作为已通过、已完成或可继续实现的事实。必须先让本子计划通过 fresh spec review + fresh quality / feasibility review 双 PASS,才允许处理当前 AI 草稿实现;双 PASS 前不得继续 AI 草稿测试、提交、推送或把任何 dirty baseline 清理成“完成状态”。 - -执行主线固定为: - -```mermaid -flowchart LR - Fact["AI owner 本域事实
muse_ai_task_event done/error"] --> Outbox["AI publish outbox/job
muse_ai_event_publish_outbox"] - Outbox --> Worker["AI publish worker
claim + retry + dead_letter"] - Worker --> Api["EventsPublishApi
只依赖 muse-module-events-api"] - Api --> Store[("muse_unified_event
accepted/rejected/blocked")] - Store --> Query["Events visible query
tenant + ownerUserId + sequence"] - Query --> SSE["/app-api/muse/events
SSE 可见"] -``` - -执行版完成后仍只是计划;必须经过 fresh spec review + fresh quality review 双 PASS,才允许进入实现。实现完成后也只推进 source propagation evidence / needs_verification,不自动把 Events、P1R-7 或 Market 32 operations 标为 `completed`。 - -本轮计划修订的硬边界: - -1. 只允许修改本文件和 `docs/agent-specs/.agent`。 -2. 不修改任何 Java / SQL / test / `pom.xml` 文件。 -3. 不修改 OpenAPI、scanner、coverage JSON/Markdown。 -4. 不清理、不回退、不废弃当前 dirty baseline;任何废弃草稿动作都必须等用户明确批准。 -5. 不引入独立 source 模块,也不把 shared publish 库作为当前 P1R-7b 必做项。 - -## 目标与范围边界 - -目标: - -1. 在全局 P1R7 Source Owner Propagation 执行版约束下,以 AI task terminal event 为第一切片,证明真实 owner 事实可以传播到统一 Events 投影。 -2. 只发布用户可见 terminal `done` / `error` 摘要事件,payload 严格匹配 `docs/api-contracts/events/openapi.yaml` 现有 schema。 -3. 新增 AI 本域 publish outbox/job,异步 worker 调用 `EventsPublishApi`。 -4. 建立可验证链路:AI owner 本域事实 -> AI publish outbox/job -> AI worker -> `EventsPublishApi` -> `muse_unified_event` -> `/app-api/muse/events` SSE 可见。 -5. 形成 TDD、focused tests、migration tests、dependency gate、coverage gate、review gate 的可执行实施计划。 - -范围边界: - -1. 只在 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` 工作。 -2. 当前分支必须保持 `dev/1.0.0`。 -3. 当前 dirty baseline 已包含 AI 草稿代码、迁移、测试和 `docs/agent-specs/` 文档草稿;本子计划只记录处置策略,不清理、回退或改写 dirty/untracked baseline。 -4. 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 写任何文件。 -5. 不修改受保护文件: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -## 非目标 - -1. 不实现代码;本文件只定义执行计划。 -2. 不修改任何 OpenAPI 合同。 -3. 不修改 scanner 或 coverage JSON/Markdown 来达成 coverage。 -4. 不把 Events、P1R-7、Market 32 operations 标为 `completed`。 -5. 不复用 AI runtime job 作为 Events publish 队列。 -6. 不复用 Market projection outbox。 -7. 不同时改造 Knowledge、Market、Member(Account) 的 source owner propagation。 -8. 不让 Events server 反向依赖 AI / Knowledge / Market / Member / Content server。 -9. 不让统一 SSE Controller 扫描 AI 业务表。 -10. 不处理 Knowledge / Market / Account / Content 的 propagation 实现。 -11. 不新增独立 source 模块,不把 shared publish 库设为本切片必做前置。 -12. 不把 `DuplicateKeyException` 捕获作为正常幂等路径。 - -## 已验证事实 - -1. `git status --short` 在正确 worktree 显示 AI P1R-7b 草稿代码、迁移、测试和 `docs/agent-specs/` 文档草稿;这些 dirty/untracked 文件尚未通过本子计划 fresh review,也尚未运行本子计划验证。 -2. `git branch --show-current` 输出 `dev/1.0.0`。 -3. `git log -1 --oneline` 输出 `fa29753 test(p1r): 收口 Events SSE 门禁`。 -4. `AGENTS.md` 只引用 `@CLAUDE.md`;`CLAUDE.md` 明确本仓是设计文档 SSOT,Source 传播采用事件驱动 + 各模块自治,统一实时通信为 SSE。 -5. `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md` 推荐 P1R-7b 只选 AI task terminal event 第一切片,`done` / `error` 为最小事件类型,不进入 completed approval。 -6. `docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md` 记录 P1R-7a 未接入 source owner propagation,未运行 source owner publish tests,Events 和 Market 仍不得写成 `completed`。 -7. `docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md` 固定跨 owner 发布策略为 source owner 本域 outbox + worker 调用 Events API;Events server 不反向拉取 source owner 数据。 -8. Coverage JSON 当前 `summary` 为:total 233、completed 100、needsVerification 133、incomplete 0、genericPersistence 0、ssePlaceholder 0。 -9. Coverage operation 明细显示 `events streamEvents GET /app-api/muse/events = dedicated / needs_verification / P1R-7 End-to-End Acceptance`。 -10. Coverage operation 明细显示 Market 32 个 operation 的 completionStatus 均为 `needs_verification`。 -11. `EventsPublishApi.publish(EventsPublishReqDTO)` 已存在于 `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApi.java`。 -12. `EventsPublishReqDTO` 字段包含 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`eventType`、`resourceType`、`resourceId`、`payloadSummary`、`emittedAt`。 -13. `EventsPublishServiceImpl` 已实现 commandId 优先、source tuple 次级的幂等回查,非法 payload 写入 `rejected` 并不进入可见 SSE 查询。 -14. `UnifiedEventMapper.selectVisibleEventsForOwner` 只查询 `publish_status = accepted`、同租户、同 ownerUserId、未删除、`sequenceNo` 大于 cursor 的事件。 -15. `EventsStreamServiceImpl` 将 `done` event 投影为 `taskId`、`suggestionId` 和可选 `summary`;将 `error` event 投影为 `code`、`message`、可选 `detail`、可选 `retryable`。 -16. `docs/api-contracts/events/openapi.yaml` 已声明 `SSEDoneEvent` 必填 `taskId`、`suggestionId`,可选 `summary`;`SSEErrorEvent` 必填 `code`、`message`,可选 `detail`、`retryable`。 -17. `muse_ai_task_event` 已由 V12 创建,字段包含 `task_id`、`sequence_no`、`event_type`、`task_status`、`owner_user_id`、`actor_user_id`、`job_id`、`correlation_id`、`payload_summary`、`error_code`、`error_message`、`emitted_at`,并有 terminal 唯一约束 `uk_muse_ai_task_event_terminal`。 -18. `MuseAiTaskEventMapper.selectTerminalByTaskId` 已限定 terminal event 为 `done` / `error`。 -19. `MuseAiRuntimeProjectionService.applyRuntimeResponse` 成功终态写入 `done` task event,非可重试失败终态写入 `error` task event;可重试失败只让 job 回到 `queued`,不写 terminal event。 -20. `MuseJobServiceImpl.appendTaskCancellationEventIfAbsent` 在 cancel 路径写入 `eventType = error`、`taskStatus = cancelled`、`errorCode = AI_TASK_CANCELLED`、`errorMessage = AI task cancelled`,并且代码注释明确取消原因不能进入 SSE payload。 -21. `MuseAiRuntimeJobDispatcher` 采用 `@Scheduled(initialDelayString=..., fixedDelayString=...)` 调用 `dispatchOnce()` 的最小 worker 生命周期风格。 -22. `MuseSourceEventServiceImpl.retrySourceEvent` 只提交 `source_event_retry` job,并有注释说明不能把来源传播伪造成完成。 -23. 当前 dirty `muse-module-ai-server/pom.xml` 已出现 `muse-module-events-api` 依赖;它仍只是 AI 草稿的一部分,必须在实现阶段重新通过 dependency tree 证明只依赖 events-api、不依赖 events-server。 -24. `BaseDO` 暴露 `createTime/updateTime/creator/updater/deleted`,`TenantBaseDO` 继承 `BaseDO` 并新增 `tenantId`;项目 PostgreSQL DDL 约定字段名为 `create_time/update_time`。 -25. `muse-cloud/sql/muse/V1__init_content_schema.sql` 中 `update_updated_at_column()` 更新的是 `NEW.update_time`。 -26. 当前 `muse-cloud/sql/muse` 文件列表按数字排序最新已包含未提交草稿 `V17__extend_ai_events_publish_outbox.sql`;它不是已通过 migration 证据,后续必须在 P1R-7b 实现阶段决定修订沿用该 V17,或经用户明确批准后废弃重做。 -27. 当前草稿的 `MuseAiEventPublishOutboxServiceImpl` 存在捕获 `DuplicateKeyException` 作为 outbox 创建幂等回查路径的实现痕迹;这违反全局执行版“`ON CONFLICT DO NOTHING` / `insertIgnore` + 回查”的幂等合同,必须修订。 -28. 当前受保护文件定向 `git status --short` 无输出: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -## 当前 dirty 文件处置表 - -结论:以下 dirty baseline 不是完成证据。P1R-7b 子计划双 review PASS 前,所有 AI 草稿代码、迁移和测试都只能停留在候选或待修订状态;任何废弃、清理或回退都必须等用户明确批准。 - -| path | 处置策略 | 理由 | 是否需要用户批准 | -|---|---|---|---| -| `muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` | 修订沿用候选 | 可能需要新增 `muse-module-events-api` 依赖,但必须重新证明只依赖 api、不依赖 server。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionService.java` | 修订沿用候选 | 可能承接 terminal fact 同事务创建 outbox,但必须重新验证事务边界、payload allowlist 和非 terminal 不发布。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceImpl.java` | 修订沿用候选 | cancellation terminal `error` 可作为用户可见摘要候选,但必须保证取消原因不进入 SSE payload。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionServiceTest.java` | 修订沿用候选 | 可作为 terminal fact/outbox 绑定测试素材,但必须补本全局合同断言。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceTest.java` | 修订沿用候选 | 可作为 cancellation `error` 安全 payload 测试素材,但不能替代 fresh review。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxService.java` | 修订沿用候选 | AI 本域 publish outbox service 职责符合方向,但接口必须复核是否只覆盖 `done/error` terminal 摘要。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceImpl.java` | 必须修订 | 当前草稿捕获 `DuplicateKeyException` 作为正常幂等路径;P1R-7b 必须改为 `ON CONFLICT DO NOTHING` / `insertIgnore` + 回查,避免 PostgreSQL 同事务异常导致 terminal fact 回滚风险。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/dataobject/muse/MuseAiEventPublishOutboxDO.java` | 修订沿用候选 | DO 可承接 AI outbox 表,但字段必须与全局 outbox 合同、BaseDO/TenantBaseDO 字段约定和 V17 migration 一致。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapper.java` | 必须修订 | mapper 必须提供 `insertIgnore` 或等价 `ON CONFLICT DO NOTHING`,并用原子 claim 支持 queued/retryable/stale running。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceTest.java` | 必须修订 | 现有测试如断言 `DuplicateKeyException` 幂等路径,必须改为 `insertIgnore` 返回 0 + 回查;否则会固化错误实现。 | 否;废弃或回退需要 | -| `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapperTest.java` | 修订沿用候选 | 可承接 mapper claim / insertIgnore SQL 断言,但必须覆盖全局 outbox 状态机。 | 否;废弃或回退需要 | -| `muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql` | 修订沿用候选 | V17 版本号需在实现前重新 live 查证;表结构必须对齐 outbox 合同、claim 索引和 update_time trigger。 | 否;废弃或回退需要 | -| `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishMigrationSqlTest.java` | 修订沿用候选 | 可作为 migration SQL 文本门禁,但必须补 V17/字段/索引/trigger/独立测试库保护断言。 | 否;废弃或回退需要 | -| `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishFlywayMigrationIT.java` | 修订沿用候选 | 可作为 Flyway `_test` 验证素材,但必须证明只跑独立 `_test` 库且 surefire XML 有真实 test count。 | 否;废弃或回退需要 | -| `docs/agent-specs/*` | 保持不动 | 本轮只允许修订本执行版和 `.agent`;其他审阅版/全局执行版/草稿不能被顺手改写。 | 否;废弃或删除需要 | - -废弃规则: - -1. 上表任何代码、迁移、测试草稿如果后续选择“废弃重做”,必须先向用户说明废弃范围、原因、替代方案和回滚影响,并取得明确批准。 -2. 未获批准前,不得删除 untracked 文件,不得 `git checkout --` 回退已修改文件,不得用清理 dirty baseline 的方式制造“计划已收口”的假象。 - -## 推断 - -1. AI 是 P1R-7b 的最小 owner 切片,因为它已有 `muse_ai_task_event` 终态事实、ownerUserId、tenantId、sequence/replay 基础和 focused tests。 -2. 需要新增清晰职责的 AI publish outbox/job,因为现有 AI runtime job 的职责是调用 New-API runtime,不是统一 Events 发布补偿。 -3. `done` payload 不能直接复用 `MuseAiRuntimeProjectionService.donePayload`,因为现有 payload 缺少 Events OpenAPI 必填的 `taskId`,需要由 outbox mapper/service 从 task event + task/suggestion 事实组合生成 allowlist payload。 -4. `error` payload 可以从 `MuseAiTaskEventDO.errorCode/errorMessage/payloadSummary.retryable` 归一化生成,但必须先做敏感词和字段 allowlist 过滤。 -5. 如果 worker 与 terminal fact 写入不在同一事务内补 outbox,存在终态事实已落库但未发布的不可追踪缺口;因此 outbox 写入必须和 terminal fact 绑定在同一事务边界内。 - -## 假设 - -1. P1R-7b 允许在 AI owner 内新增 `muse_ai_event_publish_outbox` 或等价职责表。 -2. 实现阶段可以只在 AI 模块新增 `muse-module-events-api` 编译依赖,不需要引入 events-server。 -3. 实现阶段可以新增 focused integration test 或 mapper-level test 来证明 `muse_ai_event_publish_outbox` migration、claim、retry、dead_letter 和 SSE 可见链路。 -4. P1R `_test` PostgreSQL 验证仍使用项目既有 `~/.config/muse-repo/infra.env` 方式,但运行前必须确认目标库是独立测试库。 - -## 全局架构主线 - -1. AI owner 只写本域事实和本域 outbox,不直接写 `muse_unified_event`。 -2. AI worker 只调用 `EventsPublishApi`,不依赖 Events server 实现类。 -3. Events owner 只接收发布 DTO、做幂等、payload 校验、脱敏、落库和 SSE 可见性过滤。 -4. SSE 查询只读 `muse_unified_event`,不反查 AI 表。 -5. replay 只能重放 AI 本域 outbox 记录;禁止 operator 或补偿任务绕过 source owner 直接向 Events 注入伪造 payload。 - -## 继承全局合同 - -结论:P1R-7b 不重新发明合同,只把全局 P1R7 Source Owner Propagation 合同投影到 AI terminal event 第一切片。若本文件与全局执行版冲突,以全局执行版为准;后续实现前必须先修订本文件并重新 review。 - -### 依赖合同 - -1. `muse-module-ai-server` 只允许新增或保留 `muse-module-events-api` 依赖。 -2. `muse-module-ai-server` 禁止依赖 `muse-module-events-server`。 -3. `muse-module-events-server` 禁止依赖 AI / Knowledge / Market / Member / Content server。 -4. `muse-server` 可以装配 AI server 与 Events server,但不能把装配关系写成业务反向依赖。 -5. P1R-7b 实现前和实现后都必须运行 dependency tree 验证;验证命令只作为框架写入本计划,本轮不运行。 - -### 发布 envelope 合同 - -1. `commandId` 必须稳定、短、幂等,推荐 `ai_evt:`,长度 `<= 128`。 -2. `tenantId`、`ownerUserId`、`sourceOwner=ai`、`sourceType=ai_task_event`、`sourceId`、`sourceRevision`、`eventType`、`resourceType/resourceId`、`payloadSummary`、`emittedAt` 都必须在 outbox 中可追溯。 -3. `eventType` 只允许使用 Events OpenAPI 既有 `done` / `error`,不得新增 AI 私有事件类型绕过合同。 -4. `sourceRevision` 使用 terminal event `sequence_no` 字符串;为空时不得创建可发布 outbox。 -5. `emittedAt` 使用 AI terminal fact 发生时间,不使用 worker 发布时间替代事实时间。 - -### 用户可见事件判定 - -1. P1R-7b 只发布用户可见 terminal `done/error` 摘要。 -2. 可重试 runtime failure、worker heartbeat、projection rebuild、source retry job、内部调度状态不进入统一 SSE。 -3. cancellation 只能作为 `error` 摘要发布,且只包含 `AI_TASK_CANCELLED`、`AI task cancelled`、`retryable=false`,不得包含用户提交的取消原因或内部审计备注。 -4. payload 需要完整 prompt、provider raw、正文、授权详情、风控原始证据才能解释时,必须 fail-closed,不得发布到 SSE。 -5. 本切片不处理 Knowledge / Market / Account / Content 的用户可见事件。 - -### Outbox 合同 - -1. AI outbox 必须支持 `queued/running/retryable/published/dead_letter` 或全局执行版认可的等价状态。 -2. claim 必须使用 `FOR UPDATE SKIP LOCKED` 或等价原子领取机制,并覆盖 queued、到期 retryable、stale running。 -3. claim 时递增 `attempt_count`;失败处理不得二次递增 attempt。 -4. 必须有 `max_attempt`、`next_retry_at`、`claimed_at`、`claim_expires_at`、claim 索引、固定退避策略。 -5. 幂等插入必须使用 `ON CONFLICT DO NOTHING` / `insertIgnore` + 回查;禁止捕获 `DuplicateKeyException` 作为正常幂等路径。 -6. `dead_letter` 不自动 replay;只有确认是临时故障的记录才能由用户批准后重置为 `retryable`。 -7. 状态转换必须有中文日志,包含 owner、tenantId、ownerUserId、source tuple、outboxId、attempt、状态转换和安全错误摘要。 - -### Payload 合同 - -1. `done` payload 只允许 `taskId`、`suggestionId`、可选安全短 `summary`。 -2. `error` payload 只允许 `code`、`message`、可选安全 `detail`、可选 `retryable`。 -3. `taskId` 和 `suggestionId` 必须满足 Events OpenAPI int64 语义;不满足时 fail-closed 到 `dead_letter`,不得调用 `EventsPublishApi`。 -4. 禁止 prompt、userInstruction、provider request/response raw body、token、apiKey、authorization、bearer、secret、runtimePermissionEnvelope、source private context、full output、contentSnapshot 正文进入 payload。 -5. payload invalid、Events rejected、Events blocked 都不得进入 SSE 可见查询。 - -## 涉及模块与文件路径 - -### 必须只读核对 - -- `AGENTS.md` -- `CLAUDE.md` -- `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md` -- `docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md` -- `docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md` -- `docs/api-contracts/events/openapi.yaml` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -### 实现阶段允许修改 - -说明:本节的“新增/修改”描述 P1R-7b clean baseline 目标写集;如果当前 dirty baseline 已经存在同名草稿文件,后续 implementer 必须先按“当前 dirty 文件处置表”判断修订沿用或用户批准后废弃重做,不能因为文件已存在就跳过 TDD、验证或 fresh review。 - -- `muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql`,当前 dirty baseline 已存在该草稿;实现阶段若选择修订沿用,必须重新证明版本号、字段、索引和 Flyway `_test` 全部合规;若选择废弃重做,必须先获得用户明确批准。 -- `muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/dataobject/muse/MuseAiEventPublishOutboxDO.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapper.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxService.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceImpl.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishWorker.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionService.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceImpl.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/framework/ai/config/MuseAiProperties.java`,如果实现阶段复用项目集中配置承载 worker 开关;否则必须在实现报告写明等价配置承载点。 - -### 实现阶段允许新增或修改测试 - -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapperTest.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceTest.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishWorkerTest.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionServiceTest.java` -- `muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishMigrationSqlTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishFlywayMigrationIT.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishDependencyTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishEndToEndTest.java` - -### 必须保持不修改 - -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 数据流与依赖方向 - -```mermaid -sequenceDiagram - participant Runtime as MuseAiRuntimeProjectionService - participant TaskEvent as muse_ai_task_event - participant Outbox as muse_ai_event_publish_outbox - participant Worker as MuseAiEventPublishWorker - participant Api as EventsPublishApi - participant Unified as muse_unified_event - participant SSE as /app-api/muse/events - - Runtime->>TaskEvent: 同事务写 done/error terminal fact - Runtime->>Outbox: 同事务写 queued publish job - Worker->>Outbox: SKIP LOCKED claim queued/retryable - Worker->>Api: publish(EventsPublishReqDTO) - Api->>Unified: 幂等落库 accepted/rejected/blocked - Api-->>Worker: eventId/sequenceNo/status/duplicate - Worker->>Outbox: published 或 dead_letter 或 retryable - SSE->>Unified: 只读 accepted + tenant + ownerUserId + sequence -``` - -依赖方向: - -1. `muse-module-ai-server` 可以新增 `muse-module-events-api` 依赖。 -2. `muse-module-ai-server` 禁止依赖 `muse-module-events-server`。 -3. `muse-module-events-server` 禁止依赖 AI / Knowledge / Market / Member / Content server。 -4. `muse-server` 继续装配 AI server 和 Events server。 - -## 数据契约与 payload allowlist - -统一发布 DTO: - -| 字段 | AI outbox 来源 | 规则 | -|---|---|---| -| `commandId` | outbox.command_id | 必填,冻结为短格式或 hash 格式,推荐 `ai_evt:`;禁止使用包含 tenantId/taskId/sourceRevision/eventType 的长拼接格式作为推荐格式,测试必须覆盖长度 `<= 128` | -| `tenantId` | outbox.tenant_id | 必填 | -| `ownerUserId` | task event owner_user_id | 必填 | -| `sourceOwner` | 常量 `ai` | 必填 | -| `sourceType` | 常量 `ai_task_event` | 必填 | -| `sourceId` | task event task_id | 必填,字符串形式 | -| `sourceRevision` | task event sequence_no | 必填,字符串形式;为空时不允许生成 outbox | -| `eventType` | task event event_type | 只允许 `done` / `error` | -| `resourceType` | 常量 `ai_task` | 必填或可空,但写入 outbox 时保持一致 | -| `resourceId` | task event task_id | 字符串形式 | -| `payloadSummary` | allowlist mapper 生成 | 只允许下方字段 | -| `emittedAt` | task event emitted_at | 必填 | - -全局 payload 规则: - -1. `muse_ai_task_event.task_id` 是 `VARCHAR`,但 SSE `done` payload 的 `taskId` 必须是 int64 语义数字;P1R-7b 最小规则要求 `done` 和 `error` terminal event 在进入 Events publish 前都先校验 taskId 可解析为 int64。 -2. 非数字 taskId 必须 fail-closed 到 AI outbox `dead_letter`,`last_error_code = AI_EVENTS_TASK_ID_NOT_NUMERIC`,不得调用 `EventsPublishApi`,不得发布 `done` 或 `error` 到 SSE 可见事件。 -3. `command_id` 必须由稳定 source tuple 计算短 hash,例如对 `tenantId|taskId|sourceRevision|eventType` 做 sha256 后取 32 位十六进制摘要并加 `ai_evt:` 前缀;测试必须证明生成结果长度 `<= 128`。 - -`done` payload allowlist: - -```json -{ - "taskId": 3001, - "suggestionId": 2001, - "summary": "AI task completed" -} -``` - -规则: - -1. `taskId` 必须是 int64 语义数字。 -2. `suggestionId` 必须是 int64 语义数字;如果 task 没有 suggestionId,则不得发布 `done`,outbox 写入或保持 `dead_letter`,`last_error_code = AI_EVENTS_DONE_PAYLOAD_INCOMPLETE`,并且不得进入 SSE 可见事件。 -3. `summary` 可选,必须是安全短文本;不得来自 provider raw body 或完整输出正文。 - -`error` payload allowlist: - -```json -{ - "code": "AI_NEW_API_UNAVAILABLE", - "message": "AI task failed", - "detail": "runtime unavailable", - "retryable": false -} -``` - -规则: - -1. `code` 必须来自 AI 本域归一化错误码,缺失时使用 `AI_TASK_FAILED`。 -2. `message` 必须是安全错误摘要,不能包含 provider 原始响应、prompt、token、授权头或私有上下文。 -3. `detail` 可选,只允许开发安全摘要;如包含敏感关键词则丢弃。 -4. `retryable` 可选;terminal error 对应非可重试失败时为 `false`。 -5. cancellation terminal event 是 `MuseJobServiceImpl.appendTaskCancellationEventIfAbsent` 写出的合同内 `error` 事件,payload allowlist 只能包含 `code = AI_TASK_CANCELLED`、`message = AI task cancelled`、`retryable = false`;不得包含 `detail`,不得携带 admin/app 用户提交的取消原因。 - -禁止字段: - -1. prompt / userInstruction 原文。 -2. provider raw body / provider request body / provider response body。 -3. token / apiKey / authorization / bearer / secret。 -4. runtimePermissionEnvelope 原文。 -5. source private context / full output / contentSnapshot 正文。 -6. 未在 `docs/api-contracts/events/openapi.yaml` 声明的扩展字段。 - -## 迁移与表设计计划 - -表名:`muse_ai_event_publish_outbox`。 - -当前 dirty baseline 已存在未提交 `V17__extend_ai_events_publish_outbox.sql` 草稿。实现前必须重新 live 查证 `muse-cloud/sql/muse` 版本号:如果 V17 草稿仍存在,只能修订沿用或经用户明确批准后废弃重做;如果上游已新增后续版本,必须先停下报告冲突,并按用户确认选择下一 migration 号。 - -字段计划: - -| 字段 | 类型 | 约束 | -|---|---|---| -| `id` | `BIGINT GENERATED ALWAYS AS IDENTITY` | 主键 | -| `tenant_id` | `BIGINT` | `NOT NULL` | -| `owner_user_id` | `BIGINT` | `NOT NULL` | -| `task_id` | `VARCHAR(128)` | `NOT NULL` | -| `source_event_id` | `BIGINT` | `NOT NULL`,引用 `muse_ai_task_event.id` 这个 AI task event 本域自增主键 | -| `source_revision` | `VARCHAR(80)` | `NOT NULL`,使用 task event `sequence_no` 字符串 | -| `event_type` | `VARCHAR(32)` | `NOT NULL CHECK IN ('done','error')` | -| `payload_summary` | `JSONB` | `NOT NULL DEFAULT '{}'::jsonb` | -| `command_id` | `VARCHAR(128)` | `NOT NULL` | -| `publish_status` | `VARCHAR(32)` | `NOT NULL CHECK IN ('queued','running','published','retryable','dead_letter')` | -| `attempt_count` | `INT` | `NOT NULL DEFAULT 0` | -| `claimed_at` | `TIMESTAMP` | 可空;worker claim 时写入当前时间 | -| `claim_expires_at` | `TIMESTAMP` | 可空;worker claim 时写入 `now + lease`,用于 crash-after-claim 后回收 stale running | -| `next_retry_at` | `TIMESTAMP` | 可空 | -| `last_error_code` | `VARCHAR(64)` | 可空 | -| `last_error_message` | `TEXT` | 可空,必须是安全摘要 | -| `published_event_id` | `VARCHAR(128)` | 可空 | -| `published_sequence_no` | `BIGINT` | 可空 | -| `create_time` | `TIMESTAMP` | `NOT NULL DEFAULT CURRENT_TIMESTAMP`,对齐 `BaseDO.createTime` / 项目 DDL 字段约定 | -| `update_time` | `TIMESTAMP` | `NOT NULL DEFAULT CURRENT_TIMESTAMP`,对齐 `BaseDO.updateTime` / 项目 DDL 字段约定 | -| `creator` | `VARCHAR(64)` | `NOT NULL DEFAULT ''`,如项目基类需要 | -| `updater` | `VARCHAR(64)` | `NOT NULL DEFAULT ''`,如项目基类需要 | -| `deleted` | `BOOLEAN` | `NOT NULL DEFAULT FALSE` | - -约束与索引: - -1. `uk_muse_ai_event_publish_outbox_command`:`UNIQUE (tenant_id, command_id)`。 -2. `uk_muse_ai_event_publish_outbox_source`:`UNIQUE (tenant_id, task_id, source_revision, event_type)`。 -3. `idx_muse_ai_event_publish_outbox_claim`:`(publish_status, claim_expires_at, next_retry_at, create_time, id)`,过滤 `deleted = FALSE`;必须支持 claim 查询同时覆盖 `queued`、到期 `retryable`、以及 `running AND claim_expires_at < now` 的 stale running。 -4. `idx_muse_ai_event_publish_outbox_owner_status`:`(tenant_id, owner_user_id, publish_status, create_time)`。 -5. `trg_muse_ai_event_publish_outbox_update_time`:复用 `update_updated_at_column()`,触发器必须更新 `update_time`。 - -## 状态机 - -```mermaid -stateDiagram-v2 - [*] --> queued: terminal fact 同事务创建 outbox - queued --> running: claim, attempt_count + 1, 写 lease - retryable --> running: next_retry_at 到期 claim, attempt_count + 1, 写 lease - running --> running: claim_expires_at < now stale reclaim, attempt_count + 1, 刷新 lease - running --> published: Events accepted 或 duplicate - running --> dead_letter: Events rejected/blocked 或 payload 不合规 - running --> retryable: Events temporary failure 或调用异常 - running --> dead_letter: stale reclaim 后 attempt_count 超过 max attempt - retryable --> dead_letter: claim 或失败处理发现 attempt_count 超过 max attempt - published --> [*] - dead_letter --> [*] -``` - -状态含义: - -1. `queued`:待发布,`attempt_count = 0`,`claimed_at` / `claim_expires_at` 为空。 -2. `running`:被 worker 原子领取,必须写 `claimed_at = now`、`claim_expires_at = now + lease`,并有中文日志记录 outbox id、tenantId、taskId、eventType、attempt、claimExpiresAt。 -3. `published`:Events `CommonResult` 成功且 `EventsPublishRespDTO.publishStatus = accepted`,或 `publishStatus = accepted` 且 `duplicate = true`;必须记录 `published_event_id` 和 `published_sequence_no`。 -4. `retryable`:临时失败,设置 `next_retry_at`,保留安全错误码和摘要。 -5. `dead_letter`:payload 不合规、Events 返回 rejected/blocked、或超过最大重试次数;不得进入 SSE 可见事件。 - -注意: - -1. AI outbox 本地 `publish_status` 不存在 `blocked`;Events 侧 `accepted/rejected/blocked` 是统一事件投影层状态,不是 AI outbox 本地状态。 -2. worker 不能只看 `CommonResult` 成功/失败;必须读取 `EventsPublishRespDTO.publishStatus` 并按 `accepted/duplicate/rejected/blocked` 分流。`CommonResult` 成功但 `publishStatus` 为空或未知时,必须 fail-closed 写 `retryable` 或 `dead_letter`,不得伪造成 `published`。 -3. claim 查询必须覆盖三类可领取记录:`queued`、`retryable AND next_retry_at <= now`、`running AND claim_expires_at < now`。未到期的 `running` 不得被领取。 -4. attempt 语义冻结为 claim 次数:每次从 `queued`、到期 `retryable` 或 stale `running` 被 claim 时统一 `attempt_count + 1`;失败处理不得再次递增 attempt,避免一次 publish 失败被计算两次。 -5. stale running reclaim 采用 `running -> running` 最小语义:重领时刷新 `claimed_at/claim_expires_at` 并递增 attempt;如果递增后超过 `max_attempt`,必须进入 `dead_letter`,错误码 `AI_EVENTS_PUBLISH_RETRY_EXHAUSTED`,避免 crash-after-claim 无限循环。 - -## 幂等策略 - -1. AI outbox 创建幂等:同一 `(tenant_id, task_id, source_revision, event_type)` 只能有一条 outbox。 -2. command 幂等:同一 `(tenant_id, command_id)` 只能有一条 outbox。 -3. Events publish 幂等:同一 `commandId` 或同一 source tuple 重放必须得到相同 `eventId / sequenceNo`。 -4. worker 幂等:worker 重试只读取 outbox 记录,不重新读取不稳定上下文构造不同 payload。 -5. terminal fact 幂等:`MuseAiTaskEventMapper.selectTerminalByTaskId` 和 DB terminal 唯一约束继续阻止同一 task 多个 terminal event。 -6. replay 幂等:人工 replay 只允许把 `dead_letter` 中确认为临时故障的记录重置为 `retryable`;`AI_EVENTS_DONE_PAYLOAD_INCOMPLETE` 等 payload 不合规记录不得直接 replay。 - -## 失败、重试与 dead_letter - -临时失败: - -1. `EventsPublishApi` 调用抛出网络、超时、RPC unavailable 等异常。 -2. `CommonResult` 表示临时不可用或无响应。 -3. worker 处理时发生非 payload/contract 类异常。 - -处理方式: - -1. `attempt_count` 已在 claim 时递增,失败处理不得再次 `+ 1`。 -2. 当前 `attempt_count` 未达到上限时写 `retryable`,`next_retry_at = now + backoff`,并清空或保留 lease 字段时必须有一致测试;推荐清空 `claimed_at/claim_expires_at`,让下一次 claim 只由 `next_retry_at` 控制。 -3. backoff 建议最小固定序列 `1s, 2s, 4s, 8s, 16s`,超出使用 16s;实现代码必须用中文注释说明这是 P1R-7b 最小退避,不引入全局任务框架。 -4. 当前 `attempt_count` 达到或超过上限写 `dead_letter`,错误码 `AI_EVENTS_PUBLISH_RETRY_EXHAUSTED`。 - -stale running: - -1. worker crash-after-claim 后,记录会停留在 `running`,直到 `claim_expires_at < now`。 -2. stale running 由后续 claim 原子重领,状态保持 `running -> running`,刷新 `claimed_at/claim_expires_at`,并按 claim 次数递增 `attempt_count`。 -3. stale running 重领前或重领后发现 `attempt_count` 超过 `max_attempt`,必须写 `dead_letter`,不得无限重领。 -4. `claim_expires_at` 为空的 `running` 属于不合规历史状态;实现阶段必须 fail-closed 到 `retryable` 或 `dead_letter`,并用测试固定规则,不得被永久卡住。 - -永久失败: - -1. payload 缺少 OpenAPI 必填字段。 -2. payload 包含 prompt、provider raw body、token、authorization、bearer、secret 等敏感内容。 -3. `EventsPublishApi` 返回 `rejected` 或 `blocked`。 - -处理方式: - -1. 立即写 `dead_letter`。 -2. 不重试。 -3. 记录安全日志和审计摘要。 -4. 不向 SSE 伪造事件。 - -## Worker 生命周期与停止策略 - -1. `MuseAiEventPublishWorker` 采用现有 `MuseAiRuntimeJobDispatcher` 的最小生命周期风格:`@Scheduled(initialDelayString = "${muse.ai.events.publish-worker.initial-delay-ms:1000}", fixedDelayString = "${muse.ai.events.publish-worker.fixed-delay-ms:1000}")` 只调用 `dispatchOnce()`。 -2. 新增配置开关 `muse.ai.events.publish-worker.enabled`,默认 `true`;实现阶段如项目配置类已有更适合的布尔默认约定,必须保持默认启用或在实现报告写明偏差原因。 -3. `enabled=false` 时 scheduled 入口和手动入口都不得 claim outbox;`enabled=true` 时 scheduled 入口调用 `dispatchOnce()` 并按 claim 结果处理一批或一条记录。 -4. `dispatchOnce()` 必须保留可测试的同步方法,测试不依赖真实时间、不等待 scheduler。 -5. 回滚或应急停止方式:关闭 `muse.ai.events.publish-worker.enabled`,或在更高风险回滚中移除 worker bean 装配。已经 claim 为 `running` 的记录不做人工改写;重新启用 worker 后由 `claim_expires_at` lease 回收 stale running,并按 retry/dead_letter 规则继续处理。 - -## 日志、审计与可观测性 - -实现阶段必须补中文日志和注释: - -1. 创建 outbox:记录 `tenantId`、`ownerUserId`、`taskId`、`eventType`、`sourceRevision`、`commandId`,不记录 payload 正文。 -2. worker claim:记录 outbox id、attempt、状态转换、claimedAt、claimExpiresAt,以及是否 stale running reclaim。 -3. publish 成功:记录 outbox id、`publishedEventId`、`publishedSequenceNo`、`duplicate`。 -4. publish rejected/blocked:记录 outbox id、错误码、安全摘要。 -5. temporary failure:记录 outbox id、错误类型、nextRetryAt。 -6. dead_letter:记录 outbox id、最终错误码、attemptCount。 -7. payload sanitizer 或 allowlist 拒绝:必须有安全日志,不能输出原始敏感值。 - -审计要求: - -1. 如 AI 现有 `MuseAiAuditService` 适用于内部业务审计,则记录 `operationId = aiEventsPublish` 或实现阶段冻结的等价 operation id。 -2. 审计 request/response summary 只能包含 outbox id、taskId、eventType、publishStatus、publishedEventId、publishedSequenceNo、errorCode。 -3. 审计不阻塞主业务事实落库;审计失败不得伪造成 publish 成功。 - -## TDD 与 review gate 总规则 - -P1R-7b 子计划 gate: - -1. 本文件必须先完成 fresh spec review。 -2. 本文件必须再完成 fresh quality / feasibility review。 -3. 双 PASS 前不得继续当前 AI 草稿实现,不得运行当前草稿测试,不得提交或推送。 -4. 若任一 review FAIL,只修订本文件和必要 `.agent` 状态说明;不得通过改代码、改 OpenAPI、改 scanner、改 coverage 绕过 review。 - -每个实现 Task 必须按以下顺序执行: - -1. fresh subagent implementer 领取单个 Task。 -2. 先写失败测试或门禁。 -3. 运行 focused test,确认因目标缺口失败。 -4. 写最小实现。 -5. 运行 focused tests,确认通过。 -6. 运行该 Task 指定验证命令。 -7. fresh spec review。 -8. fresh quality review。 -9. 双 PASS 后才允许进入下一 Task。 - -任何 Task 若出现 FAIL: - -1. implementer 只修当前 Task 范围内问题。 -2. 修复后重新跑 focused tests。 -3. 重新进行 fresh spec review + fresh quality review。 -4. 不得用 OpenAPI/scanner/coverage 状态改动绕过失败。 - -## Task 0:子计划 fresh review gate - -执行角色:fresh spec reviewer + fresh quality / feasibility reviewer。 - -文件范围: - -- 允许修改:`docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md` -- 允许修改:`docs/agent-specs/.agent` -- 保持不动:任何 Java / SQL / test / `pom.xml` / OpenAPI / scanner / coverage 文件。 - -步骤: - -1. 只读核对全局审阅版和全局执行版已把 P1R-7b 定义为 AI terminal event 第一切片。 -2. 只读核对本文件是否继承依赖合同、发布 envelope 合同、用户可见事件判定、outbox 合同和 payload 合同。 -3. 只读核对当前 dirty 文件处置表是否覆盖 AI 草稿代码、迁移、测试和 `docs/agent-specs/*`。 -4. 只读核对 `DuplicateKeyException` 幂等路径是否被列为必须修订点。 -5. 只读核对本文件是否明确双 review PASS 前不得继续 AI 草稿实现。 - -验证命令框架,本轮计划修订不运行: - -```bash -git status --short --branch -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -rg -n "P1R-7b|AI terminal|fresh spec review|fresh quality|DuplicateKeyException|insertIgnore|ON CONFLICT|completed" docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md -``` - -完成条件: - -1. Fresh spec review PASS。 -2. Fresh quality / feasibility review PASS。 -3. protected files 定向状态无改动。 -4. 输出下一步是“由 fresh implementer 处理 Task 1/Task 2”,不是直接继续当前 AI 草稿。 - -## Task 1:Preflight 与依赖/现状冻结 - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 只读:`AGENTS.md` -- 只读:`CLAUDE.md` -- 只读:`docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation审阅版.md` -- 只读:`docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md` -- 只读:`docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md` -- 只读:`docs/superpowers/reports/p1r-api-coverage.json` -- 只读:`muse-cloud/muse-module-events/**` -- 只读:`muse-cloud/muse-module-ai/**` -- 只读:`muse-cloud/sql/muse/**` - -步骤: - -1. 记录 `pwd`、`git status --short --branch`、`git branch --show-current`、`git log -1 --oneline`。 -2. 定向检查受保护文件和 coverage 报告无改动。 -3. 读取 coverage summary 和 Events/Market operation 明细。 -4. 检查 `muse-module-ai-server/pom.xml` 当前 dirty 草稿中的 `muse-module-events-api` 依赖,并确认没有 `muse-module-events-server` 依赖。 -5. 检查 `muse-cloud/sql/muse` 当前最新 migration 版本,区分已推送基线和当前未提交 V17 草稿。 -6. 检查 Events API、Events publish service、UnifiedEvent mapper、Events stream service 的现状。 -7. 检查 AI task event、runtime projection、runtime dispatcher、job mapper、source event retry 的现状。 - -验证命令: - -```bash -pwd -git status --short --branch -git branch --show-current -git log -1 --oneline -git diff --name-only -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -jq '.summary' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="events") | [.domain,.operationId,.method,.path,.implementationStatus,.completionStatus,.targetStage] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '[.operations[] | select(.domain=="market") | (.implementationStatus + "/" + .completionStatus)] | group_by(.)[] | [(.[0]), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -rg -n "muse-module-events-api|muse-module-events-server" muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml muse-cloud/muse-server/pom.xml muse-cloud/muse-module-events/muse-module-events-server/pom.xml -find muse-cloud/sql/muse -maxdepth 1 -type f -name 'V*__*.sql' \ - | sed -E 's#.*/V([0-9]+)__.*#\1&#' \ - | sort -n \ - | tail -n 10 -``` - -完成条件: - -1. 工作区、分支、dirty baseline 与本执行版约束一致。 -2. 受保护文件和 coverage 报告无改动。 -3. AI 当前 `muse-module-events-api` / `muse-module-events-server` 依赖状态有 live 证据。 -4. 最新 migration 版本有 live 证据,且必须按 `V` 后数字排序;如果当前 V17 草稿仍存在,必须把它列为“修订沿用候选”,不能误写成已通过 migration。 -5. spec review PASS + quality review PASS。 - -## Task 2:AI publish outbox/job schema - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。Task 2 是第一个可写代码/迁移/测试的实现 Task,必须等本子计划 Task 0 双 review PASS 后,由 fresh implementer 重新领取;当前会话不得直接继续 AI 草稿实现。 - -文件范围: - -- 修订或新增:`muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql`。当前 dirty baseline 已存在 V17 草稿;Task 2 implementer 必须先判断它是修订沿用还是在用户批准后废弃重做,不能把已存在文件当成已通过事实。 -- 修订或新增:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishMigrationSqlTest.java`。当前 dirty baseline 已存在草稿;Task 2 必须让测试针对当前 V17 真实缺口失败,再修订到通过。 -- 修订或新增:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishFlywayMigrationIT.java`。当前 dirty baseline 已存在草稿;Task 2 必须重新证明独立 `_test` 库保护和真实 test count。 - -测试范围: - -1. SQL 文本测试必须断言表名、字段、唯一约束、claim 索引、状态 CHECK、event_type CHECK、`update_time` trigger,且 `source_event_id` 字段类型唯一冻结为 `BIGINT NOT NULL`。 -2. SQL 文本测试必须断言 outbox schema 能支持后续 claim:`claimed_at TIMESTAMP`、`claim_expires_at TIMESTAMP` 两个 lease 字段存在,`publish_status`、`next_retry_at`、`attempt_count` 字段存在,且 claim 索引包含 `publish_status, claim_expires_at, next_retry_at, create_time, id`。 -3. SQL 文本测试必须断言 outbox 表使用项目约定字段 `creator/create_time/updater/update_time/deleted`,并通过断言 `BaseDO.createTime/updateTime`、`TenantBaseDO extends BaseDO` 或等价反射/文本证据锁定项目基类约定。 -4. SQL 文本测试必须断言 `update_updated_at_column()` 更新 `update_time`,并断言 outbox migration 不使用非项目约定的时间字段名。 -5. SQL 文本测试必须断言 `command_id VARCHAR(128)`,并配套 service/mapper 测试冻结 `ai_evt:` 或等价短 hash 格式,生成长度 `<= 128`。 -6. SQL 文本测试只断言 schema 层 claim 支撑能力:`publish_status` 状态 CHECK、`attempt_count` 非负约束、lease 字段和 retry 字段可空性、claim 索引字段顺序;不得要求断言具体 mapper claim SQL 或 mapper 注解。 -7. Flyway `_test` 必须在独立测试库执行,不能 clean 开发库或真实库。 -8. migration 必须和 V12/V16 共存,不修改既有表语义。 -9. 如果沿用当前 V17 草稿,必须先修订所有不符合全局 outbox 合同的字段、索引、状态和幂等语义;不能因为文件已存在就跳过 TDD 失败测试和 fresh review。 - -TDD 步骤: - -1. 先写或修订 `P1rAiEventsPublishMigrationSqlTest`,让它针对当前 V17 草稿的真实缺口失败;如果 V17 草稿被用户批准废弃后不存在,则测试应因 migration 未定义失败。 -2. 运行 focused test,确认失败原因来自 schema 合同缺口,而不是测试未运行。 -3. 修订或新增 migration,包含本执行版字段、lease 字段、约束、索引和 trigger。 -4. 运行 SQL 文本测试通过。 -5. 新增或扩展 Flyway IT,证明所有 migrations 在独立 `_test` 库可执行。 -6. 运行 Flyway `_test`。 - -验证命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am -Dtest=P1rAiEventsPublishMigrationSqlTest test -set -a -source ~/.config/muse-repo/infra.env -set +a -export P1R7B_TEST_DB="${P1R7B_TEST_DB:-muse_p1r7b_events_publish_test}" -case "$P1R7B_TEST_DB" in *_test) ;; *) echo "P1R7B_TEST_DB must end with _test"; exit 1;; esac -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" timeout 240 mvn test \ - -pl muse-server \ - -Dtest=P1rAiEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -DsocksNonProxyHosts="127.0.0.1|localhost|100.64.*|100.*" \ - -Dhttp.proxyHost= \ - -Dhttps.proxyHost= \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7B_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -完成条件: - -1. migration 版本号来自 Task 1 live 查证。 -2. 表字段覆盖本执行版迁移计划要求;`source_event_id` 必须是 `BIGINT NOT NULL`,引用 `muse_ai_task_event.id`,如果实现阶段发现 DO/mapper 名称不同,也必须先 live 查证后保持 Long id 语义。 -3. 唯一约束覆盖 commandId 和 task/source/event tuple。 -4. SQL 文本测试通过,且 Task 2 完成证据只来自 migration SQL 与 migration tests,不依赖 Task 4 才新增或修改的 mapper 文件。 -5. 独立 `_test` Flyway IT 通过,并断言目标 version、description、已执行 migration count 随 Task 1 live 下一版本号更新;保留 `_test` 库名保护,非 `_test` 库必须 fail-fast。 -6. SQL 文本测试和 Flyway IT 都必须证明目标测试类实际运行:`target/surefire-reports/TEST-...P1rAiEventsPublishMigrationSqlTest.xml` 与 `TEST-...P1rAiEventsPublishFlywayMigrationIT.xml` 存在,且各自 test count > 0。 -7. spec review PASS + quality review PASS。 - -## Task 3:AI DO / Mapper / Service 与 terminal fact 同事务写 outbox - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/dataobject/muse/MuseAiEventPublishOutboxDO.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapper.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxService.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceImpl.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionService.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceImpl.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapperTest.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceTest.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiRuntimeProjectionServiceTest.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseJobServiceTest.java` - -测试范围: - -1. DO 字段映射覆盖 migration 字段。 -2. Mapper 能按 commandId 和 source tuple 查重。 -3. Service 只为 `done` / `error` terminal event 创建 outbox;`cancelled` task status 只能通过 `eventType = error` 发布,不新增 cancelled event type。 -4. Service 对 `done` 缺少 suggestionId 的事件写入或保持 `dead_letter`,记录 `last_error_code = AI_EVENTS_DONE_PAYLOAD_INCOMPLETE`,并证明不会发布到 SSE 可见事件。 -5. Service 对非数字 taskId 必须 fail-closed 写入或保持 `dead_letter`,`last_error_code = AI_EVENTS_TASK_ID_NOT_NUMERIC`,并证明不会调用 `EventsPublishApi`,不会发布 `done` 或 `error` 到 SSE 可见事件。 -6. Service 生成 payload allowlist,不包含 prompt/provider raw body/token/auth/private context/full output。 -7. Service 生成 `command_id` 必须使用 `ai_evt:` 或等价短 hash 格式,测试覆盖长度 `<= 128`,禁止长拼接格式作为推荐或默认实现。 -8. `MuseAiRuntimeProjectionService` 在写 terminal task event 的同一事务内调用 outbox service。 -9. `MuseJobServiceImpl.appendTaskCancellationEventIfAbsent` 在写 cancellation `error` terminal event 的同一事务内调用 outbox service;payload 只能包含 `code=AI_TASK_CANCELLED`、`message=AI task cancelled`、`retryable=false`,不能携带用户取消原因。 -10. 可重试 failure 不写 terminal event,也不写 Events publish outbox。 -11. outbox 创建幂等必须通过 mapper `insertIgnore` / SQL `ON CONFLICT DO NOTHING` 返回插入行数,再按 commandId/source tuple 回查;测试必须禁止把 `DuplicateKeyException` 作为正常分支。 - -TDD 步骤: - -1. 针对当前 dirty DO/Mapper/Service/Test 草稿写或修失败测试,失败点必须来自真实合同缺口,而不是“类不存在”。 -2. 优先写 duplicate 幂等失败测试:当前草稿如果仍捕获 `DuplicateKeyException` 作为正常路径,测试必须失败;目标路径必须是 `insertIgnore` / `ON CONFLICT DO NOTHING` 返回 0 + commandId/source tuple 回查。 -3. 写 projection 失败测试,断言 terminal done/error 后必须创建 outbox;如果当前草稿已有调用点,测试必须核对事务边界、sourceEventId、payload 和幂等语义。 -4. 写 `MuseJobServiceTest` 失败测试,断言 cancel admin/app 触发 `appendTaskCancellationEventIfAbsent` 后必须同事务创建 outbox,且 cancellation payload 不包含 request reason。 -5. 写 payload 安全失败测试,构造包含敏感字段的 payload,断言 outbox payload 不包含敏感值。 -6. 写非数字 taskId 失败测试,断言 taskId 为 `abc`、UUID 或带前缀字符串时 fail-closed 到 `dead_letter`,不会产生 SSE 可见事件。 -7. 写 commandId 长度测试,断言 `ai_evt:` 或等价短 hash 格式长度 `<= 128`,且不使用长拼接格式。 -8. 运行 focused tests,确认失败原因来自上述真实缺口且测试类实际运行。 -9. 修订 DO/Mapper/Service 最小实现,先实现 `insertIgnore` / `ON CONFLICT DO NOTHING` + 回查,再接 terminal fact 调用点。 -10. 复核 `muse-module-ai-server/pom.xml` 当前 dirty 草稿中的 `muse-module-events-api` 依赖;保留或修订时只允许 api,不允许 server。 -11. 在 `MuseAiRuntimeProjectionService.appendTaskEvent` 写入 terminal event 后调用 outbox service;关键事务和安全 allowlist 必须有中文注释。 -12. 在 `MuseJobServiceImpl.appendTaskCancellationEventIfAbsent` 写入 cancellation `error` terminal event 后调用 outbox service;中文注释必须说明取消原因不能进入统一 Events payload。 -13. 运行 focused tests。 - -验证命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server -am \ - -Dtest=MuseAiEventPublishOutboxMapperTest,MuseAiEventPublishOutboxServiceTest,MuseAiRuntimeProjectionServiceTest,MuseJobServiceTest test -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server -am dependency:tree \ - -Dincludes=cn.iocoder.cloud:muse-module-events-api -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server -am dependency:tree \ - | rg 'muse-module-events-server' && exit 1 || true -``` - -完成条件: - -1. `muse-module-ai-server` 只新增 `muse-module-events-api` 依赖。 -2. runtime done/error terminal fact 与 outbox 写入在同一事务链路内。 -3. cancellation error terminal fact 与 outbox 写入在同一事务链路内,且 cancellation payload 只包含 `AI_TASK_CANCELLED`、`AI task cancelled`、`retryable=false` 三个安全事实。 -4. payload allowlist 测试覆盖 done/error/cancellation、非数字 taskId fail-closed、commandId 短 hash 长度 `<= 128` 和敏感字段排除。 -5. `MuseAiEventPublishOutboxServiceImpl` 不捕获 `DuplicateKeyException` 作为正常幂等路径;对应测试证明 duplicate 走 `insertIgnore=0` + 回查。 -6. focused tests 通过,并必须证明 `MuseAiEventPublishOutboxMapperTest`、`MuseAiEventPublishOutboxServiceTest`、`MuseAiRuntimeProjectionServiceTest`、`MuseJobServiceTest` 实际运行:对应 surefire reports XML 存在且 test count > 0。 -7. dependency tree 证明 AI 不依赖 events-server。 -8. spec review PASS + quality review PASS。 - -## Task 4:AI worker / dispatcher 发布链路 - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishWorker.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/framework/ai/config/MuseAiProperties.java`,如果实现阶段选择用集中配置承载 `muse.ai.events.publish-worker.enabled`。 -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapper.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishOutboxServiceImpl.java` -- 新增:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/MuseAiEventPublishWorkerTest.java` -- 修改:`muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/dal/mysql/muse/MuseAiEventPublishOutboxMapperTest.java` - -测试范围: - -1. Mapper claim 使用单条 SQL 原子 `UPDATE ... WHERE id=(SELECT ... FOR UPDATE SKIP LOCKED) RETURNING *` 或等价机制。 -2. claim 只领取 `queued`、到期 `retryable`、以及 `running AND claim_expires_at < now` 的 stale running,不领取未到期 `running`、`published`、`dead_letter`。 -3. claim 时必须原子写 `publish_status = running`、`claimed_at = now`、`claim_expires_at = now + lease`、`attempt_count = attempt_count + 1`。 -4. stale running reclaim 必须证明 `running -> running`、刷新 lease、递增 attempt;递增后超过 max attempt 时进入 `dead_letter`,错误码 `AI_EVENTS_PUBLISH_RETRY_EXHAUSTED`。 -5. worker 的 `@Scheduled(initialDelayString=..., fixedDelayString=...)` 入口只调用 `dispatchOnce()`。 -6. `muse.ai.events.publish-worker.enabled=false` 时 worker 不 claim;`enabled=true` 时 scheduled 入口调用 `dispatchOnce()`。 -7. worker 对 `CommonResult` 成功且 `publishStatus=accepted` 标记 `published`,写入 `published_event_id/published_sequence_no`。 -8. worker 对 `publishStatus=accepted` 且 `duplicate=true` 标记 `published`,复用返回 eventId/sequenceNo。 -9. worker 对 `publishStatus=rejected/blocked` 标记 `dead_letter`。 -10. worker 对 `publishStatus` 为空或未知 fail-closed,不得标记 `published`。 -11. worker 对 CommonResult 失败、调用异常或临时不可用标记 `retryable` 并设置 `next_retry_at`,失败处理不得再次递增 `attempt_count`。 -12. worker 达到最大 attempt 后标记 `dead_letter`。 -13. worker 不把 payload 或敏感错误原文写入日志/审计。 - -TDD 步骤: - -1. 写 worker 失败测试,mock `EventsPublishApi` 返回 `publishStatus=accepted/accepted+duplicate/rejected/blocked/unknown`、CommonResult 失败和抛异常。 -2. 写生命周期失败测试,断言 `enabled=false` 不 claim,`enabled=true` 的 scheduled 入口调用 `dispatchOnce()`。 -3. 写 mapper claim SQL 反射测试,断言 SQL 包含 `FOR UPDATE SKIP LOCKED`、`claimed_at`、`claim_expires_at`、`attempt_count + 1`、`queued`、到期 `retryable`、stale `running`、`next_retry_at` 条件。 -4. 写 stale running 测试,断言未到期 running 不领取,`claim_expires_at < now` 的 running 可 reclaim,且超过 max attempt 进入 `dead_letter`。 -5. 运行 focused tests,确认失败。 -6. 实现 mapper claim、worker `dispatchOnce()`、`@Scheduled` scheduled 入口、配置开关、状态转换、lease 和日志。 -7. 实现固定 backoff、claim lease 和最大 attempt,中文注释说明 P1R-7b 最小策略。 -8. 运行 focused tests。 - -验证命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server -am \ - -Dtest=MuseAiEventPublishWorkerTest,MuseAiEventPublishOutboxMapperTest test -``` - -完成条件: - -1. worker 能处理 accepted、duplicate、rejected、blocked、temporary failure、retry exhausted。 -2. worker 能证明 `enabled=false` 不 claim,`enabled=true` 调 `dispatchOnce()`;`@Scheduled(initialDelayString=..., fixedDelayString=...)` 入口存在且只负责触发。 -3. worker 能证明不只看 `CommonResult` 成功失败,而是按 `EventsPublishRespDTO.publishStatus` 的 `accepted/duplicate/rejected/blocked` 分流。 -4. claim 具备并发安全语义。 -5. 日志不泄露 payload 原文或敏感错误。 -6. focused tests 通过,并必须证明 `MuseAiEventPublishWorkerTest`、`MuseAiEventPublishOutboxMapperTest` 实际运行:对应 surefire reports XML 存在且 test count > 0。 -7. spec review PASS + quality review PASS。 - -## Task 5:端到端 focused evidence 与 SSE 可见性 - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 新增:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishEndToEndTest.java` -- 必要时修改:AI outbox service / worker test fixture,不修改 OpenAPI、scanner 或 coverage。 - -测试范围: - -1. 构造 AI terminal `done` fact,写入 outbox,worker 调用 `EventsPublishApi`,Events 写入 `muse_unified_event` accepted。 -2. 查询 `EventsPublishService.listVisibleEvents` 或 `EventsStreamService.streamEvents`,证明当前 tenant + ownerUserId 可见。 -3. 构造 AI terminal `error` fact,证明 error payload 可见且不泄露敏感 detail。 -4. 构造重复 publish,证明 Events 返回同一 `eventId/sequenceNo`,outbox 仍为 `published`。 -5. 构造 rejected payload,证明 outbox dead_letter 且 SSE 不可见。 - -TDD 步骤: - -1. 先写 E2E focused test,断言当前链路缺少 AI outbox/worker 时失败。 -2. 接入 Task 3/4 实现后的最小 fixture。 -3. 运行 E2E focused test。 -4. 若 E2E 需要 Spring 装配,优先复用现有 P1R gate 测试风格;不要引入新测试框架。 - -验证命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am \ - -Dtest=P1rAiEventsPublishEndToEndTest test -``` - -完成条件: - -1. AI done/error 两类 terminal event 均有 source owner -> Events -> SSE 可见证据。 -2. duplicate/rejected 安全路径有 focused evidence。 -3. 不需要修改 OpenAPI/scanner/coverage。 -4. 必须证明 `P1rAiEventsPublishEndToEndTest` 实际运行:对应 surefire reports XML 存在且 test count > 0。 -5. spec review PASS + quality review PASS。 - -## Task 6:依赖方向、coverage gate 与迁移 gate - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 新增:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiEventsPublishDependencyTest.java` -- 运行并前后检查 diff:`muse-cloud/scripts/p1r-audit-api-coverage.py` -- Task 6 scanner 必须在临时隔离副本中运行;当前 worktree 的 coverage JSON/Markdown 与 scanner 脚本仍是受保护文件,运行前后 diff 必须为空。 -- 检查:受保护文件和 coverage 报告状态 - -测试范围: - -1. AI server 依赖 `muse-module-events-api`。 -2. AI server 不依赖 `muse-module-events-server`。 -3. Events server 不依赖 AI / Knowledge / Market / Member / Content server。 -4. `P1rApiCoverageReportTest`、`P1rEventsRealApiGateTest`、`P1rEventsRouteOwnershipTest`、`P1rAiRealApiGateTest`、`P1rAiRouteOwnershipTest` 仍通过。 -5. Coverage scanner `--check` 必须真实运行,但只能在临时隔离副本写报告;当前 worktree 运行前后受保护文件 diff 必须为空。不得通过修改 scanner/coverage 达成状态。 - -TDD 步骤: - -1. 写或修订 dependency test,断言 live 依赖方向:AI server 可以有 `muse-module-events-api`,不得有 `muse-module-events-server`;Events server 不得依赖 AI / Knowledge / Market / Member / Content server。 -2. 运行 dependency test,确认它针对当前 dirty `pom.xml` 真实依赖状态做机械断言;不得再以缺失态作为预期失败前提。 -3. 运行 P1R focused gates。 -4. 用 `rsync` 或等价方式复制当前 worktree 到 `/tmp` 临时隔离目录,排除 `.git`、`target`、`node_modules` 等构建产物;在副本中运行 coverage scanner `--check`,记录副本中的 scanner exit code、summary、Events/Market 状态,并检查当前 worktree 受保护文件 diff 仍为空。 -5. 运行 Flyway SQL/Flyway IT。 - -验证命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am \ - -Dtest=P1rAiEventsPublishDependencyTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - test -git -C .. diff -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -cd .. -tmpdir=$(mktemp -d /tmp/p1r7b-coverage-scan.XXXXXX) -rsync -a --delete \ - --exclude .git \ - --exclude 'muse-cloud/**/target' \ - --exclude 'node_modules' \ - ./ "$tmpdir"/ -cd "$tmpdir/muse-cloud" -python3 scripts/p1r-audit-api-coverage.py --check -cd "$tmpdir" -jq '.summary' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="events") | [.domain,.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '[.operations[] | select(.domain=="market") | (.implementationStatus + "/" + .completionStatus)] | group_by(.)[] | [(.[0]), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -git -C .. diff -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server -am dependency:tree \ - | rg 'muse-module-events|muse-module-(ai|knowledge|market|member|content)-server' -``` - -完成条件: - -1. 依赖方向 gate 通过。 -2. P1R focused gates 通过。 -3. scanner `--check` 在临时隔离副本中 exit 0,副本报告 summary 符合 P1R-7b 边界,当前 worktree 运行前后受保护文件和 coverage 报告 diff 为空。 -4. Coverage 状态不得被推进到 completed。 -5. Market 32 operations 保持 `dedicated / needs_verification`。 -6. 必须证明 `P1rAiEventsPublishDependencyTest` 以及本命令列出的 P1R gate 测试类实际运行:对应 surefire reports XML 存在且 test count > 0。 -7. spec review PASS + quality review PASS。 - -## Task 7:最终验证、文档留痕与交接 - -执行角色:fresh subagent implementer + fresh spec review + fresh quality review。 - -文件范围: - -- 新增:`docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路计划.md` 或实现收口时由用户确认的 10 到 20 字任务描述文件名。 -- 修改:必要的实现报告或计划状态文档;不得修改 coverage completed 状态。 - -测试范围: - -1. AI module focused tests。 -2. Events module focused tests。 -3. P1R gate tests。 -4. Flyway `_test`。 -5. dependency tree。 -6. coverage scanner。 -7. reactor build。 -8. protected files status。 - -最终验证命令: - -```bash -git status --short --branch -git diff --name-only -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install \ - -DskipTests \ - -Dspring-boot.repackage.skip=true \ - -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server,muse-module-events/muse-module-events-server -am \ - -Dtest=MuseAiEventPublishOutboxMapperTest,MuseAiEventPublishOutboxServiceTest,MuseAiEventPublishWorkerTest,MuseAiRuntimeProjectionServiceTest,EventsPublishServiceTest,EventsStreamServiceTest test -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am \ - -Dtest=P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - test -set -a -source ~/.config/muse-repo/infra.env -set +a -export P1R7B_TEST_DB="${P1R7B_TEST_DB:-muse_p1r7b_events_publish_test}" -case "$P1R7B_TEST_DB" in *_test) ;; *) echo "P1R7B_TEST_DB must end with _test"; exit 1;; esac -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" timeout 240 mvn test \ - -pl muse-server \ - -Dtest=P1rAiEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -DsocksNonProxyHosts="127.0.0.1|localhost|100.64.*|100.*" \ - -Dhttp.proxyHost= \ - -Dhttps.proxyHost= \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7B_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -git -C .. diff --name-only -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -cd .. -tmpdir=$(mktemp -d /tmp/p1r7b-final-coverage-scan.XXXXXX) -rsync -a --delete \ - --exclude .git \ - --exclude 'muse-cloud/**/target' \ - --exclude 'node_modules' \ - ./ "$tmpdir"/ -cd "$tmpdir/muse-cloud" -python3 scripts/p1r-audit-api-coverage.py --check -cd "$tmpdir" -jq '.summary' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="events") | [.domain,.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '[.operations[] | select(.domain=="market") | (.implementationStatus + "/" + .completionStatus)] | group_by(.)[] | [(.[0]), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -git -C .. diff --name-only -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -完成条件: - -1. 所有 focused tests 和 gates 有命令、exit code、关键输出。 -2. 必须证明新增目标测试类和混合 gate 中列出的目标测试类实际运行:对应 surefire reports XML 存在且 test count > 0;不能仅凭 Maven exit 0 判断。 -3. 必须证明真实 Flyway `_test` IT `P1rAiEventsPublishFlywayMigrationIT` 实际运行:`target/surefire-reports/TEST-...P1rAiEventsPublishFlywayMigrationIT.xml` 存在且 test count > 0;命令必须使用独立 `_test` 库、`flyway.postgresql.transactional.lock=false`、`p1r.flyway.locations=filesystem:sql/muse`。 -4. source propagation evidence 明确推进到 needs_verification 证据层。 -5. Events / P1R-7 / Market 不被标记 completed。 -6. 受保护文件无改动。 -7. git status 报告必须区分本任务实现、本任务文档留痕和已记录且用户未批准清理的既有 dirty baseline;不得为满足最终状态而清理、回退或删除既有 dirty baseline。 -8. spec review PASS + quality review PASS。 - -## P1R-7b 验证命令框架 - -结论:以下命令是后续 fresh implementer 和 reviewer 的验证框架;本轮文档修订不运行这些命令,不把任何命令结果写成已通过事实。 - -### Protected status - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -### Focused Maven tests - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -o clean install \ - -DskipTests \ - -Dspring-boot.repackage.skip=true \ - -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-ai/muse-module-ai-server -am \ - -Dtest=MuseAiEventPublishOutboxMapperTest,MuseAiEventPublishOutboxServiceTest,MuseAiEventPublishWorkerTest,MuseAiRuntimeProjectionServiceTest,MuseJobServiceTest \ - -Dsurefire.failIfNoSpecifiedTests=false test -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-server -am \ - -Dtest=P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false test -``` - -### Dependency tree - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-ai/muse-module-ai-server -am dependency:tree \ - -Dincludes=cn.iocoder.cloud:muse-module-events-api -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-ai/muse-module-ai-server -am dependency:tree \ - | rg 'muse-module-events-server' && exit 1 || true -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-events/muse-module-events-server -am dependency:tree \ - | rg 'muse-module-(ai|knowledge|market|member|content)-server' && exit 1 || true -``` - -### Migration SQL test - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-server -am \ - -Dtest=P1rAiEventsPublishMigrationSqlTest \ - -Dsurefire.failIfNoSpecifiedTests=false test -``` - -### Flyway `_test` 独立库 - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud -set -a -source ~/.config/muse-repo/infra.env -set +a -export P1R7B_TEST_DB="${P1R7B_TEST_DB:-muse_p1r7b_events_publish_test}" -case "$P1R7B_TEST_DB" in *_test) ;; *) echo "P1R7B_TEST_DB must end with _test"; exit 1;; esac -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" timeout 240 mvn test \ - -pl muse-server \ - -Dtest=P1rAiEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -DsocksNonProxyHosts="127.0.0.1|localhost|100.64.*|100.*" \ - -Dhttp.proxyHost= \ - -Dhttps.proxyHost= \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7B_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -### Coverage 状态只读核查 - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -jq '.summary' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="events") | [.domain,.operationId,.method,.path,.implementationStatus,.completionStatus,.targetStage] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '[.operations[] | select(.domain=="market") | (.implementationStatus + "/" + .completionStatus)] | group_by(.)[] | [(.[0]), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -git diff -- docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -## Review Gate - -每个 Task 的 review 必须记录: - -1. implementer agent 名称或 ID。 -2. spec reviewer 名称或 ID、结果、关键意见。 -3. quality reviewer 名称或 ID、结果、关键意见。 -4. FAIL 修复摘要和 re-review 结果。 -5. 本 Task 的 focused test 命令与结果。 -6. 本 Task 是否触碰受保护文件。 - -双 PASS 定义: - -1. spec review PASS:确认范围、契约、依赖方向、状态机、测试计划符合本执行版。 -2. quality review PASS:确认实现最小、可维护、错误路径完整、日志/审计安全、测试有实际断言。 -3. 任一 FAIL 均不得进入下一 Task。 - -## 完成条件 - -执行版计划完成条件: - -1. 本文件存在于 `docs/agent-specs/2026-06-06-P1R7bSourceOwnerPropagation执行版.md`。 -2. 包含目标、边界、全局架构、非目标、事实/推断/假设、文件路径、数据流、契约、迁移、状态机、幂等、失败重试、日志审计、测试、验证命令、review gate、完成条件、回滚策略。 -3. 每个实现 Task 都包含文件范围、测试范围、验证命令和完成条件。 -4. 不含不可执行空洞表述。 -5. 受保护文件无改动。 -6. 本轮计划修订差异只允许落在本执行版和必要 `.agent` 状态说明;整体 `git status` 可以继续包含“当前 dirty 文件处置表”列出的既有 AI 草稿 baseline,禁止为满足计划完成条件而清理、回退或删除这些 baseline。 - -实现阶段完成条件: - -1. AI done/error terminal event 能通过本域 outbox/worker 发布到 Events。 -2. `muse_unified_event` 有 accepted 记录,且 SSE 可见查询能读到当前 tenant + ownerUserId 的事件。 -3. duplicate/rejected/temporary failure/retry/dead_letter 均有测试。 -4. payload allowlist 和敏感信息拒绝均有测试。 -5. 依赖方向 gate 证明 AI 只依赖 events-api,Events server 不反向依赖 source owner server。 -6. Coverage 不被改口径;Market 32 operations 保持 `dedicated / needs_verification`。 -7. 所有新增测试类、混合 gate 目标测试类和 `P1rAiEventsPublishFlywayMigrationIT` 都必须有 surefire XML 与 test count > 0 证据,禁止用空测试命令或未匹配测试类的 Maven 成功输出冒充通过。 - -## 回滚策略 - -计划阶段回滚: - -1. 仅删除本执行版文档和 `docs/agent-specs/.agent` 即可回滚本轮计划产物。 -2. 不触碰审阅版文档和其他 dirty baseline。 - -实现阶段代码回滚: - -1. 回滚 AI server 对 `muse-module-events-api` 的依赖。 -2. 回滚 AI outbox DO/Mapper/Service/Worker 与相关测试。 -3. 回滚 `MuseAiRuntimeProjectionService` 写 outbox 的调用点。 -4. 保留或回滚 migration 必须按数据库发布状态决策:如果 migration 未发布,可随代码一起回滚;如果 migration 已发布,不删除表,改为停止 worker 和保留空表,避免破坏已部署数据库。 -5. 停止 worker 后,AI terminal fact 仍保留,业务行为回到 P1R-7a 后的状态;统一 Events 不再接收新的 AI terminal event。 -6. 因故停止 worker 期间已经 claim 为 `running` 的记录不得人工批量改状态;重新启用后由 `claim_expires_at` lease 识别 stale running 并进入 reclaim/retry/dead_letter 规则。 - -## 计划自检清单 - -1. 本计划没有要求修改 OpenAPI 或 scanner。 -2. 本计划没有要求修改 coverage JSON/Markdown 来达成 coverage。 -3. 本计划没有把 Events、P1R-7 或 Market 标为 `completed`。 -4. 本计划没有复用 AI runtime job 或 Market projection outbox。 -5. 本计划明确了 AI publish outbox/job、worker、EventsPublishApi、`muse_unified_event`、SSE 可见的真实链路。 -6. 本计划每个实现 Task 都要求 fresh subagent implementer + fresh spec review + fresh quality review。 -7. 本计划要求双 PASS 前不得进入下一 Task。 -8. 本计划把执行版完成和实现完成分开:执行版完成仍只是计划;实现完成也只推进 source propagation evidence / needs_verification。 diff --git a/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation审阅版.md b/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation审阅版.md deleted file mode 100644 index 260af9f4..00000000 --- a/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation审阅版.md +++ /dev/null @@ -1,373 +0,0 @@ -# P1R7c Knowledge Source Owner Propagation 审阅版 - -## 结论 - -推荐进入 P1R-7c,但只进入 Knowledge source owner propagation 的第二 owner 切片,不进入 Events / P1R-7 / Knowledge / Market completed approval。 - -推荐方案: - -1. P1R-7c 只选择 `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 这一条 canonical source status / projection summary 链路,使用 Events 现有 `notification` event type。 -2. 在 Knowledge owner 内新增独立 publish outbox / worker 语义,outbox 按受影响 `muse_knowledge_source_binding_projection.ownerUserId` fan-out,worker 只依赖 `muse-module-events-api` 调用 `EventsPublishApi`。 -3. 不复用 `muse_knowledge_projection_task` 作为 Events publish 队列;它当前是内部投影任务读模型,且在现有链路里被同步写为 `completed`。 -4. 本轮不覆盖 `MuseKnowledgeAccessPolicyService`、`MuseKnowledgeBaseService`、`MuseKnowledgeDocumentService` 中直接构造 `MuseKnowledgeSourceEventDO` 的入口;这些入口缺少统一 `eventType` / affected count 合同,必须另起后续切片或先做标准化入口设计。 -5. `muse_knowledge_source_event.ownerUserId=0` 只代表全局来源事实,不能直接作为 SSE 可见 owner;Events publish 请求的 `ownerUserId` 必须来自受影响 projection owner。 -6. 不发布知识资料原文、RAGFlow 原始返回、GraphRAG 细节、授权快照或 provider/error raw detail。 -7. P1R-7c 完成后最多推进 Knowledge propagation evidence 到 `needs_verification` 证据层,不把 Events、P1R-7、Knowledge 或 Market 标为 `completed`。 - -```mermaid -flowchart LR - Source["triggerGlobalKBSourceEvent
canonical source status event"] --> Projection["Knowledge projection update
内部同步投影"] - Projection --> Fanout["Affected binding projections
ownerUserId fan-out"] - Source --> Fanout - Fanout --> Outbox["Knowledge Events publish outbox
per owner queued/running/retryable/dead_letter"] - Outbox --> Worker["Knowledge publish worker
claim + retry + dead_letter"] - Worker --> Api["EventsPublishApi
muse-module-events-api only"] - Api --> Unified[("muse_unified_event
notification / accepted")] - Unified --> SSE["/app-api/muse/events
SSE 可见"] -``` - -本审阅版只做方案审阅,不实现代码,不修改 Java / SQL / OpenAPI / scanner / coverage,不提交,不 push。 - -## 已验证事实 - -### 工作区与提交状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- `git push origin dev/1.0.0` 已完成,远端从 `fa29753` 推进到 `230152c`。 -- 当前最近提交: - - `230152c test(p1r): 收口 AI 事件传播真实链路门禁` - - `3db5fbe feat(p1r): 接入 AI 事件传播真实链路` - - `fa29753 test(p1r): 收口 Events SSE 门禁` - -### Coverage 与阶段边界 - -- 当前 coverage summary: - - `totalOperations = 233` - - `completedOperations = 100` - - `needsVerificationOperations = 133` - - `incompleteOperations = 0` - - `genericPersistenceOperations = 0` - - `ssePlaceholderOperations = 0` -- Knowledge 59 operations 当前为 `dedicated / completed`。 -- Events 当前唯一 operation `streamEvents GET /app-api/muse/events` 为 `dedicated / needs_verification`,target stage 仍是 `P1R-7 End-to-End Acceptance`。 -- Market 32 operations 当前仍为 `dedicated / needs_verification`。 -- P1R-7 completed approval 必须另起任务,不能由 P1R-7b 或 P1R-7c 自动推出。 - -### 全局 P1R7 Source Owner Propagation 约束 - -- 全局审阅版把 P1R-7 后续拆分为: - - P1R-7b:AI terminal event 第一 owner 切片。 - - P1R-7c:Knowledge source status / projection event propagation。 - - P1R-7d:Market lifecycle / account projection / governance event propagation。 - - P1R-7e:Account(Member) security / entitlement / usage notification propagation。 - - P1R-7f:Content canonical change / block saved / export task event propagation。 -- 全局执行版要求每个 owner 保持 source owner 自治:事实归 owner,发布补偿归 owner,统一可见投影归 Events。 -- 全局执行版要求 source owner server 可以依赖 `muse-module-events-api`,不得依赖 `muse-module-events-server`;Events server 不得反向依赖 source owner server。 -- 全局执行版要求每个 owner 子计划必须包含 outbox 合同、payload allowlist、dependency gate、migration gate、focused tests 和 fresh spec / quality review gate。 - -### Events owner 当前合同 - -- `EventsPublishApi` 位于 `muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApi.java`。 -- `EventsPublishReqDTO` 必填字段包括 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`eventType`、`payloadSummary`、`emittedAt`。 -- `EventsPublishRespDTO` 返回 `eventId`、`sequenceNo`、`publishStatus`、`publishErrorCode`、`duplicate`。 -- `EventsPublishServiceImpl` 现有 OpenAPI payload schema 校验只接受 `chunk`、`quality_check`、`done`、`error`、`notification`。 -- `notification` payload 要求 `type` 是已声明类型,且 `message` 是字符串。 -- Events OpenAPI 与 `EventsPublishServiceImpl` 当前已声明 `notification.type` 包括 `source_status_change`、`knowledge_projection_done`、`governance_action`、`quota_alert`。 -- Events publish 当前对 payload 含敏感信息、schema mismatch 或未知 event type 的服务路径会写 `rejected` 审计记录;`blocked` 是既有 publish status 与 SSE 过滤兼容口径,执行版如覆盖 blocked 必须引用实际产生路径或作为兼容状态处理。 -- Events publish 支持 commandId 优先、source tuple 次级幂等;source owner 重放同一 source fact 会返回同一 `eventId/sequenceNo`。 - -### Knowledge owner 当前能力 - -- Knowledge server 当前 POM 直接依赖: - - `muse-module-knowledge-api` - - `muse-module-infra-api` - - `muse-module-content-server` - - web / security / tenant / mybatis / validation / test starter -- Knowledge server 当前没有 `muse-module-events-api` 依赖。 -- `muse_knowledge_source_event` 已存在,字段包括 `source_event_id`、`source_owner`、`source_type`、`source_id`、`source_revision`、`owner_user_id`、`kb_id`、`status`、`action_policy`、`command_id`、`event_summary`、`error_code`、`error_message`、`tenant_id`。 -- `muse_knowledge_source_event` 约束 status 只允许 `active`、`stale`、`revoked`、`recalled`、`delisted`、`blocked`、`owner_missing`、`unauthorized`。 -- `muse_knowledge_source_event` 约束 action policy 只允许 `allowed`、`read_only`、`blocked`、`needs_recheck`。 -- `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 当前在一个事务中: - 1. 校验 global KB。 - 2. 预占 Knowledge command。 - 3. 写入 `muse_knowledge_source_event`。 - 4. 同步更新 `muse_knowledge_source_binding_projection`。 - 5. 写入 `muse_knowledge_projection_task`。 - 6. 记录 command completed。 -- `MuseKnowledgeSourceEventService.createProjectionTask` 当前明确把 projection task 写为 `completed`,代码注释写明 projection 已在当前事务同步更新,避免制造无执行者的 queued task。 -- `muse_knowledge_projection_task` 有 `status`、`attempt_no`、`retry_count`、`retryable`、`next_retry_at`、`result_summary`、`error_code`、`error_message`、`started_at`、`finished_at` 字段和 retry 索引,但当前没有 claim/retry/dead-letter worker。 -- `MuseKnowledgeProjectionTaskMapper` 只提供 `selectByTaskId` 和按 status 分页查询,没有 `claimNext...` / `FOR UPDATE SKIP LOCKED` / 状态转换 SQL。 -- `MuseKnowledgeProjectionTaskService` 当前只是 projection task 查询服务,不是执行 worker。 -- `MuseKnowledgeSourceEventServiceTest` 已覆盖 source event 写入、binding projection 同步更新、projection task `completed` 记录和非法事件不写事实。 -- `MuseKnowledgeSourceEventService` 当前把 `eventType` 写入 `eventSummary` / projection summary JSON,把 `affectedTargets` / `affectedProjectionCount` 写入 projection task summary JSON;这些不是 `MuseKnowledgeSourceEventDO` 顶层字段。 -- `muse_knowledge_source_binding_projection` 当前有 `owner_user_id`、`work_id`、`kb_id`、`binding_id`、`status`、`action_policy`、`last_event_id` 和 `projection_summary` 字段,并有 `(tenant_id, owner_user_id, source_owner, source_type, source_id, kb_id)` 唯一约束。 -- Events SSE visible query 只返回同租户、同 `ownerUserId`、`publishStatus=accepted`、未删除、已到 `visibleFrom` 且 sequence 大于 cursor 的事件。 -- `triggerGlobalKBSourceEvent` 当前写入 `MuseKnowledgeSourceEventDO.ownerUserId=0`;该值代表 global KB 来源事实 owner,不代表普通用户 SSE 可见 owner。 - -### 其他 Knowledge source event 入口 - -- `MuseKnowledgeAccessPolicyService` 会写 `MuseKnowledgeSourceEventDO`,用于 policy publish 后 source event 记录;该入口当前写 `sourceType=access_policy`、`status=active`、`actionPolicy=allowed`,event summary 只有 `policyId` 和 `kbId`,没有标准化 `eventType`。 -- `MuseKnowledgeBaseService` 会写 `MuseKnowledgeSourceEventDO`,用于 KB 状态、版本和 user KB block 相关 source event;其中 `enableGlobalKnowledgeBase` 的通用 `changeGlobalStatus` 当前只更新 KB 状态并记录 command completed,不写 source event。 -- `MuseKnowledgeDocumentService` 会写 `MuseKnowledgeSourceEventDO`,用于 document deletion 相关 source event;该入口 event summary 写入 `affectedBindings`,但没有标准化 `eventType`。 -- 这些入口当前只是写 Knowledge 本域 source event,没有写 Events publish outbox。 - -## 推断 - -- Knowledge 适合作为 P1R-7c 第二 owner,因为 canonical `triggerGlobalKBSourceEvent` 链路已有 source event、source binding projection、ownerUserId、kbId、sourceRevision、eventSummary 和 command 幂等基础。 -- Knowledge 不适合直接复用 `muse_knowledge_projection_task` 作为 Events publish queue,因为现有 projection task 是内部投影完成记录,不是可领取的补偿队列。 -- P1R-7c 要证明 `/app-api/muse/events` 对用户可见,必须把 global source event fan-out 到受影响 projection owner;如果只按 source event 顶层 `ownerUserId=0` 发布,普通登录用户不会看到事件。 -- P1R-7c 最小用户可见事件应是 `notification`,而不是 `done/error/chunk/quality_check`: - - Knowledge source status 变化不是 AI task terminal result。 - - Knowledge 不能发布知识原文 chunk。 - - 当前 Events OpenAPI 没有专用 knowledge event type。 -- 第一轮 P1R-7c 应优先覆盖 `triggerGlobalKBSourceEvent` 已显式接受并规范化的 source status 事件;其他 service 直接构造 source event 的入口需要先补标准化事件合同,不能混入本轮。 -- 如果 P1R-7c 直接发布 RAGFlow parse / retrieval / GraphRAG 细节,会把外部 provider runtime 状态混进用户可见 Events,不符合全局 payload 合同。 - -## 假设 - -- P1R-7c 允许在 Knowledge owner 内新增独立 Events publish outbox 表,建议命名为 `muse_knowledge_event_publish_outbox` 或等价本域 publish outbox。 -- P1R-7c 允许 `muse-module-knowledge-server` 新增 `muse-module-events-api` direct dependency。 -- P1R-7c 只复用 Events 现有 OpenAPI `notification` schema,不修改 `docs/api-contracts/events/openapi.yaml`。 -- Knowledge notification 的 `type` 使用 Events 当前已声明的 `source_status_change`。 - -## 目标 - -- 证明 Knowledge owner 能把 canonical source status / projection summary 安全传播到统一 Events 投影。 -- 建立链路:`triggerGlobalKBSourceEvent` -> `muse_knowledge_source_event` + affected binding projections -> per-owner Knowledge publish outbox -> Knowledge worker -> `EventsPublishApi` -> `muse_unified_event` -> `/app-api/muse/events` SSE 可见。 -- 明确 Knowledge 内部 source/projection 事实与用户可见 notification 的边界。 -- 为后续 P1R-7d Market propagation 提供第二个 owner 的 outbox/worker 复用经验,但不抽取共享库。 - -## 非目标 - -- 不实现代码;本文档只做人类审阅版设计。 -- 不修改任何 OpenAPI 合同。 -- 不修改 coverage scanner 或 coverage JSON/Markdown。 -- 不把 Knowledge 59 operations 从 `completed` 改成其他状态,也不把 Events / P1R-7 / Market 推进到 `completed`。 -- 不发布 Knowledge 文档原文、chunk 内容、RAGFlow 原始响应、GraphRAG trace、授权快照或 provider 凭据。 -- 不复用 `muse_knowledge_projection_task` 作为 Events publish worker 队列。 -- 不把 `MuseKnowledgeSourceEventDO.ownerUserId=0` 直接当作 app SSE 可见 owner。 -- 不让 Events server 依赖 Knowledge server。 -- 不让统一 SSE Controller 查询 Knowledge 业务表。 -- 不把 `MuseKnowledgeAccessPolicyService`、`MuseKnowledgeBaseService`、`MuseKnowledgeDocumentService` 的直接 source event 写入入口纳入 P1R-7c 第一批。 -- 不同时改造 Market / Account / Content propagation。 -- 不把 P1R-7c 写成总 P1R-7 End-to-End Acceptance。 - -## 推荐方案 - -采用“Knowledge source event notification + 本域 publish outbox”的最小方案。 - -### 事件选择 - -第一批允许进入 Events 的 Knowledge source event 只来自 `triggerGlobalKBSourceEvent` canonical 入口: - -| Knowledge event | 入口 | source status | action policy | Events eventType | notification type | 用户可见 message 原则 | -|---|---|---|---|---|---|---| -| `version_changed` | `triggerGlobalKBSourceEvent` | `stale` | `needs_recheck` | `notification` | `source_status_change` | 知识库版本变化,需要重新检查相关使用方 | -| `policy_changed` | `triggerGlobalKBSourceEvent` | `stale` | `needs_recheck` | `notification` | `source_status_change` | 知识库访问策略变化,需要重新确认使用影响 | -| `kb_disabled` | `triggerGlobalKBSourceEvent` | `blocked` | `blocked` | `notification` | `source_status_change` | 知识库已禁用,相关能力不可用 | -| `kb_enabled` | `triggerGlobalKBSourceEvent` | `active` | `allowed` | `notification` | `source_status_change` | 知识库已恢复可用 | -| `status_changed` | `triggerGlobalKBSourceEvent` | 按 action policy 归一化 | 按请求或默认值归一化 | `notification` | `source_status_change` | 知识库状态已变化 | - -不进入第一批: - -- `MuseKnowledgeAccessPolicyService` 直接写入的 access policy source event。 -- `MuseKnowledgeBaseService` 直接写入的 KB status / version / user KB block source event。 -- `MuseKnowledgeDocumentService` 直接写入的 document deletion source event。 -- `document_deleted` 只有在后续切片先补标准化入口后才能纳入;不能把现有 document service 入口直接当作 P1R-7c 已覆盖路径。 -- RAGFlow parse polling 内部状态。 -- retrieval / GraphRAG runtime call 细节。 -- projection rebuild heartbeat。 -- Knowledge draft conflict / decision 内部状态。 -- 文档正文或 chunk 内容。 - -### Outbox 边界 - -新增 Knowledge 本域 publish outbox,而不是扩展 `muse_knowledge_projection_task`。Outbox 粒度是 per affected owner,不是 per source event: - -| 维度 | `muse_knowledge_projection_task` | P1R-7c publish outbox | -|---|---|---| -| 当前职责 | 内部投影任务读模型 | 对外 Events 发布补偿队列 | -| 当前写入 | source event 事务内同步写 `completed` | source event 事务内按合法 affected owner fan-out 写 `queued`;非法 owner 不写 outbox | -| 当前 worker | 无 | 必须新增 claim/retry/dead-letter worker | -| 可见性 | 管理端任务查询 | SSE 用户可见 notification | -| payload | projection result summary | Events OpenAPI allowlist payload | - -### 可见性 fan-out 合同 - -P1R-7c 选择“per affected owner notification”,不选择 admin/system-only notification: - -- `MuseKnowledgeSourceEventDO.ownerUserId=0` 只保留为 global source fact owner。 -- Outbox 创建必须在 `triggerGlobalKBSourceEvent` 同一事务内,基于更新后的 `muse_knowledge_source_binding_projection` 查询受影响 projection。 -- 受影响 owner 集合来自 projection 的 `ownerUserId`,必须去重,且必须排除 `null` 与 `0`。 -- 每个受影响 owner 生成一条 outbox;`EventsPublishReqDTO.ownerUserId` 使用该 projection owner。 -- 空受影响 owner 集合不是发布失败:source event 与 projection task 仍保持成功,publish outbox 不写 `queued`,只在 source event / projection task summary 中保留 `affectedTargets=0` 证据。 -- 如果 projection 行存在但 `ownerUserId` 缺失或非法,P1R-7c 第一批保持 outbox DDL 的 `target_owner_user_id > 0` 严格约束:该行只记录 fan-out 审计摘要和安全日志,不创建 outbox,不发布给 owner `0`,不要求写入 `dead_letter` outbox 行。 -- Outbox 不跨 owner 合并;不同 owner 的 publish failure/retry/dead_letter 独立推进。 - -P1R-7c 执行版必须补 mapper 能力,能按 `lastEventId` 或本次 source event 关联条件读取受影响 projection owner;不能用全表 scan 或从 request payload 推导 owner。 - -### 依赖方向 - -P1R-7c 允许: - -- `muse-module-knowledge-server` 直接依赖 `muse-module-events-api`。 -- `muse-server` 同时装配 Knowledge server 和 Events server。 - -P1R-7c 禁止: - -- `muse-module-knowledge-server` 依赖 `muse-module-events-server`。 -- `muse-module-events-server` 依赖 `muse-module-knowledge-server`。 -- Events stream controller 查询 Knowledge 表。 - -### Payload 合同 - -Knowledge outbox service 必须从 `MuseKnowledgeSourceEventDO` 顶层字段和 canonical summary allowlist 重建 payload: - -- 允许进入 internal outbox 的 DO 顶层字段: - - `kbId` - - `sourceEventId` - - `sourceType` - - `sourceId` - - `sourceRevision` - - `status` - - `actionPolicy` - - `commandId` - - `tenantId` - - `ownerUserId` 仅作为 source fact owner 记录,不作为 Events publish owner -- 允许进入 internal outbox 的 projection fan-out 字段: - - `targetOwnerUserId` - - `targetWorkId` - - `bindingId` - - `projectionId` -- 允许从 canonical `eventSummary` JSON 读取的字段: - - `eventType` - - `targetVersion` - - `sourceStatus` - - `actionPolicy` -- 允许从 canonical projection task summary JSON 读取的字段: - - `affectedTargets` - - `affectedProjectionCount` -- 允许进入 Events `notification` payload 的字段按 Events OpenAPI 当前 schema 收敛,至少必须有: - - `type` - - `message` -- 默认不写入 `resourceRef`;如执行版确需使用,只允许写用户已可见资源引用,禁止把内部 provider id、raw source id、授权快照 id 或不可见 KB 内部标识透出到 SSE。 -- 禁止进入 Events payload: - - 文档原文。 - - chunk 内容。 - - RAGFlow API key、token、Authorization。 - - RAGFlow raw request/response。 - - GraphRAG trace。 - - authorization snapshot 原文。 - - admin reason 原文如果包含用户输入或敏感上下文;需要归一化为安全 message。 - -### 状态策略 - -- outbox 创建时: - - payload 可映射到现有 `notification` schema 且 `targetOwnerUserId` 合法 -> `queued`。 - - payload 缺少必需字段或无法映射 -> `dead_letter`,记录安全错误码。 - - 没有受影响 owner -> 不创建 `queued` outbox,不视为失败。 -- worker claim: - - 使用 `FOR UPDATE SKIP LOCKED` 或等价原子领取。 - - claim 阶段递增 attempt count。 - - claim lease 过期可被重新领取。 -- publish success: - - Events `accepted` -> outbox `published`,回写 `published_event_id` / `published_sequence_no`。 -- publish rejected / blocked: - - outbox `dead_letter`,记录 `publishErrorCode`。 - - 验证 SSE visible query 不返回 rejected / blocked。 -- temporary failure: - - 未耗尽重试 -> `retryable` + `next_retry_at`。 - - 耗尽重试 -> `dead_letter`。 - -### Events 幂等键 - -为避免 per-owner fan-out 被 V16 `muse_unified_event` 的 command/source tuple 唯一约束相互撞车,P1R-7c 执行版必须固定以下规则: - -- outbox `commandId` 必须包含原 Knowledge commandId、`sourceEventId`、`targetOwnerUserId` 和 notification type。 -- Events `sourceOwner = knowledge`。 -- Events `sourceType` 必须使用 per-owner fan-out source type,例如 `knowledge_source_status_owner`。 -- Events `sourceId` 必须包含 `sourceEventId` 与 `targetOwnerUserId`,不能只用 global KB id。 -- Events `sourceRevision` 使用 source event 的 `sourceRevision`,为空时用 Events 现有 `__none__` 归一化。 -- Events `eventType = notification`。 -- 重放同一 source event + 同一 target owner 必须返回同一 `eventId/sequenceNo`;不同 target owner 必须生成不同 unified event。 - -## 关键取舍 - -- 选择 Knowledge,不选择 Market:全局计划已把 Knowledge 定位为 P1R-7c,且 Knowledge 已有 source event/projection 事实;Market 仍需先拆清 Account projection outbox 与 Events publish outbox 职责。 -- 选择 `notification`,不新增 event type:当前 OpenAPI 已声明 `notification`,能表达用户可见状态变化;新增事件类型会触发合同变更,不是 P1R-7c 最小路径。 -- 选择 canonical `triggerGlobalKBSourceEvent`,不直接覆盖所有 source event 写入入口:该入口有显式 `eventType`、归一化 status/action policy、projection update 和 affected count;其他入口当前缺少统一事件合同,贸然纳入会制造伪 E2E。 -- 选择 per affected owner fan-out,不选择 owner `0` 系统事件:P1R-7c 的证据目标是 app SSE 用户可见,Events visible query 按登录用户过滤;系统 owner `0` 会绕开主目标。 -- 新增 publish outbox,不复用 projection task:projection task 当前同步完成,没有 claim worker;复用会把内部投影任务和用户可见发布补偿混在一起。 -- 只发布摘要,不发布资料内容:Knowledge 事件最容易泄露资料正文、检索内容和 RAGFlow 细节,必须用 allowlist 重建 payload。 -- 不抽共享库:AI 与 Knowledge 可复制全局 outbox 合同,但不在第二 owner 就抽象 shared publisher,避免过早长期抽象。 - -## 影响范围 - -后续执行版预计涉及: - -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/pom.xml` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeSourceEventService.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/mysql/muse/MuseKnowledgeSourceBindingProjectionMapper.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeProjectionTaskService.java` 只作为读取 affected count 的参考,不改造成 publish worker。 -- 新增 Knowledge publish outbox service / worker / DO / Mapper。 -- 新增 V18 或后续 Knowledge publish outbox migration。 -- 新增 Knowledge outbox service/worker/mapper tests。 -- 新增 P1R-7c dependency / migration / E2E gate tests。 -- 更新 `docs/agent-specs/.agent` 与 `docs/memorys/` 留痕。 - -本审阅版不做上述实现。 - -## 风险与兼容性 - -- notification type 风险:执行版必须固定使用已声明的 `source_status_change`,不得为 P1R-7c 偷偷新增 OpenAPI type。 -- 入口覆盖风险:本轮只覆盖 canonical `triggerGlobalKBSourceEvent`;AccessPolicy/Base/Document 直接 source event 写入入口必须被 dependency/E2E gate 明确排除,后续若纳入必须先做标准化入口合同。 -- owner 可见性风险:source event owner `0` 不能直接进入 Events publish;执行版必须证明 outbox 按 projection owner fan-out,且 owner `0` 不会被普通用户 SSE 误用。 -- payload 泄露风险:Knowledge event summary 可能包含 reason、kbName 或外部细节;执行版必须定义 allowlist 和敏感词/结构过滤。 -- 语义混淆风险:`muse_knowledge_projection_task.status=completed` 是业务任务状态,不等于 coverage completed,也不等于 Events publish 成功。 -- 幂等风险:Knowledge source event 的 `source_event_id` 当前随机 UUID;Events source tuple 应使用稳定 `source_event_id` 或稳定 source revision,否则 replay 语义可能依赖 commandId 而非 source fact。 -- 依赖风险:Knowledge 当前依赖 content server;P1R-7c dependency gate 不能误禁既有 Knowledge -> Content 依赖,但必须禁止 Knowledge -> Events server 和 Events server -> Knowledge server。 -- 完成口径风险:Knowledge operations 已是 `completed`,P1R-7c 不能把这一事实外推成 Events / P1R-7 completed。 - -## 验收标准 - -审阅版通过标准: - -- 明确 P1R-7c 只做 Knowledge source owner propagation。 -- 明确 P1R-7c 第一批只覆盖 `triggerGlobalKBSourceEvent` canonical 入口,并排除其他直接 source event 写入入口。 -- 明确 P1R-7c 使用 affected projection owner fan-out,不把 source event owner `0` 当作 SSE visible owner。 -- 明确不复用 projection task 作为 Events publish queue。 -- 明确 notification payload 只使用 OpenAPI 已声明 schema。 -- 明确不发布知识资料原文、RAGFlow raw、GraphRAG trace 或授权快照。 -- 明确 Events / P1R-7 / Market / Knowledge completed 边界。 - -后续执行版必须包含: - -1. live `git status --short --branch` 和受保护文件 diff 检查。 -2. Events OpenAPI notification type 枚举复核,并固定使用已声明的 `source_status_change`。 -3. 第一批入口矩阵:仅 `triggerGlobalKBSourceEvent`,并列出 `version_changed` / `policy_changed` / `kb_disabled` / `kb_enabled` / `status_changed` 的 source status、action policy、summary allowlist 和 notification mapping。 -4. Knowledge source event -> affected projection owner fan-out -> outbox 同事务写入策略。 -5. Knowledge outbox migration 设计,唯一键必须支持同一 source event 发布给多个 owner。 -6. Knowledge outbox mapper/service/worker 设计。 -7. dependency gate:Knowledge server 有 events-api、无 events-server;Events server 无 Knowledge server。 -8. E2E gate:`triggerGlobalKBSourceEvent` -> affected owner outbox -> worker -> Events publish -> unified event -> 对应 owner SSE visible。 -9. owner 可见性 gate:owner `0` 不可作为 app SSE 可见 owner;跨 owner 不可见。 -10. 排除 gate:AccessPolicy/Base/Document 直接 source event 写入入口不被本轮 E2E 伪覆盖。 -11. rejected / blocked / duplicate / retry / dead_letter 测试;blocked 若无实际生产路径,只作为兼容状态不可见验证。 -12. Flyway `_test` 验证。 -13. 隔离副本 coverage scanner 验证,真实 worktree protected diff 为空。 - -## 待确认项 - -1. P1R-7c 是否确认只覆盖 `triggerGlobalKBSourceEvent` canonical source status / projection notification,不覆盖 RAGFlow runtime progress。 -2. P1R-7c 是否确认排除 AccessPolicy/Base/Document 直接 source event 写入入口,后续另起标准化入口切片。 -3. P1R-7c 是否确认采用 per affected owner fan-out,而不是 owner `0` admin/system-only notification。 -4. P1R-7c 第一批事件是否按 `version_changed`、`policy_changed`、`kb_disabled`、`kb_enabled`、`status_changed` 收口。 -5. 是否允许新增 `muse_knowledge_event_publish_outbox` 表作为 V18 或后续 migration。 - -## 下一步 - -1. 对本文档执行 fresh spec compliance review。 -2. 对本文档执行 fresh quality / feasibility review。 -3. 双 PASS 后,编写 `docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation执行版.md`。 -4. 执行版再经过 fresh spec / quality 双 review 后,才能进入实现。 diff --git a/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation执行版.md b/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation执行版.md deleted file mode 100644 index 78e0b67c..00000000 --- a/docs/agent-specs/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation执行版.md +++ /dev/null @@ -1,591 +0,0 @@ -# P1R7c Knowledge Source Owner Propagation 执行版 - -## 结论 - -P1R-7c 只执行 Knowledge source owner propagation 的第二 owner 切片:把 `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 产生的 canonical Knowledge source status event,按受影响 projection owner fan-out 到 Knowledge 本域 Events publish outbox,再由 Knowledge worker 调用 `EventsPublishApi` 写入 `muse_unified_event`,最终通过 `/app-api/muse/events` 对对应 owner SSE 可见。 - -本执行版不允许把 Events / P1R-7 / Knowledge / Market 推进到 `completed`,不允许修改 OpenAPI、coverage scanner 或 coverage report,不允许把 `MuseKnowledgeSourceEventDO.ownerUserId=0` 直接作为 SSE 可见 owner。 - -```mermaid -flowchart LR - Request["Admin canonical request
triggerGlobalKBSourceEvent"] --> Fact["muse_knowledge_source_event
ownerUserId=0 global source fact"] - Fact --> Projection["muse_knowledge_source_binding_projection
lastEventId + status/actionPolicy"] - Projection --> Fanout["select fan-out targets
ownerUserId > 0, dedup per owner"] - Fanout --> Outbox["muse_knowledge_event_publish_outbox
one row per target owner"] - Outbox --> Worker["Knowledge publish worker
claim/retry/dead_letter"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified["muse_unified_event
notification/source_status_change"] - Unified --> Stream["/app-api/muse/events
owner visible SSE"] -``` - -## 边界 - -### 本轮目标 - -- 在 Knowledge owner 内新增 source status notification publish outbox。 -- 只接入 `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` canonical 入口。 -- 在 source event 同一事务内按受影响 binding projection owner 建立 per-owner outbox。 -- 新增 Knowledge publish worker,唯一跨 owner 调用点是 `EventsPublishApi`。 -- 用 focused tests、P1R gate、Flyway `_test`、dependency gate 和隔离 coverage scanner 证明链路进入 `needs_verification` 证据层。 - -### 本轮非目标 - -- 不覆盖 `MuseKnowledgeAccessPolicyService`、`MuseKnowledgeBaseService`、`MuseKnowledgeDocumentService` 的直接 source event 写入入口。 -- 不覆盖 `document_deleted`,直到后续切片先统一 document source event 合同。 -- 不发布知识库正文、文档 chunk、RAGFlow raw request/response、GraphRAG trace、授权快照、provider 凭据或用户不可见内部资源 id。 -- 不复用 `muse_knowledge_projection_task` 作为 publish queue。 -- 不抽取跨 AI / Knowledge 的共享 outbox 框架。 -- 不修改 `docs/api-contracts/*/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json` 或 `.md`。 - -### 受保护文件 - -实现代理不得修改以下文件: - -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 已验证事实 - -- 正确 worktree 是 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支是 `dev/1.0.0`,当前远端跟踪是 `origin/dev/1.0.0`。 -- 当前 HEAD 为 `230152c test(p1r): 收口 AI 事件传播真实链路门禁`。 -- P1R-7b 已提交并 push:`3db5fbe` 与 `230152c`。 -- P1R-7b Task 6 fresh review:Huygens spec PASS,Boyle quality PASS。 -- P1R-7b Task 7 fresh review:Mencius spec PASS,Hume quality PASS。 -- Turing 返回 `completed:null` 的旧 review 已丢弃并关闭,不计入 review gate。 -- Reactor build 在 P1R-7b final gate 中为 47/47 modules success。 -- AI/Events focused tests 在 P1R-7b final gate 中为 87 tests pass。 -- P1R mixed gates 在 P1R-7b final gate 中为 37 tests pass。 -- `P1rAiEventsPublishFlywayMigrationIT` 在 `_test` 数据库中 4 tests pass,V17 验证成功。 -- Dependency gate 已证明 AI server 有 events-api、无 events-server;Events server 无 AI/Knowledge/Market/Member/Content server。 -- Coverage scanner 只在 `/tmp/p1r7b-final-coverage-scan.AoKcDl` 隔离副本运行,exit 0。 -- P1R-7b final gate 后真实 worktree 受保护文件 diff 为空。 -- 当前 coverage 边界为 `completedOperations=100`、`needsVerificationOperations=133`、`incompleteOperations=0`、`genericPersistenceOperations=0`、`ssePlaceholderOperations=0`。 -- Events `streamEvents` 当前是 `dedicated / needs_verification`。 -- Market 32 operations 当前是 `dedicated / needs_verification`。 -- 当前最新 migration 是 `muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql`,P1R-7c 下一 migration 编号应为 `V18`;若实现前 live 文件列表已出现更高版本,必须停止并修订本执行版的 migration 编号。 -- `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 当前在同一事务内写 source event、批量更新 binding projection、写 completed projection task、记录 command completed。 -- `MuseKnowledgeSourceEventService` 当前 `GLOBAL_OWNER_USER_ID = 0L`,source event owner 0 代表 global source fact owner。 -- `MuseKnowledgeSourceBindingProjectionDO` 当前包含 `ownerUserId`、`workId`、`kbId`、`bindingId`、`projectionId`、`lastEventId`、`status`、`actionPolicy`、`projectionSummary`。 -- `MuseKnowledgeSourceBindingProjectionMapper.updateStatusByKbId` 当前会设置 `lastEventId`、`status`、`actionPolicy`,并可合并 projection summary JSONB patch。 -- `UnifiedEventMapper.selectVisibleEventsForOwner` 当前按 `tenantId`、`ownerUserId`、`publishStatus=accepted`、`deleted=false`、`visibleFrom<=now`、`sequenceNo>afterSequenceNo` 查询 SSE 可见事件。 -- `EventsPublishServiceImpl` 当前已声明 `notification` event type。 -- `EventsPublishServiceImpl` 当前已声明 `source_status_change` notification type。 -- `EventsPublishReqDTO` 必填 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`eventType`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 当前先按 `commandId` 幂等回放,再按 source tuple 幂等回放。 -- `muse-module-knowledge-server/pom.xml` 当前没有 `muse-module-events-api` direct dependency。 -- `muse-module-events-server` 当前不依赖 Knowledge server。 -- P1R-7c 审阅版第三轮 review 已双 PASS:Parfit spec PASS,Aquinas quality PASS。 - -## 推断 - -- P1R-7c 可以复用 P1R-7b AI outbox 的状态机思路、SQL claim 模式和 dependency gate 风格,但不应抽象共享 outbox 框架,因为 Knowledge fan-out 粒度、payload 合同和 source fact owner 与 AI 不同。 -- 按 `lastEventId` 查询本次受影响 projection,是最小且可审计的 fan-out 来源;它来自本事务刚刚更新的 projection 行,不需要从 request payload 推导 owner。 -- 如果直接把 source event owner 0 发布给 Events,普通用户 SSE 查询不会命中,因此 owner fan-out 是 P1R-7c 的必要条件,不是增强项。 -- Knowledge source status notification 使用 Events 现有 `notification/source_status_change`,可避免 OpenAPI 合同变更。 - -## 假设 - -- P1R-7c 允许 Knowledge server 新增 `muse-module-events-api` direct dependency。 -- P1R-7c 允许新增 Knowledge 本域表 `muse_knowledge_event_publish_outbox`。 -- P1R-7c 实现前 `muse-cloud/sql/muse` 最新 migration 仍是 V17;若不是,必须按 live 编号调整并重新审阅。 -- P1R-7c 第一批只发布用户可见的状态摘要 message,不发布 `resourceType/resourceId`。 - -## 设计合同 - -### 事件合同 - -第一批只允许以下 canonical event type 进入 publish outbox: - -| Knowledge eventType | source status | action policy | Events eventType | notification type | message | -|---|---|---|---|---|---| -| `version_changed` | `stale` | `needs_recheck` | `notification` | `source_status_change` | `知识库版本变化,需要重新检查相关使用方` | -| `policy_changed` | `stale` | `needs_recheck` | `notification` | `source_status_change` | `知识库访问策略变化,需要重新确认使用影响` | -| `kb_disabled` | `blocked` | `blocked` | `notification` | `source_status_change` | `知识库已禁用,相关能力不可用` | -| `kb_enabled` | `active` | `allowed` | `notification` | `source_status_change` | `知识库已恢复可用` | -| `status_changed` | 按现有方法归一化 | 按现有方法归一化 | `notification` | `source_status_change` | `知识库状态已变化` | - -`document_deleted` 不进入 P1R-7c 第一批。实现必须在 outbox 创建服务中二次校验 event type allowlist,即使上游 `triggerGlobalKBSourceEvent` 当前允许 `document_deleted`,也不能为其创建 queued outbox。 - -### 可见性 fan-out 合同 - -- Source event 顶层 `ownerUserId=0` 只作为 global source fact owner 进入 internal outbox 审计字段。 -- Events publish 请求的 `ownerUserId` 必须来自 `MuseKnowledgeSourceBindingProjectionDO.ownerUserId`。 -- Fan-out target 查询必须限定: - - 当前 tenant。 - - 当前 `kbId`。 - - 当前 source event `lastEventId`。 - - `deleted=false`。 - - `ownerUserId > 0`。 -- Fan-out 需要按 `ownerUserId` 去重;同一 owner 多条 projection 只生成一条 notification outbox。 -- Outbox payload 可记录该 owner 的一个 representative `workId/bindingId/projectionId` 和该 owner 的 affected projection count;Events payload 仍只发 `type/message`。 -- 空 fan-out target 是合法结果:source event、projection update、projection task 保持成功,不写 queued outbox。 -- 非法 owner 行不得回落到 owner 0,不得发布给 admin/system owner。 -- P1R-7c 保持 V18 DDL `target_owner_user_id > 0` 严格约束;`ownerUserId` 为 `null` 或 `0` 的 projection 行只记录安全日志和 owner fan-out 审计摘要,不创建 outbox。实现测试必须断言“不发布给 owner 0 且无 queued outbox”,不能要求为非法 owner 写入 dead_letter 行。 -- Fan-out 审计摘要必须可测试,不能只依赖控制台日志。新增 mapper 能力必须同时返回 valid owner count、invalid owner count、total projection count;摘要落点为 `muse_knowledge_projection_task.result_summary` 的 `ownerFanoutSummary` 字段。 - -### 幂等合同 - -- 每个 target owner 的 outbox 独立幂等。 -- `commandId` 格式:`kn_evt:` + SHA-256(`tenantId|sourceCommandId|sourceEventId|targetOwnerUserId|source_status_change`) 前 32 位 hex。 -- `outboxId` 格式:`kn_out:` + SHA-256(`tenantId|sourceEventId|targetOwnerUserId|source_status_change`) 前 32 位 hex。 -- Events source tuple: - - `sourceOwner = knowledge` - - `sourceType = knowledge_source_status_owner` - - `sourceId = sourceEventId + ":owner:" + targetOwnerUserId` - - `sourceRevision = sourceEvent.sourceRevision`,为空时用 Events 现有 `__none__` - - `eventType = notification` -- 同一 source event + 同一 owner 重放必须得到同一 Events `eventId/sequenceNo`。 -- 同一 source event + 不同 owner 必须生成不同 `commandId` 和不同 source tuple,不能被 V16 unique constraint 合并。 - -### Payload 合同 - -Internal outbox 允许保存以下字段: - -- `sourceEventId` -- `sourceFactOwnerUserId` -- `targetOwnerUserId` -- `kbId` -- `sourceType` -- `sourceId` -- `sourceRevision` -- `sourceStatus` -- `actionPolicy` -- `knowledgeEventType` -- `sourceCommandId` -- `targetWorkId` -- `bindingId` -- `projectionId` -- `affectedOwnerProjectionCount` - -Events `payloadSummary` 只能发布: - -```json -{ - "type": "source_status_change", - "message": "知识库状态已变化" -} -``` - -实现不得把以下字段放入 Events payload: - -- `reason` -- `kbName` -- 文档正文 -- chunk 内容 -- RAGFlow request/response -- GraphRAG trace -- authorization snapshot -- source snapshot -- provider id -- API key、token、Authorization、Bearer、secret、password - -### 状态机合同 - -Outbox 本域状态只允许: - -- `queued` -- `running` -- `retryable` -- `published` -- `dead_letter` - -状态流转: - -```mermaid -stateDiagram-v2 - [*] --> queued - [*] --> dead_letter: valid owner but invalid payload - queued --> running: claim - retryable --> running: retry due - running --> running: expired lease reclaim - running --> published: Events accepted - running --> dead_letter: Events rejected/blocked or non-retryable invalid state - running --> retryable: transient failure and attempts remain - running --> dead_letter: retry exhausted -``` - -Worker claim 要求: - -- 使用 PostgreSQL `FOR UPDATE SKIP LOCKED` 或等价 `UPDATE ... RETURNING` 原子领取。 -- 调度线程无请求租户上下文时必须先忽略租户拦截器领取,再使用 outbox 行内 `tenantId` 恢复租户上下文发布。 -- Claim 阶段唯一递增 `attemptCount`。 -- 临时失败只释放 claim、设置 `nextRetryAt` 和安全错误摘要,不二次递增 attempt count。 -- `dead_letter` 是终态,不自动重放。 -- `ownerUserId` 为 `null` 或 `0` 的 projection 行不进入状态机;该类行必须在 fan-out 阶段被过滤并记录安全日志,不能通过 outbox DDL 旁路写入 `dead_letter`。 - -### 依赖合同 - -允许: - -- `muse-module-knowledge-server` 依赖 `muse-module-events-api`。 -- `muse-server` 同时装配 Knowledge server 与 Events server。 - -禁止: - -- `muse-module-knowledge-server` 依赖 `muse-module-events-server`。 -- `muse-module-events-server` 依赖 `muse-module-knowledge-server`。 -- Events server 依赖 AI / Knowledge / Market / Member / Content server。 -- Events stream controller 查询 Knowledge 业务表。 - -## 允许修改的路径 - -实现阶段允许修改以下路径: - -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/pom.xml` -- `muse-cloud/sql/muse/V18__extend_knowledge_events_publish_outbox.sql` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/dataobject/muse/MuseKnowledgeEventPublishOutboxDO.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/mysql/muse/MuseKnowledgeEventPublishOutboxMapper.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeEventPublishOutboxService.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeEventPublishOutboxServiceImpl.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeEventPublishWorker.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/mysql/muse/MuseKnowledgeSourceBindingProjectionMapper.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/MuseKnowledgeSourceEventService.java` -- Focused tests under `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/test/java/cn/iocoder/muse/module/knowledge` -- P1R gate tests under `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api` -- `docs/agent-specs/.agent` -- `docs/memorys/YYYY-MM-DD-P1R7cKnowledgeSourceOwnerPropagation真实链路.md` only after implementation evidence is available - -## 数据库迁移要求 - -### V18 表 - -新增 `muse_knowledge_event_publish_outbox`: - -- `id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY` -- `tenant_id BIGINT NOT NULL` -- `outbox_id VARCHAR(128) NOT NULL` -- `source_event_id VARCHAR(128) NOT NULL` -- `source_fact_owner_user_id BIGINT NOT NULL` -- `target_owner_user_id BIGINT NOT NULL` -- `kb_id BIGINT NOT NULL` -- `source_type VARCHAR(64) NOT NULL` -- `source_id VARCHAR(128) NOT NULL` -- `source_revision VARCHAR(80) NOT NULL` -- `source_status VARCHAR(32) NOT NULL` -- `action_policy VARCHAR(32) NOT NULL` -- `knowledge_event_type VARCHAR(64) NOT NULL` -- `event_type VARCHAR(32) NOT NULL` -- `notification_type VARCHAR(64) NOT NULL` -- `target_work_id BIGINT` -- `binding_id BIGINT` -- `projection_id VARCHAR(128)` -- `affected_owner_projection_count INT NOT NULL DEFAULT 0` -- `payload_summary JSONB NOT NULL DEFAULT '{}'::jsonb` -- `command_id VARCHAR(128) NOT NULL` -- `publish_status VARCHAR(32) NOT NULL` -- `attempt_count INT NOT NULL DEFAULT 0` -- `max_attempt INT NOT NULL DEFAULT 5` -- `claimed_at TIMESTAMP` -- `claim_expires_at TIMESTAMP` -- `next_retry_at TIMESTAMP` -- `last_error_code VARCHAR(64)` -- `last_error_message TEXT` -- `published_event_id VARCHAR(128)` -- `published_sequence_no BIGINT` -- `creator VARCHAR(64) NOT NULL DEFAULT ''` -- `create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP` -- `updater VARCHAR(64) NOT NULL DEFAULT ''` -- `update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP` -- `deleted BOOLEAN NOT NULL DEFAULT FALSE` - -### V18 约束 - -- `uk_muse_knowledge_event_publish_outbox_id UNIQUE (tenant_id, outbox_id)` -- `uk_muse_knowledge_event_publish_outbox_command UNIQUE (tenant_id, command_id)` -- `uk_muse_knowledge_event_publish_outbox_owner_source UNIQUE (tenant_id, source_event_id, target_owner_user_id, notification_type)` -- `chk_muse_knowledge_event_publish_outbox_event_type CHECK (event_type IN ('notification'))` -- `chk_muse_knowledge_event_publish_outbox_notification_type CHECK (notification_type IN ('source_status_change'))` -- `chk_muse_knowledge_event_publish_outbox_status CHECK (publish_status IN ('queued','running','published','retryable','dead_letter'))` -- `chk_muse_knowledge_event_publish_outbox_attempt_count CHECK (attempt_count >= 0)` -- `chk_muse_knowledge_event_publish_outbox_max_attempt CHECK (max_attempt > 0)` -- `chk_muse_knowledge_event_publish_outbox_target_owner CHECK (target_owner_user_id > 0)` -- `chk_muse_knowledge_event_publish_outbox_source_fact_owner CHECK (source_fact_owner_user_id >= 0)` -- `chk_muse_knowledge_event_publish_outbox_affected_count CHECK (affected_owner_projection_count >= 0)` - -### V18 索引与触发器 - -- `idx_muse_knowledge_event_publish_outbox_claim` on `(publish_status, claim_expires_at, next_retry_at, create_time, id)` where `deleted=false` -- `idx_muse_knowledge_event_publish_outbox_owner_status` on `(tenant_id, target_owner_user_id, publish_status, create_time)` -- `idx_muse_knowledge_event_publish_outbox_source_event` on `(tenant_id, source_event_id, publish_status)` -- `idx_muse_knowledge_source_projection_last_event` on `muse_knowledge_source_binding_projection(tenant_id, kb_id, last_event_id)` where `last_event_id IS NOT NULL` -- `trg_muse_knowledge_event_publish_outbox_update_time` before update, execute `update_updated_at_column()` - -V18 不新增数据库外键。source event 与 projection 的引用完整性由 Knowledge 应用层在同一事务内保证,避免跨切片外键阻塞后续演进。 - -## 实施任务 - -### Task 0:preflight - -- [ ] 进入正确 worktree:`cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- [ ] 运行 `git status --short --branch` -- [ ] 运行 `git log --oneline -5` -- [ ] 运行 `git pull --ff-only origin dev/1.0.0` -- [ ] 确认受保护文件 diff 为空。 -- [ ] 确认 `muse-cloud/sql/muse` 最新 migration 仍为 V17;若不是,停止并修订本执行版。 - -### Task 1:dependency gate - -- [ ] 在 Knowledge server POM 新增 `muse-module-events-api` direct dependency。 -- [ ] 不新增 `muse-module-events-server` 依赖。 -- [ ] 新增 `P1rKnowledgeEventsPublishDependencyTest`: - - [ ] 证明 Knowledge server 有 `muse-module-events-api`。 - - [ ] 证明 Knowledge server 无 `muse-module-events-server`。 - - [ ] 证明 Events server 无 AI / Knowledge / Market / Member / Content server。 - - [ ] XML 解析关闭外部实体。 - -### Task 2:V18 migration 与 mapper - -- [ ] 新增 `V18__extend_knowledge_events_publish_outbox.sql`。 -- [ ] 新增 `MuseKnowledgeEventPublishOutboxDO`,继承 `TenantBaseDO`,JSONB 字段使用 Knowledge 现有 `JsonbStringTypeHandler`。 -- [ ] 新增 `MuseKnowledgeEventPublishOutboxMapper`: - - [ ] `insertIgnore` - - [ ] `selectByOutboxId` - - [ ] `selectByCommandId` - - [ ] `selectByOwnerSourceTuple` -- [ ] `claimNextPublishOutbox` - - [ ] `markPublished` - - [ ] `markRetryable` - - [ ] `markDeadLetter` -- [ ] 在 `MuseKnowledgeSourceBindingProjectionMapper` 新增 `selectFanoutTargetsByLastEventId(Long kbId, String lastEventId)`。 -- [ ] `selectFanoutTargetsByLastEventId` 必须过滤 `deleted=false`、`ownerUserId>0`,并按 `ownerUserId`、`bindingId`、`projectionId` 稳定排序。 -- [ ] 在 `MuseKnowledgeSourceBindingProjectionMapper` 新增 `selectFanoutOwnerAuditSummaryByLastEventId(Long kbId, String lastEventId)` 或等价 mapper 方法,返回当前 source event 下 total projection count、valid owner projection count、invalid owner projection count、distinct target owner count。 -- [ ] `selectFanoutOwnerAuditSummaryByLastEventId` 必须包含 `ownerUserId IS NULL OR ownerUserId <= 0` 的计数,供 service 写入 projection task summary 与测试断言。 -- [ ] 新增 `P1rKnowledgeEventsPublishMigrationSqlTest` 覆盖表、约束、索引、trigger、敏感字段、重复对象、BaseDO/TenantBaseDO 字段口径。 -- [ ] 新增 mapper focused tests 覆盖 `insertIgnore` 幂等、claim lease、retryable due、running expired reclaim、mark 状态转换。 - -### Task 3:同事务 fan-out outbox 创建 - -- [ ] 新增 `MuseKnowledgeEventPublishOutboxService`。 -- [ ] 新增 `MuseKnowledgeEventPublishOutboxServiceImpl`。 -- [ ] 在 `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 写入 source event、更新 projection 后,在同一事务内调用 outbox service 生成 fan-out outbox 与 `ownerFanoutSummary`,再创建 projection task,最后记录 command completed。 -- [ ] Outbox service 只创建 outbox,不调用 `EventsPublishApi`。 -- [ ] Outbox service 对 event type 做本地 allowlist,拒绝为 `document_deleted` 创建 queued outbox。 -- [ ] Outbox service 通过 `selectFanoutTargetsByLastEventId(kbId, sourceEventId)` 获取 targets。 -- [ ] Outbox service 通过 `selectFanoutOwnerAuditSummaryByLastEventId(kbId, sourceEventId)` 获取 fan-out 审计摘要。 -- [ ] Outbox service 对 owner 去重,并记录每个 owner 的 affected projection count。 -- [ ] 空 target 不写 outbox,不抛异常。 -- [ ] Outbox service 写入 outbox 前必须把空 `sourceRevision` 归一化为 Events 现有 `__none__`,保证 V18 `source_revision NOT NULL` 与 Events source tuple 口径一致。 -- [ ] `ownerUserId` 为 `null` 或 `0` 的 projection 行只记录安全日志和 owner fan-out 审计摘要,不创建 outbox,不写 owner 0,不写 dead_letter。 -- [ ] Outbox service 必须返回结构化 `ownerFanoutSummary` 给 `MuseKnowledgeSourceEventService`,不能只写日志;该摘要用于 projection task summary 和 focused test 断言。 -- [ ] `MuseKnowledgeSourceEventService.createProjectionTask` 的 `projectionTaskSummary` 必须增加 `ownerFanoutSummary`,至少包含 `totalProjectionCount`、`validOwnerProjectionCount`、`invalidOwnerProjectionCount`、`distinctTargetOwnerCount`、`queuedOutboxCount`。 -- [ ] 重复 source event + owner 使用 `insertIgnore`,重复即按幂等成功处理。 -- [ ] 所有核心分支添加中文日志,日志不得包含 `reason`、kbName、文档内容、raw provider detail 或敏感 token。 -- [ ] 补充 `MuseKnowledgeSourceEventServiceTest`,证明 source event / projection / projection task / outbox 同事务创建。 -- [ ] 补充 service focused tests,证明 invalid owner 不发布给 owner 0、无 queued outbox 且有安全日志/审计摘要,空 target 不写 queued outbox,`document_deleted` 不写 queued outbox。 - -### Task 4:Knowledge publish worker - -- [ ] 新增 `MuseKnowledgeEventPublishWorker`。 -- [ ] 新增 Knowledge events publish worker 配置对象,配置键为 `muse.knowledge.events.publish-worker.enabled`,默认 `false`;未启用时 `dispatchOnce()` 必须直接返回 0 且不得 claim outbox。 -- [ ] Scheduled 入口使用 `muse.knowledge.events.publish-worker.initial-delay-ms` 与 `muse.knowledge.events.publish-worker.fixed-delay-ms`,默认各为 1000ms。 -- [ ] Worker 使用 fixed claim lease 和 fixed retry backoff,先沿用 P1R-7b 的 60 秒策略。 -- [ ] Worker 领取 outbox 后恢复 tenant context。 -- [ ] Worker 重新校验 payload allowlist,只构建 Events payload `{type,message}`。 -- [ ] Worker 构建 `EventsPublishReqDTO`: - - [ ] `tenantId = outbox.tenantId` - - [ ] `ownerUserId = outbox.targetOwnerUserId` - - [ ] `sourceOwner = knowledge` - - [ ] `sourceType = knowledge_source_status_owner` - - [ ] `sourceId = outbox.sourceEventId + ":owner:" + outbox.targetOwnerUserId` - - [ ] `sourceRevision = outbox.sourceRevision` - - [ ] `eventType = notification` - - [ ] `payloadSummary = {"type":"source_status_change","message": mappedMessage}` - - [ ] `emittedAt = outbox.createTime`,为空时 fail closed 为 `dead_letter` -- [ ] Worker 不设置 `resourceType/resourceId`。 -- [ ] `EventsPublishApi` 返回 `accepted` 时 mark `published`,保存 `eventId/sequenceNo`。 -- [ ] `EventsPublishApi` 返回 `rejected` 或 `blocked` 时 mark `dead_letter`。 -- [ ] `CommonResult` error、runtime exception 按 transient failure 进入 `retryable`,达到 max attempt 进入 `dead_letter`。 -- [ ] 未知 publish status、payload 解析失败、source tuple 构建失败进入 `dead_letter`。 -- [ ] 补充 worker focused tests 覆盖 disabled 不 claim、enabled 才 claim、accepted/rejected/blocked/retryable/retry exhausted/duplicate replay。 - -### Task 5:P1R E2E 与 Flyway gate - -- [ ] 新增 `P1rKnowledgeEventsPublishEndToEndTest`。 -- [ ] E2E harness 必须模拟或装配: - - [ ] Knowledge source event canonical entry。 - - [ ] binding projection fan-out 到两个 owner。 - - [ ] Knowledge worker 调用真实 `EventsPublishServiceImpl` 或等价 in-memory mapper harness。 - - [ ] `listVisibleEvents` 对 target owner 可见。 - - [ ] owner 0 不可见。 - - [ ] 非受影响 owner 不可见。 - - [ ] 同一 source event + same owner replay 返回同一 event identity。 - - [ ] 同一 source event + different owners 生成不同 unified event。 - - [ ] rejected publish 进入 outbox `dead_letter` 且 SSE invisible。 -- [ ] 新增 `P1rKnowledgeEventsPublishFlywayMigrationIT`: - - [ ] 只允许 `_test` 数据库。 - - [ ] password 只允许从环境变量读取。 - - [ ] JDBC URL query credentials 必须拒绝。 - - [ ] target version 为 V18。 - - [ ] clean 后成功 migration count 为 18。 - - [ ] 验证 V18 table、indexes、constraints、trigger。 -- [ ] P1R mixed gate 继续运行 coverage、Events gate 与 AI gate,但不得修改 coverage report 文件。 - -### Task 6:最终验证与留痕 - -- [ ] 先运行 reactor build,再运行 focused tests 和 P1R gates,避免 `clean` 清理 surefire XML 后丢失 test count 证据。 -- [ ] coverage scanner 只能在 `/tmp` 隔离副本运行。 -- [ ] 真实 worktree 受保护文件 diff 必须为空。 -- [ ] 若双 review 与验证均通过,新增 `docs/memorys/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation真实链路.md`。 -- [ ] 更新 `docs/agent-specs/.agent`,记录 reviewer ID、PASS/FAIL、命令、测试计数、protected diff、coverage 边界。 -- [ ] 不提交、不 push,除非用户明确批准。 - -## 验证命令 - -以下命令由实现代理按阶段运行;执行版编写阶段不运行实现测试。 - -### Preflight - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -git diff --name-only -- \ - docs/api-contracts/market/openapi.yaml \ - docs/api-contracts/ai/openapi.yaml \ - docs/api-contracts/knowledge/openapi.yaml \ - docs/api-contracts/events/openapi.yaml \ - muse-cloud/scripts/p1r-audit-api-coverage.py \ - docs/superpowers/reports/p1r-api-coverage.json \ - docs/superpowers/reports/p1r-api-coverage.md -``` - -### Focused Knowledge tests - -```bash -cd muse-cloud -mvn -o test \ - -pl muse-module-knowledge/muse-module-knowledge-server \ - -Dtest=MuseKnowledgeSourceEventServiceTest,MuseKnowledgeEventPublishOutboxServiceTest,MuseKnowledgeEventPublishOutboxMapperTest,MuseKnowledgeEventPublishWorkerTest -``` - -### P1R mixed gates - -```bash -cd muse-cloud -mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest -``` - -`P1rKnowledgeFlywayMigrationIT` 是 P1R-5/V14 专用真实 Flyway gate,固定 target version V14;P1R-7c 不得把它混入 mixed gate。P1R-7c 的 V18 真实 Flyway 证据只来自本轮新增 `P1rKnowledgeEventsPublishFlywayMigrationIT`。 - -`P1rKnowledgeRuntimeEndToEndLiveAcceptanceIT` 是 P1R-5 Knowledge/RAGFlow opt-in live acceptance,启用外部验收时会迁移到 V14 并外呼真实 RAGFlow;P1R-7c 不得把它作为 required PASS gate。如发布前需要额外回归,只能单列为 P1R-5 optional external regression,不计入 P1R-7c execution review 或最终收口门禁。 - -P1R-7b AI source-owner 回归在本轮 mixed gate 中保留 AI real API/route ownership gate;不重跑 `P1rAiEventsPublish*` 三个 gate,除非 P1R-7c 实现实际触碰 AI outbox/worker 或 Events publish 公共合同。 - -### Reactor build - -```bash -cd muse-cloud -mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -``` - -### Flyway `_test` - -```bash -cd muse-cloud -P1R_FLYWAY_PASSWORD="$P1R_FLYWAY_PASSWORD" mvn -o test \ - -pl muse-server \ - -Dtest=P1rKnowledgeEventsPublishFlywayMigrationIT \ - -Dp1r.flyway.url="$P1R_FLYWAY_URL" \ - -Dp1r.flyway.user="$P1R_FLYWAY_USER" \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dflyway.postgresql.transactional.lock=false -``` - -`P1R_FLYWAY_URL` 必须指向真实 PostgreSQL `_test` 数据库,例如 `muse_p1r7c_events_publish_test`。密码不得通过 JVM system property 传入。 - -### Dependency tree gate - -```bash -cd muse-cloud -mvn -o dependency:tree -pl muse-module-knowledge/muse-module-knowledge-server -Dincludes=cn.iocoder.cloud:muse-module-events-api -mvn -o dependency:tree -pl muse-module-knowledge/muse-module-knowledge-server -Dincludes=cn.iocoder.cloud:muse-module-events-server -mvn -o dependency:tree -pl muse-module-events/muse-module-events-server -Dincludes=cn.iocoder.cloud:muse-module-ai-server,cn.iocoder.cloud:muse-module-knowledge-server,cn.iocoder.cloud:muse-module-market-server,cn.iocoder.cloud:muse-module-member-server,cn.iocoder.cloud:muse-module-content-server -``` - -### Coverage scanner 隔离 gate - -```bash -tmpdir="$(mktemp -d /tmp/p1r7c-final-coverage-scan.XXXXXX)" -rsync -a --delete \ - --exclude .git \ - --exclude muse-cloud/target \ - --exclude 'muse-cloud/**/target' \ - /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/ "$tmpdir/" -cd "$tmpdir" -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -隔离 scanner 结果必须保持: - -- `incompleteOperations = 0` -- `genericPersistenceOperations = 0` -- `ssePlaceholderOperations = 0` -- Events `streamEvents = dedicated / needs_verification` -- Market 32 operations 仍为 `dedicated / needs_verification` - -## Fresh review gate - -执行版写完后,必须先派发 fresh review,且只接受本执行版之后新开的 reviewer: - -- fresh spec compliance review:检查是否符合 P1R-7c 审阅版第三轮双 PASS 方案、protected 文件边界、owner fan-out、Events visible query、V18 migration、dependency gate、payload allowlist。 -- fresh quality/feasibility review:检查实现复杂度、状态机、事务边界、幂等、retry/dead_letter、测试可行性、日志安全、回滚策略。 - -任一 reviewer FAIL 时: - -- 不实现代码。 -- 先按 `superpowers:receiving-code-review` 验证反馈。 -- 有效反馈修执行版。 -- 重新派发 fresh review。 - -双 PASS 后才允许进入实现。 - -## 完成条件 - -P1R-7c 实现可进入阶段收口的最低条件: - -- `MuseKnowledgeSourceEventService.triggerGlobalKBSourceEvent` 真实创建 source event、projection update、projection task、per-owner outbox。 -- Knowledge worker 真实调用 `EventsPublishApi`。 -- `muse_unified_event` 真实写入 `notification/source_status_change`。 -- `/app-api/muse/events` 对 target owner 可见,对 owner 0 和非 target owner 不可见。 -- Duplicate replay 对 same source event + same owner 返回同一 event identity。 -- Different owners 不被 command/source tuple 合并。 -- Invalid payload / rejected / blocked / retry exhausted 有 dead_letter 证据。 -- Focused tests、P1R mixed gates、Flyway `_test`、dependency tree、隔离 coverage scanner 均有命令与计数证据。 -- 真实 worktree 受保护文件 diff 为空。 -- `.agent` 与 `docs/memorys` 记录 reviewer、PASS/FAIL、测试计数、coverage 边界和 completed 禁止边界。 - -达到以上条件也只代表 Knowledge source owner propagation evidence 可推进到 `needs_verification`,不代表 Events / P1R-7 / Knowledge / Market completed。 - -## 回滚策略 - -实现提交前回滚: - -- 删除 V18 migration。 -- 删除新增 Knowledge outbox DO / Mapper / Service / Worker / tests。 -- 回退 Knowledge POM 的 `events-api` dependency。 -- 回退 `MuseKnowledgeSourceEventService` 和 `MuseKnowledgeSourceBindingProjectionMapper` 的 P1R-7c 改动。 -- 保留或更新 `.agent` 说明本轮未进入实现完成。 - -实现提交后但未部署回滚: - -- 新提交 revert P1R-7c 代码与 migration。 -- 不修改 OpenAPI/scanner/coverage report。 -- 重新运行 dependency gate 与 protected diff。 - -已部署后回滚: - -- 先通过 `muse.knowledge.events.publish-worker.enabled=false` 关闭 Knowledge publish worker。 -- 保留 outbox 表与 unified event 审计记录,不物理删除已发布事件。 -- 如需补偿,另起 migration 或运维脚本把未发布 outbox 标为 `dead_letter`,脚本必须按 tenant 与 owner 范围限定。 -- 重新审阅后再恢复 worker。 diff --git a/docs/agent-specs/2026-06-07-P1R7CompletedApproval预检审阅版.md b/docs/agent-specs/2026-06-07-P1R7CompletedApproval预检审阅版.md deleted file mode 100644 index 471a55ba..00000000 --- a/docs/agent-specs/2026-06-07-P1R7CompletedApproval预检审阅版.md +++ /dev/null @@ -1,234 +0,0 @@ -# P1R7 Completed Approval 预检审阅版 - -## 结论 - -推荐启动 P1R-7 completed approval 预检,但不推荐现在直接把 Events / P1R-7 写成 `completed`。 - -当前 AI、Knowledge、Market 三条 source owner propagation 真实链路已经分别收口到 `needs_verification` 证据层,并已提交推送到 `origin/dev/1.0.0`。但是按现有全局执行版,P1R-7 completed approval 至少还需要先回答两个问题: - -1. completed approval 的目标是否只覆盖 Events `streamEvents GET /app-api/muse/events` 这一项 operation。 -2. Account(Member) 与 Content 是否仍属于 P1R-7 completed approval 必须覆盖的 source owner。 - -如果答案是“Account / Content 仍必须覆盖”,下一阶段应进入 P1R-7e Account 和 P1R-7f Content,而不是 completed。 -如果答案是“本轮 completed approval 只审批 Events streamEvents,且 AI / Knowledge / Market 三个 source owner 足够作为 evidence”,则需要先写执行版、跑最终验证、fresh 双 review,再由用户单独批准 coverage 状态推进。 - -本审阅版只做预检范围冻结,不实现代码,不修改 OpenAPI、scanner 或 coverage report,不推进 `completed`。 - -## Review Gate - -- Fresh spec/scope review(Confucius)PASS,无 P0/P1/P2 blocker。 -- Fresh quality/feasibility review(Carver)PASS,无 P0/P1/P2 blocker。 - -两位 reviewer 均确认:本审阅版只通过“预检文档进入下一步”的门槛,不代表任何 coverage 状态可推进,也不代表 P1R-7 completed approval 已获批准。 - -非阻塞风险已经纳入后续执行要求: - -- 如果用户没有明确批准“只审批 Events `streamEvents`”,后续执行版应默认走 Account / Content 补证路径,否则会与全局门槛冲突。 -- 如果后续走 Events-only approval,执行版必须把 focused harness 与真实 HTTP/SSE live gap 转成明确 gate,或者写明用户批准的 waiver。 -- 后续仍必须只在 `/tmp` 隔离副本运行会写 coverage report 的 scanner,并在真实 worktree 前后复查 protected diff。 -- 最终验证顺序必须先 reactor build,再 focused/P1R tests,避免 `clean` 抹掉 surefire XML 证据。 - -```mermaid -flowchart TB - Current["当前已验证 evidence
P1R-7a Events SSE
P1R-7b AI
P1R-7c Knowledge
P1R-7d Market"] --> Precheck["P1R-7 completed approval 预检"] - Precheck --> ScopeDecision{"completed scope 决策"} - ScopeDecision -->|Account/Content 必须覆盖| MoreOwners["继续 P1R-7e Account
P1R-7f Content"] - ScopeDecision -->|仅审批 Events streamEvents| FinalPlan["写 completed approval 执行版
最终验证 + fresh review"] - FinalPlan --> Approval{"用户单独批准?"} - Approval -->|是| Coverage["更新 coverage completed"] - Approval -->|否| Stay["保持 needs_verification"] -``` - -## 已验证事实 - -### 工作区与提交状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- 当前分支已与 `origin/dev/1.0.0` 对齐到 `e8ed7d7 test(p1r): 收口 Market 事件传播真实链路门禁`。 -- P1R-7 source owner propagation 相关提交链: - - `fa29753 test(p1r): 收口 Events SSE 门禁` - - `3db5fbe feat(p1r): 接入 AI 事件传播真实链路` - - `230152c test(p1r): 收口 AI 事件传播真实链路门禁` - - `cef686b feat(p1r): 接入 Knowledge 事件传播真实链路` - - `e55618f test(p1r): 收口 Knowledge 事件传播真实链路门禁` - - `b36b153 feat(p1r): 接入 Market 事件传播真实链路` - - `e8ed7d7 test(p1r): 收口 Market 事件传播真实链路门禁` - -### 当前 coverage 边界 - -当前真实 worktree 的 coverage report 显示: - -```text -totalOperations=233 -completedOperations=100 -needsVerificationOperations=133 -incompleteOperations=0 -genericPersistenceOperations=0 -ssePlaceholderOperations=0 -``` - -按 domain 聚合: - -```text -ai dedicated/completed:41 -knowledge dedicated/completed:59 -events dedicated/needs_verification:1 -market dedicated/needs_verification:32 -account dedicated/needs_verification:33 -content dedicated/needs_verification:51 -meta dedicated/needs_verification:16 -``` - -因此当前如果进入 completed approval,直接候选 operation 只能是 Events 的 `streamEvents GET /app-api/muse/events`。Market 32、Account 33、Content 51、Meta 16 仍不是本审阅版自动推进对象。 - -### P1R-7a Events SSE evidence - -- P1R-7a 已把 `streamEvents GET /app-api/muse/events` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`。 -- P1R-7a 证据包括: - - frontend SSE focused tests。 - - P1R Events API gate。 - - route ownership gate。 - - reactor build。 - - dependency tree。 - - V16 Flyway `_test`。 -- P1R-7a 明确没有接入 source owner propagation,不能作为 completed evidence。 - -### P1R-7b AI source owner evidence - -- AI terminal event -> AI publish outbox -> AI worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible 链路已收口到 `needs_verification`。 -- 已验证: - - reactor build 47/47 success。 - - AI / Events focused tests 87 pass。 - - P1R mixed gates 37 pass。 - - V17 Flyway `_test` 4 pass。 - - AI server 有 events-api、无 events-server。 - - Events server 无 AI / Knowledge / Market / Member / Content server 反向依赖。 - - coverage scanner 只在 `/tmp` 隔离副本运行,summary 保持 `completed=100 / needsVerification=133`。 -- fresh Task 7 review:Mencius spec PASS,Hume quality PASS。 - -### P1R-7c Knowledge source owner evidence - -- Knowledge source event -> Knowledge publish outbox -> Knowledge worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible 链路已收口到 `needs_verification`。 -- 已验证: - - reactor build 62/62 success。 - - Knowledge focused tests 15 pass。 - - P1R mixed gates 37 pass。 - - V18 Flyway `_test` 4 pass。 - - dependency gate 与 protected diff gate 通过。 - - coverage scanner 只在 `/tmp` 隔离副本运行,summary 保持 `completed=100 / needsVerification=133`。 -- fresh review:Rawls correctness/spec PASS,Boole data-integrity/testing PASS。 - -### P1R-7d Market source owner evidence - -- Market governance terminal fact -> Market publish outbox -> Market worker -> EventsPublishApi -> `muse_unified_event` -> `/app-api/muse/events` 对资产发布者可见链路已收口到 `needs_verification`。 -- 已验证: - - reactor build 62/62 success。 - - Market focused tests 28 pass。 - - P1R mixed gates 56 pass。 - - V19 Flyway `_test` 4 pass。 - - dependency tree gate 通过。 - - coverage scanner 只在 `/tmp/p1r7d-final-coverage-scan.nhBVhM` 隔离副本运行,summary 保持 `completed=100 / needsVerification=133`。 - - protected diff 与 `git diff --check` 通过。 -- fresh review:Pascal spec/correctness PASS,Mill quality/data-integrity/testing PASS。 -- P1R-7d 已拆分提交并 push 到 `origin/dev/1.0.0`。 - -## 推断 - -- 当前证据足以证明 Events SSE 入口已从占位变成 dedicated,并且 AI、Knowledge、Market 三个 owner 的用户可见事件可以通过本域 outbox / worker 发布到 Events。 -- 当前证据还不足以自动证明 P1R-7 completed,因为全局执行版仍把 Account(Member) 与 Content 列为后续 source owner 候选,并且 completed approval 明确要求另起任务、单独审批。 -- 如果 completed approval 目标只针对 Events `streamEvents`,AI / Knowledge / Market 三个 owner 可以构成较强的 source owner propagation evidence,但仍需要最终执行版明确验证命令、fresh review 和用户批准。 -- 如果 completed approval 目标被解释为“所有 declared source owner 都必须覆盖”,当前缺 Account(Member) 与 Content source owner propagation,应该继续 P1R-7e / P1R-7f。 - -## 假设 - -- `docs/superpowers/reports/p1r-api-coverage.json` 中 P1R-7 对应的可推进 operation 是 Events `streamEvents`。 -- P1R-7 completed approval 不自动包含 Market 32 operations、Account 33 operations、Content 51 operations 或 Meta 16 operations 的 completed 推进。 -- 用户仍要求 completed 状态必须明确批准,不能由 gate PASS 自动推出。 - -## 推荐路径 - -### 方案 A:继续补齐 Account / Content - -适用条件: - -- 用户确认 P1R-7 completed approval 必须覆盖全局执行版列出的 Account(Member) 与 Content source owner。 - -下一步: - -1. 写 P1R-7e Account(Member) source owner propagation 审阅版。 -2. fresh spec review + fresh quality review。 -3. 双 PASS 后写执行版并实现。 -4. 再做 P1R-7f Content source owner propagation。 - -优点: - -- 最符合当前全局执行版。 -- completed approval 争议最小。 - -代价: - -- 周期更长。 -- Account / Content 的用户可见事件边界需要重新审阅,不能直接套 AI / Knowledge / Market 模板。 - -### 方案 B:先做 Events streamEvents completed approval 执行版 - -适用条件: - -- 用户确认本轮 completed approval 只审批 Events `streamEvents`,不把 Account / Content source owner propagation 作为硬前置。 - -下一步: - -1. 写 `P1R7CompletedApproval执行版`。 -2. 执行版必须包含最终验证命令、隔离 coverage scanner、protected diff、dependency tree、Flyway `_test`、fresh final overall review。 -3. 执行版 review 双 PASS 后,才允许运行最终验证。 -4. 最终验证和 review 均 PASS 后,再由用户单独批准是否修改 coverage `completionStatus`。 - -优点: - -- 可以尽快完成 Events `streamEvents` 的审批闭环。 -- 不会把 Market / Account / Content 其他 operation 混入本轮。 - -风险: - -- 需要显式修改或解释全局执行版中的 Account / Content owner 前置口径,否则 reviewer 可能判定 scope 不闭合。 -- 即使批准,也只能影响 Events `streamEvents`,不能把 Market / Account / Content / Meta 一并标 completed。 - -## 非目标 - -- 不实现 Account / Content source owner。 -- 不运行最终 full verification。 -- 不修改 OpenAPI。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json` 或 `.md`。 -- 不推进 `completed`。 -- 不把 Market 32 operations、Account 33 operations、Content 51 operations 或 Meta 16 operations 作为本审阅版自动 completed 对象。 - -## 风险 - -- scope 风险:如果不先确认 Account / Content 是否是 completed 前置,直接写执行版会在 fresh review 中反复 FAIL。 -- 证据风险:当前 AI / Knowledge / Market 多数 E2E 是 focused harness,不等于真实浏览器 SSE 或全 source API HTTP live flow。 -- 状态风险:coverage 当前 `completed=100 / needsVerification=133`,任何 completed 推进都会改受保护报告,必须在用户明确批准后进行。 -- 回归风险:最终验证应先跑 reactor build,再跑会生成 surefire XML 的 tests,避免 `clean` 抹掉证据。 - -## 验收标准 - -本审阅版通过的标准: - -1. 明确当前 evidence 与 completed 的边界。 -2. 明确 P1R-7 completed approval 的候选对象只能是 Events `streamEvents`,除非用户另行扩大 scope。 -3. 明确 Account / Content 是否必须先补齐是待确认决策。 -4. 不修改受保护文件。 -5. fresh spec review + fresh quality review 双 PASS 后,才允许写执行版。 - -## 待确认项 - -1. P1R-7 completed approval 是否只审批 Events `streamEvents GET /app-api/muse/events`? -2. Account(Member) 与 Content 是否仍是 P1R-7 completed approval 的硬前置 source owner? -3. 如果先走 Events `streamEvents` approval,是否允许在执行版中明确“Account / Content 作为后续 source owner,不阻塞本次 Events operation completed approval”? - -## 下一步 - -1. 对本审阅版派发 fresh spec compliance review。 -2. 对本审阅版派发 fresh quality / feasibility review。 -3. 双 PASS 后,根据用户对待确认项的答案,进入 P1R-7e/P1R-7f 或 P1R7CompletedApproval执行版。 diff --git a/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md b/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md deleted file mode 100644 index 13236858..00000000 --- a/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md +++ /dev/null @@ -1,204 +0,0 @@ -# P1R7d Market Source Owner Propagation 审阅版 - -## 结论 - -推荐进入 P1R-7d,但第一切片只覆盖 Market governance / source status 的用户可见通知,不覆盖 purchase、install、handoff、Account projection 同步或目标 owner 授权消费。 - -推荐方案: - -1. 选择 `AdminMarketGovernanceServiceImpl` 已落库的 `muse_market_governance_action` / `muse_market_source_status_event` 作为 P1R-7d 第一批 source fact。 -2. 新增 Market 本域 Events publish outbox / worker,调用 `EventsPublishApi`,发布 Events 已声明的 `notification/governance_action`。 -3. 不复用 `MarketAccountProjectionOutboxService` 作为 Events publish outbox;它只表达 Market -> Member Account 投影同步状态。 -4. 不修改 OpenAPI、scanner 或 coverage report;不推进 Events / P1R-7 / Market completed。 - -```mermaid -flowchart LR - Admin["Admin governance command
delist / recall"] --> Fact["Market source fact
governance_action / source_status_event"] - Fact --> Outbox["Market events publish outbox
queued / running / retryable / published / dead_letter"] - Outbox --> Worker["Market worker
claim + retry + audit log"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified[("muse_unified_event
notification / governance_action")] - Unified --> SSE["/app-api/muse/events
publisher visible"] -``` - -本审阅版不实现代码,只冻结目标、非目标、取舍、影响范围、风险和验收标准。执行版、实现和提交必须在本审阅版 fresh spec review + fresh quality review 双 PASS 后再继续。 - -## 已验证事实 - -### 工作区与状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 当前本地与 `origin/dev/1.0.0` 对齐到 `e55618f test(p1r): 收口 Knowledge 事件传播真实链路门禁`。 -- 受保护文件 diff 为空: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 全局 P1R-7 边界 - -- P1R-7 全局审阅版建议阶段: - - P1R-7b:AI terminal event。 - - P1R-7c:Knowledge source status / projection event。 - - P1R-7d:Market lifecycle / account projection / governance event。 - - P1R-7 completed approval 必须另起任务并由用户单独批准。 -- P1R-7 全局执行版要求每个 owner 都必须 fresh implementer + fresh spec review + fresh quality / feasibility review。 -- 全局合同要求 source owner 只能依赖 `muse-module-events-api`,Events server 不得反向依赖 AI / Knowledge / Market / Member / Content server。 -- 当前 coverage summary 仍是 `completedOperations=100`、`needsVerificationOperations=133`、`incompleteOperations=0`、`genericPersistenceOperations=0`、`ssePlaceholderOperations=0`。 -- Events `streamEvents` 与 Market 32 operations 均仍为 `dedicated / needs_verification`。 - -### Events 可复用合同 - -- `EventsPublishReqDTO` 已包含 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`eventType`、`resourceType`、`resourceId`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 先按 `commandId` 回放,再按 source tuple 回放。 -- `EventsPublishServiceImpl` 已声明事件类型包含 `notification`。 -- Events OpenAPI 的 `SSENotificationEvent.data.type` 已包含 `governance_action`。 -- `UnifiedEventMapper.selectVisibleEventsForOwner` 只返回 `accepted`、同租户、同 owner、未删除、已到 visible time 的事件。 - -### Market 当前事实模型 - -- `AdminMarketGovernanceServiceImpl.delistAsset` 在治理命令成功后写入 `muse_market_governance_action`,字段包括 `actionId`、`previewId`、`assetId`、`actorUserId`、`ownerUserId`、`actionType`、`commandId`、`requestHash`、`status`、`revision`、`actionSnapshot`、`reason`。 -- `AdminMarketGovernanceServiceImpl.recallAsset` 在治理命令成功后写入 `muse_market_governance_action`,并额外写入 `muse_market_source_status_event`,其中 `sourceStatus` 固定为合同状态如 `recalled`,`needs_recheck` 只写入 `actionPolicy/recheckReasons`。 -- `MuseMarketGovernanceActionMapper` 支持按 tenant + asset 查询最新或列表,但没有 publish claim / retry / dead_letter 语义。 -- `MuseMarketSourceStatusEventMapper` 支持按 `sourceEventId` 或 tenant + asset 查询最新,但没有 publish claim / retry / dead_letter 语义。 -- `MarketAccountProjectionOutboxServiceImpl` 只有 `recordSynced` / `recordBlocked`,职责是 Market -> Member Account projection 同步状态;当前没有领取 retryable outbox、重试成功、dead_letter 或 replay worker。 -- `MarketAccountProjectionProvider` 明确把 Account 查询读模型写入 member 表,失败时记录 Market 侧 blocked outbox 后抛错,防止伪成功。 -- `MarketHandoffServiceImpl` 保存 handoff token hash 和生命周期状态,但 token 明文只在创建响应中返回;后续 P1R-7d 第一切片不发布 handoff token 或授权详情。 - -## 推断 - -- Market P1R-7d 第一切片应优先选择 governance/source status,而不是 purchase/install/handoff。原因是 governance/source status 已有明确 publisher owner、source fact、actionId/sourceEventId 和安全摘要空间,且用户需要实时知道资产被下架或召回。 -- 直接复用 `muse_market_account_projection` 作为 Events publish outbox 会混淆职责:它记录 Account 投影同步,不记录 Events publish claim、lease、retry、published event 或 dead_letter。 -- `notification/governance_action` 已在 Events 合同内,可避免本阶段修改 OpenAPI。 -- recall 比 delist 更适合作为第一条 source status 证据,因为它同时写 governance action 和 source status blocked 事实;但执行版仍可把 delist 作为同一事件族的正向治理通知。 - -## 假设 - -- P1R-7d 允许在 Market owner 内新增独立 Events publish outbox 表或等价状态表。 -- P1R-7d 允许 `muse-module-market-server` 新增对 `muse-module-events-api` 的直接依赖,但不允许依赖 `muse-module-events-server`。 -- Market publisher 是第一批通知的 `ownerUserId`;admin 操作者只作为 `actorUserId` 写入 Market fact / 安全摘要,不作为 SSE 可见 owner。 -- `notification/governance_action` 的 payload 最小字段可以收敛为 `type`、`message`、可选 `resourceRef`、`timestamp`,不需要新增 OpenAPI 字段。 - -## 目标 - -1. 证明 Market source owner 的真实治理事实可以通过 Market 本域 publish outbox / worker 发布到 Events。 -2. 让发布者用户可以通过统一 SSE 看到自己的资产治理通知。 -3. 建立 Market 与 Events 的单向依赖证据:Market server 依赖 events-api,不依赖 events-server;Events server 不反向依赖 Market server。 -4. 为后续 Market source propagation / Account projection / handoff 事件扩展保留清晰边界。 - -## 非目标 - -1. 不把 Market 32 operations 从 `needs_verification` 推进到 `completed`。 -2. 不修改 Market / Events OpenAPI。 -3. 不修改 coverage scanner 或 coverage report。 -4. 不把 purchase、install、license、handoff token、authorization snapshot 或 Account projection 同步纳入第一切片。 -5. 不把 `muse_market_account_projection` 复用为 Events publish outbox。 -6. 不发布授权快照、安装详情、handoff token、目标 owner 私有事实、治理 preview 原始 riskSummary、管理员内部备注原文或完整 actionSnapshot。 -7. 不让 Events server 查询 Market 表。 - -## 推荐方案 - -### 事件选择 - -第一批只允许: - -| Market source fact | 触发入口 | Events eventType | notification type | SSE owner | -|---|---|---|---|---| -| `muse_market_governance_action(actionType=delist)` | `adminDelistAsset` | `notification` | `governance_action` | asset.publisherId | -| `muse_market_governance_action(actionType=recall)` | `adminRecallAsset` | `notification` | `governance_action` | asset.publisherId | -| `muse_market_source_status_event(sourceStatus=recalled)` | `adminRecallAsset` | `notification` | `governance_action` | event.ownerUserId | - -不把 `previewGovernanceImpact` 发布到 SSE。preview 只是管理员决策前的影响预览,不是用户必须实时感知的终态事实。 - -### Payload allowlist - -第一批 payload 只允许: - -```json -{ - "type": "governance_action", - "message": "资产治理状态已更新", - "resourceRef": { - "resourceType": "market_asset", - "resourceId": 1001 - }, - "timestamp": "2026-06-07T12:00:00" -} -``` - -执行版可以把 `message` 细化为“资产已下架”或“资产已召回”,但不能放入管理员备注原文、治理证据、授权快照、handoff token、target owner 内部细节或错误堆栈。 - -### Outbox 边界 - -推荐新增独立 Market Events publish outbox: - -- source table:`muse_market_governance_action` / `muse_market_source_status_event`。 -- source tuple:`sourceOwner=market`,`sourceType=market_governance_action` 或 `market_source_status`,`sourceId=actionId/sourceEventId`,`sourceRevision=revision`。 -- commandId:稳定短幂等键,建议 `market_evt:`。 -- 状态机:`queued/running/retryable/published/dead_letter`。 -- claim:必须原子领取,具备 lease / retry / attempt ownership。 -- 终态回写:必须防 stale worker 覆盖新 claim 终态。 -- worker 默认关闭,配置启用后才 claim。 - -### 依赖边界 - -- Market server 可以依赖 `muse-module-events-api`。 -- Market server 不得依赖 `muse-module-events-server`。 -- Events server 不得依赖 Market server。 -- `muse-server` 作为装配层可以同时包含 Market server 与 Events server。 - -## 关键取舍 - -- 选择 governance/source status,不选择 Account projection:前者是 Market source owner 的用户可见事实;后者是 Account 查询读模型同步状态,复用会制造职责混乱。 -- 选择 publisher 可见,不选择 admin 可见:SSE app 侧事件面向资源 owner;管理员操作已由管理端审计和治理记录承载。 -- 使用 `notification/governance_action`,不新增 event type:Events 合同已经声明该 subtype,足够承载第一批摘要。 -- 第一切片不发布 handoff:handoff 涉及 token、授权摘要和目标 owner 消费,泄露面和跨 owner 行为更复杂,应后续单独设计。 - -## 影响范围 - -- 直接影响: - - Market governance/source status 事实发布链路。 - - Market server 依赖 events-api。 - - Market 本域 outbox / worker / migration / focused tests。 - - Events unified event focused E2E。 -- 间接影响: - - P1R-7 completed approval 的证据链更完整。 -- 不影响: - - OpenAPI 合同。 - - coverage scanner / coverage report。 - - Market 32 operations completion status。 - - Account projection 查询读模型。 - - handoff token 和目标 owner 授权消费。 - -## 风险与兼容性 - -- 事件重复风险:同一治理动作 replay 不能创建多条 unified event;必须用 commandId 与 source tuple 双重幂等。 -- 并发风险:worker terminal update 必须绑定本次 claim attempt,避免 stale worker 覆盖新 claim。 -- 敏感信息风险:`reason`、`actionSnapshot`、preview `riskSummary` 和 target owner 细节不能进入 payload。 -- 职责混淆风险:Account projection outbox 只能继续服务 Account 读模型同步,不参与 Events publish。 -- 可见性风险:ownerUserId 必须是 publisher / source fact owner,不能使用 adminUserId,否则发布者无法通过 app SSE 看到通知。 -- 兼容性:新增 outbox 与 worker 默认关闭,不改变现有 Market API 同步返回语义。 - -## 验收标准 - -审阅版通过后,执行版必须包含: - -1. Market outbox DO / Mapper / Service / Worker / 配置 / V19 migration 的文件边界。 -2. `AdminMarketGovernanceServiceImpl` 创建 governance/source status fact 后创建 outbox 的事务边界。 -3. payload allowlist 与敏感字段拒绝策略。 -4. worker success / duplicate / retryable / rejected / blocked / dead_letter / stale claim tests。 -5. Market governance source fact -> Market outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible focused E2E。 -6. dependency gate:Market server 有 events-api、无 events-server;Events server 无 Market server。 -7. V19 SQL 静态 gate 与 Flyway `_test`。 -8. P1R mixed gates 继续确认 Events / AI / Knowledge 已有 source owner gates 不回退,Market 仍为 `dedicated / needs_verification`。 -9. coverage scanner 只在 `/tmp` 隔离副本运行,真实 worktree protected diff 为空。 - -## 待确认项 - -1. P1R-7d 第一切片是否确认只做 governance/source status,而不做 purchase/install/handoff。 -2. `notification/governance_action` 的 message 是否允许按动作区分为“资产已下架 / 资产已召回”,还是统一为“资产治理状态已更新”。 -3. 如果 recall 同时产生 governance action 和 source status event,执行版是否只发布一条 governance action 通知,还是允许两条不同 sourceType 的通知。 diff --git a/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation执行版.md b/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation执行版.md deleted file mode 100644 index 516a1fa9..00000000 --- a/docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation执行版.md +++ /dev/null @@ -1,650 +0,0 @@ -# P1R7d Market Source Owner Propagation 执行版 - -## 结论 - -P1R-7d 只执行 Market source owner propagation 的第三 owner 切片:把 Market governance terminal fact 转成 Market 本域 Events publish outbox,再由 Market worker 调用 `EventsPublishApi` 写入 `muse_unified_event`,最终通过 `/app-api/muse/events` 对资产发布者 SSE 可见。 - -本执行版不允许把 Events / P1R-7 / Market 推进到 `completed`,不允许修改 OpenAPI、coverage scanner 或 coverage report,不允许复用 Account projection outbox 作为 Events publish outbox。 - -```mermaid -flowchart LR - Command["adminDelistAsset / adminRecallAsset
expectedStatus + impactPreview"] --> Fact["muse_market_governance_action
applied"] - Command --> SourceStatus["muse_market_source_status_event
recall only, evidence only"] - Fact --> Outbox["muse_market_event_publish_outbox
one row per governance action"] - Outbox --> Worker["Market publish worker
claim/retry/dead_letter"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified["muse_unified_event
notification/governance_action"] - Unified --> Stream["/app-api/muse/events
publisher visible SSE"] -``` - -## Review Gate - -P1R-7d 审阅版已通过 fresh review: - -- Ptolemy spec compliance review:PASS,无 P0/P1/P2。 -- Galileo quality/feasibility review:PASS,无 P0/P1/P2。 - -P3 已纳入本执行版: - -- `recall` 第一切片只发布一条 `market_governance_action` 通知,`muse_market_source_status_event` 只作为 recall 事实旁证和后续扩展点。 -- `resourceRef.resourceId` 必须用数值型,不能把 DTO 的 string `resourceId` 直接塞入 payload。 -- command replay 不补历史 outbox;历史 governance fact backfill 不是 P1R-7d 第一切片目标。 -- 实现必须由 fresh implementer 执行,完成后再做 fresh spec review + fresh quality / feasibility review。 - -## 边界 - -### 本轮目标 - -- 在 Market owner 内新增独立 Events publish outbox。 -- 只接入 `AdminMarketGovernanceServiceImpl.delistAsset` 和 `AdminMarketGovernanceServiceImpl.recallAsset` 成功路径。 -- 只发布 `muse_market_governance_action` 形成的用户可见治理终态通知。 -- 通过 Market worker 调用 `EventsPublishApi`。 -- 用 focused tests、P1R mixed gates、Flyway `_test`、dependency gate 和隔离 coverage scanner 证明链路进入 `needs_verification` 证据层。 - -### 本轮非目标 - -- 不发布 `previewGovernanceImpact`。 -- 不发布 purchase / install / license / handoff / Account projection 同步事件。 -- 不发布 `muse_market_source_status_event` 独立 SSE;recall 的 source status event 只作为 E2E 事实旁证。 -- 不发布 authorization snapshot、handoff token、target owner 私有事实、完整 actionSnapshot、preview riskSummary、管理员内部备注原文或治理证据详情。 -- 不复用 `muse_market_account_projection` 作为 Events publish outbox。 -- 不抽取 AI / Knowledge / Market 共享 outbox 框架。 -- 不为历史 governance action 做 backfill。 -- 不修改 `docs/api-contracts/*/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json` 或 `.md`。 - -### 受保护文件 - -实现代理不得修改以下文件: - -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 已验证事实 - -- 正确 worktree 是 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支是 `dev/1.0.0`,远端跟踪是 `origin/dev/1.0.0`。 -- 当前 HEAD 为 `e55618f test(p1r): 收口 Knowledge 事件传播真实链路门禁`。 -- P1R-7b 已提交并 push:`3db5fbe` 与 `230152c`。 -- P1R-7c 已提交并 push:`cef686b` 与 `e55618f`。 -- P1R-7d 审阅版路径为 `docs/agent-specs/2026-06-07-P1R7dMarketSourceOwnerPropagation审阅版.md`。 -- P1R-7d 审阅版 fresh review 已双 PASS:Ptolemy spec PASS,Galileo quality PASS。 -- 当前 coverage 边界仍为 `completedOperations=100`、`needsVerificationOperations=133`、`incompleteOperations=0`、`genericPersistenceOperations=0`、`ssePlaceholderOperations=0`。 -- Events `streamEvents` 当前是 `dedicated / needs_verification`。 -- Market 32 operations 当前是 `dedicated / needs_verification`。 -- 当前受保护文件 diff 为空。 -- 当前最新 migration 是 `muse-cloud/sql/muse/V18__extend_knowledge_events_publish_outbox.sql`;P1R-7d 下一 migration 编号应为 `V19`。若实现前 live 文件列表已出现更高版本,必须停止并修订本执行版的 migration 编号。 -- `EventsPublishReqDTO` 必填 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`eventType`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 当前先按 `commandId` 回放,再按 source tuple 回放。 -- `EventsPublishServiceImpl` 当前已声明 `notification` event type。 -- `EventsPublishServiceImpl` 当前已声明 `governance_action` notification type。 -- Events OpenAPI `SSENotificationEvent.data.resourceRef.resourceId` 为 int64。 -- `AdminMarketGovernanceServiceImpl.delistAsset` 成功后写 `muse_market_governance_action`。 -- `AdminMarketGovernanceServiceImpl.recallAsset` 成功后写 `muse_market_governance_action`,并额外写 `muse_market_source_status_event`。 -- `MuseMarketGovernanceActionDO` 包含 `actionId`、`previewId`、`assetId`、`actorUserId`、`ownerUserId`、`actionType`、`commandId`、`requestHash`、`status`、`revision`、`actionSnapshot`、`reason`。 -- `MuseMarketSourceStatusEventDO` 包含 `sourceEventId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`ownerUserId`、`assetId`、`sourceStatus`、`actionPolicy`、`recheckReasons`、`eventSummary`、`errorCode`、`errorMessage`。 -- `MarketAccountProjectionOutboxServiceImpl` 当前只有 `recordSynced` / `recordBlocked`,无 Events publish claim / retry / dead_letter / published event 语义。 -- `muse-module-market-server/pom.xml` 当前已有对 member server 的依赖;实现前必须只新增 events-api direct dependency,不得新增 events-server dependency。 - -## 推断 - -- P1R-7d 可复用 P1R-7b/P1R-7c 的 outbox 状态机、worker claim、stale claim guard、dependency gate 和 Flyway `_test` 模式。 -- Market 第一切片只发 governance action,比同时发布 governance action + source status event 更稳:用户收到一条明确治理通知,source status event 仍保留为 Market 内部 source propagation evidence。 -- Account projection outbox 和 Events publish outbox 必须保持两张表 / 两套状态机,否则 Account 读模型同步失败与 SSE 发布失败会互相污染。 - -## 假设 - -- P1R-7d 允许 Market server 新增 `muse-module-events-api` direct dependency。 -- P1R-7d 允许新增 `muse_market_event_publish_outbox`。 -- P1R-7d 第一批通知的 SSE owner 是资产发布者 `ownerUserId`,不是 admin actor。 -- P1R-7d 第一批使用 Events 现有 `notification/governance_action`,不新增 OpenAPI event type 或 notification type。 - -## 设计合同 - -### 事件合同 - -第一批只允许以下 Market governance action 进入 publish outbox: - -| Market source fact | actionType | Events eventType | notification type | SSE owner | message | -|---|---|---|---|---|---| -| `muse_market_governance_action` | `delist` | `notification` | `governance_action` | `ownerUserId` | `资产已下架` | -| `muse_market_governance_action` | `recall` | `notification` | `governance_action` | `ownerUserId` | `资产已召回` | - -`muse_market_source_status_event(sourceStatus=recalled)` 不单独创建 outbox。实现测试必须证明 recall 成功路径上 source status event 存在,但最终只产生一条 governance action notification。 - -### 幂等合同 - -- 每个 governance action 只生成一条 outbox。 -- `commandId` 格式:`mk_evt:` + SHA-256(`tenantId|actionId|ownerUserId|governance_action`) 前 32 位 hex。 -- `outboxId` 格式:`mk_out:` + SHA-256(`tenantId|actionId|ownerUserId|governance_action`) 前 32 位 hex。 -- Events source tuple: - - `sourceOwner = market` - - `sourceType = market_governance_action` - - `sourceId = actionId` - - `sourceRevision = revision`,为空时使用 Events 现有 `__none__` - - `eventType = notification` -- 同一 governance action 重放必须得到同一 Events `eventId/sequenceNo`。 -- 同一 asset 的不同 governance action 必须生成不同 commandId 和不同 source tuple。 -- Command replay 策略:`delistAsset` / `recallAsset` 命中 Market command replay 时不补历史 outbox;只有首次成功写入 governance action 的事务内创建 outbox。历史 backfill 若需要,必须另起任务。 - -### Payload 合同 - -Internal outbox 允许保存以下字段: - -- `actionId` -- `assetId` -- `actionType` -- `sourceCommandId` -- `actorUserId` -- `ownerUserId` -- `previewId` -- `requestHash` -- `sourceRevision` -- `message` -- `resourceType` -- `resourceId` - -Events `payloadSummary` 只能发布: - -```json -{ - "type": "governance_action", - "message": "资产已召回", - "resourceRef": { - "resourceType": "market_asset", - "resourceId": 1001 - }, - "timestamp": "2026-06-07T12:00:00" -} -``` - -约束: - -- `resourceRef.resourceId` 必须是数值型 Long / Integer 语义,不能是字符串。 -- `message` 只允许 `资产已下架` 或 `资产已召回`。 -- `timestamp` 使用 governance action 的 `createTime` 或 worker 构造时可回退到 outbox `createTime`。 -- 不得发布 `reason`、完整 `actionSnapshot`、preview riskSummary、authorization snapshot、handoff token、target owner、admin note、error stack、requestHash。 -- `payloadSummary` 必须是 exact allowlist:顶层只能包含 `type`、`message`、`resourceRef`、`timestamp`;`resourceRef` 只能包含 `resourceType`、`resourceId`。 - -### 状态机合同 - -Outbox 本域状态只允许: - -- `queued` -- `running` -- `retryable` -- `published` -- `dead_letter` - -状态流转: - -```mermaid -stateDiagram-v2 - [*] --> queued - queued --> running: claim - retryable --> running: retry due - running --> running: expired lease reclaim - running --> published: Events accepted - running --> dead_letter: Events rejected/blocked - running --> retryable: temporary failure and attempts remain - running --> dead_letter: retry exhausted -``` - -Worker claim 要求: - -- 使用 PostgreSQL `FOR UPDATE SKIP LOCKED` 或等价 `UPDATE ... RETURNING` 原子领取。 -- 调度线程无请求租户上下文时必须先忽略租户拦截器领取,再使用 outbox 行内 `tenantId` 恢复租户上下文发布。 -- Claim 阶段唯一递增 `attemptCount`。 -- 临时失败只释放 claim、设置 `nextRetryAt` 和安全错误摘要,不二次递增 attempt count。 -- `markPublished` / `markRetryable` / `markDeadLetter` 必须绑定 `publish_status='running' AND attempt_count=#{claimedAttemptCount}`。 -- terminal update 返回 0 时必须记录 stale claim 诊断日志,包含 tenantId、ownerUserId、outboxId、actionId、attempt、targetStatus、errorCode。 -- Worker 默认关闭:`muse.market.events.publish-worker.enabled=false`。 -- 调度配置键: - - `muse.market.events.publish-worker.initial-delay-ms` - - `muse.market.events.publish-worker.fixed-delay-ms` - - `muse.market.events.publish-worker.max-attempt` - - `muse.market.events.publish-worker.claim-timeout-seconds` - -### 失败路径合同 - -| 路径 | 结果 | -|---|---| -| 无 governance action | 不创建 outbox | -| actionType 非 `delist/recall` | 不创建 outbox | -| ownerUserId null 或 <= 0 | 不创建 outbox,记录安全日志 | -| payload 构造缺 required 字段 | 创建 outbox 前拒绝并记录安全日志,不创建 outbox,不发布,错误码固定为 `MARKET_EVENTS_PAYLOAD_INVALID` | -| Events accepted | outbox -> published,回写 eventId / sequenceNo | -| Events duplicate accepted | outbox -> published,回写既有 eventId / sequenceNo | -| Events rejected / blocked | outbox -> dead_letter | -| CommonResult error / exception | attempt 未耗尽 -> retryable;耗尽 -> dead_letter | -| command replay | 不补历史 outbox,不重复发布 | - -## 涉及文件 - -### 生产代码 - -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/pom.xml` -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/AdminMarketGovernanceServiceImpl.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishOutboxService.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishOutboxServiceImpl.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishWorker.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/dataobject/muse/MuseMarketEventPublishOutboxDO.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketEventPublishOutboxMapper.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/framework/config/MarketEventsConfiguration.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/framework/config/MarketEventsProperties.java` -- Create: `muse-cloud/sql/muse/V19__extend_market_events_publish_outbox.sql` - -### 测试代码 - -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/AdminMarketGovernanceServiceTest.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishOutboxServiceTest.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishWorkerTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishDependencyTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishEndToEndTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishFlywayMigrationIT.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishMigrationSqlTest.java` - -### 文档 - -- Modify: `docs/agent-specs/.agent` -- Create: `docs/memorys/YYYY-MM-DD-P1R7dMarketSourceOwnerPropagation真实链路.md` - -## 实施任务 - -### Task 0:fresh implementer preflight - -- 核对 `pwd`、`git status --short --branch`、`git log --oneline -5`。 -- `git pull --ff-only origin dev/1.0.0`。 -- 核对受保护文件 diff 为空。 -- Git / protected diff / coverage scanner 均从 worktree 根目录执行;Maven / Flyway 命令必须先 `cd muse-cloud`,因为 reactor POM 位于 `muse-cloud/pom.xml`。 -- 核对 `muse-cloud/sql/muse` 最新 migration 是否仍为 V18;若不是,停止并修订本执行版。 -- 只读核对 Market server 当前没有 events-api dependency。 -- 只读核对 Events server 仍不依赖 Market server。 - -### Task 1:V19 migration 与 SQL gate - -新增 `muse_market_event_publish_outbox`。 - -必须字段: - -- `id` -- `outbox_id` -- `action_id` -- `asset_id` -- `action_type` -- `source_command_id` -- `owner_user_id` -- `actor_user_id` -- `preview_id` -- `request_hash` -- `source_revision` -- `event_type` -- `notification_type` -- `payload_summary` -- `command_id` -- `publish_status` -- `attempt_count` -- `max_attempt` -- `claimed_at` -- `claim_expires_at` -- `next_retry_at` -- `last_error_code` -- `last_error_message` -- `published_event_id` -- `published_sequence_no` -- `tenant_id` -- `deleted` -- `creator` -- `updater` -- `create_time` -- `update_time` -- `update_time` 自动维护 trigger - -必须约束: - -- `event_type = 'notification'` -- `notification_type = 'governance_action'` -- `action_type IN ('delist','recall')` -- `publish_status IN ('queued','running','retryable','published','dead_letter')` -- `owner_user_id > 0` -- `attempt_count >= 0` -- `max_attempt > 0` - -必须唯一键: - -- `(tenant_id, outbox_id)` -- `(tenant_id, command_id)` -- `(tenant_id, action_id, notification_type)` - -必须索引: - -- claim index:tenant + publish_status + next_retry_at + id -- owner status index:tenant + owner_user_id + publish_status -- action index:tenant + action_id - -SQL gate 必须验证: - -- 表存在。 -- check 约束存在且不是宽松 OR 表达式。 -- unique key 和 claim index 存在。 -- update_time trigger 存在。 -- `resourceRef.resourceId` 不在 DB 约束中表达,留给 payload 单元测试验证。 - -### Task 2:Outbox DO / Mapper - -实现 DO 与 Mapper。 - -Mapper 必须包含: - -- `insertIgnore` -- `selectByTenantIdAndOutboxId` -- `selectByTenantIdAndCommandId` -- `selectByTenantIdAndActionIdAndNotificationType` -- `claimNext` -- `markPublished` -- `markRetryable` -- `markDeadLetter` - -Mapper 约束: - -- `claimNext` 必须只领取 `queued/retryable/running expired`。 -- `claimNext` 必须设置 `publish_status='running'`、递增 `attempt_count`、设置 `claimed_at/claim_expires_at`。 -- `markPublished/markRetryable/markDeadLetter` 必须要求 `publish_status='running' AND attempt_count=#{claimedAttemptCount}`。 - -### Task 3:Outbox service - -实现 `MarketEventPublishOutboxService.createForGovernanceAction(action)`。 - -必须行为: - -- 只接受 `delist/recall`。 -- ownerUserId null 或 <= 0 时跳过并记录安全日志。 -- 构造 payload 时 `resourceRef.resourceId` 必须是数值型 action.assetId。 -- message 按 actionType 固定: - - `delist` -> `资产已下架` - - `recall` -> `资产已召回` -- 使用 `insertIgnore + 回查` 幂等。 -- `sourceRevision` 为空时归一为 `__none__`。 -- 不读取 `reason` 或完整 `actionSnapshot` 拼 payload。 -- command replay 不补历史 outbox。 - -### Task 4:接入 governance service - -修改 `AdminMarketGovernanceServiceImpl.applyGovernanceAction`: - -- 在 governance action insert 成功后创建 outbox。 -- 保持同事务:治理事实写入成功且 outbox 创建失败时,整个 governance command 回滚,避免治理事实和 publish intent 不一致。 -- `recallAsset` 仍写 source status event,但不为 source status event 创建独立 outbox。 -- 命中 command replay 时仍直接回放,不补历史 outbox。 - -### Task 5:Worker - -新增 Market publish worker。 - -必须行为: - -- 默认 disabled,不 claim。 -- enabled 时 claim 一条并发布。 -- 构造 `EventsPublishReqDTO`: - - `sourceOwner=market` - - `sourceType=market_governance_action` - - `sourceId=actionId` - - `sourceRevision=sourceRevision` 或 `__none__` - - `eventType=notification` - - `resourceType=market_asset` - - `resourceId=assetId.toString()` - - `payloadSummary` 使用 outbox 中 JSON 解析后的 allowlist map - - `ownerUserId=outbox.ownerUserId` -- `EventsPublishReqDTO.resourceId` 仍按 Events API DTO 使用字符串;`payloadSummary.resourceRef.resourceId` 必须保持 Number / Long 语义,测试必须分别断言两者,不能混写。 -- Events accepted -> `published`。 -- Events rejected/blocked -> `dead_letter`。 -- CommonResult error / exception -> `retryable` 或 retry exhausted 后 `dead_letter`。 -- terminal update 0 rows 记录 stale claim 诊断日志。 - -### Task 6:Focused tests - -必须覆盖: - -- delist action 创建 queued outbox。 -- recall action 只创建一条 governance action outbox;source status event 存在但不创建第二条 outbox。 -- command replay 不补历史 outbox。 -- ownerUserId invalid 不创建 outbox。 -- payload 使用 exact allowlist:顶层只能有 `type/message/resourceRef/timestamp`,`resourceRef` 只能有 `resourceType/resourceId`。 -- payload 不包含 reason / actionSnapshot / preview riskSummary / authorization snapshot / handoff token / target owner / admin note / error stack / requestHash。 -- payload invalid fail-closed 使用固定错误码 `MARKET_EVENTS_PAYLOAD_INVALID`,并断言只进入安全日志或安全 lastError 摘要,不创建 outbox、不发布。 -- resourceRef.resourceId 是数值型。 -- duplicate create 复用既有 outbox。 -- worker disabled 不 claim。 -- worker accepted -> published。 -- worker duplicate accepted -> published。 -- worker rejected / blocked -> dead_letter。 -- worker CommonResult error -> retryable;last attempt -> dead_letter;lastError 只保留安全摘要,不持久化原始错误正文。 -- worker exception -> retryable;last attempt -> dead_letter。 -- stale claim terminal update 被忽略并记录日志。 - -### Task 7:P1R mixed gates - -必须新增: - -- `P1rMarketEventsPublishDependencyTest` -- `P1rMarketEventsPublishEndToEndTest` -- `P1rMarketEventsPublishMigrationSqlTest` -- `P1rMarketEventsPublishFlywayMigrationIT` - -E2E 必须证明: - -- Market governance action -> Market outbox -> worker -> EventsPublishApi -> `muse_unified_event`。 -- publisher owner 可见。 -- admin actor 不因 actorUserId 可见。 -- 非 owner 不可见。 -- recall 只发布一条 governance action notification。 -- blocked/rejected 不进入 visible query。 - -Mixed gate 命令必须继续包括: - -- `P1rApiCoverageReportTest` -- `P1rEventsRealApiGateTest` -- `P1rEventsRouteOwnershipTest` -- `P1rAiRealApiGateTest` -- `P1rAiRouteOwnershipTest` -- `P1rAiEventsPublishMigrationSqlTest` -- `P1rAiEventsPublishDependencyTest` -- `P1rAiEventsPublishEndToEndTest` -- `P1rKnowledgeEventsPublishMigrationSqlTest` -- `P1rKnowledgeEventsPublishDependencyTest` -- `P1rKnowledgeEventsPublishEndToEndTest` -- P1R-7d Market 新增 gate - -### Task 8:Flyway `_test` - -新增 `P1rMarketEventsPublishFlywayMigrationIT`。 - -要求: - -- 只允许 `_test` 结尾数据库。 -- 禁止 JDBC URL 携带 password / credential query。 -- password 只能来自 `P1R_FLYWAY_PASSWORD`。 -- 推荐复用禁用 JVM 代理参数: - - `-Djava.net.useSystemProxies=false` - - `-DsocksProxyHost=` - - `-DsocksProxyPort=` - - `-Dhttp.proxyHost=` - - `-Dhttp.proxyPort=` - - `-Dhttps.proxyHost=` - - `-Dhttps.proxyPort=` -- 验证 V19 表、约束、索引、trigger。 -- 真实 PostgreSQL 负向 insert 探针至少覆盖: - - illegal event_type - - illegal notification_type - - illegal action_type - - illegal publish_status - - owner_user_id <= 0 - -### Task 9:Coverage scanner 与 protected diff - -只允许在 `/tmp` 隔离副本运行: - -```bash -tmpdir=$(mktemp -d /tmp/p1r7d-coverage-scan.XXXXXX) -rsync -a --delete --exclude .git --exclude muse-cloud/target --exclude 'muse-cloud/**/target' \ - /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/ "$tmpdir/" -cd "$tmpdir" -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -真实 worktree 必须继续确认 protected diff 为空。 - -### Task 10:文档留痕与 review - -- 更新 `docs/agent-specs/.agent`。 -- 新增 `docs/memorys/YYYY-MM-DD-P1R7dMarketSourceOwnerPropagation真实链路.md`。 -- 实现完成后必须 fresh spec review + fresh quality/feasibility review。 -- 双 PASS 也只代表 `needs_verification` evidence 收口,不代表 completed。 - -## 最终验证命令 - -实现完成后按顺序运行: - -1. Reactor build: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -2. Market focused tests: - -先断言新增测试类存在,避免 `failIfNoSpecifiedTests=false` 掩盖新测试漏建或拼错: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -test -f muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishOutboxServiceTest.java && \ -test -f muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketEventPublishWorkerTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishMigrationSqlTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishDependencyTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishEndToEndTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketEventsPublishFlywayMigrationIT.java -``` - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-module-market/muse-module-market-server -Dtest=AdminMarketGovernanceServiceTest,MarketEventPublishOutboxServiceTest,MarketEventPublishWorkerTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -3. P1R mixed gates: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest=P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -4. Dependency tree: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o -pl muse-module-market/muse-module-market-server -am dependency:tree \ - > /tmp/p1r7d-market-server-dependency-tree.txt && \ -rg 'muse-module-events-api' /tmp/p1r7d-market-server-dependency-tree.txt && \ -! rg 'muse-module-events-server' /tmp/p1r7d-market-server-dependency-tree.txt -``` - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o -pl muse-module-events/muse-module-events-server -am dependency:tree \ - > /tmp/p1r7d-events-server-dependency-tree.txt && \ -! rg 'muse-module-(ai|knowledge|market|member|content)-server' /tmp/p1r7d-events-server-dependency-tree.txt -``` - -5. Flyway `_test`: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -set -a -source ~/.config/muse-repo/infra.env -set +a -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am \ - -Dtest=P1rMarketEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/muse_p1r7d_events_publish_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -Dhttp.proxyHost= \ - -Dhttp.proxyPort= \ - -Dhttps.proxyHost= \ - -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -6. Surefire XML 计数断言: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -python3 - <<'PY' -from pathlib import Path -import xml.etree.ElementTree as ET - -targets = [ - "muse-cloud/muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.application.muse.MarketEventPublishOutboxServiceTest.xml", - "muse-cloud/muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.application.muse.MarketEventPublishWorkerTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishFlywayMigrationIT.xml", -] -for target in targets: - path = Path(target) - if not path.exists(): - raise SystemExit(f"missing surefire xml: {target}") - 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")) - if tests <= 0 or failures or errors or skipped: - raise SystemExit(f"bad surefire result: {target} tests={tests} failures={failures} errors={errors} skipped={skipped}") -PY -``` - -7. 隔离 coverage scanner: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -tmpdir=$(mktemp -d /tmp/p1r7d-coverage-scan.XXXXXX) && \ -rsync -a --delete --exclude .git --exclude muse-cloud/target --exclude 'muse-cloud/**/target' \ - /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/ "$tmpdir/" && \ -cd "$tmpdir" && \ -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -8. Protected diff: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -git diff --quiet -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -## 完成条件 - -- P1R-7d Market governance action -> Market outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible 证据成立。 -- 所有 focused / mixed / dependency tree / Flyway / coverage / protected diff gate 通过。 -- fresh implementation spec review + fresh quality/feasibility review 双 PASS。 -- 不修改 OpenAPI、scanner、coverage report。 -- 不推进 Events / P1R-7 / Market completed。 - -## 回滚策略 - -- 配置关闭 `muse.market.events.publish-worker.enabled=false` 后 worker 不 claim。 -- 数据层可保留 `muse_market_event_publish_outbox` 作为审计事实,不影响现有 Market API。 -- 如需要撤销应用行为,回滚 `AdminMarketGovernanceServiceImpl` 对 outbox service 的调用和 worker bean 配置;V19 表可保留到后续清理迁移。 diff --git a/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation审阅版.md b/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation审阅版.md deleted file mode 100644 index 344877cf..00000000 --- a/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation审阅版.md +++ /dev/null @@ -1,265 +0,0 @@ -# P1R7e Account Source Owner Propagation 审阅版 - -## 结论 - -推荐进入 P1R-7e,但第一切片只覆盖 Account 配额调整成功后的用户可见 `quota_alert` 通知,不覆盖全部 Account 安全事件、导出任务、New-API binding、quota request、usage attribution 或 completed approval。 - -推荐方案: - -1. 选择 `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 成功后写入的 `muse_member_entitlement_audit_log(change_type=quota_adjustment)` 作为 Account 第一批 source fact。 -2. 新增 Member(Account) 本域 Events publish outbox / worker,调用 `EventsPublishApi`,发布 Events 已声明的 `notification/quota_alert`。 -3. 不复用 `muse_account_integration_call`、`muse_account_quota_request`、`muse_account_audit` 或 `muse_member_security_event` 作为 Events publish outbox。 -4. 不修改 OpenAPI、scanner 或 coverage report;不推进 Account / Events / P1R-7 completed。 - -```mermaid -flowchart LR - Admin["Admin quota adjustment command"] --> Quota["muse_member_quota
limit / used / revision"] - Admin --> Audit["muse_member_entitlement_audit_log
quota_adjustment"] - Audit --> Outbox["Account events publish outbox
queued / running / retryable / published / dead_letter"] - Outbox --> Worker["Account worker
claim + retry + stale-claim guard"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified[("muse_unified_event
notification / quota_alert")] - Unified --> SSE["/app-api/muse/events
account owner visible"] -``` - -本审阅版只冻结目标、非目标、取舍、风险和验收标准。执行版、实现和提交必须在本审阅版 fresh spec review + fresh quality / feasibility review 双 PASS 后再继续。 - -## Review Gate - -旧 feasibility review(Goodall)对早期候选 `MemberSecurityEventDO` 安全事件通知给出 FAIL。该 FAIL 已验证有效,并已用于排除 security event 第一切片: - -- 安全事件生产写入当前只在高敏导出路径后段,但高敏导出会在 `normalizeRequest` 中 fail-closed,无法形成当前可达 source fact。 -- ack 是用户处理轨迹,不是新安全事实产生。 -- Events 当前 notification subtype 不包含 `security_event`,在不改 OpenAPI 的约束下不能发布安全事件通知。 - -修订后的最终审阅版已改为 `quota adjustment -> notification/quota_alert`,并完成 fresh 双 review: - -- Gibbs spec compliance review:PASS,无 P0/P1/P2。 -- Ampere quality / feasibility review:PASS,无 P0/P1/P2。 - -非阻塞风险已纳入执行版前置决策: - -- 执行版必须冻结为每条 entitlement audit log 一条通知。 -- `resourceRef.resourceId` 必须使用数值型 audit log id,不能使用 account user id。 -- `sourceRevision` 固定为 Events 现有 `__none__`,不为 audit log 新增 revision。 - -## 已验证事实 - -### 工作区与状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 返回 `Already up to date.`。 -- 当前 HEAD 包含 P1R-7d 两个已推送提交: - - `b36b153 feat(p1r): 接入 Market 事件传播真实链路` - - `e8ed7d7 test(p1r): 收口 Market 事件传播真实链路门禁` -- 受保护文件 diff 为空: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` -- 当前未提交内容包括 P1R-7 completed approval 预检审阅文档和 `.agent` 状态补记;本阶段不回退、不清理。 - -### 全局 P1R-7 边界 - -- P1R-7b AI、P1R-7c Knowledge、P1R-7d Market source owner propagation 已形成 `needs_verification` evidence,不代表 Events / P1R-7 / 对应业务域 completed。 -- P1R-7 completed approval 必须另起任务并由用户明确批准。 -- source owner 只能依赖 `muse-module-events-api`;Events server 不得反向依赖 AI / Knowledge / Market / Member / Content server。 -- coverage 当前边界仍保持: - - `completedOperations=100` - - `needsVerificationOperations=133` - - `incompleteOperations=0` - - `genericPersistenceOperations=0` - - `ssePlaceholderOperations=0` - - Events `streamEvents=dedicated / needs_verification` - - Account 33 operations `dedicated / needs_verification` - -### Events 可复用合同 - -- `EventsPublishReqDTO` 已包含 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`eventType`、`resourceType`、`resourceId`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 先按 `commandId` 回放,再按 source tuple 回放。 -- `EventsPublishServiceImpl` 已声明事件类型 `notification`,并允许 notification subtype `quota_alert`。 -- `docs/api-contracts/events/openapi.yaml` 的 `SSENotificationEvent.data.type` 已声明 `quota_alert`。 -- `EventsStreamServiceImpl.notificationData` 支持 `type/message/resourceRef/timestamp`,并对 `resourceRef.resourceId` 做 int64 语义过滤。 - -### Account 当前事实模型 - -- Account owner 位于 `muse-module-member` 的 Account 子域,没有独立 `muse-module-account`。 -- `muse-module-member-server/pom.xml` 当前没有依赖 `muse-module-events-api`。 -- `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 在同一事务内: - - 校验 Account 用户存在。 - - 用 `AccountCommandService.reserveCommand` 做 `commandId` 幂等。 - - 按 `MemberQuotaDO.revision` 更新或创建 quota。 - - 写入 `AccountAuditDO`。 - - 每个调整项写入 `MemberEntitlementAuditLogDO(changeType=quota_adjustment)`。 - - `MemberEntitlementAuditLogDO.accountUserId` 是被调整账户 owner。 - - `MemberEntitlementAuditLogDO.idempotencyKey` 使用 `commandId:resourceType`。 - - `commandService.recordSucceeded` 保存结果快照。 -- `MemberEntitlementAuditLogMapper.selectPageByAccountUserId` 已把 Account 配额 ledger 固定在 `quota_adjustment` 语义,并按 OpenAPI source owner allowlist 过滤。 -- Events 合同已有 `quota_alert`,因此本阶段不需要修改 Events OpenAPI。 -- `MemberSecurityEventDO` 存在并有 App 读侧、ack 和脱敏转换;但 `AccountExportServiceImpl` 的高敏导出当前在 `normalizeRequest` 中 fail-closed,生产路径不会走到 `insertSensitiveExportEvent`。因此它不适合作为 P1R-7e 第一切片事实源。 -- `AccountQuotaRequestDO` 和 `AccountIntegrationCallDO` 当前表达 quota request / 外部归因等待状态,不是终态配额变化事实。 - -## 推断 - -- P1R-7e 第一切片应选 quota adjustment,而不是 security event。原因是 quota adjustment 当前真实可达,且成功后已有 quota、command、audit、entitlement audit log 和 ownerUserId;security event 的高敏导出生产入口当前被 fail-closed 阻断。 -- `notification/quota_alert` 已由 Events 合同声明,适合承载“额度已调整”的用户可见摘要。 -- `muse_member_entitlement_audit_log` 是 source fact,不应直接兼任 publish outbox。它没有 claim lease、attempt、retry、published event、dead_letter、last_error 等发布队列语义。 -- 每个 quota adjustment command 可能写多条资源类型调整;第一切片可以按每条 entitlement audit log 发布一条 `quota_alert`,也可以在执行版中聚合为每个 command 一条通知。推荐每条 log 一条通知,因为它已有 `idempotencyKey=commandId:resourceType`,与 source tuple 更自然闭合。 - -## 假设 - -- P1R-7e 允许在 Member(Account) owner 内新增独立 Events publish outbox 表或等价状态表。 -- P1R-7e 允许 `muse-module-member-server` 新增对 `muse-module-events-api` 的直接依赖,但不允许依赖 `muse-module-events-server`。 -- Account quota alert 的 SSE owner 是 `MemberEntitlementAuditLogDO.accountUserId`,不是 admin operator。 -- `quota_alert` payload 可以使用 Events 现有 `SSENotificationEvent` 字段,不需要新增 OpenAPI 字段。 - -## 目标 - -1. 证明 Account source owner 的真实 quota adjustment 事实可以通过 Account 本域 publish outbox / worker 发布到 Events。 -2. 让被调整账户用户可以通过统一 SSE 看到自己的额度变更通知。 -3. 建立 Account 与 Events 的单向依赖证据:Member server 依赖 events-api,不依赖 events-server;Events server 不反向依赖 Member server。 -4. 为后续 Account security event、quota request、integration call、export task 等事件扩展保留清晰边界。 - -## 非目标 - -1. 不把 Account 33 operations 从 `needs_verification` 推进到 `completed`。 -2. 不把 Events `streamEvents` 或总 P1R-7 推进到 `completed`。 -3. 不修改 Account / Events OpenAPI。 -4. 不修改 coverage scanner 或 coverage report。 -5. 不发布全部 Account security event,不发布高敏导出安全事件。 -6. 不发布 New-API binding、quota request、integration call、call attribution job、usage record 或 export download credential。 -7. 不发布 admin reason 原文、requestHash、before/after 完整快照、operatorUserId、integration correlationId、外部调用详情、错误堆栈或文件凭证。 -8. 不让 Events server 查询 Member / Account 表。 - -## 推荐方案 - -### 事件选择 - -第一批只允许: - -| Account source fact | 触发入口 | Events eventType | notification type | SSE owner | -|---|---|---|---|---| -| `muse_member_entitlement_audit_log(change_type=quota_adjustment)` | `adminCreateQuotaAdjustment` | `notification` | `quota_alert` | `account_user_id` | - -暂不选择: - -| 候选事实 | 暂不选择原因 | -|---|---| -| `muse_member_security_event` | 高敏导出创建入口当前 fail-closed,第一切片没有真实可达生产事实;且安全事件 payload 脱敏要求更高 | -| `muse_account_security_event_ack` | ack 是用户处理轨迹,不是新安全事实产生 | -| `muse_account_quota_request` | 状态是 request queued / pending external attribution,不是额度已变化终态 | -| `muse_account_integration_call` | 内部外部调用跟踪,不适合直接推给 App SSE | -| `muse_member_usage_record` | 用量流水可能高频且含 attribution / gateway 信息,需单独节流和脱敏设计 | - -### Payload allowlist - -第一批 payload 只允许: - -```json -{ - "type": "quota_alert", - "message": "额度已调整", - "resourceRef": { - "resourceType": "account_quota", - "resourceId": 8001 - }, - "timestamp": "2026-06-07T12:00:00" -} -``` - -执行版可以把 message 收窄为安全固定枚举,例如: - -- `额度已调整` -- `AI 调用额度已调整` -- `导出额度已调整` - -不得把以下字段放入 Events payload: - -- `requestHash` -- `reasonMessage` -- `operatorUserId` -- `beforeValueSnapshot` -- `deltaValueSnapshot` -- `afterValueSnapshot` -- `correlationId` -- `integrationCallId` -- 外部 provider 返回值 -- 错误堆栈 - -### Outbox 边界 - -推荐新增独立 Account Events publish outbox: - -- source table:`muse_member_entitlement_audit_log`。 -- source tuple:`sourceOwner=account`,`sourceType=account_quota_adjustment`,`sourceId=entitlementAuditLog.id`,`sourceRevision=__none__` 或 audit log revision 等价值。 -- commandId:稳定短幂等键,建议 `acct_evt:`。 -- 状态机:`queued/running/retryable/published/dead_letter`。 -- claim:必须原子领取,具备 lease / retry / attempt ownership。 -- 终态回写:必须防 stale worker 覆盖新 claim 终态。 -- worker 默认关闭,配置启用后才 claim。 -- invalid owner 或 invalid payload 必须 fail-closed,不得发布给 owner `0` 或 admin operator。 - -### 依赖边界 - -- Member server 可以依赖 `muse-module-events-api`。 -- Member server 不得依赖 `muse-module-events-server`。 -- Events server 不得依赖 Member server。 -- `muse-server` 作为装配层可以同时包含 Member server 与 Events server。 - -## 关键取舍 - -- 选择 quota adjustment,不选择 security event:前者当前真实可达且 Events 合同有 `quota_alert`;后者虽然有读侧和脱敏转换,但首个生产写入路径被 fail-closed 阻断。 -- 选择 account owner 可见,不选择 admin operator 可见:统一 SSE 是 App 侧 owner stream;管理员操作应继续由 admin audit / ledger 承载。 -- 使用 `notification/quota_alert`,不新增 event type:Events 合同已经声明 subtype,足够承载第一批摘要。 -- 不复用 entitlement audit log 作为 publish queue:source fact 与 publish compensation 是两个职责,混用会缺 claim、retry 和 dead_letter 证据。 - -## 影响范围 - -- 直接影响: - - Account quota adjustment source fact 发布链路。 - - Member server 依赖 events-api。 - - Member(Account) 本域 outbox / worker / migration / focused tests。 - - Events unified event focused E2E。 -- 间接影响: - - P1R-7 completed approval 的 source owner evidence 更完整。 -- 不影响: - - OpenAPI 合同。 - - coverage scanner / coverage report。 - - Account 33 operations completion status。 - - Account quota adjustment 同步返回语义。 - - New-API、FileService、Market、Content 或 security event 业务逻辑。 - -## 风险与兼容性 - -- 事件重复风险:同一 entitlement audit log retry 不能创建多条 unified event;必须用 commandId 与 source tuple 双重幂等。 -- 多调整项风险:一个 command 可能产生多条 audit log;执行版必须明确按 log 逐条发布或按 command 聚合,不能两种同时存在。 -- 并发风险:worker terminal update 必须绑定本次 claim attempt,避免 stale worker 覆盖新 claim。 -- 敏感信息风险:quota adjustment 的 reason、requestHash、before/after snapshot 和 operatorUserId 不得进入 payload。 -- owner 风险:SSE owner 必须是 accountUserId,不能是 operatorUserId。 -- 可达性风险:security event 不能在本阶段被包装成已可达 production fact。 -- 兼容性:新增 outbox 与 worker 默认关闭,不改变现有 Account API 同步返回语义。 - -## 验收标准 - -审阅版通过后,执行版必须包含: - -1. Account outbox DO / Mapper / Service / Worker / 配置 / V20 migration 的文件边界。 -2. `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 在 entitlement audit log 写入后同事务创建 outbox 的事务边界。 -3. payload exact allowlist 与敏感字段拒绝策略。 -4. worker success / duplicate / retryable / CommonResult error / rejected / blocked / dead_letter / stale claim tests。 -5. Account quota adjustment source fact -> Account outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible focused E2E。 -6. dependency gate:Member server 有 events-api、无 events-server;Events server 无 AI / Knowledge / Market / Member / Content server。 -7. V20 SQL 静态 gate 与 Flyway `_test`。 -8. P1R mixed gates 继续确认 AI / Knowledge / Market source owner gates 不回退,Account 仍为 `dedicated / needs_verification`。 -9. coverage scanner 只在 `/tmp` 隔离副本运行,真实 worktree protected diff 为空。 -10. `git diff --check` 和 protected diff gate。 - -## 待确认项 - -1. P1R-7e 第一切片是否确认只做 Account quota adjustment `quota_alert`。 -2. 多个 resourceType 同一 command 时,是每条 audit log 一条通知,还是按 command 聚合一条通知。 -3. `resourceRef.resourceId` 是否使用 entitlement audit log `id`,还是使用 account user id;推荐使用 audit log id,避免把用户 ID 当业务资源 ID 暴露。 diff --git a/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation执行版.md b/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation执行版.md deleted file mode 100644 index 3b6f5a3c..00000000 --- a/docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation执行版.md +++ /dev/null @@ -1,786 +0,0 @@ -# P1R7e Account Source Owner Propagation 执行版 - -## 结论 - -P1R-7e 只执行 Account/Member source owner propagation 的第四 owner 切片:把 Account quota adjustment terminal fact 转成 Account 本域 Events publish outbox,再由 Account worker 调用 `EventsPublishApi` 写入 `muse_unified_event`,最终通过 `/app-api/muse/events` 对被调整账户用户 SSE 可见。 - -本执行版不允许把 Events / P1R-7 / Account 推进到 `completed`,不允许修改 OpenAPI、coverage scanner 或 coverage report,不允许把 security event、quota request、integration call 或 account audit 表复用为 Events publish outbox。 - -```mermaid -flowchart LR - Command["adminCreateQuotaAdjustment
commandId + expected snapshot"] --> Quota["muse_member_quota
limit / used / revision"] - Command --> Audit["muse_account_audit
admin operation audit"] - Command --> Log["muse_member_entitlement_audit_log
one row per resourceType"] - Log --> Outbox["muse_account_event_publish_outbox
one row per entitlement audit log"] - Outbox --> Worker["Account publish worker
claim/retry/dead_letter"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified["muse_unified_event
notification/quota_alert"] - Unified --> Stream["/app-api/muse/events
account owner visible SSE"] -``` - -## Review Gate - -P1R-7e 审阅版已通过 fresh review: - -- Gibbs spec compliance review:PASS,无 P0/P1/P2。 -- Ampere quality / feasibility review:PASS,无 P0/P1/P2。 - -旧 Goodall feasibility review 对早期 security event 候选 FAIL。该反馈已验证有效,并已在审阅版中通过改选 `quota adjustment -> quota_alert` 关闭;旧 FAIL 不计入当前最终审阅版 PASS gate。 - -P1R-7e 执行版第三轮 fresh review 已通过: - -- Pauli execution spec compliance review:PASS,无 P0/P1/P2;已验证 scope 未扩大、protected staged/unstaged diff gate、V20 migration 编号前提、P1R mixed gate、Surefire XML 计数、隔离 coverage scanner 和 completed 边界。 -- Hooke execution quality / feasibility review:PASS,无 P0/P1/P2;已验证 `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 同事务 audit log / outbox 接入点、Events `quota_alert` 合同、payload/resourceRef/sourceRevision/timestamp 合同、最终验证假绿口。 - -旧执行版 review 中,Pascal PASS / Schrodinger FAIL、Feynman PASS / Kant FAIL 均因后续执行版修订失效,不计入当前最终 PASS gate;其中有效反馈已经关闭到本执行版。本轮双 PASS 只允许进入 fresh implementer 实现,不代表 P1R-7e 实现完成,也不代表 Events / P1R-7 / Account completed。 - -非阻塞风险已在本执行版冻结: - -- 每条 `muse_member_entitlement_audit_log` 发布一条 `quota_alert`,不做 command 聚合。 -- `resourceRef.resourceId` 固定为数值型 entitlement audit log id。 -- `sourceRevision` 固定使用 Events 现有 `__none__`,不新增 audit log revision。 - -## 边界 - -### 本轮目标 - -- 在 Member(Account) owner 内新增独立 Events publish outbox。 -- 只接入 `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 首次成功路径。 -- 只发布 `muse_member_entitlement_audit_log(change_type=quota_adjustment)` 形成的用户可见 quota alert。 -- 每条 entitlement audit log 对应一条 outbox 和一条 Events notification。 -- 通过 Account worker 调用 `EventsPublishApi`。 -- 用 focused tests、P1R mixed gates、Flyway `_test`、dependency gate、dependency tree 和隔离 coverage scanner 证明链路进入 `needs_verification` 证据层。 - -### 本轮非目标 - -- 不发布 `muse_member_security_event`。 -- 不发布 `muse_account_security_event_ack`。 -- 不发布 `muse_account_quota_request`。 -- 不发布 `muse_account_integration_call`。 -- 不发布 `muse_member_usage_record`。 -- 不发布 New-API binding、call attribution job、export task、download credential 或 file service 事件。 -- 不发布 admin reason 原文、requestHash、before/after snapshot、operatorUserId、correlationId、external provider detail、error stack 或 credential。 -- 不抽取 AI / Knowledge / Market / Account 共享 outbox 框架。 -- 不为历史 entitlement audit log 做 backfill。 -- 不修改 `docs/api-contracts/*/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json` 或 `.md`。 - -### 受保护文件 - -实现代理不得修改以下文件: - -- `docs/api-contracts/account/openapi.yaml` -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 已验证事实 - -- 正确 worktree 是 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支是 `dev/1.0.0`,远端跟踪是 `origin/dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 返回 `Already up to date.`。 -- 当前 HEAD 为 `e8ed7d7 test(p1r): 收口 Market 事件传播真实链路门禁`。 -- P1R-7b 已提交并 push:`3db5fbe` 与 `230152c`。 -- P1R-7c 已提交并 push:`cef686b` 与 `e55618f`。 -- P1R-7d 已提交并 push:`b36b153` 与 `e8ed7d7`。 -- P1R-7e 审阅版路径为 `docs/agent-specs/2026-06-07-P1R7eAccountSourceOwnerPropagation审阅版.md`。 -- P1R-7e 审阅版 fresh review 已双 PASS:Gibbs spec PASS,Ampere quality PASS。 -- 当前 coverage 边界仍为 `completedOperations=100`、`needsVerificationOperations=133`、`incompleteOperations=0`、`genericPersistenceOperations=0`、`ssePlaceholderOperations=0`。 -- Events `streamEvents` 当前是 `dedicated / needs_verification`。 -- Account 33 operations 当前是 `dedicated / needs_verification`。 -- 当前受保护文件 diff 为空。 -- 当前最新 migration 是 `muse-cloud/sql/muse/V19__extend_market_events_publish_outbox.sql`;P1R-7e 下一 migration 编号应为 `V20`。若实现前 live 文件列表已出现更高版本,必须停止并修订本执行版的 migration 编号。 -- `EventsPublishReqDTO` 必填 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`eventType`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 当前先按 `commandId` 回放,再按 source tuple 回放。 -- `EventsPublishServiceImpl` 当前已声明 `notification` event type。 -- `EventsPublishServiceImpl` 当前已声明 `quota_alert` notification type。 -- Events OpenAPI `SSENotificationEvent.data.resourceRef.resourceId` 为 int64。 -- `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 成功后写 `muse_member_entitlement_audit_log(changeType=quota_adjustment)`,每个 adjustment 一条 log。 -- `MemberEntitlementAuditLogDO` 包含 `id`、`accountUserId`、`changeType`、`sourceOwner`、`sourceId`、`idempotencyKey`、`beforeValueSnapshot`、`deltaValueSnapshot`、`afterValueSnapshot`、`reasonCode`、`reasonMessage`、`operatorUserId`、`auditEventId`。 -- `MemberEntitlementAuditLogMapper.selectPageByAccountUserId` 已把 quota ledger 固定为 `quota_adjustment`,并按 OpenAPI source owner allowlist 过滤。 -- `muse-module-member-server/pom.xml` 当前没有 `muse-module-events-api` 依赖;实现只允许新增 events-api direct dependency,不得新增 events-server dependency。 -- `MemberSecurityEventDO` 虽有 App 读侧和 ack,但当前高敏导出生产写入路径被 fail-closed 阻断,不是 P1R-7e 第一切片 source fact。 - -## 推断 - -- P1R-7e 可复用 P1R-7b/P1R-7c/P1R-7d 的 outbox 状态机、worker claim、stale claim guard、dependency tree gate 和 Flyway `_test` 模式。 -- Account 第一切片按 entitlement audit log 逐条发布,比按 command 聚合更稳:source fact 粒度、idempotencyKey 粒度和 resourceType 粒度一致。 -- Account outbox 与 entitlement audit log 必须保持两张表 / 两套状态机,否则 Account ledger 查询事实与 Events publish 补偿状态会互相污染。 - -## 假设 - -- P1R-7e 允许 Member server 新增 `muse-module-events-api` direct dependency。 -- P1R-7e 允许新增 `muse_account_event_publish_outbox`。 -- P1R-7e 第一批通知的 SSE owner 是 `MemberEntitlementAuditLogDO.accountUserId`,不是 admin operator。 -- P1R-7e 第一批使用 Events 现有 `notification/quota_alert`,不新增 OpenAPI event type 或 notification type。 - -## 设计合同 - -### 事件合同 - -第一批只允许以下 Account source fact 进入 publish outbox: - -| Account source fact | changeType | Events eventType | notification type | SSE owner | message | -|---|---|---|---|---|---| -| `muse_member_entitlement_audit_log` | `quota_adjustment` | `notification` | `quota_alert` | `accountUserId` | `额度已调整` | - -多 resourceType 同一 command 时,必须按 entitlement audit log 逐条发布,不允许同时做 command 聚合。 - -### 幂等合同 - -- 每条 entitlement audit log 只生成一条 outbox。 -- `commandId` 格式:`acct_evt:` + SHA-256(`tenantId|auditLogId|accountUserId|quota_alert`) 前 32 位 hex。 -- `outboxId` 格式:`acct_out:` + SHA-256(`tenantId|auditLogId|accountUserId|quota_alert`) 前 32 位 hex。 -- Events source tuple: - - `sourceOwner = account` - - `sourceType = account_quota_adjustment` - - `sourceId = entitlementAuditLog.id` - - `sourceRevision = __none__` - - `eventType = notification` -- 同一 entitlement audit log 重放必须得到同一 Events `eventId/sequenceNo`。 -- 同一 command 下不同 resourceType 的 entitlement audit log 必须生成不同 commandId 和不同 source tuple。 -- Command replay 策略:`adminCreateQuotaAdjustment` 命中 Account command replay 时不补历史 outbox;只有首次成功写入 entitlement audit log 的事务内创建 outbox。历史 backfill 若需要,必须另起任务。 - -### Payload 合同 - -Internal outbox 允许保存以下字段: - -- `auditLogId` -- `sourceCommandId` -- `accountUserId` -- `operatorUserId` -- `resourceType` -- `deltaAmount` -- `sourceOwner` -- `auditEventId` -- `requestHash` -- `sourceRevision` -- `message` -- `resourceRefType` -- `resourceRefId` - -Events `payloadSummary` 只能发布: - -```json -{ - "type": "quota_alert", - "message": "额度已调整", - "resourceRef": { - "resourceType": "account_quota", - "resourceId": 8001 - }, - "timestamp": "2026-06-07T12:00:00" -} -``` - -约束: - -- `resourceRef.resourceId` 必须是数值型 Long / Integer 语义,值为 entitlement audit log id。 -- DTO `EventsPublishReqDTO.resourceId` 是 String,可以传 `String.valueOf(auditLogId)`;payload `resourceRef.resourceId` 必须保持 Number,不能是 String。 -- `message` 第一切片只允许 `额度已调整`。 -- `timestamp` 使用 entitlement audit log 的 `createTime`,缺失时才回退 outbox `createTime`。 -- 不得发布 `reasonMessage`、`reasonCode`、`beforeValueSnapshot`、`deltaValueSnapshot`、`afterValueSnapshot`、operatorUserId、correlationId、external provider detail、requestHash、error stack。 -- `payloadSummary` 必须是 exact allowlist:顶层只能包含 `type`、`message`、`resourceRef`、`timestamp`;`resourceRef` 只能包含 `resourceType`、`resourceId`。 - -### 状态机合同 - -Outbox 本域状态只允许: - -- `queued` -- `running` -- `retryable` -- `published` -- `dead_letter` - -状态流转: - -```mermaid -stateDiagram-v2 - [*] --> queued - queued --> running: claim - retryable --> running: retry due - running --> running: expired lease reclaim - running --> published: Events accepted - running --> dead_letter: Events rejected/blocked - running --> retryable: temporary failure and attempts remain - running --> dead_letter: retry exhausted -``` - -Worker claim 要求: - -- 使用 PostgreSQL `FOR UPDATE SKIP LOCKED` 或等价 `UPDATE ... RETURNING` 原子领取。 -- 调度线程无请求租户上下文时必须先忽略租户拦截器领取,再使用 outbox 行内 `tenantId` 恢复租户上下文发布。 -- Claim 阶段唯一递增 `attemptCount`。 -- 临时失败只释放 claim、设置 `nextRetryAt` 和安全错误摘要,不二次递增 attempt count。 -- `markPublished` / `markRetryable` / `markDeadLetter` 必须绑定 `publish_status='running' AND attempt_count=#{claimedAttemptCount}`。 -- terminal update 返回 0 时必须记录 stale claim 诊断日志,包含 tenantId、accountUserId、outboxId、auditLogId、attempt、targetStatus、errorCode。 -- Worker 默认关闭:`muse.account.events.publish-worker.enabled=false`。 -- 调度配置键: - - `muse.account.events.publish-worker.initial-delay-ms` - - `muse.account.events.publish-worker.fixed-delay-ms` - - `muse.account.events.publish-worker.max-attempt` - - `muse.account.events.publish-worker.claim-timeout-seconds` - -### 失败路径合同 - -| 路径 | 结果 | -|---|---| -| 无 entitlement audit log | 不创建 outbox | -| changeType 非 `quota_adjustment` | 不创建 outbox | -| accountUserId null 或 <= 0 | 不创建 outbox,记录安全日志 | -| audit log id null | 不创建 outbox,记录安全日志 | -| payload 构造缺 required 字段 | 创建 outbox 前拒绝并记录安全日志,不创建 outbox,不发布,错误码固定为 `ACCOUNT_EVENTS_PAYLOAD_INVALID` | -| Events accepted | outbox -> published,回写 eventId / sequenceNo | -| Events duplicate accepted | outbox -> published,回写既有 eventId / sequenceNo | -| Events rejected / blocked | outbox -> dead_letter | -| CommonResult error / exception | attempt 未耗尽 -> retryable;耗尽 -> dead_letter | -| command replay | 不补历史 outbox,不重复发布 | - -## 涉及文件 - -### 生产代码 - -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/pom.xml` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishOutboxService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishOutboxServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishWorker.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEventPayloads.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountEventPublishOutboxDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountEventPublishOutboxMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountEventsConfiguration.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountEventsProperties.java` -- Create: `muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql` - -### 测试代码 - -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceTest.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishOutboxServiceTest.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishWorkerTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishDependencyTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishEndToEndTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishFlywayMigrationIT.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishMigrationSqlTest.java` - -### 文档 - -- Modify: `docs/agent-specs/.agent` -- Create: `docs/memorys/YYYY-MM-DD-P1R7eAccountSourceOwnerPropagation真实链路.md` - -## 实施任务 - -### Task 0:fresh implementer preflight - -- 在 worktree 根执行: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -ls muse-cloud/sql/muse/V*__*.sql | sort -V | tail -``` - -- 若最新 migration 已不是 V19,停止并修订 V20 编号。 -- 若 protected diff 非空,停止并汇报。 -- 若远端有更新且无法 fast-forward,停止并汇报。 - -完成条件: - -- 记录 dirty baseline。 -- 确认 protected diff 为空。 -- 确认 V20 编号仍可用。 - -### Task 1:依赖与配置骨架 - -目标: - -- Member server 只新增 `muse-module-events-api` direct dependency。 -- 新增 Account Events worker 配置,默认关闭。 - -文件: - -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/pom.xml` -- Create: `MuseAccountEventsConfiguration.java` -- Create: `MuseAccountEventsProperties.java` - -要求: - -- 不新增 `muse-module-events-server`。 -- 配置 prefix:`muse.account.events`。 -- 默认: - - `publish-worker.enabled=false` - - `publish-worker.max-attempt=5` - - `publish-worker.claim-timeout-seconds=60` -- 配置类必须可被 Spring 扫描。 - -Focused tests: - -- 配置默认值。 -- worker disabled 时 `dispatchOnce()` 不 claim。 - -### Task 2:V20 outbox schema - -目标: - -- 新增 `muse_account_event_publish_outbox`,只服务 Account -> Events publish compensation。 - -文件: - -- Create: `muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql` -- Create: `AccountEventPublishOutboxDO.java` -- Create: `AccountEventPublishOutboxMapper.java` -- Create: `P1rAccountEventsPublishMigrationSqlTest.java` - -表字段必须包含: - -- `id` -- `tenant_id` -- `outbox_id` -- `command_id` -- `source_command_id` -- `audit_log_id` -- `account_user_id` -- `operator_user_id` -- `resource_type` -- `delta_amount` -- `source_owner` -- `audit_event_id` -- `request_hash` -- `source_revision` -- `event_type` -- `notification_type` -- `resource_ref_type` -- `resource_ref_id` -- `payload_summary` -- `publish_status` -- `attempt_count` -- `max_attempt` -- `next_retry_at` -- `claimed_at` -- `claim_expires_at` -- `last_error_code` -- `last_error_message` -- `published_event_id` -- `published_sequence_no` -- `creator` -- `updater` -- `create_time` -- `update_time` -- `deleted` - -唯一约束: - -- `(tenant_id, outbox_id)` -- `(tenant_id, command_id)` -- `(tenant_id, audit_log_id, notification_type)` - -Check 约束: - -- `event_type = 'notification'` -- `notification_type = 'quota_alert'` -- `publish_status in ('queued','running','retryable','published','dead_letter')` -- `account_user_id > 0` -- `audit_log_id > 0` -- `attempt_count >= 0` -- `max_attempt > 0` -- `resource_ref_id > 0` - -索引: - -- claim index:`publish_status, next_retry_at, claim_expires_at` -- owner status index:`account_user_id, publish_status` -- audit log index:`audit_log_id` - -Trigger: - -- `update_time` trigger。 - -Mapper 方法: - -- `insertIgnore` -- `selectByTenantIdAndOutboxId` -- `selectByTenantIdAndCommandId` -- `selectByTenantIdAndAuditLogIdAndNotificationType` -- `claimNextPublishOutbox` -- `markPublished` -- `markRetryable` -- `markDeadLetter` - -SQL gate: - -- 断言字段、唯一约束、check 约束、索引、trigger。 -- 断言非法 `event_type`、`notification_type`、`publish_status`、`account_user_id`、`audit_log_id`、`resource_ref_id` 在 Flyway `_test` 中被 DB 拒绝。 - -### Task 3:Account outbox service - -目标: - -- 在 Account quota adjustment 首次成功事务内,为每条 entitlement audit log 创建一条 outbox。 - -文件: - -- Create: `AccountEventPublishOutboxService.java` -- Create: `AccountEventPublishOutboxServiceImpl.java` -- Create: `AccountEventPayloads.java` -- Modify: `AccountQuotaServiceImpl.java` -- Test: `AccountEventPublishOutboxServiceTest.java` -- Test: `AccountQuotaServiceTest.java` - -接入点: - -- `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` -- 在每个 `MemberEntitlementAuditLogDO` insert 后调用 outbox service。 -- command replay 返回时不得创建 outbox。 - -Outbox service 输入: - -- `MemberEntitlementAuditLogDO log` -- source command id:必须优先由调用方传入原始 `reqVO.getCommandId()`;只有回查历史 log 且调用方无法传入时,才允许从 `log.idempotencyKey` 用 `lastIndexOf(':')` 切出 commandId,禁止用第一个冒号切分。 -- request hash:允许 outbox 内部保存,不允许 Events payload 发布。 - -Payload 构造: - -```json -{ - "type": "quota_alert", - "message": "额度已调整", - "resourceRef": { - "resourceType": "account_quota", - "resourceId": 8001 - }, - "timestamp": "2026-06-07T12:00:00" -} -``` - -Payload validator: - -- 顶层 exact keys:`type/message/resourceRef/timestamp`。 -- `resourceRef` exact keys:`resourceType/resourceId`。 -- `type=quota_alert`。 -- `message` 只允许 `额度已调整`。 -- `resourceType=account_quota`。 -- `resourceId` 必须是 Number。 -- 不允许 requestHash、reason、snapshot、operator、correlation、error 等字段。 - -错误码: - -- payload invalid 固定为 `ACCOUNT_EVENTS_PAYLOAD_INVALID`。 - -Focused tests: - -- 首次 successful quota adjustment 为每个 audit log 创建 outbox。 -- command replay 不创建 outbox。 -- invalid owner 不创建 outbox并记录安全日志。 -- payload exact allowlist,敏感字段负断言。 -- 多 resourceType 产生多条不同 outbox。 -- configured maxAttempt 生效。 -- timestamp 使用 audit log createTime,缺失时回退 outbox createTime。 - -### Task 4:Account publish worker - -目标: - -- Claim Account outbox,构造 `EventsPublishReqDTO`,调用 `EventsPublishApi`,按结果更新 outbox 状态。 - -文件: - -- Create: `AccountEventPublishWorker.java` -- Test: `AccountEventPublishWorkerTest.java` - -Events request: - -- `commandId = outbox.commandId` -- `tenantId = outbox.tenantId` -- `ownerUserId = outbox.accountUserId` -- `sourceOwner = account` -- `sourceType = account_quota_adjustment` -- `sourceId = String.valueOf(outbox.auditLogId)` -- `sourceRevision = __none__` -- `eventType = notification` -- `resourceType = outbox.resourceRefType` -- `resourceId = String.valueOf(outbox.resourceRefId)` -- `payloadSummary = parsed exact allowlist payload` -- `emittedAt = outbox.createTime` - -状态处理: - -- accepted -> `published` -- duplicate accepted -> `published` -- rejected / blocked -> `dead_letter` -- CommonResult error -> `retryable` 或耗尽后 `dead_letter` -- exception -> `retryable` 或耗尽后 `dead_letter` -- payload invalid -> `dead_letter`,不调用 Events -- unknown status -> `dead_letter` - -Focused tests: - -- disabled 不 claim。 -- enabled claim 使用配置 claim timeout。 -- accepted 回写 eventId / sequenceNo。 -- duplicate accepted 回写既有 eventId / sequenceNo。 -- rejected / blocked dead_letter。 -- CommonResult error 非最后一次 retryable,最后一次 dead_letter。 -- exception 非最后一次 retryable,最后一次 dead_letter。 -- invalid payload dead_letter 且不调用 Events。 -- owner invalid dead_letter 且不调用 Events。 -- terminal update 使用 claimed attempt ownership。 -- stale terminal update 返回 0 时记录诊断日志。 - -### Task 5:P1R executable gates - -目标: - -- 用 server 侧 gate 证明依赖方向、focused E2E、V20 migration 和既有 owner gate 不回退。 - -文件: - -- Create: `P1rAccountEventsPublishDependencyTest.java` -- Create: `P1rAccountEventsPublishEndToEndTest.java` -- Create: `P1rAccountEventsPublishFlywayMigrationIT.java` -- Create: `P1rAccountEventsPublishMigrationSqlTest.java` - -Dependency test: - -- Member server 有 direct `muse-module-events-api`。 -- Member server 无 `muse-module-events-server`。 -- Events server 无 AI / Knowledge / Market / Member / Content server。 - -E2E focused test: - -- 构造 quota adjustment source fact。 -- Account outbox service 创建 outbox。 -- Worker 发布到 fake / in-memory Events publish API 或真实 Events service harness。 -- `muse_unified_event` 中 owner 为 accountUserId。 -- SSE visible query 对 account owner 可见,对其他 owner 不可见。 -- payload 不含 requestHash、reason、snapshot、operator、correlation。 -- 多 resourceType 产生多条独立通知。 -- rejected -> outbox dead_letter,visible query 为空。 -- stale claim terminal update 被忽略。 - -Flyway `_test`: - -- 使用真实 PostgreSQL `_test` 数据库。 -- 跑到 V20。 -- 验证 table / indexes / constraints / trigger。 -- 验证非法 insert 被 DB 拒绝。 - -### Task 6:最终验证、review 和留痕 - -最终验证顺序必须先 build,再 tests,避免 `clean` 清理 surefire XML。 - -#### 1. Full reactor build - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -期望:62/62 或 live reactor 全量 modules success。 - -#### 2. Account focused tests - -先断言新增测试文件存在: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -test -f muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishOutboxServiceTest.java && \ -test -f muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEventPublishWorkerTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishMigrationSqlTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishDependencyTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishEndToEndTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountEventsPublishFlywayMigrationIT.java -``` - -运行: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-module-member/muse-module-member-server -am \ - -Dtest=AccountQuotaServiceTest,AccountEventPublishOutboxServiceTest,AccountEventPublishWorkerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须检查 surefire XML,目标测试类 `tests>0` 且 failures/errors/skipped 均为 0。 - -#### 3. P1R mixed executable gates - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须检查 surefire XML,新增 Account 三个 P1R gate 和既有 Account owner gate 测试类 `tests>0` 且 failures/errors/skipped 均为 0: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -python3 - <<'PY' -from pathlib import Path -import xml.etree.ElementTree as ET - -targets = [ - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRouteOwnershipTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRouteOwnershipTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRouteOwnershipTest.xml", -] -for target in targets: - path = Path(target) - if not path.exists(): - raise SystemExit(f"missing surefire xml: {target}") - 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")) - if tests <= 0 or failures or errors or skipped: - raise SystemExit(f"bad surefire result: {target} tests={tests} failures={failures} errors={errors} skipped={skipped}") -PY -``` - -#### 4. Flyway `_test` - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -. ~/.config/muse-repo/infra.env && \ -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/muse_p1r7e_account_events_publish_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -Dhttp.proxyHost= \ - -Dhttp.proxyPort= \ - -Dhttps.proxyHost= \ - -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望: - -- migrations_executed = 20。 -- target_schema_version = 20。 -- `muse_account_event_publish_outbox` 存在。 -- 目标测试 XML tests > 0,failures/errors/skipped 均为 0。 - -#### 5. Dependency tree gate - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-member/muse-module-member-server -DskipTests dependency:tree > /tmp/p1r7e-member-server-dependency-tree.txt && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-events/muse-module-events-server -DskipTests dependency:tree > /tmp/p1r7e-events-server-dependency-tree.txt && \ -rg "muse-module-events-api" /tmp/p1r7e-member-server-dependency-tree.txt && \ -! rg "muse-module-events-server" /tmp/p1r7e-member-server-dependency-tree.txt && \ -! rg "muse-module-(ai|knowledge|market|member|content)-server" /tmp/p1r7e-events-server-dependency-tree.txt -``` - -#### 6. Isolated coverage scanner - -只允许在 `/tmp` 隔离副本运行 scanner: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -tmpdir=$(mktemp -d /tmp/p1r7e-coverage-scan.XXXXXX) && \ -rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/ && \ -cd "$tmpdir" && \ -python3 -m venv .venv && \ -. .venv/bin/activate && \ -python -m pip install --quiet PyYAML && \ -python muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -期望: - -- scanner exit 0。 -- completed=100。 -- needsVerification=133。 -- incomplete=0。 -- genericPersistence=0。 -- ssePlaceholder=0。 -- Events `streamEvents=dedicated / needs_verification`。 -- Account 33 operations 仍为 `dedicated / needs_verification`。 - -#### 7. Protected diff and whitespace - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -git diff --check && \ -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md && \ -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -#### 8. Memory - -新增: - -- `docs/memorys/2026-06-07-P1R7eAccountSourceOwnerPropagation真实链路.md` - -必须记录: - -- 目标和非目标。 -- 关键实现文件。 -- focused tests 数量。 -- P1R mixed gates 数量。 -- Flyway `_test` 结果。 -- dependency tree gate。 -- isolated coverage scanner 目录和 summary。 -- protected diff。 -- fresh implementation review 结果。 -- 明确说明仍不代表 Events / P1R-7 / Account completed。 - -#### 9. Fresh implementation review - -实现完成和最终验证后必须重新派发: - -- fresh spec / correctness review。 -- fresh quality / data-integrity / testing review。 - -双 PASS 前不得提交、push 或推进 completed。 - -## 完成条件 - -P1R-7e 实现完成条件: - -- Account quota adjustment terminal fact -> Account outbox -> worker -> `EventsPublishApi` -> `muse_unified_event` -> SSE owner-visible focused evidence 成立。 -- Member server 只依赖 events-api,不依赖 events-server。 -- Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。 -- V20 SQL 静态 gate 和 Flyway `_test` 通过。 -- focused tests、P1R mixed gates、dependency tree、isolated coverage scanner、protected diff、`git diff --check` 均通过。 -- fresh implementation spec review + quality review 双 PASS。 - -仍不得称为 completed: - -- Account 33 operations。 -- Events `streamEvents`。 -- 总 P1R-7。 -- Market / Knowledge / AI 之外任何未单独批准的域。 diff --git a/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation审阅版.md b/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation审阅版.md deleted file mode 100644 index 88d9d934..00000000 --- a/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation审阅版.md +++ /dev/null @@ -1,260 +0,0 @@ -# P1R7f Content Source Owner Propagation 审阅版 - -## 结论 - -推荐进入 P1R-7f,但第一切片只覆盖 Content 用户保存 Block 后形成的 canonical source attribution 事实,不覆盖全部 Content 写命令、导入、解析、导出、planning、meta projection 或 completed approval。 - -推荐方案: - -1. 选择 `ContentAppServiceImpl.saveBlock` 成功后写入的 `muse_content_block_source_attribution(source_status=active)` 作为 Content 第一批 source fact。 -2. 新增 Content 本域 Events publish outbox / worker,调用 `EventsPublishApi`,发布 Events 已声明的 `notification/source_status_change`。 -3. 不把 Block 正文、sourceSnapshot 原始对象、auditReason、authorizationSnapshotId 或 lineage payload 放入 Events payload。 -4. 不修改 OpenAPI、scanner 或 coverage report;不推进 Content / Events / P1R-7 completed。 - -```mermaid -flowchart LR - Save["App saveBlock command"] --> Block["muse_content_block
content + revision"] - Save --> Attr["muse_content_block_source_attribution
blockId + revision + active"] - Attr --> Outbox["Content events publish outbox
queued / running / retryable / published / dead_letter"] - Outbox --> Worker["Content worker
claim + retry + stale-claim guard"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified[("muse_unified_event
notification / source_status_change")] - Unified --> SSE["/app-api/muse/events
work owner visible"] -``` - -本审阅版只冻结目标、非目标、取舍、风险和验收标准。执行版、实现和提交必须在本审阅版 fresh spec review + fresh quality / feasibility review 双 PASS 后再继续。 - -## Review Gate - -P1R-7f 审阅版已通过 fresh review: - -- Harvey spec / scope review:PASS,无 P0/P1/P2;已验证本审阅版严格停留在 Content `saveBlock -> block source attribution -> Events notification` 第一切片,没有把 completed、OpenAPI、coverage 或全部 Content 写命令混入。 -- Chandrasekhar quality / feasibility review:PASS,无 P0/P1/P2;已验证 `saveBlock -> muse_content_block_source_attribution(source_status=active) -> Content outbox -> EventsPublishApi -> muse_unified_event -> SSE` 路径可落地,并确认 Content 不应误用 `chunk`,执行版必须加入防误用 `chunk` 的测试或静态 gate。 - -Halley quality reviewer 长时间超时未返回,已丢弃,不计入 review gate。 - -审阅版双 PASS 只允许进入执行版编写,不代表 P1R-7f 实现完成,也不代表 Content / Events / P1R-7 completed。执行版完成后仍必须派发 fresh execution spec review + fresh execution quality / feasibility review;执行版双 PASS 前不得实现代码、提交、push 或推进 completed。 - -## 已验证事实 - -### 工作区与状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 当前 HEAD:`68cee5d test(p1r): 收口 Account 事件传播真实链路门禁`。 -- 当前分支与 `origin/dev/1.0.0` 对齐。 -- `git pull --ff-only origin dev/1.0.0` 返回 `Already up to date.`。 -- 当前 coverage summary: - - `completedOperations=100` - - `needsVerificationOperations=133` - - `incompleteOperations=0` - - `genericPersistenceOperations=0` - - `ssePlaceholderOperations=0` -- Content 51 operations 当前均为 `dedicated / needs_verification`。 -- 受保护文件 staged / unstaged diff 为空: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 全局 P1R-7 边界 - -- P1R-7a 到 P1R-7e 已分别覆盖 Events SSE、AI、Knowledge、Market、Account source owner propagation evidence。 -- P1R-7b / 7c / 7d / 7e 均只把对应真实链路推进到 `needs_verification` evidence,不代表 Events / P1R-7 / 对应业务域 completed。 -- P1R-7 全局审阅版把 P1R-7f 定义为 `Content canonical change / block saved / export task event propagation`,状态口径仍是只推进 `needs_verification` 证据。 -- P1R-7 completed approval 预检明确:如果 Account / Content 仍是硬前置,则应继续 P1R-7e Account 与 P1R-7f Content,而不是直接 completed。Account 已在 P1R-7e 收口并推送,因此 Content 是剩余 owner 切片。 - -### Events 可复用合同 - -- `EventsPublishReqDTO` 已包含 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`sourceRevision`、`eventType`、`resourceType`、`resourceId`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 先按 `commandId` 回放,再按 source tuple 回放。 -- `docs/api-contracts/events/openapi.yaml` 已声明 `notification` 事件,`SSENotificationEvent.data.type` 允许 `source_status_change`。 -- `EventsPublishServiceImpl` 与 `EventsStreamServiceImpl` 均允许 notification subtype `source_status_change`。 -- Events 当前没有 `block_saved` notification subtype;本阶段不得新增 subtype 或修改 Events OpenAPI。 -- `SSEChunkEvent` 语义是 AI 生成文本片段,要求 `content/sequenceNo`。Content block saved 不应使用 `chunk`,否则会泄露正文或伪造 AI chunk 语义。 - -### Content 当前事实模型 - -- Content owner 位于 `muse-module-content/muse-module-content-server`。 -- `muse-module-content-server/pom.xml` 当前没有依赖 `muse-module-events-api`,也没有依赖 `muse-module-events-server`。 -- 最新 migration 当前是 `V20__extend_account_events_publish_outbox.sql`;若 P1R-7f 进入实现且 live 文件列表不变,下一 migration 应为 `V21`。 -- `ContentAppServiceImpl.saveBlock` 在同一事务内: - - 用 `ContentCommandService.reserveCommand` 对 `commandId` 做幂等预占。 - - 通过 `requireOwnedBlock(userId, workId, blockId)` 校验作品 owner。 - - 校验 Block `expectedRevision`。 - - 更新 `muse_content_block.content_text / word_count / revision`。 - - 写入 `muse_content_block_source_attribution`,其中 `workId`、`blockId`、`revision`、`sourceType`、`sourceObjectId`、`sourceVersion`、`authorizationSnapshotId`、`sourceStatus=active`。 - - 调用 `ContentAuditService.recordSucceededWithAudit` 写入 command result snapshot 与 audit 摘要。 -- `muse_content_block_source_attribution` 有独立自增 `id`,并有唯一约束 `(tenant_id, block_id, revision)`。 -- `WorkDO.ownerUserId` 是作品 owner;`saveBlock` 的 `userId` 经 owner guard 后与该 owner 一致。 -- `BlockSaveReqVO` 要求 `commandId`、`content`、`expectedRevision` 和 `sourceSnapshot`。 - -## 推断 - -- P1R-7f 第一切片应选择 `saveBlock`,因为它是 Content 自有 canonical change,具备 owner、workId、blockId、revision、commandId、requestHash、source attribution 和审计链路。 -- `muse_content_block_source_attribution` 比 `muse_content_block` 更适合作为第一批 source fact:它表达“该 Block revision 的来源状态 active”,可以自然映射到 `source_status_change`,并避免把正文变化伪装成 AI `chunk`。 -- 复用 `notification/source_status_change` 是当前不改 OpenAPI 下的最小可行方案;如果未来产品需要明确的 `block_saved` subtype,应另起 Events OpenAPI 合同审批。 -- 不应把 Content 全部写命令放进 P1R-7f 第一切片。`create_block`、`delete_block`、`splitBlock`、`mergeBlocks`、导入、解析、导出、planning、meta projection 都有不同语义和不同敏感 payload 边界,混入会扩大 blast radius。 -- `muse_content_block_source_attribution` 是 source fact,不应直接兼任 publish outbox。它没有 claim lease、attempt、retry、published event、dead_letter、last_error 等发布补偿语义。 - -## 假设 - -- P1R-7f 允许在 Content owner 内新增独立 Events publish outbox 表或等价状态表。 -- P1R-7f 允许 `muse-module-content-server` 新增对 `muse-module-events-api` 的直接依赖,但不允许依赖 `muse-module-events-server`。 -- Content block save notification 的 SSE owner 是作品 owner,即 `WorkDO.ownerUserId` / 当前 app `userId`。 -- `source_status_change` 可以表达 Content block source attribution 从用户保存命令进入 active 状态的用户可见摘要。 - -## 目标 - -1. 证明 Content source owner 的真实 Block 保存事实可以通过 Content 本域 publish outbox / worker 发布到 Events。 -2. 让作品 owner 可以通过统一 SSE 看到自己的 Block 来源状态变更摘要。 -3. 建立 Content 与 Events 的单向依赖证据:Content server 依赖 events-api,不依赖 events-server;Events server 不反向依赖 Content server。 -4. 为后续 Content create/delete/split/merge/import/export/planning/meta projection 等事件扩展保留清晰边界。 - -## 非目标 - -1. 不把 Content 51 operations 从 `needs_verification` 推进到 `completed`。 -2. 不把 Events `streamEvents` 或总 P1R-7 推进到 `completed`。 -3. 不修改 Content / Events OpenAPI。 -4. 不修改 coverage scanner 或 coverage report。 -5. 不发布 Block 正文、完整 sourceSnapshot、lineage payload、license restriction、authorizationSnapshotId、auditReason、requestHash、command result snapshot 或错误堆栈。 -6. 不覆盖 `create_block`、`delete_block`、`splitBlock`、`mergeBlocks`、`mergeBlockSuggestion`、import/parse/export、planning、meta projection、admin risk action。 -7. 不让 Events server 查询 Content 表。 -8. 不把 SSE `chunk` 用作 Content block save 事件。 - -## 推荐方案 - -### 事件选择 - -第一批只允许: - -| Content source fact | 触发入口 | Events eventType | notification type | SSE owner | -|---|---|---|---|---| -| `muse_content_block_source_attribution(source_status=active)` | `ContentAppServiceImpl.saveBlock` | `notification` | `source_status_change` | `WorkDO.ownerUserId` | - -暂不选择: - -| 候选事实 | 暂不选择原因 | -|---|---| -| `muse_content_block` 正文变化 | 直接以正文变化为 SSE payload 有泄露风险;应通过 source attribution 摘要表达 | -| `create_block` | 创建类命令首次执行前 targetId 为空,source tuple 和 replay 边界需要单独冻结 | -| `delete_block` | 删除前 source attribution 是 deleted snapshot,用户可见语义与保存不同 | -| `splitBlock` / `mergeBlocks` | 一次命令可能影响多个 Block 和章节 revision,第一切片不扩大结构编辑语义 | -| import / parse / export task | 异步任务状态和文件凭证边界不同,容易误暴露内部任务或下载信息 | -| planning / meta projection | 更接近 AI / Meta 协作投影,需单独定义用户可见摘要 | -| admin risk action | 属于治理动作,已有 Market governance 类似模式但 Content 侧需单独审阅 | - -### Payload allowlist - -第一批 payload 只允许: - -```json -{ - "type": "source_status_change", - "message": "内容来源状态已更新", - "resourceRef": { - "resourceType": "content_block", - "resourceId": 8001 - }, - "timestamp": "2026-06-08T12:00:00" -} -``` - -执行版可以把 message 收窄为安全固定枚举,例如: - -- `内容来源状态已更新` -- `Block 来源状态已更新` - -不得把以下字段放入 Events payload: - -- Block 正文 `contentText` -- 请求正文 `content` -- `sourceSnapshot` 原始对象 -- `sourceObjectId` -- `authorizationSnapshotId` -- `lineagePayload` -- `licenseRestrictionSnapshot` -- `auditReason` -- `requestHash` -- `resultSnapshot` -- 错误堆栈 - -### Outbox 边界 - -推荐新增独立 Content Events publish outbox: - -- source table:`muse_content_block_source_attribution`。 -- source tuple:`sourceOwner=content`,`sourceType=content_block_source_attribution`,`sourceId=blockId`,`sourceRevision=blockRevision`。 -- commandId:稳定短幂等键,建议 `content_evt:`。 -- 唯一约束:建议覆盖 `(tenant_id, block_id, block_revision, notification_type)`,避免同一 Block revision 重复发布。 -- 状态机:`queued/running/retryable/published/dead_letter`。 -- claim:必须原子领取,具备 lease / retry / attempt ownership。 -- 终态回写:必须绑定本次 claimed attempt,避免 stale worker 覆盖新 claim。 -- worker 默认关闭,配置启用后才 claim。 -- invalid owner、invalid payload 或 missing attribution 必须 fail-closed,不得发布给 owner `0` 或其他用户。 - -### 依赖边界 - -- Content server 可以依赖 `muse-module-events-api`。 -- Content server 不得依赖 `muse-module-events-server`。 -- Events server 不得依赖 Content server。 -- `muse-server` 作为装配层可以同时包含 Content server 与 Events server。 - -## 关键取舍 - -- 选择 `saveBlock`,不选择全部 Content 写命令:第一切片需要最小真实链路,避免把结构编辑、任务状态和治理语义混在一起。 -- 选择 block source attribution,不选择正文本身:source attribution 是已落库、可审计、可脱敏的 source fact;正文不应进入统一 SSE payload。 -- 选择 `notification/source_status_change`,不新增 `block_saved`:当前 Events 合同已声明该 subtype;新增 subtype 必须另起 OpenAPI 审批。 -- 不使用 SSE `chunk`:`chunk` 是 AI 生成文本片段语义,Content 保存正文不应伪造为 AI chunk,也不应发布正文片段。 -- 不复用 attribution 表作为 publish queue:source fact 与 publish compensation 是两个职责,混用会缺 claim、retry 和 dead_letter 证据。 - -## 影响范围 - -- 直接影响: - - Content `saveBlock` source attribution 发布链路。 - - Content server 依赖 events-api。 - - Content 本域 outbox / worker / migration / focused tests。 - - Events unified event focused E2E。 -- 间接影响: - - P1R-7 completed approval 的 source owner evidence 更完整。 -- 不影响: - - OpenAPI 合同。 - - coverage scanner / coverage report。 - - Content 51 operations completion status。 - - Content `saveBlock` 同步返回语义。 - - Import / parse / export / planning / meta projection / admin governance 业务逻辑。 - -## 风险与兼容性 - -- 语义风险:`source_status_change` 原先主要用于 Knowledge;执行版必须说明 Content 使用该 subtype 的语义边界,不能把它扩展成万能通知。 -- 事件重复风险:同一 `(blockId, revision)` retry 不能创建多条 unified event;必须用 commandId 与 source tuple 双重幂等。 -- 敏感信息风险:正文、sourceSnapshot 原文、authorizationSnapshotId、lineage、license restriction 和 auditReason 不得进入 payload。 -- owner 风险:SSE owner 必须是作品 owner,不能由 path 参数或 payload 自带 owner 推断。 -- 并发风险:`saveBlock` 已有 revision guard;outbox 创建必须与成功 attribution 写入同事务,且不能在 command replay 分支重复写 outbox。 -- 可达性风险:如果实现时发现 `sourceAttributionMapper.insert` 后无法可靠拿到 attribution id,应以 `(blockId, revision)` 作为 source tuple,不为拿 id 改动既有 mapper 行为。 -- 兼容性:新增 outbox 与 worker 默认关闭,不改变现有 Content API 同步返回语义。 - -## 验收标准 - -审阅版通过后,执行版必须包含: - -1. Content outbox DO / Mapper / Service / Worker / 配置 / V21 migration 的文件边界。 -2. `ContentAppServiceImpl.saveBlock` 在 Block 更新和 attribution 写入后同事务创建 outbox 的事务边界。 -3. payload exact allowlist 与敏感字段拒绝策略。 -4. worker success / duplicate / retryable / CommonResult error / rejected / blocked / dead_letter / stale claim tests。 -5. Content saveBlock source fact -> Content outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE visible focused E2E。 -6. dependency gate:Content server 有 events-api、无 events-server;Events server 无 AI / Knowledge / Market / Member / Content server。 -7. V21 SQL 静态 gate 与 Flyway `_test`。 -8. P1R mixed gates 继续确认 AI / Knowledge / Market / Account source owner gates 不回退,Content 仍为 `dedicated / needs_verification`。 -9. coverage scanner 只在 `/tmp` 隔离副本运行,真实 worktree protected diff 为空。 -10. `git diff --check` 和 protected staged / unstaged diff gate。 - -## 待确认项 - -1. 是否接受 P1R-7f 第一切片只覆盖 `saveBlock`,其他 Content 写命令进入后续切片。 -2. 是否接受在不改 OpenAPI 的前提下复用 `notification/source_status_change` 表达 Content block source attribution active。 -3. 如果 reviewer 认为 `source_status_change` 语义不适配 Content,是否先停止 P1R-7f 实现,改走 Events OpenAPI subtype 变更审批。 diff --git a/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation执行版.md b/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation执行版.md deleted file mode 100644 index 5db4ea41..00000000 --- a/docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation执行版.md +++ /dev/null @@ -1,822 +0,0 @@ -# P1R7f Content Source Owner Propagation 执行版 - -## 结论 - -P1R-7f 只执行 Content source owner propagation 的第五 owner 切片:把 `ContentAppServiceImpl.saveBlock` 首次成功写入的 `muse_content_block_source_attribution(source_status=active)` 转成 Content 本域 Events publish outbox,再由 Content worker 调用 `EventsPublishApi` 写入 `muse_unified_event`,最终通过 `/app-api/muse/events` 对作品 owner SSE 可见。 - -本执行版不允许把 Events / P1R-7 / Content 推进到 `completed`,不允许修改 OpenAPI、coverage scanner 或 coverage report,不允许把 Content 保存正文伪造成 SSE `chunk`。 - -```mermaid -flowchart LR - Save["saveBlock
commandId + expectedRevision"] --> Block["muse_content_block
content_text + revision"] - Save --> Attr["muse_content_block_source_attribution
source_status=active"] - Attr --> Outbox["muse_content_event_publish_outbox
one row per block revision"] - Outbox --> Worker["Content publish worker
claim/retry/dead_letter"] - Worker --> Api["EventsPublishApi
events-api only"] - Api --> Unified["muse_unified_event
notification/source_status_change"] - Unified --> Stream["/app-api/muse/events
work owner visible SSE"] -``` - -## Review Gate - -P1R-7f 审阅版已通过 fresh review: - -- Harvey spec / scope review:PASS,无 P0/P1/P2;已验证本审阅版严格停留在 Content `saveBlock -> block source attribution -> Events notification` 第一切片,没有把 completed、OpenAPI、coverage 或全部 Content 写命令混入。 -- Chandrasekhar quality / feasibility review:PASS,无 P0/P1/P2;已验证 `saveBlock -> muse_content_block_source_attribution(source_status=active) -> Content outbox -> EventsPublishApi -> muse_unified_event -> SSE` 路径可落地,并确认 Content 不应误用 `chunk`。 - -Halley quality reviewer 长时间超时未返回,已丢弃,不计入 review gate。 - -Chandrasekhar P3 已冻结为本执行版硬 gate: - -- Content worker / outbox 只能发布 `eventType=notification`、`notificationType=source_status_change`。 -- 必须有测试或静态 gate 防止 Content 误用 `chunk`。 - -P1R-7f 执行版首轮 quality / feasibility review(Hypatia)FAIL,两个 P1 已验证有效: - -- `source_status=active` 合同只停留在 service 描述,V21 schema、Flyway `_test` 与 worker gate 没有兜住非 active outbox;由于 `deleteBlock` 真实会写 `sourceStatus=deleted`,该风险实际可达。 -- E2E wording 允许 bare fake `EventsPublishApi` 返回 accepted,不能证明真实 `EventsPublishServiceImpl -> muse_unified_event -> visible query` 链路。 - -执行版已修订:V21 check 约束、SQL gate、Flyway `_test`、worker fail-closed 与 focused tests 均补 `source_status = 'active'`;E2E 成功路径收紧为必须使用真实 `EventsPublishServiceImpl`,只允许使用 in-memory / mocked `UnifiedEventMapper`,禁止 bare fake `EventsPublishApi` 作为成功证据。 - -旧 spec reviewer(Arendt)在首轮文档修订前长时间未返回,关闭动作被会话中断;该旧 review 视为 stale / discarded,即使后续异步返回也不计入 gate。 - -P1R-7f 执行版第二轮 fresh review 已通过: - -- Huygens execution spec compliance review:PASS,无 P0/P1/P2/P3;已验证 scope、非目标、protected 边界、completed 边界、依赖边界、Chandrasekhar P3、Hypatia P1 修订、V21 / worker / E2E / Flyway / P1R mixed / `/tmp` scanner / protected diff gate 均已闭合。 -- Raman execution quality / feasibility review:PASS,无 P0/P1/P2/P3;已验证 `saveBlock` 事务接入点、command replay 早退、`BlockSourceAttributionDO` 字段、Events `notification/source_status_change` 合同、`chunk` 不适配 Content save、V21 `source_status=active` 约束、payload exact allowlist、focused tests、真实 `EventsPublishServiceImpl` E2E 与防假绿门禁均可落地。 - -本轮双 PASS 只允许进入 fresh implementer 实现,不代表 P1R-7f 实现完成,也不代表 Events / P1R-7 / Content completed。实现后必须重新进行 fresh implementation spec / correctness review + fresh implementation quality / data-integrity / testing review,双 PASS 前不得提交、push 或推进 completed。 - -## 边界 - -### 本轮目标 - -- 在 Content owner 内新增独立 Events publish outbox。 -- 只接入 `ContentAppServiceImpl.saveBlock` 首次成功路径。 -- 只发布 `muse_content_block_source_attribution(source_status=active)` 形成的用户可见 source status 摘要。 -- 通过 Content worker 调用 `EventsPublishApi`。 -- 用 focused tests、P1R mixed gates、Flyway `_test`、dependency gate、dependency tree 和隔离 coverage scanner 证明链路进入 `needs_verification` 证据层。 - -### 本轮非目标 - -- 不覆盖 `createBlock`、`deleteBlock`、`splitBlock`、`mergeBlocks`、`mergeBlockSuggestion`。 -- 不覆盖 import、parse、export、planning、meta projection、admin risk action。 -- 不发布 Block 正文、请求正文、完整 `sourceSnapshot`、`sourceObjectId`、`authorizationSnapshotId`、`lineagePayload`、`licenseRestrictionSnapshot`、`auditReason`、`requestHash`、`resultSnapshot` 或错误堆栈。 -- 不发布 `chunk`、`done`、`quality_check` 或 `error` 事件。 -- 不新增 Events notification subtype,不新增 `block_saved`。 -- 不抽取 AI / Knowledge / Market / Account / Content 共享 outbox 框架。 -- 不为历史 block source attribution 做 backfill。 -- 不修改 `docs/api-contracts/*/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json` 或 `.md`。 - -### 受保护文件 - -实现代理不得修改以下文件: - -- `docs/api-contracts/account/openapi.yaml` -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 已验证事实 - -- 正确 worktree 是 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支是 `dev/1.0.0`,远端跟踪是 `origin/dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 返回 `Already up to date.`。 -- 当前 HEAD 为 `68cee5d test(p1r): 收口 Account 事件传播真实链路门禁`。 -- P1R-7e 已提交并 push:`0f49802`、`0e10caf`、`68cee5d`。 -- P1R-7f 审阅版路径为 `docs/agent-specs/2026-06-08-P1R7fContentSourceOwnerPropagation审阅版.md`。 -- P1R-7f 审阅版 fresh review 已双 PASS:Harvey spec PASS,Chandrasekhar quality PASS。 -- 当前 coverage 边界仍为 `completedOperations=100`、`needsVerificationOperations=133`、`incompleteOperations=0`、`genericPersistenceOperations=0`、`ssePlaceholderOperations=0`。 -- Events `streamEvents` 当前是 `dedicated / needs_verification`。 -- Content 51 operations 当前均为 `dedicated / needs_verification`。 -- 当前受保护文件 staged / unstaged diff 为空。 -- 当前最新 migration 是 `muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql`;P1R-7f 下一 migration 编号应为 `V21`。若实现前 live 文件列表已出现更高版本,必须停止并修订本执行版的 migration 编号。 -- `muse-module-content-server/pom.xml` 当前没有 `muse-module-events-api` 依赖,也没有 `muse-module-events-server` 依赖。 -- `muse-module-events-server/pom.xml` 当前不依赖 Content server。 -- `ContentAppServiceImpl.saveBlock` 当前在同一事务内完成 command 预占、owner guard、revision 校验、Block 更新、source attribution 插入和 command audit。 -- `saveBlock` 命中 command replay 时直接回放 `ContentCommandDO.resultSnapshot`,不会更新 Block,也不会插入 `BlockSourceAttributionDO`。 -- `BlockSourceAttributionDO` 包含 `id`、`workId`、`blockId`、`revision`、`sourceType`、`sourceObjectId`、`sourceVersion`、`lineagePayload`、`authorizationSnapshotId`、`sourceStatus`、`licenseRestrictionSnapshot`。 -- `muse_content_block_source_attribution` 已有唯一约束 `(tenant_id, block_id, revision)`。 -- `EventsPublishReqDTO` 必填 `commandId`、`tenantId`、`ownerUserId`、`sourceOwner`、`sourceType`、`sourceId`、`eventType`、`payloadSummary`、`emittedAt`。 -- `EventsPublishServiceImpl` 当前先按 `commandId` 回放,再按 source tuple 回放。 -- `EventsPublishServiceImpl` 当前已声明 `notification` event type。 -- `EventsPublishServiceImpl` 当前已声明 `source_status_change` notification type。 -- `EventsPublishServiceImpl` 当前把 `chunk` 识别为需要 `content/sequenceNo` 的 AI 文本片段语义;Content saveBlock 不能使用 `chunk`。 -- `UnifiedEventMapper.selectVisibleEventsForOwner` 当前按 `tenantId`、`ownerUserId`、`publishStatus=accepted`、`deleted=false`、`visibleFrom<=now`、`sequenceNo>afterSequenceNo` 查询 SSE 可见事件。 - -## 推断 - -- P1R-7f 可复用 P1R-7b/P1R-7c/P1R-7d/P1R-7e 的 outbox 状态机、worker claim、stale claim guard、dependency tree gate、Surefire XML 防假绿和 Flyway `_test` 模式。 -- `muse_content_block_source_attribution` 比 `muse_content_block` 更适合作为第一批 source fact,因为它表达“该 Block revision 的来源状态 active”,可脱敏映射为 `source_status_change`。 -- Content outbox 与 block source attribution 必须保持两张表 / 两套状态机,否则 source fact 与 publish 补偿状态会互相污染。 -- `source_status_change` 在 Content 中只能表达“Block 来源状态已更新”,不能扩展成正文保存、任务完成或万能通知。 - -## 假设 - -- P1R-7f 允许 Content server 新增 `muse-module-events-api` direct dependency。 -- P1R-7f 允许新增 `muse_content_event_publish_outbox`。 -- P1R-7f 第一批通知的 SSE owner 是作品 owner,即 `WorkDO.ownerUserId` / 当前 `saveBlock` userId。 -- P1R-7f 第一批使用 Events 现有 `notification/source_status_change`,不新增 OpenAPI event type 或 notification type。 - -## 设计合同 - -### 事件合同 - -第一批只允许以下 Content source fact 进入 publish outbox: - -| Content source fact | sourceStatus | Events eventType | notification type | SSE owner | message | -|---|---|---|---|---|---| -| `muse_content_block_source_attribution` | `active` | `notification` | `source_status_change` | `WorkDO.ownerUserId` | `内容来源状态已更新` | - -强制约束: - -- Content outbox service 必须二次校验 `sourceStatus=active`。 -- Content worker 构造的 `EventsPublishReqDTO.eventType` 必须固定为 `notification`。 -- Content worker 构造的 payload `type` 必须固定为 `source_status_change`。 -- Content 相关生产代码和 P1R gate 不得把 Content saveBlock 发布为 `chunk`。 -- V21 SQL、worker tests、E2E tests 至少各有一处证明 Content 只发布 `notification/source_status_change`,并负断言 `chunk`。 - -### 幂等合同 - -- 每个 `(tenantId, blockId, blockRevision, source_status_change)` 只生成一条 outbox。 -- `commandId` 格式:`content_evt:` + SHA-256(`tenantId|blockId|blockRevision|source_status_change`) 前 32 位 hex。 -- `outboxId` 格式:`content_out:` + SHA-256(`tenantId|blockId|blockRevision|source_status_change`) 前 32 位 hex。 -- Events source tuple: - - `sourceOwner = content` - - `sourceType = content_block_source_attribution` - - `sourceId = blockId` - - `sourceRevision = blockRevision` - - `eventType = notification` -- 同一 Block revision 重放必须得到同一 Events `eventId/sequenceNo`。 -- 同一 Block 的不同 revision 必须生成不同 commandId 和不同 source tuple。 -- Command replay 策略:`saveBlock` 命中 Content command replay 时不补历史 outbox;只有首次成功写入 `BlockSourceAttributionDO(sourceStatus=active)` 的事务内创建 outbox。历史 backfill 若需要,必须另起任务。 - -### Payload 合同 - -Internal outbox 允许保存以下字段: - -- `sourceAttributionId` -- `sourceCommandId` -- `ownerUserId` -- `workId` -- `blockId` -- `blockRevision` -- `sourceStatus` -- `eventType` -- `notificationType` -- `resourceRefType` -- `resourceRefId` -- `payloadSummary` - -Events `payloadSummary` 只能发布: - -```json -{ - "type": "source_status_change", - "message": "内容来源状态已更新", - "resourceRef": { - "resourceType": "content_block", - "resourceId": 8001 - }, - "timestamp": "2026-06-08T12:00:00" -} -``` - -约束: - -- `resourceRef.resourceId` 必须是数值型 Long / Integer 语义,值为 `blockId`。 -- DTO `EventsPublishReqDTO.resourceId` 是 String,可以传 `String.valueOf(blockId)`;payload `resourceRef.resourceId` 必须保持 Number,不能是 String。 -- `message` 第一切片只允许 `内容来源状态已更新`。 -- `timestamp` 使用 source attribution `createTime`,缺失时才回退 outbox `createTime`。 -- 不得发布 `contentText`、请求 `content`、`sourceSnapshot` 原文、`sourceObjectId`、`authorizationSnapshotId`、`lineagePayload`、`licenseRestrictionSnapshot`、`auditReason`、`requestHash`、`resultSnapshot`、错误堆栈。 -- `payloadSummary` 必须是 exact allowlist:顶层只能包含 `type`、`message`、`resourceRef`、`timestamp`;`resourceRef` 只能包含 `resourceType`、`resourceId`。 - -### 状态机合同 - -Outbox 本域状态只允许: - -- `queued` -- `running` -- `retryable` -- `published` -- `dead_letter` - -状态流转: - -```mermaid -stateDiagram-v2 - [*] --> queued - queued --> running: claim - retryable --> running: retry due - running --> running: expired lease reclaim - running --> published: Events accepted - running --> dead_letter: Events rejected/blocked - running --> retryable: temporary failure and attempts remain - running --> dead_letter: retry exhausted -``` - -Worker claim 要求: - -- 使用 PostgreSQL `FOR UPDATE SKIP LOCKED` 或等价 `UPDATE ... RETURNING` 原子领取。 -- 调度线程无请求租户上下文时必须先忽略租户拦截器领取,再使用 outbox 行内 `tenantId` 恢复租户上下文发布。 -- Claim 阶段唯一递增 `attemptCount`。 -- 临时失败只释放 claim、设置 `nextRetryAt` 和安全错误摘要,不二次递增 attempt count。 -- `markPublished` / `markRetryable` / `markDeadLetter` 必须绑定 `publish_status='running' AND attempt_count=#{claimedAttemptCount}`。 -- terminal update 返回 0 时必须记录 stale claim 诊断日志,包含 tenantId、ownerUserId、outboxId、blockId、blockRevision、attempt、targetStatus、errorCode。 -- Worker 默认关闭:`muse.content.events.publish-worker.enabled=false`。 -- 调度配置键: - - `muse.content.events.publish-worker.initial-delay-ms` - - `muse.content.events.publish-worker.fixed-delay-ms` - - `muse.content.events.publish-worker.max-attempt` - - `muse.content.events.publish-worker.claim-timeout-seconds` - -### 失败路径合同 - -| 路径 | 结果 | -|---|---| -| command replay | 不补历史 outbox,不重复发布 | -| revision guard 失败 | 不更新 Block,不创建 source attribution,不创建 outbox | -| owner guard 失败 | 不更新 Block,不创建 source attribution,不创建 outbox | -| source attribution 缺失 | 不创建 outbox,记录安全日志 | -| sourceStatus 非 `active` | 不创建 outbox,记录安全日志 | -| ownerUserId null 或 <= 0 | 不创建 outbox,记录安全日志 | -| blockId null 或 <= 0 | 不创建 outbox,记录安全日志 | -| blockRevision null 或 <= 0 | 不创建 outbox,记录安全日志 | -| payload 构造缺 required 字段 | 创建 outbox 前拒绝并记录安全日志,不创建 outbox,不发布,错误码固定为 `CONTENT_EVENTS_PAYLOAD_INVALID` | -| Events accepted | outbox -> published,回写 eventId / sequenceNo | -| Events duplicate accepted | outbox -> published,回写既有 eventId / sequenceNo | -| Events rejected / blocked | outbox -> dead_letter | -| CommonResult error / exception | attempt 未耗尽 -> retryable;耗尽 -> dead_letter | -| worker 构造出 `chunk` | 测试必须失败;生产实现不得出现该路径 | - -### 依赖合同 - -允许: - -- `muse-module-content-server` 依赖 `muse-module-events-api`。 -- `muse-server` 同时装配 Content server 与 Events server。 - -禁止: - -- `muse-module-content-server` 依赖 `muse-module-events-server`。 -- `muse-module-events-server` 依赖 `muse-module-content-server`。 -- Events server 依赖 AI / Knowledge / Market / Member / Content server。 -- Events stream controller 查询 Content 业务表。 - -## 涉及文件 - -### 生产代码 - -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/pom.xml` -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentAppServiceImpl.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxService.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxServiceImpl.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentEventPublishWorker.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentEventPayloads.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/dal/dataobject/ContentEventPublishOutboxDO.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/dal/mysql/ContentEventPublishOutboxMapper.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/framework/config/MuseContentEventsConfiguration.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/framework/config/MuseContentEventsProperties.java` -- Create: `muse-cloud/sql/muse/V21__extend_content_events_publish_outbox.sql` - -### 测试代码 - -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAppServiceTest.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxServiceTest.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishWorkerTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishDependencyTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishEndToEndTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishFlywayMigrationIT.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishMigrationSqlTest.java` - -### 文档 - -- Modify: `docs/agent-specs/.agent` -- Create: `docs/memorys/YYYY-MM-DD-P1R7fContentSourceOwnerPropagation真实链路.md` only after implementation evidence is available - -## 实施任务 - -### Task 0:fresh implementer preflight - -在 worktree 根执行: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -ls muse-cloud/sql/muse/V*__*.sql | sort -V | tail -``` - -完成条件: - -- 记录 dirty baseline。 -- 确认 protected staged / unstaged diff 为空。 -- 确认 V21 编号仍可用;若最新 migration 已不是 V20,停止并修订本执行版。 -- 只读核对 Content server 当前没有 events-api dependency。 -- 只读核对 Events server 仍不依赖 Content server。 - -### Task 1:依赖与配置骨架 - -目标: - -- Content server 只新增 `muse-module-events-api` direct dependency。 -- 新增 Content Events worker 配置,默认关闭。 - -文件: - -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/pom.xml` -- Create: `MuseContentEventsConfiguration.java` -- Create: `MuseContentEventsProperties.java` - -要求: - -- 不新增 `muse-module-events-server`。 -- 配置 prefix:`muse.content.events`。 -- 默认: - - `publish-worker.enabled=false` - - `publish-worker.max-attempt=5` - - `publish-worker.claim-timeout-seconds=60` -- 配置类必须可被 Spring 扫描。 -- 关键配置、状态转换和 worker 禁用分支必须写中文注释,说明 WHY。 - -Focused tests: - -- 配置默认值。 -- worker disabled 时 `dispatchOnce()` 不 claim。 - -### Task 2:V21 outbox schema - -目标: - -- 新增 `muse_content_event_publish_outbox`,只服务 Content -> Events publish compensation。 - -文件: - -- Create: `muse-cloud/sql/muse/V21__extend_content_events_publish_outbox.sql` -- Create: `ContentEventPublishOutboxDO.java` -- Create: `ContentEventPublishOutboxMapper.java` -- Create: `P1rContentEventsPublishMigrationSqlTest.java` - -表字段必须包含: - -- `id` -- `tenant_id` -- `outbox_id` -- `command_id` -- `source_command_id` -- `source_attribution_id` -- `owner_user_id` -- `work_id` -- `block_id` -- `block_revision` -- `source_status` -- `source_revision` -- `event_type` -- `notification_type` -- `resource_ref_type` -- `resource_ref_id` -- `payload_summary` -- `publish_status` -- `attempt_count` -- `max_attempt` -- `next_retry_at` -- `claimed_at` -- `claim_expires_at` -- `last_error_code` -- `last_error_message` -- `published_event_id` -- `published_sequence_no` -- `creator` -- `updater` -- `create_time` -- `update_time` -- `deleted` - -唯一约束: - -- `(tenant_id, outbox_id)` -- `(tenant_id, command_id)` -- `(tenant_id, block_id, block_revision, notification_type)` - -Check 约束: - -- `event_type = 'notification'` -- `notification_type = 'source_status_change'` -- `source_status = 'active'` -- `publish_status in ('queued','running','retryable','published','dead_letter')` -- `owner_user_id > 0` -- `block_id > 0` -- `block_revision > 0` -- `resource_ref_id > 0` -- `attempt_count >= 0` -- `max_attempt > 0` - -索引: - -- claim index:`publish_status, next_retry_at, claim_expires_at` -- owner status index:`owner_user_id, publish_status` -- block revision index:`block_id, block_revision` - -Trigger: - -- `update_time` trigger。 - -Mapper 方法: - -- `insertIgnore` -- `selectByTenantIdAndOutboxId` -- `selectByTenantIdAndCommandId` -- `selectByTenantIdAndBlockIdAndBlockRevisionAndNotificationType` -- `claimNextPublishOutbox` -- `markPublished` -- `markRetryable` -- `markDeadLetter` - -SQL gate: - -- 断言字段、唯一约束、check 约束、索引、trigger。 -- 断言 `event_type` check 是单值 `notification`,不能包含 `chunk` 或 OR 放宽。 -- 断言 `notification_type` check 是单值 `source_status_change`。 -- 断言 `source_status` check 是单值 `active`,不能包含 `deleted` 或 OR 放宽。 -- 断言非法 `event_type='chunk'`、非法 `notification_type`、非法 `source_status='deleted'`、非法 `publish_status`、非法 `owner_user_id`、非法 `block_id`、非法 `block_revision`、非法 `resource_ref_id` 在 Flyway `_test` 中被 DB 拒绝。 - -### Task 3:Content outbox service - -目标: - -- 在 `saveBlock` 首次成功事务内,为 `BlockSourceAttributionDO(sourceStatus=active)` 创建一条 outbox。 - -文件: - -- Create: `ContentEventPublishOutboxService.java` -- Create: `ContentEventPublishOutboxServiceImpl.java` -- Create: `ContentEventPayloads.java` -- Modify: `ContentAppServiceImpl.java` -- Test: `ContentEventPublishOutboxServiceTest.java` -- Test: `ContentAppServiceTest.java` - -接入点: - -- `ContentAppServiceImpl.saveBlock` -- 在 `sourceAttributionMapper.insert(...)` 后调用 outbox service。 -- command replay 返回时不得创建 outbox。 -- revision guard / owner guard 失败时不得创建 outbox。 - -Outbox service 输入: - -- `userId` -- `workId` -- `blockId` -- `blockRevision` -- `sourceCommandId = reqVO.getCommandId()` -- `BlockSourceAttributionDO sourceAttribution` - -Payload 构造: - -```json -{ - "type": "source_status_change", - "message": "内容来源状态已更新", - "resourceRef": { - "resourceType": "content_block", - "resourceId": 8001 - }, - "timestamp": "2026-06-08T12:00:00" -} -``` - -Payload validator: - -- 顶层 exact keys:`type/message/resourceRef/timestamp`。 -- `resourceRef` exact keys:`resourceType/resourceId`。 -- `type=source_status_change`。 -- `message` 只允许 `内容来源状态已更新`。 -- `resourceType=content_block`。 -- `resourceId` 必须是 Number。 -- 不允许 `contentText`、`content`、`sourceSnapshot`、`sourceObjectId`、`authorizationSnapshotId`、`lineagePayload`、`licenseRestrictionSnapshot`、`auditReason`、`requestHash`、`resultSnapshot`、`stackTrace`。 - -错误码: - -- payload invalid 固定为 `CONTENT_EVENTS_PAYLOAD_INVALID`。 - -Focused tests: - -- successful saveBlock 首次执行在 source attribution 插入后创建 outbox。 -- command replay 不创建 outbox。 -- revision conflict 不创建 outbox。 -- owner mismatch 不创建 outbox。 -- duplicate `(blockId, revision, source_status_change)` 不创建第二条 outbox。 -- invalid owner 不创建 outbox 并记录安全日志。 -- sourceStatus 非 active 不创建 outbox。 -- payload exact allowlist,敏感字段负断言。 -- `eventType` 固定 `notification`,`notificationType` 固定 `source_status_change`。 -- 负断言 Content outbox service 不生成 `chunk`。 -- configured maxAttempt 生效。 -- timestamp 使用 source attribution createTime,缺失时回退 outbox createTime。 - -### Task 4:Content publish worker - -目标: - -- Claim Content outbox,构造 `EventsPublishReqDTO`,调用 `EventsPublishApi`,按结果更新 outbox 状态。 - -文件: - -- Create: `ContentEventPublishWorker.java` -- Test: `ContentEventPublishWorkerTest.java` - -Events request: - -- `commandId = outbox.commandId` -- `tenantId = outbox.tenantId` -- `ownerUserId = outbox.ownerUserId` -- `sourceOwner = content` -- `sourceType = content_block_source_attribution` -- `sourceId = String.valueOf(outbox.blockId)` -- `sourceRevision = String.valueOf(outbox.blockRevision)` -- `eventType = notification` -- `resourceType = outbox.resourceRefType` -- `resourceId = String.valueOf(outbox.resourceRefId)` -- `payloadSummary = parsed exact allowlist payload` -- `emittedAt = outbox.createTime` - -状态处理: - -- accepted -> `published` -- duplicate accepted -> `published` -- rejected / blocked -> `dead_letter` -- CommonResult error -> `retryable` 或耗尽后 `dead_letter` -- exception -> `retryable` 或耗尽后 `dead_letter` -- payload invalid -> `dead_letter`,不调用 Events -- unknown status -> `dead_letter` - -Focused tests: - -- disabled 不 claim。 -- enabled claim 使用配置 claim timeout。 -- accepted 回写 eventId / sequenceNo。 -- duplicate accepted 回写既有 eventId / sequenceNo。 -- rejected / blocked dead_letter。 -- CommonResult error 非最后一次 retryable,最后一次 dead_letter。 -- exception 非最后一次 retryable,最后一次 dead_letter。 -- invalid payload dead_letter 且不调用 Events。 -- owner invalid dead_letter 且不调用 Events。 -- sourceStatus 非 active dead_letter 且不调用 Events。 -- terminal update 使用 claimed attempt ownership。 -- stale terminal update 返回 0 时记录诊断日志。 -- worker 发出的 `EventsPublishReqDTO.eventType` 必须是 `notification`。 -- worker payload `type` 必须是 `source_status_change`。 -- 负断言 worker 不调用 `chunk`,且 payload 不包含正文。 - -### Task 5:P1R executable gates - -目标: - -- 用 server 侧 gate 证明依赖方向、focused E2E、V21 migration、anti-chunk 合同和既有 owner gate 不回退。 - -文件: - -- Create: `P1rContentEventsPublishDependencyTest.java` -- Create: `P1rContentEventsPublishEndToEndTest.java` -- Create: `P1rContentEventsPublishFlywayMigrationIT.java` -- Create: `P1rContentEventsPublishMigrationSqlTest.java` - -Dependency test: - -- Content server 有 direct `muse-module-events-api`。 -- Content server 无 `muse-module-events-server`。 -- Events server 无 AI / Knowledge / Market / Member / Content server。 - -E2E focused test: - -- 构造 `saveBlock` source fact。 -- Content outbox service 创建 outbox。 -- Worker 成功路径必须调用真实 `EventsPublishServiceImpl`;只允许使用 in-memory / mocked `UnifiedEventMapper` 承接 unified event 行,禁止 bare fake `EventsPublishApi` 直接返回 accepted 作为成功证据。 -- `muse_unified_event` 中 owner 为作品 owner。 -- SSE visible query 对作品 owner 可见,对其他 owner 不可见。 -- unified event `eventType=notification`,payload `type=source_status_change`。 -- 负断言没有 `chunk` event。 -- payload 不含正文、sourceSnapshot、authorizationSnapshotId、lineage、license restriction、auditReason、requestHash、resultSnapshot、错误堆栈。 -- 同一 block revision duplicate replay 返回同一 eventId / sequenceNo。 -- 同一 block 不同 revision 产生不同 notification。 -- rejected / blocked -> outbox dead_letter,visible query 为空。 -- sourceStatus 非 active -> outbox dead_letter 或创建前拒绝,visible query 为空,且不调用 Events。 -- stale claim terminal update 被忽略。 - -Flyway `_test`: - -- 使用真实 PostgreSQL `_test` 数据库。 -- 跑到 V21。 -- 验证 table / indexes / constraints / trigger。 -- 验证非法 insert 被 DB 拒绝。 -- 验证 `event_type='chunk'` 被 DB 拒绝。 -- 验证 `source_status='deleted'` 被 DB 拒绝。 - -### Task 6:最终验证、review 和留痕 - -最终验证顺序必须先 build,再 tests,避免 `clean` 清理 surefire XML。 - -#### 1. Full reactor build - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -期望:live reactor 全量 modules success。 - -#### 2. Content focused tests - -先断言新增测试文件存在: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -test -f muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishOutboxServiceTest.java && \ -test -f muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentEventPublishWorkerTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishMigrationSqlTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishDependencyTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishEndToEndTest.java && \ -test -f muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishFlywayMigrationIT.java -``` - -运行: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-module-content/muse-module-content-server -am \ - -Dtest=ContentAppServiceTest,ContentEventPublishOutboxServiceTest,ContentEventPublishWorkerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须检查 surefire XML,目标测试类 `tests>0` 且 failures/errors/skipped 均为 0。 - -#### 3. P1R mixed executable gates - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rContentEventsPublishMigrationSqlTest,P1rContentEventsPublishDependencyTest,P1rContentEventsPublishEndToEndTest,P1rContentRealApiGateTest,P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须检查 surefire XML,新增 Content 三个 P1R gate、既有 Content real API gate、Account/Market/Knowledge/AI/Events 目标测试类 `tests>0` 且 failures/errors/skipped 均为 0: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -python3 - <<'PY' -from pathlib import Path -import xml.etree.ElementTree as ET - -targets = [ - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rContentRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountRouteOwnershipTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishMigrationSqlTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishDependencyTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiEventsPublishEndToEndTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rEventsRouteOwnershipTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml", - "muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAiRouteOwnershipTest.xml", -] -for target in targets: - path = Path(target) - if not path.exists(): - raise SystemExit(f"missing surefire xml: {target}") - 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")) - if tests <= 0 or failures or errors or skipped: - raise SystemExit(f"bad surefire result: {target} tests={tests} failures={failures} errors={errors} skipped={skipped}") -PY -``` - -#### 4. Flyway `_test` - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -. ~/.config/muse-repo/infra.env && \ -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) 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/muse_p1r7f_content_events_publish_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -Dhttp.proxyHost= \ - -Dhttp.proxyPort= \ - -Dhttps.proxyHost= \ - -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望: - -- migrations_executed = 21。 -- target_schema_version = 21。 -- `muse_content_event_publish_outbox` 存在。 -- `event_type='chunk'` insert 被拒绝。 -- `source_status='deleted'` insert 被拒绝。 -- 目标测试 XML tests > 0,failures/errors/skipped 均为 0。 - -#### 5. Dependency tree gate - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-content/muse-module-content-server -DskipTests dependency:tree > /tmp/p1r7f-content-server-dependency-tree.txt && \ -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-events/muse-module-events-server -DskipTests dependency:tree > /tmp/p1r7f-events-server-dependency-tree.txt && \ -rg "muse-module-events-api" /tmp/p1r7f-content-server-dependency-tree.txt && \ -! rg "muse-module-events-server" /tmp/p1r7f-content-server-dependency-tree.txt && \ -! rg "muse-module-(ai|knowledge|market|member|content)-server" /tmp/p1r7f-events-server-dependency-tree.txt -``` - -#### 6. Isolated coverage scanner - -只允许在 `/tmp` 隔离副本运行 scanner: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -tmpdir=$(mktemp -d /tmp/p1r7f-coverage-scan.XXXXXX) && \ -rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/ && \ -cd "$tmpdir" && \ -python3 -m venv .venv && \ -. .venv/bin/activate && \ -python -m pip install --quiet PyYAML && \ -python muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -期望: - -- scanner exit 0。 -- completed=100。 -- needsVerification=133。 -- incomplete=0。 -- genericPersistence=0。 -- ssePlaceholder=0。 -- Events `streamEvents=dedicated / needs_verification`。 -- Content 51 operations 仍为 `dedicated / needs_verification`。 -- AI / Knowledge / Market / Account source owner gates 不回退。 - -#### 7. Protected diff and whitespace - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 && \ -git diff --check && \ -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md && \ -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -#### 8. Memory - -实现 evidence 可用后新增: - -- `docs/memorys/2026-06-08-P1R7fContentSourceOwnerPropagation真实链路.md` - -必须记录: - -- 目标和非目标。 -- 关键实现文件。 -- focused tests 数量。 -- P1R mixed gates 数量。 -- Flyway `_test` 结果。 -- dependency tree gate。 -- isolated coverage scanner 目录和 summary。 -- protected diff。 -- fresh implementation review 结果。 -- 明确说明仍不代表 Events / P1R-7 / Content completed。 - -## 回滚策略 - -- 配置层回滚:保持或恢复 `muse.content.events.publish-worker.enabled=false`,worker 不再 claim 新 outbox。 -- 代码层回滚:回退 Content outbox service 注入点与 worker/config/pom 改动;不触碰 OpenAPI/scanner/coverage report。 -- 数据层回滚:V21 只新增 outbox 表、索引、约束和 trigger;若未发布生产数据,可按迁移策略 drop 新表;若已有数据,先停 worker,再导出 outbox 审计数据后按 DBA 审批处理。 -- 状态层回滚:不得把 rollback 描述成 completed 撤销;P1R coverage 状态仍保持 `needs_verification`。 diff --git a/docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md b/docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md deleted file mode 100644 index a8e542db..00000000 --- a/docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md +++ /dev/null @@ -1,264 +0,0 @@ -# P1R7 Completed Approval 审阅版 - -日期:2026-06-09 - -## 结论 - -推荐进入 P1R-7 completed approval,但本轮审批对象只能是 Events 唯一 operation: - -```text -events / streamEvents / GET /app-api/muse/events -``` - -本审阅版不建议把 Market、Account、Content、Meta 或总 P1R-7 自动推进 `completed`。AI 与 Knowledge 已在历史审批中是 `completed`;Market、Account、Content、Meta 当前仍保持 `dedicated / needs_verification`。 - -如果后续执行版获 fresh 双 review PASS 且用户明确批准,推荐采用 operation-level approval,把 `events:streamEvents` 单独列入 approved completed operations。不要简单把整个 `events` domain 加入 domain-level completed 白名单;虽然当前 Events 只有一个 operation,但 domain-level 白名单会让未来新增 Events operation 误继承 completed。 - -本审阅版只冻结审批范围、证据口径和后续执行要求;不修改 OpenAPI、coverage scanner、coverage report 或业务代码,不推进 `completed`。 - -## 审批主线 - -```mermaid -flowchart TB - Start["当前 HEAD
P1R-7a-f evidence 已提交推送"] --> Candidate["审批候选
events streamEvents"] - Candidate --> Evidence["证据包
Events SSE + AI/Knowledge/Market/Account/Content owner propagation"] - Evidence --> Review["Completed approval 执行版
fresh spec + quality review"] - Review --> Approval{"用户明确批准?"} - Approval -->|否| Stay["保持 needs_verification"] - Approval -->|是| OperationAllowlist["operation-level approval
events:streamEvents"] - OperationAllowlist --> Gates["scanner + report + P1R gates
fresh implementation review"] - Gates --> Commit["提交 / 推送
不得连带推进其它 domain"] -``` - -## 已验证事实 - -### 工作区与远端 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- 写入本文档前,`git status --short --branch` 显示: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -- 本轮已执行 `git pull --ff-only origin dev/1.0.0`,结果: - -```text -Already up to date. -``` - -- 当前最新提交: - -```text -f84c18f test(p1r): 收口 Content 事件传播真实链路门禁 -``` - -### 当前 coverage 状态 - -真实 worktree 当前报告显示: - -```text -totalOperations 233 -completedOperations 100 -needsVerificationOperations 133 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -按 domain 聚合: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/needs_verification:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -Events 当前唯一 operation: - -```text -events streamEvents GET /app-api/muse/events dedicated needs_verification P1R-7 End-to-End Acceptance -``` - -### 隔离 scanner 证据 - -本轮已在一次性 `/tmp/p1r7-completed-approval-scan.JqNuWY` 隔离副本执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果 exit 0,并只在隔离副本生成 coverage JSON / Markdown。该路径只作为本轮证据留痕,后续执行版不得复用,必须重新创建隔离副本。summary 与真实 worktree 当前报告一致: - -```text -233 100 133 0 0 0 -``` - -真实 worktree 受保护文件 staged / unstaged diff 均为空: - -- `docs/api-contracts/account/openapi.yaml` -- `docs/api-contracts/market/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/events/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -### P1R-7a Events SSE evidence - -- P1R-7a 已把 `streamEvents GET /app-api/muse/events` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`。 -- P1R-7a 证据包括 frontend SSE focused tests、P1R Events API gate、route ownership gate、reactor build、dependency tree 与 V16 Flyway `_test`。 -- P1R-7a 明确没有接入 source owner propagation,因此当时不能进入 completed approval。 - -### P1R-7b 到 P1R-7f owner propagation evidence - -P1R-7b AI: - -- AI terminal event -> AI publish outbox -> AI worker -> EventsPublishApi -> `muse_unified_event` -> SSE 可见链路已形成 `needs_verification` evidence。 -- 已提交并 push:`3db5fbe`、`230152c`。 - -P1R-7c Knowledge: - -- Knowledge source owner terminal event -> Knowledge outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE 可见链路已形成 `needs_verification` evidence。 -- 已提交并 push:`cef686b`、`e55618f`。 - -P1R-7d Market: - -- Market governance terminal fact -> Market outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE owner-visible 链路已形成 `needs_verification` evidence。 -- 已提交并 push:`b36b153`、`e8ed7d7`。 - -P1R-7e Account: - -- Account quota adjustment terminal fact -> Account outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE owner-visible 链路已形成 `needs_verification` evidence。 -- 已提交并 push:`0e10caf`、`68cee5d`;`0f49802` 记录旧 completed approval 预检边界。 - -P1R-7f Content: - -- Content `saveBlock` 首次成功 -> `muse_content_block_source_attribution(source_status=active)` -> Content outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE owner-visible 链路已形成 `needs_verification` evidence。 -- 已提交并 push:`acda7a2`、`6f5b9b3`、`f84c18f`。 - -### 当前 scanner completed 白名单机制 - -当前 `muse-cloud/scripts/p1r-audit-api-coverage.py` 使用 domain-level 白名单: - -```text -APPROVED_COMPLETED_DOMAINS = {"ai", "knowledge"} -``` - -当前 `P1rApiCoverageReportTest` 也使用同名 domain-level 白名单验证 completed 只能来自已批准域。 - -这说明后续如果要把 `events:streamEvents` 推进 completed,必须修改 scanner 与 report gate 的批准模型,或者临时把整个 `events` domain 加入批准域。后者不是推荐方案。 - -## 推断 - -- P1R-7a 到 P1R-7f 已覆盖 Events SSE 入口与 AI、Knowledge、Market、Account、Content 五个 owner 的第一批用户可见事件传播 evidence,旧预检中 “Account / Content 是否仍是硬前置” 的问题已被后续 P1R-7e / P1R-7f 关闭。 -- 现在唯一合理的 completed approval 候选是 Events `streamEvents`,因为 coverage 中 P1R-7 只对应这一项 operation。 -- Market、Account、Content、Meta 的 operation 仍代表各自 domain API 本身,不应因为它们提供过 Events source owner evidence 就被自动推进 completed。 -- operation-level approval 比 domain-level approval 更符合长期演进:它能准确表达“本次只批准 `events:streamEvents`”,并避免未来新增 Events API 被误标 completed。 - -## 假设 - -- 当前 P1R-7 completed approval 的目标是关闭 P1R-7 Events End-to-End Acceptance,而不是重新审批所有 P1R domain API。 -- 用户仍要求 coverage `completed` 必须单独批准,不能由 fresh review PASS 或 `needs_verification` evidence 自动推出。 -- 后续执行版可以修改 coverage scanner、coverage report 和 coverage gate,但必须先通过 fresh review,并在用户明确批准后执行。 - -## 推荐方案 - -### 方案 A:operation-level approval - -后续执行版应把 completed 批准模型从纯 domain-level 扩展为 operation-level: - -- 保留 AI / Knowledge 既有 completed 状态。 -- 新增显式批准 key:`events:streamEvents`。 -- `approved_completed_operation` 同时支持既有 approved domains 和 approved operation keys。 -- `P1rApiCoverageReportTest` 同步断言 completed operation 必须属于 approved domain 或 approved operation key。 -- scanner 生成 report 后,只有 `events / streamEvents` 从 `needs_verification` 变为 `completed`。 - -推荐选择该方案。 - -理由: - -- 最小、准确、可审计。 -- 不影响 Market / Account / Content / Meta。 -- 不会让未来 Events 新 operation 自动继承 completed。 - -### 方案 B:把 `events` 加入 approved domain - -后续执行版也可以把 `events` 加入 `APPROVED_COMPLETED_DOMAINS`。 - -不推荐选择该方案。 - -理由: - -- 当前虽然只会影响 `streamEvents` 一项,但模型表达不准确。 -- 未来若 Events OpenAPI 新增 operation,scanner 会默认把 dedicated Events operation 标为 completed,产生阶段越权风险。 - -## 非目标 - -- 不修改 OpenAPI。 -- 不修改业务实现。 -- 不修改真实 coverage scanner。 -- 不修改真实 coverage JSON / Markdown。 -- 不推进 Market 32 operations completed。 -- 不推进 Account 33 operations completed。 -- 不推进 Content 51 operations completed。 -- 不推进 Meta 16 operations completed。 -- 不把 P1R-7f 或任一 owner evidence 直接写成该 owner domain completed。 - -## 后续执行版必须包含 - -1. 只读 preflight: - - `git status --short --branch` - - `git log --oneline -5` - - `git pull --ff-only origin dev/1.0.0` - - protected staged / unstaged diff gate -2. 批准模型修改方案: - - 推荐 operation-level approval - - 明确只批准 `events:streamEvents` -3. coverage report 更新方式: - - 用户明确批准前只能在 `/tmp` 隔离副本验证 - - 用户批准后才允许在真实 worktree 运行 scanner 生成 report -4. 验证命令: - - scanner `--check` - - `P1rApiCoverageReportTest` - - `P1rEventsRealApiGateTest` - - `P1rEventsRouteOwnershipTest` - - AI / Knowledge / Market / Account / Content 既有 P1R owner propagation gates 的最小回归集合 - - `git diff --check` -5. 复审 gate: - - fresh implementation spec/correctness review - - fresh implementation quality/data-integrity/testing review -6. 明确提交边界: - - scanner/test 逻辑改动、coverage report 生成、`.agent` / `docs/memorys` 状态留痕可同阶段提交 - - 真实 coverage report 更新必须发生在用户明确批准 completed 推进之后 - - OpenAPI 不进入提交 - - 不连带推进其它 domain completed - -## 验收标准 - -本审阅版通过标准: - -1. 审批对象唯一且明确:`events:streamEvents`。 -2. 已区分 P1R-7 Events completed approval 与 Market / Account / Content / Meta domain completed。 -3. 已确认 P1R-7e / P1R-7f 关闭旧预检中的 Account / Content source owner 前置疑问。 -4. 已指出当前 scanner 是 domain-level completed approval,后续执行版必须改成 operation-level 或显式解释为什么不改。 -5. 未修改 OpenAPI、scanner、coverage report 或业务代码。 - -## 待确认项 - -1. 是否确认本轮 completed approval 只审批 `events:streamEvents`? -2. 是否确认采用 operation-level approval,而不是把整个 `events` domain 加入 approved domain? -3. 是否确认执行版 review 双 PASS 后,再由用户单独批准真实 worktree 的 coverage report 状态推进? - -## 下一步 - -1. 对本审阅版派发 fresh spec/scope review。 -2. 对本审阅版派发 fresh quality/feasibility review。 -3. 双 PASS 后写 `docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md`。 -4. 执行版仍需 fresh 双 review;双 PASS 前不得修改 scanner、coverage report 或推进 completed。 diff --git a/docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md b/docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md deleted file mode 100644 index 57a14ad4..00000000 --- a/docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md +++ /dev/null @@ -1,657 +0,0 @@ -# P1R7 Completed Approval 执行版 - -日期:2026-06-09 - -## 结论 - -本执行版只推进 P1R-7 Events End-to-End Acceptance 的 completed approval 执行准备,审批对象唯一: - -```text -events:streamEvents / GET /app-api/muse/events -``` - -推荐实现方式是 operation-level approval:保留既有 AI / Knowledge domain-level completed,新增 `events:streamEvents` 单项 operation-level completed。禁止把整个 `events` domain 加入 completed 白名单,禁止连带推进 Market、Account、Content、Meta 或总 P1R-7 completed。 - -本执行版当前只允许进入 fresh execution review;双 PASS 前不得实现、不得修改 scanner、不得修改 coverage report、不得提交或 push。执行版双 PASS 后,仍必须由用户单独明确批准,才允许在真实 worktree 更新 scanner / report / gate。 - -## Review 前置状态 - -审阅版: - -- `docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md` - -审阅版 fresh review gate: - -- Curie fresh quality/feasibility review:PASS,无 P0/P1/P2/P3 blocker。 -- Lorentz fresh final spec/scope review:PASS,无 P0/P1 blocker。 -- Descartes 超时关闭,不计入 gate。 -- Godel 返回空结果,不计入 gate。 -- Gibbs 对旧版 PASS 后提出的非阻塞反馈已修订到审阅版,不计入最终新版本 PASS gate。 - -审阅版双 PASS 只代表可以编写本执行版,不代表用户已经批准真实 coverage 状态推进。 - -## 当前已验证事实 - -### 工作区 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 已返回 `Already up to date.`。 -- 当前最新提交: - -```text -f84c18f test(p1r): 收口 Content 事件传播真实链路门禁 -``` - -- 当前未提交变更只应来自 completed approval 文档: - - `docs/agent-specs/.agent` - - `docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md` - - `docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md` - -### 当前 coverage - -当前真实 coverage summary: - -```text -totalOperations 233 -completedOperations 100 -needsVerificationOperations 133 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -当前 domain 状态: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/needs_verification:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -Events 当前唯一 operation: - -```text -events streamEvents GET /app-api/muse/events dedicated needs_verification P1R-7 End-to-End Acceptance -``` - -### P1R-7 evidence 现状 - -- P1R-7a Events SSE:`streamEvents` 已从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`。 -- P1R-7b AI:AI terminal event -> AI outbox -> worker -> Events -> SSE evidence 已收口。 -- P1R-7c Knowledge:Knowledge source owner event -> Knowledge outbox -> worker -> Events -> SSE evidence 已收口。 -- P1R-7d Market:Market governance terminal fact -> Market outbox -> worker -> Events -> SSE evidence 已收口。 -- P1R-7e Account:Account quota adjustment terminal fact -> Account outbox -> worker -> Events -> SSE evidence 已收口。 -- P1R-7f Content:Content `saveBlock` 首次成功 -> Content outbox -> worker -> Events -> SSE evidence 已收口。 - -这些 evidence 支撑 `events:streamEvents` completed approval,但不支撑 Market / Account / Content / Meta domain completed。 - -## 范围 - -本执行版允许的后续实现范围: - -- 修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 修改 `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java`。 -- 在用户明确批准后,重新生成: - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` -- 新增 completed approval 真实推进留痕: - - `docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md` -- 更新: - - `docs/agent-specs/.agent` - -本执行版禁止: - -- 修改任何 OpenAPI: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/content/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/meta/openapi.yaml` -- 修改业务实现代码。 -- 修改 Events source owner propagation 实现。 -- 把 `events` 整个 domain 加入 approved domain。 -- 推进 Market 32 operations completed。 -- 推进 Account 33 operations completed。 -- 推进 Content 51 operations completed。 -- 推进 Meta 16 operations completed。 -- 把 P1R-7f 或任一 owner evidence 直接写成该 owner domain completed。 -- 宣称总 P1R / 全业务域 completed。 - -## 目标状态 - -用户明确批准并完成实现后,coverage summary 必须变成: - -```text -totalOperations 233 -completedOperations 101 -needsVerificationOperations 132 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -domain 状态必须变成: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/completed:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -唯一新增 completed operation 必须是: - -```text -events streamEvents GET /app-api/muse/events dedicated completed P1R-7 End-to-End Acceptance -``` - -## 实现设计 - -### Scanner approval 模型 - -当前 scanner 使用: - -```python -APPROVED_COMPLETED_DOMAINS = {"ai", "knowledge"} -``` - -后续实现必须保留该集合,并新增 operation-level 白名单: - -```python -APPROVED_COMPLETED_OPERATIONS = {"events:streamEvents"} -``` - -推荐新增 helper: - -```python -def operation_key(domain: str, operation_id: str) -> str: - return f"{domain}:{operation_id}" -``` - -`dedicated_status_for` 必须从只接收 `domain` 改为至少接收 `domain` 与 `operation_id`: - -```python -def dedicated_status_for(domain: str, operation_id: str) -> tuple[str, str]: - if domain in APPROVED_COMPLETED_DOMAINS or operation_key(domain, operation_id) in APPROVED_COMPLETED_OPERATIONS: - return ( - "completed", - "Dedicated entry exists; user-approved P1R acceptance evidence allows this operation to advance to completed coverage.", - ) - return ( - "needs_verification", - "Dedicated entry exists; later P1R stage must verify DTO, state machine, persistence, and real external closure.", - ) -``` - -`classify_implementation` 调用点必须传入 `operation_id`: - -```python -dedicated_completion, dedicated_notes = dedicated_status_for(domain, operation_id) -``` - -`approved_completed_operation(operation)` 必须允许 approved domain 或 approved operation key: - -```python -def approved_completed_operation(operation: dict[str, Any]) -> bool: - if operation["implementationStatus"] != "dedicated": - return False - if operation["domain"] in APPROVED_COMPLETED_DOMAINS: - return True - return operation_key(operation["domain"], operation["operationId"]) in APPROVED_COMPLETED_OPERATIONS -``` - -中文注释必须说明:AI / Knowledge 是历史 domain-level 用户批准;Events 本次只批准 `streamEvents`,不得给未来 Events operation 自动继承 completed。 - -### Report gate approval 模型 - -`P1rApiCoverageReportTest` 必须同步新增 operation-level 白名单: - -```java -/** 已获用户批准、并有 P1R 外部端到端验收证据支撑的 completed operation。 */ -private static final Set APPROVED_COMPLETED_OPERATIONS = Set.of( - "events:streamEvents" -); -``` - -推荐新增 helper: - -```java -private static String operationKey(JsonNode operation) { - return operation.path("domain").asText() + ":" + operation.path("operationId").asText(); -} -``` - -`should_only_mark_approved_dedicated_operations_as_completed` 必须改为: - -```java -assertTrue( - APPROVED_COMPLETED_DOMAINS.contains(operation.path("domain").asText()) - || APPROVED_COMPLETED_OPERATIONS.contains(operationKey(operation)), - operationId + " 只有已批准 domain 或 operation 可以标记为 completed" -); -``` - -必须新增测试断言 completed summary 与非目标 domain: - -```java -@Test -void should_only_promote_events_stream_events_for_p1r7_completed_approval() throws IOException { - JsonNode operations = readOperations(); - - int completed = 0; - for (JsonNode operation : operations) { - if ("completed".equals(operation.path("completionStatus").asText())) { - completed++; - } - } - assertEquals(101, completed, "P1R-7 completed approval 后 completed 总数只能从 100 增至 101"); - - assertOperationStatus("events", "streamEvents", "dedicated", "completed"); - assertDomainStatusCount("market", "dedicated", "needs_verification", 32); - assertDomainStatusCount("account", "dedicated", "needs_verification", 33); - assertDomainStatusCount("content", "dedicated", "needs_verification", 51); - assertDomainStatusCount("meta", "dedicated", "needs_verification", 16); -} -``` - -helper 可以按本测试类现有风格实现,不做长期抽象。 - -### Coverage report 生成 - -用户明确批准前: - -- 只能在 `/tmp` 隔离副本验证 scanner 修改后的报告输出。 -- 真实 worktree 的 `docs/superpowers/reports/p1r-api-coverage.json` 和 `.md` 必须保持无 diff。 - -用户明确批准后: - -- 才允许在真实 worktree 执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -并把真实 report 更新纳入本阶段提交。 - -## 执行步骤 - -### Task 0:preflight - -执行位置: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -命令: - -```bash -pwd -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -git diff --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/meta/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -git diff --cached --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/meta/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -期望: - -- `pwd` 是正确 worktree。 -- `git pull` 为 fast-forward 或 already up to date。 -- protected staged / unstaged diff 为空。 -- 若存在非本文档范围的 dirty 变更,必须停止并先确认来源。 - -### Task 1:执行版 review gate - -本文件写完后必须派发: - -- fresh execution spec/scope review。 -- fresh execution quality/feasibility review。 - -双 PASS 前不得实现 Task 2 之后任何内容。 - -### Task 2:用户 completed 推进批准 - -执行版双 PASS 后,必须让用户明确批准以下三项: - -1. 本轮只审批 `events:streamEvents / GET /app-api/muse/events`。 -2. 采用 operation-level approval,不采用 `events` domain-level 白名单。 -3. 允许在真实 worktree 修改 scanner、coverage gate test 与 coverage report,把 completed summary 从 `100/133` 推进到 `101/132`。 - -没有这三项明确批准,不得进入 Task 3。 - -### Task 3:修改 scanner 与 report gate - -允许修改: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `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/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` - -必须做到: - -- 新增 `APPROVED_COMPLETED_OPERATIONS = {"events:streamEvents"}`。 -- `dedicated_status_for` 支持 operation-level approval。 -- `approved_completed_operation` 支持 approved domain 或 approved operation key。 -- JUnit gate 同步支持 operation-level approval。 -- JUnit gate 新增 `events:streamEvents` 唯一新增 completed 的断言。 -- JUnit gate 明确断言 Market / Account / Content / Meta 仍为 `dedicated / needs_verification`。 -- 既有 RealApi gate 只允许同步本轮 approved coverage 口径: - - `completedOperations = 101` - - `needsVerificationOperations = 132` - - `events:streamEvents = dedicated / completed` - - `summary.ssePlaceholderOperations = 0` - - 非目标 Market / Account / Content / Meta 仍为 `dedicated / needs_verification` - -不得修改: - -- OpenAPI。 -- 业务实现。 -- P1R owner propagation 代码。 - -### Task 4:隔离 scanner 预验证 - -在真实 report 更新前,必须先隔离验证: - -```bash -tmpdir=$(mktemp -d /tmp/p1r7-completed-approval-impl-scan.XXXXXX) -rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/ -cd "$tmpdir" -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 | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain == "events") | [.domain,.operationId,.method,.path,.implementationStatus,.completionStatus,.targetStage] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -期望 summary: - -```text -233 101 132 0 0 0 -``` - -期望 domain: - -```text -account dedicated/needs_verification:33 -ai dedicated/completed:41 -content dedicated/needs_verification:51 -events dedicated/completed:1 -knowledge dedicated/completed:59 -market dedicated/needs_verification:32 -meta dedicated/needs_verification:16 -``` - -期望 Events: - -```text -events streamEvents GET /app-api/muse/events dedicated completed P1R-7 End-to-End Acceptance -``` - -回到真实 worktree 后,必须确认真实 report 仍无 diff: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git diff --name-only -- docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -### Task 5:真实 report 更新 - -只有 Task 2 用户批准后才允许执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -随后必须核对: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain == "events") | [.domain,.operationId,.method,.path,.implementationStatus,.completionStatus,.targetStage] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出必须等于 Task 4 期望。 - -### Task 6:P1R focused gates - -从 `muse-cloud` 执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rContentEventsPublishMigrationSqlTest,P1rContentEventsPublishDependencyTest,P1rContentEventsPublishEndToEndTest,P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest,P1rKnowledgeRealApiGateTest,P1rKnowledgeRouteOwnershipTest,P1rMarketRealApiGateTest,P1rMarketRouteOwnershipTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -目标测试类和静态 test count 下限: - -| 测试类 | 最少 tests | -|---|---:| -| `P1rContentEventsPublishMigrationSqlTest` | 3 | -| `P1rContentEventsPublishDependencyTest` | 2 | -| `P1rContentEventsPublishEndToEndTest` | 5 | -| `P1rAccountEventsPublishMigrationSqlTest` | 3 | -| `P1rAccountEventsPublishDependencyTest` | 2 | -| `P1rAccountEventsPublishEndToEndTest` | 4 | -| `P1rMarketEventsPublishMigrationSqlTest` | 3 | -| `P1rMarketEventsPublishDependencyTest` | 2 | -| `P1rMarketEventsPublishEndToEndTest` | 4 | -| `P1rKnowledgeEventsPublishMigrationSqlTest` | 3 | -| `P1rKnowledgeEventsPublishDependencyTest` | 2 | -| `P1rKnowledgeEventsPublishEndToEndTest` | 5 | -| `P1rAiEventsPublishMigrationSqlTest` | 4 | -| `P1rAiEventsPublishDependencyTest` | 2 | -| `P1rAiEventsPublishEndToEndTest` | 4 | -| `P1rApiCoverageReportTest` | 6 | -| `P1rEventsRealApiGateTest` | 5 | -| `P1rEventsRouteOwnershipTest` | 4 | -| `P1rAiRealApiGateTest` | 7 | -| `P1rAiRouteOwnershipTest` | 7 | -| `P1rKnowledgeRealApiGateTest` | 8 | -| `P1rKnowledgeRouteOwnershipTest` | 6 | -| `P1rMarketRealApiGateTest` | 6 | -| `P1rMarketRouteOwnershipTest` | 4 | -| `P1rAccountRealApiGateTest` | 4 | -| `P1rAccountRouteOwnershipTest` | 4 | -| `P1rContentRealApiGateTest` | 5 | -| `P1rMetaRealApiGateTest` | 4 | - -合计最少 `108` tests,failures/errors/skipped 必须均为 0。 - -执行后必须用 XML 计数脚本确认每个目标类实际运行,禁止空跑: - -```bash -python3 - <<'PY' -from pathlib import Path -import xml.etree.ElementTree as ET - -expected = { - "P1rContentEventsPublishMigrationSqlTest": 3, - "P1rContentEventsPublishDependencyTest": 2, - "P1rContentEventsPublishEndToEndTest": 5, - "P1rAccountEventsPublishMigrationSqlTest": 3, - "P1rAccountEventsPublishDependencyTest": 2, - "P1rAccountEventsPublishEndToEndTest": 4, - "P1rMarketEventsPublishMigrationSqlTest": 3, - "P1rMarketEventsPublishDependencyTest": 2, - "P1rMarketEventsPublishEndToEndTest": 4, - "P1rKnowledgeEventsPublishMigrationSqlTest": 3, - "P1rKnowledgeEventsPublishDependencyTest": 2, - "P1rKnowledgeEventsPublishEndToEndTest": 5, - "P1rAiEventsPublishMigrationSqlTest": 4, - "P1rAiEventsPublishDependencyTest": 2, - "P1rAiEventsPublishEndToEndTest": 4, - "P1rApiCoverageReportTest": 6, - "P1rEventsRealApiGateTest": 5, - "P1rEventsRouteOwnershipTest": 4, - "P1rAiRealApiGateTest": 7, - "P1rAiRouteOwnershipTest": 7, - "P1rKnowledgeRealApiGateTest": 8, - "P1rKnowledgeRouteOwnershipTest": 6, - "P1rMarketRealApiGateTest": 6, - "P1rMarketRouteOwnershipTest": 4, - "P1rAccountRealApiGateTest": 4, - "P1rAccountRouteOwnershipTest": 4, - "P1rContentRealApiGateTest": 5, - "P1rMetaRealApiGateTest": 4, -} -report_dir = Path("muse-server/target/surefire-reports") -total = 0 -for cls, minimum in expected.items(): - path = report_dir / f"TEST-cn.iocoder.muse.server.framework.api.{cls}.xml" - if not path.exists(): - raise SystemExit(f"missing surefire XML: {cls}") - 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(f"{cls}\t{tests}\t{failures}\t{errors}\t{skipped}") - if tests < minimum or failures or errors or skipped: - raise SystemExit(f"invalid surefire result: {cls}") - total += tests -if total < 108: - raise SystemExit(f"total tests too small: {total}") -print(f"p1r_completed_approval_gate_tests={total}") -PY -``` - -### Task 7:protected diff 与 whitespace - -执行: - -```bash -git diff --check -git diff --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/meta/openapi.yaml -git diff --cached --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/meta/openapi.yaml -``` - -同时必须执行最终 allowed-diff gate,防止业务实现、OpenAPI、非目标配置、非目标测试或未跟踪文件混入: - -```bash -python3 - <<'PY' -from pathlib import Path -import subprocess -import sys - -allowed_exact = { - "muse-cloud/scripts/p1r-audit-api-coverage.py", - "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/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", - "docs/superpowers/reports/p1r-api-coverage.json", - "docs/superpowers/reports/p1r-api-coverage.md", - "docs/agent-specs/.agent", - "docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md", - "docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md", - "docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md", -} - -def changed(args): - out = subprocess.check_output(["git", "-c", "core.quotePath=false", *args], text=True) - return {line.strip() for line in out.splitlines() if line.strip()} - -paths = ( - changed(["diff", "--name-only"]) - | changed(["diff", "--cached", "--name-only"]) - | changed(["ls-files", "--others", "--exclude-standard"]) -) -unexpected = sorted(path for path in paths if path not in allowed_exact) -if unexpected: - print("unexpected_paths=" + ",".join(unexpected)) - sys.exit(1) -print("allowed_diff_gate=PASS") -PY -``` - -期望: - -- `git diff --check` 无输出。 -- OpenAPI staged / unstaged diff 为空。 -- allowed-diff gate 输出 `allowed_diff_gate=PASS`。 -- `git status --short --untracked-files=all` 只能显示 allowed-diff gate 允许的路径。 - -如果本阶段已经在用户批准后修改 scanner/report,则 `muse-cloud/scripts/p1r-audit-api-coverage.py` 与 `docs/superpowers/reports/*` 允许有本阶段目标 diff;但 OpenAPI 仍必须为空。 -任何 allowed-diff 之外的路径都必须停止并说明来源;不得把额外业务实现、配置、测试或 OpenAPI 改动混入本阶段。 - -### Task 8:留痕 - -用户批准并完成真实推进后,新增: - -```text -docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md -``` - -必须记录: - -- 用户批准点。 -- old summary:`233 100 133 0 0 0`。 -- new summary:`233 101 132 0 0 0`。 -- 唯一新增 completed:`events:streamEvents`。 -- Market / Account / Content / Meta 仍为 `dedicated / needs_verification`。 -- scanner/test/report 修改文件。 -- `git diff --stat` 与关键 diff 摘要,证明唯一状态推进来自 `events:streamEvents` operation-level approval。 -- OpenAPI diff 为空。 -- allowed-diff gate 结果。 -- P1R focused gates 与 XML 计数。 -- fresh implementation review 结果。 - -同时更新 `docs/agent-specs/.agent`。 - -### Task 9:fresh implementation review - -实现和验证完成后,必须派发: - -- fresh implementation spec/correctness review。 -- fresh implementation quality/data-integrity/testing review。 - -双 PASS 前不得提交、push 或宣称 completed 已完成。 - -## 完成条件 - -只有全部满足,才可说 P1R-7 Events completed approval 已完成: - -1. 用户明确批准 Task 2 三项。 -2. operation-level approval 已落地,未使用 `events` domain-level 白名单。 -3. coverage summary 为 `233 / 101 / 132 / 0 / 0 / 0`。 -4. `events:streamEvents` 是唯一新增 completed operation。 -5. Market / Account / Content / Meta 仍为 `dedicated / needs_verification`。 -6. OpenAPI diff 为空。 -7. scanner `--check`、P1R focused gates、XML 计数、`git diff --check`、allowed-diff gate 与 `git status --short --untracked-files=all` 通过。 -8. completed approval 状态推进留痕已写入 `docs/memorys`。 -9. fresh implementation 双 review PASS。 -10. 提交 / push 仍需用户明确指令。 - -## 回滚策略 - -如果 scanner 或 gates 失败: - -- 不运行真实 report 更新,或回退本阶段 report diff。 -- 保留审阅版 / 执行版文档与失败留痕。 -- 修正 scanner/test 后重新跑 `/tmp` 隔离 scanner。 - -如果用户未批准 Task 2: - -- 保持 `events:streamEvents = dedicated / needs_verification`。 -- 不修改 scanner、coverage report 或 gate test。 - -如果误推进非目标 domain: - -- 立即停止。 -- 将 Market / Account / Content / Meta 恢复为 `dedicated / needs_verification`。 -- 重跑 scanner、P1R gate、protected diff。 diff --git a/docs/agent-specs/2026-06-10-P1RMetaCompletedApproval审阅版.md b/docs/agent-specs/2026-06-10-P1RMetaCompletedApproval审阅版.md deleted file mode 100644 index 48a8c513..00000000 --- a/docs/agent-specs/2026-06-10-P1RMetaCompletedApproval审阅版.md +++ /dev/null @@ -1,311 +0,0 @@ -# P1R Meta Completed Approval 审阅版 - -日期:2026-06-10 - -## 结论 - -推荐把 Meta 作为剩余业务域 completed approval 的第一候选,但当前 Meta 仍不是 `completed`。 - -本审阅版只冻结 Meta completed approval 的范围、证据缺口、推荐审批粒度和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务代码,不把 Meta 或其它 domain 推进 `completed`。 - -推荐审批策略: - -```text -Meta domain-level approval candidate -``` - -前提是后续执行版能一次性证明 Meta 16 个 operation 的真实运行态、数据迁移、状态机、权限、审计、幂等和失败路径证据。如果任一 operation 无法满足 completed 证据,必须退回 operation-level approval,不能整域推进。 - -## 审批路径 - -```mermaid -flowchart TB - Start["当前 coverage
Meta 16 dedicated / needs_verification"] --> Review["本审阅版
冻结 Meta completed 证据标准"] - Review --> FreshReview["fresh spec/scope review
fresh quality/feasibility review"] - FreshReview --> ExecDoc["执行版
列出测试、迁移、scanner/report/gate 修改"] - ExecDoc --> ExecReview["执行版 fresh 双 review"] - ExecReview --> UserApproval{"用户明确批准
Meta completed 推进?"} - UserApproval -->|否| Stop["保持 needs_verification"] - UserApproval -->|是| Implement["只按执行版允许路径修改
scanner / gate / report / 必要测试"] - Implement --> Verify["focused tests + P1R gates
真实 Flyway _test + XML 防空跑
allowed/protected diff"] - Verify --> ImplReview["fresh implementation 双 review"] - ImplReview --> FinalApproval{"用户批准提交和 push?"} - FinalApproval -->|否| LocalOnly["保留本地 evidence"] - FinalApproval -->|是| Commit["提交并 push"] -``` - -## 已验证事实 - -### 工作区与远端 - -- 工作区:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 当前远端:`origin/dev/1.0.0`。 -- 本轮已执行 `git pull --ff-only origin dev/1.0.0`,结果为 `Already up to date.`。 -- 当前 HEAD: - -```text -85c5422 test(p1r): 收口 P1R-7 completed approval 门禁 -``` - -### 当前 coverage 状态 - -当前 `docs/superpowers/reports/p1r-api-coverage.json` summary: - -```text -totalOperations 233 -completedOperations 101 -needsVerificationOperations 132 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -missingOperations 0 -blockedOperations 0 -``` - -Meta 当前状态: - -```text -meta_count 16 -implementationStatus dedicated:16 -completionStatus needs_verification:16 -``` - -Meta 16 个 operation: - -| operationId | method/path | 当前状态 | -| --- | --- | --- | -| `listMetaSchemas` | `GET /admin-api/muse/governance/meta-schemas` | `dedicated / needs_verification` | -| `getMetaSchema` | `GET /admin-api/muse/governance/meta-schemas/{schemaKey}` | `dedicated / needs_verification` | -| `getMetaSchemaVersion` | `GET /admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}` | `dedicated / needs_verification` | -| `saveMetaSchemaDraft` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/drafts` | `dedicated / needs_verification` | -| `validateMetaSchemaDraft` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/validate` | `dedicated / needs_verification` | -| `previewMetaSchemaDraftImpact` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/impact-preview` | `dedicated / needs_verification` | -| `publishMetaSchemaDraft` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/publish` | `dedicated / needs_verification` | -| `activateMetaSchemaVersion` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/activate` | `dedicated / needs_verification` | -| `rollbackMetaSchemaVersion` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/rollback` | `dedicated / needs_verification` | -| `deprecateMetaSchemaVersion` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/deprecate` | `dedicated / needs_verification` | -| `setMetaSchemaGrayRules` | `POST /admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/gray-rules` | `dedicated / needs_verification` | -| `listProtectionNodes` | `GET /admin-api/muse/governance/protection-nodes` | `dedicated / needs_verification` | -| `getProtectionNode` | `GET /admin-api/muse/governance/protection-nodes/{nodeKey}` | `dedicated / needs_verification` | -| `listFunctionChains` | `GET /admin-api/muse/governance/function-chains` | `dedicated / needs_verification` | -| `previewFunctionChainImpact` | `POST /admin-api/muse/governance/function-chains/{chainKey}/impact-preview` | `dedicated / needs_verification` | -| `activateFunctionChainVersion` | `POST /admin-api/muse/governance/function-chains/{chainKey}/versions/{version}/activate` | `dedicated / needs_verification` | - -### 已有 dedicated 实现证据 - -Meta 已有 dedicated Controller: - -- `AdminMetaSchemaController` -- `AdminProtectionNodeController` -- `AdminFunctionChainController` - -这三个 Controller 均使用 `/muse/governance/...` 类级路由,方法级 `@GetMapping` / `@PostMapping` 覆盖 Meta 16 个 operation,并通过 `@PreAuthorize` 绑定管理端权限。 - -这三个 Controller 都调用 `MetaApiVersionGuard.requireVersion(...)` 校验 `X-API-Version`,缺少版本头时不回退到旧合同兜底结果。 - -Meta 已有 dedicated Service / Guard / DAL: - -- `MetaSchemaServiceImpl` -- `MetaSchemaValidationServiceImpl` -- `MetaSchemaImpactPreviewServiceImpl` -- `ProtectionNodeServiceImpl` -- `FunctionChainServiceImpl` -- `MetaCommandServiceImpl` -- `MetaAuditServiceImpl` -- `MetaVersionStateGuard` -- `MetaProtectionNodeGuard` -- `FunctionChainGuard` -- Meta DO / Mapper / Convert 文件 - -Meta 旧合同兜底路由已有退役测试: - -- `AdminMuseMetaContractControllerTest.should_notKeepGenericMetaContractControllerOnClasspath` - -### 已有状态机、幂等、审计与失败边界证据 - -`MetaSchemaServiceImpl` 已覆盖: - -- save draft -- publish -- activate -- rollback -- deprecate -- gray rules -- command reservation -- command replay -- governance audit - -`MetaCommandServiceImpl` 已覆盖: - -- `reserveCommand(...)` -- `recordSucceeded(...)` -- request hash -- operation / actor / target envelope 校验 -- 未完成 command 冲突 -- succeeded command replay - -`MetaAuditServiceImpl` 已把治理审计写入 `muse_meta_governance_audit`,并有测试证明审计不是复用 command `resultSnapshot` 冒充。 - -`MetaSchemaImpactPreviewServiceImpl` 依赖 `MetaImpactFacade`;默认 `UnavailableMetaImpactFacade` 在没有真实外部 owner bean 时抛出 `META_EXTERNAL_OWNER_UNAVAILABLE`,不是空成功。 - -### 已有 SQL 与测试证据 - -Meta 已有 migration: - -- `muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql` - -V10 追加: - -- `muse_meta_command` -- `muse_meta_governance_audit` -- `muse_meta_validation_result` -- `muse_meta_impact_preview` -- `muse_meta_gray_rule` -- `muse_meta_function_chain_version` -- `muse_meta_function_chain_slot` -- `muse_meta_function_chain_node` -- `uk_muse_meta_schema_version_active_schema_id` -- `muse_meta_protection_node` 结构化字段 - -Meta 已有静态 SQL gate: - -- `P1rMetaMigrationSqlTest`,3 个 `@Test`。 - -Meta 已有 coverage gate: - -- `P1rMetaRealApiGateTest`,4 个 `@Test`。 - -该 gate 当前明确禁止 Meta operation 标记为 completed,并要求 Meta 保持 `dedicated / needs_verification`。 - -Meta 模块现有测试清单包含 16 个测试类,按源码 `@Test` 统计共 165 个测试方法;P1R Meta gate 额外包含 7 个测试方法。 - -## 推断 - -- Meta 是剩余四个业务域中最适合先走 completed approval 的候选,因为 operation 数量最少,主要围绕 Meta owner 自有治理事实,外部依赖比 Account / Market / Content 少。 -- Meta 可以作为 domain-level approval 候选,但只有在后续执行版补齐真实 `_test`、XML 防空跑、审批后 coverage gate 和运行态失败路径证据后才成立。 -- 当前 `P1rMetaRealApiGateTest` 的旧口径会阻止 Meta completed;后续用户批准前不能修改该 gate,用户批准后必须把它改成“只允许 Meta 16 个 operation completed,且不连带其它 domain”的审批后门禁。 - -## 假设 - -- 用户确认先从 Meta 推进剩余业务域 completed approval。 -- 后续执行版允许在 fresh 双 review 与用户明确批准之后,修改 scanner、coverage report、`P1rMetaRealApiGateTest`、`P1rApiCoverageReportTest` 和必要的 Meta completed approval gate 测试。 -- 如果真实 `_test` 或外部 owner fail-closed 证据证明某个 Meta operation 不满足 completed,用户接受把 Meta 从 domain-level approval 降级为 operation-level approval。 - -## 主要缺口 - -### 缺口 1:真实 Flyway `_test` 尚未覆盖 V10 - -当前只有 `P1rMetaMigrationSqlTest` 静态读取 V10 SQL。它证明 SQL 文本包含关键表、索引和字段,但不证明真实 PostgreSQL 可以执行,也不证明约束、partial unique index、trigger 和非法写入拒绝实际生效。 - -后续执行版必须新增真实 PostgreSQL `_test`,最低验证: - -- schema version 到达 V10 或当前全量迁移目标中包含 V10。 -- V10 追加表真实存在。 -- `muse_meta_command` 的 `(tenant_id, command_id)` unique 生效。 -- `muse_meta_schema_version` 的 active partial unique index 使用 `(tenant_id, schema_id)` 且同 schema 不能有两个 active 版本。 -- `muse_meta_function_chain_version` 的 active partial unique index 生效。 -- `muse_meta_protection_node` 结构化字段真实存在。 -- V10 表的 `updated_at` trigger 可执行。 -- 代表性非法 insert 被拒绝,例如重复 commandId、重复 active schema version、重复 active function chain version、必填字段为空。 - -### 缺口 2:当前 P1R gate 仍明确禁止 Meta completed - -`P1rMetaRealApiGateTest.should_not_mark_meta_operations_as_completed` 和 `should_keep_current_meta_operations_dedicated_and_needs_verification` 当前是正确的防误升门禁。 - -后续执行版必须把 gate 修改分成两步: - -1. 用户批准前:保持该 gate 不变,证明 Meta 仍未 completed。 -2. 用户批准后:改为 completed approval gate,精确断言 Meta 16 个 operation 为 `dedicated / completed`,并断言 Account / Market / Content 仍为 `dedicated / needs_verification`。 - -### 缺口 3:外部 owner impact preview 需要明确 completed 口径 - -`MetaSchemaImpactPreviewServiceImpl` 调用 `MetaImpactFacade`。默认 `UnavailableMetaImpactFacade` 抛出 `META_EXTERNAL_OWNER_UNAVAILABLE`。 - -这说明当前系统没有伪造外部 owner 成功,但也意味着 completed approval 必须选择清楚: - -- 如果 `previewMetaSchemaDraftImpact` 要算 completed,必须证明有真实 `MetaImpactFacade` 或明确接受 fail-closed 是该 operation 的完成口径。 -- 如果真实外部 owner 不可用且该 operation 需要成功预览外部影响,必须保持该 operation `needs_verification`,不能把 bare mock 或空 summary 当 completed。 - -### 缺口 4:需要把 165 个 Meta 测试变成可审计 XML 证据 - -历史留痕记录 Meta 模块全量测试曾通过 165 个测试,但 completed approval 执行必须重新运行当前代码,并读取 Surefire XML 逐类证明: - -```text -tests > 0 -failures = 0 -errors = 0 -skipped = 0 -``` - -后续执行版必须列出 required test classes 和最低测试数,不能只写 Maven exit 0。 - -### 缺口 5:scanner/report 修改必须是审批后动作 - -Meta completed approval 需要修改 scanner 的 completed allowlist 或 approval 模型,并重生成 coverage report。 - -这些动作只能在执行版 fresh 双 review 且用户明确批准后执行。用户批准前 protected diff 必须证明以下文件没有 staged/unstaged diff: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## 推荐审批粒度 - -推荐先按 domain-level approval 设计 Meta 执行版: - -```text -approved_completed_domains += {"meta"} -``` - -但执行版必须提供降级机制: - -- 如果全部 16 个 operation 证据闭合,允许 domain-level approval。 -- 如果 `previewMetaSchemaDraftImpact` 或其它高风险 operation 因外部 owner 不满足 completed 标准,必须降级为 operation-level approval。 -- 降级时 scanner/report/gate 只能推进证据满足的 operation,不得把 Meta 整域加入 completed domain allowlist。 - -## 非目标 - -- 不修改 `docs/api-contracts/meta/openapi.yaml`。 -- 不修改任何其它 OpenAPI 文件。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.md`。 -- 不修改 Meta 业务实现代码。 -- 不推进 Account / Market / Content completed。 -- 不推进总 P1R completed。 -- 不把 P1R-7 Events completed approval 或 source owner propagation evidence 当作 Meta completed 证据。 - -## 后续执行版必须包含 - -1. Meta 16 operation 的完整清单与目标状态。 -2. 允许修改文件清单;至少区分用户批准前和批准后。 -3. scanner approval 模型:domain-level Meta 或 operation-level fallback,二选一并写明触发条件。 -4. `P1rMetaRealApiGateTest` 从禁止 completed 到审批后 completed gate 的修改策略。 -5. `P1rApiCoverageReportTest` summary 期望从 `completed=101 / needsVerification=132` 到目标值的修改策略。 -6. 真实 PostgreSQL Flyway `_test`:目标 schema version、V10 表、索引、约束、trigger、非法 insert 拒绝。 -7. Meta focused tests:16 个测试类逐类 XML 计数下限。 -8. P1R mixed gates:包含 Meta gate、coverage report gate,以及用于证明非目标 domain 不被推进的 Account / Market / Content gate。 -9. `/tmp/p1r-meta-completed-approval-scan.*` 隔离 scanner 预验证。 -10. 真实 worktree scanner/report 重生成只允许在执行版双 PASS 和用户批准后执行。 -11. protected diff gate,必须覆盖 7 个 OpenAPI、scanner 和 coverage report。 -12. allowed-diff gate,必须覆盖 unstaged / staged / untracked,并使用 `core.quotePath=false`。 -13. implementation review 复核项:新增 completed 只覆盖用户批准范围;非目标 domain 状态不变;XML 防空跑和 `_test` 证据完整。 -14. rollback 策略:撤回 scanner allowlist、coverage report、gate test 和新增 `_test`,恢复 Meta 为 `dedicated / needs_verification`。 - -## 验收标准 - -本审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-10-P1RMetaCompletedApproval审阅版.md`。 -2. `.agent` 记录 Meta completed approval 审阅版状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report 无 diff。 -5. 本审阅版通过 fresh spec/scope review 和 fresh quality/feasibility review。 - -## 待确认项 - -1. 是否确认 Meta 作为剩余业务域 completed approval 第一候选。 -2. 是否确认先按 Meta domain-level approval 设计执行版,并保留 operation-level fallback。 -3. 是否确认用户批准前不得修改 scanner、coverage report、coverage gate 或 Meta 业务代码。 -4. 是否确认真实 Flyway `_test` 是 Meta completed approval 的硬门槛。 diff --git a/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md b/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md deleted file mode 100644 index d5f5bc54..00000000 --- a/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md +++ /dev/null @@ -1,333 +0,0 @@ -# P1R Meta Remaining 5 Completed Approval 审阅版 - -日期:2026-06-10 - -## 结论 - -推荐把 Meta 剩余 5 个 `dedicated / needs_verification` operation 作为下一段 completed approval 候选,但当前不建议直接推进 `completed`。 - -本审阅版只冻结范围、证据缺口、推荐审批粒度和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务代码,不把剩余 5 个 Meta operation 或其它 domain 推进 `completed`。 - -推荐审批策略: - -```text -operation-level approval for Meta remaining 5 -``` - -原因是 MetaSchema 11 个 operation 已完成 operation-level approval;剩余 5 个 operation 分属 ProtectionNode 和 FunctionChain,证据形态不同。如果改成 Meta domain-level approval,会把两类治理读模型和功能链写命令一次性绑定,风险比继续 operation-level approval 更高。 - -## 审批路径 - -```mermaid -flowchart TB - Start["当前 coverage
Meta 11 completed + 5 needs_verification"] --> Review["本审阅版
冻结剩余 5 个 operation 范围"] - Review --> FreshReview["fresh spec/scope review
fresh quality/feasibility review"] - FreshReview --> ExecDoc["执行版
列出证据、门禁、allowed diff"] - ExecDoc --> ExecReview["执行版 fresh 双 review"] - ExecReview --> UserApproval{"用户明确批准
剩余 5 个 operation completed?"} - UserApproval -->|否| Stop["保持 needs_verification"] - UserApproval -->|是| Implement["只按执行版允许路径修改
scanner / gate / report / 必要测试"] - Implement --> Verify["focused tests + P1R gates
Flyway _test + XML 防空跑
protected / allowed diff"] - Verify --> ImplReview["fresh implementation 双 review"] - ImplReview --> FinalApproval{"用户批准提交和 push?"} - FinalApproval -->|否| LocalOnly["保留本地 evidence"] - FinalApproval -->|是| Commit["提交并 push"] -``` - -## 已验证事实 - -### 工作区与当前状态 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- 当前本地与 `origin/dev/1.0.0` 对齐。 -- 当前 HEAD: - -```text -16e9f4e test(p1r): 收口 Meta completed approval 门禁 -``` - -当前 coverage summary: - -```text -totalOperations 233 -completedOperations 112 -needsVerificationOperations 121 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -Meta 当前状态: - -```text -meta dedicated/completed:11 -meta dedicated/needs_verification:5 -``` - -### 本轮候选 operation - -本审阅版只覆盖以下 5 个 Meta operation: - -| operationId | method/path | 当前状态 | -| --- | --- | --- | -| `listProtectionNodes` | `GET /admin-api/muse/governance/protection-nodes` | `dedicated / needs_verification` | -| `getProtectionNode` | `GET /admin-api/muse/governance/protection-nodes/{nodeKey}` | `dedicated / needs_verification` | -| `listFunctionChains` | `GET /admin-api/muse/governance/function-chains` | `dedicated / needs_verification` | -| `previewFunctionChainImpact` | `POST /admin-api/muse/governance/function-chains/{chainKey}/impact-preview` | `dedicated / needs_verification` | -| `activateFunctionChainVersion` | `POST /admin-api/muse/governance/function-chains/{chainKey}/versions/{version}/activate` | `dedicated / needs_verification` | - -非候选范围: - -- 已 completed 的 MetaSchema 11 个 operation。 -- Account 33、Market 32、Content 51。 -- 总 P1R completed。 -- 任何 OpenAPI 合同修改。 -- 任何 Meta 业务实现补丁。 - -### 已有 dedicated 实现证据 - -ProtectionNode 已有 dedicated Controller / Service: - -- `AdminProtectionNodeController` -- `ProtectionNodeService` -- `ProtectionNodeServiceImpl` - -`AdminProtectionNodeController` 使用 `/muse/governance/protection-nodes` 类级路由,覆盖 list/detail 两个 operation,且使用 `@PreAuthorize("@ss.hasPermission('muse:meta:query')")`。Controller 调用 `MetaApiVersionGuard.requireVersion(...)`,缺少 `X-API-Version` 时不回退到旧合同兜底结果。 - -`ProtectionNodeServiceImpl` 只读取 `muse_meta_protection_node` 持久化事实和 `policySnapshot` 中的 `chainKey` 轻量关联,不调用 `MuseContractPersistenceService`。查询只返回 `status='active'` 的保护节点;detail 不存在时抛出 `META_PROTECTION_NODE_NOT_FOUND`。 - -FunctionChain 已有 dedicated Controller / Service: - -- `AdminFunctionChainController` -- `FunctionChainService` -- `FunctionChainServiceImpl` - -`AdminFunctionChainController` 使用 `/muse/governance/function-chains` 类级路由,覆盖 list / impact-preview / activate 三个 operation,且绑定管理端权限: - -- 查询:`muse:meta:query` -- 影响预览与激活:`muse:meta:function-chain` - -`FunctionChainServiceImpl` 已覆盖: - -- active version 读取。 -- open slot 与 protection node count 计算。 -- preview commandId / requestHash 幂等预占与成功回放。 -- targetVersion 解析。 -- validation result 和 impact preview 持久化。 -- activate commandId / requestHash 幂等回放。 -- published-only 激活。 -- expected active version 冲突检测。 -- validationResult / impactPreview 同源校验。 -- protection boundary violation 阻断。 -- active version 原子更新。 -- governance audit 与 command succeeded 记录。 - -### 已有保护边界证据 - -`FunctionChainGuard` 与 `MetaProtectionNodeGuard` 已声明保护节点不能被替换或降级: - -- `requireActivationAllowed(...)` 只允许 `published` 状态激活。 -- `requireNoProtectionBoundaryViolation(...)` 在影响预览发现保护边界违规时抛出 `META_PROTECTION_NODE_IMMUTABLE`。 -- `MetaProtectionNodeGuard.requireNotReplaced(...)` 禁止把 canonical 保护节点换成其它节点。 -- `MetaProtectionNodeGuard.requireNotDowngraded(...)` 禁止不可替换保护节点降级为可替换。 -- `MetaProtectionNodeGuard.requireFunctionChainNodePreservesProtection(...)` 统一 FunctionChain 编排下的保护边界语义。 - -`FunctionChainServiceImpl` 的 activation 流程在写 active version 前会调用 `requireProtectionBindingsPreserved(...)`,并基于 active / target version 的 node binding diff 阻断保护节点移除、解绑或降级。 - -### 已有 SQL 与测试证据 - -V10 migration 已包含 FunctionChain 和 ProtectionNode 相关结构: - -- `muse_meta_function_chain_version` -- `muse_meta_function_chain_slot` -- `muse_meta_function_chain_node` -- `uk_muse_meta_function_chain_version_active` -- `muse_meta_protection_node` 结构化字段追加 -- 对应 `updated_at` trigger - -当前只有静态 SQL gate: - -- `P1rMetaMigrationSqlTest`,3 个 `@Test`。 - -与剩余 5 个 operation 直接相关的现有测试: - -| 测试类 | `@Test` 数量 | -| --- | ---: | -| `ProtectionNodeServiceTest` | 3 | -| `FunctionChainServiceTest` | 12 | -| `AdminProtectionNodeControllerTest` | 7 | -| `AdminFunctionChainControllerTest` | 7 | -| `FunctionChainGuardTest` | 4 | -| `MetaProtectionNodeGuardTest` | 5 | - -`P1rMetaRealApiGateTest` 当前明确要求: - -- Meta 16 个 operation 都必须是 `dedicated`。 -- MetaSchema 11 个 operation 必须是 `completed`。 -- FunctionChain / ProtectionNode 5 个 operation 必须保持 `needs_verification`。 -- Meta 仍不是 16/16 completed。 - -### 保护面当前无 diff - -本审阅版写入前已只读检查: - -```bash -git -c core.quotePath=false diff --name-only -- docs/api-contracts muse-cloud/scripts docs/superpowers/reports -``` - -输出为空,说明 OpenAPI、scanner、coverage report 当前无 unstaged diff。 - -## 推断 - -- ProtectionNode 的两个 operation 是治理读模型,完成证据主要来自真实持久化、版本头、权限、active 过滤、detail not-found、结构化字段和非 generic fallback。 -- FunctionChain 的三个 operation 是治理读写混合,完成证据必须覆盖幂等、状态机、active partial unique、保护节点不可降级、validation / impact preview 同源校验、审计和命令回放。 -- 这 5 个 operation 可以继续 operation-level approval;不需要把 `meta` 加入 domain-level completed 白名单。 -- 如果执行版能证明 V10 真实 `_test`、FunctionChain active 唯一约束、ProtectionNode 结构化字段、XML 防空跑和 P1R mixed gates,剩余 5 个 operation 才具备进入用户批准点的条件。 - -## 假设 - -- 用户希望继续 P1R 主线,并优先收口 Meta 剩余 operation,而不是切到 Account / Market / Content。 -- 用户仍要求 completed 状态推进必须单独批准,不能由 dedicated gate、review PASS 或文档结论自动推进。 -- 如果真实 `_test` 或 focused tests 发现 FunctionChain / ProtectionNode 证据不足,用户接受继续保持对应 operation `needs_verification`。 - -## 主要缺口 - -### 缺口 1:真实 Flyway `_test` 尚未证明 V10 FunctionChain / ProtectionNode 约束 - -现有 `P1rMetaMigrationSqlTest` 只做静态 SQL 文本检查。后续执行版必须新增真实 PostgreSQL `_test`,最低验证: - -- schema version 到达至少 V10。 -- `muse_meta_function_chain_version`、`muse_meta_function_chain_slot`、`muse_meta_function_chain_node` 真实存在。 -- `muse_meta_function_chain_version` 的 `(tenant_id, chain_id, version_no)` unique 生效。 -- `uk_muse_meta_function_chain_version_active` partial unique 生效,同 tenant / chain 不能有两个 active 版本。 -- `muse_meta_protection_node` 的结构化字段真实存在。 -- FunctionChain / ProtectionNode 相关 `updated_at` trigger 可执行。 -- 代表性非法 insert 被拒绝,例如重复 active function chain version、缺少必填字段、重复 chain version。 - -### 缺口 2:FunctionChain 影响预览仍使用 Meta owner 自有影响摘要 - -`FunctionChainServiceImpl` 当前对外部 AI runtime / New-API / SSE 影响使用固定 0 和 `externalRuntime=pending` 摘要,没有接入真实外部 owner。 - -这不一定阻塞 completed,但执行版必须明确 completed 口径: - -- 如果 `previewFunctionChainImpact` 的完成定义是 Meta owner 自有链路影响预览,则必须证明 pending 摘要是设计边界,不是伪成功。 -- 如果完成定义要求真实外部 runtime 影响,则 `previewFunctionChainImpact` 必须继续保持 `needs_verification`。 - -### 缺口 3:FunctionChain activation 需要强证据证明保护边界不可绕过 - -已有 `FunctionChainGuard` 与 `MetaProtectionNodeGuard`,但 completed approval 需要当前代码的 XML 证据和 P1R gate 明确覆盖: - -- `published` 以外版本不能激活。 -- expected active version 不匹配时拒绝。 -- validation result 与 impact preview 不同源时拒绝。 -- impact preview 包含 protection boundary violation 时拒绝。 -- active 版本切换时旧 active 被关闭,新 active 唯一。 -- commandId / requestHash 成功回放不重复写审计或状态。 - -### 缺口 4:ProtectionNode 查询需要证明不是静态配置或 generic fallback - -执行版必须要求测试证明: - -- list 从 `muse_meta_protection_node` 读取 active 节点。 -- `chainKey` 过滤来自 `policySnapshot` 中的 `chainKey`。 -- detail 返回结构化治理字段。 -- missing node 返回 `META_PROTECTION_NODE_NOT_FOUND`。 -- Controller 缺版本头或未知版本时不调用 Service。 -- Controller 不注入 `MuseContractPersistenceService`。 - -### 缺口 5:coverage gate 仍明确禁止剩余 5 个 operation completed - -`P1rMetaRealApiGateTest` 当前是正确的防误升门禁。执行版必须把 gate 修改分成两步: - -1. 用户批准前:保持剩余 5 个 operation 为 `needs_verification`。 -2. 用户批准后:只把这 5 个 operation 推进 `completed`,并断言 Account / Market / Content 仍保持 `dedicated / needs_verification`。 - -### 缺口 6:需要 XML 防空跑证据 - -后续执行版必须列出 required test classes,并读取 Surefire XML 证明: - -```text -tests > 0 -failures = 0 -errors = 0 -skipped = 0 -``` - -最低应覆盖: - -- `ProtectionNodeServiceTest` -- `FunctionChainServiceTest` -- `AdminProtectionNodeControllerTest` -- `AdminFunctionChainControllerTest` -- `FunctionChainGuardTest` -- `MetaProtectionNodeGuardTest` -- `P1rMetaRealApiGateTest` -- `P1rApiCoverageReportTest` -- 新增或修订的 Meta Flyway `_test` - -## 推荐审批粒度 - -推荐继续 operation-level approval: - -```text -APPROVED_COMPLETED_OPERATIONS += { - "meta:listProtectionNodes", - "meta:getProtectionNode", - "meta:listFunctionChains", - "meta:previewFunctionChainImpact", - "meta:activateFunctionChainVersion" -} -``` - -不推荐本轮把 `meta` 加入 completed domain allowlist,原因: - -- MetaSchema 11 已经走 operation-level approval。 -- 剩余 5 个 operation 的证据集中在 FunctionChain / ProtectionNode,不需要覆盖未来 Meta 新 operation。 -- operation-level approval 能精确防止未来 Meta operation 误继承 completed。 - -## 非目标 - -- 不修改 `docs/api-contracts/meta/openapi.yaml`。 -- 不修改任何其它 OpenAPI 文件。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.md`。 -- 不修改 Meta 业务实现代码。 -- 不推进 Account / Market / Content completed。 -- 不推进总 P1R completed。 -- 不把 MetaSchema 11 个 operation 的 completed 证据扩写成剩余 5 个 operation 的证据。 - -## 后续执行版必须包含 - -1. 剩余 5 个 operation 的完整清单与目标状态。 -2. 用户批准前和批准后的允许修改文件清单。 -3. scanner approval 模型:只允许新增 5 个 operation-level approval。 -4. `P1rMetaRealApiGateTest` 从“剩余 5 个必须 needs_verification”改为“剩余 5 个批准后必须 completed”的策略。 -5. `P1rApiCoverageReportTest` summary 从 `completed=112 / needsVerification=121` 到 `completed=117 / needsVerification=116` 的修改策略。 -6. 真实 PostgreSQL Flyway `_test`:FunctionChain / ProtectionNode 相关表、索引、约束、trigger 和非法 insert 拒绝。 -7. FunctionChain focused tests:list、preview、activate、replay、conflict、validation / impact preview 同源、protection boundary、audit。 -8. ProtectionNode focused tests:list、detail、active filter、chainKey filter、missing node、结构化字段、版本头和权限。 -9. P1R mixed gates:coverage report gate、Meta gate、Account / Market / Content 非目标 gate。 -10. XML 防空跑:逐类读取 surefire XML 并断言 tests > 0。 -11. protected diff gate:覆盖 7 个 OpenAPI、scanner 和 coverage report。 -12. allowed-diff gate:覆盖 unstaged / staged / untracked,并使用 `core.quotePath=false`。 -13. implementation review 复核项:新增 completed 只覆盖剩余 5 个 operation,Meta 最终 16/16 completed,Account / Market / Content 不变。 -14. rollback 策略:撤回 scanner allowlist、coverage report、gate test 和新增 `_test`,恢复剩余 5 个 operation 为 `dedicated / needs_verification`。 - -## 验收标准 - -本审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md`。 -2. `.agent` 记录 Meta 剩余 5 个 operation completed approval 审阅版状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report 无 diff。 -5. 本审阅版通过 fresh spec/scope review 和 fresh quality/feasibility review。 - -## 待确认项 - -1. 是否确认下一段优先处理 Meta 剩余 5 个 operation,而不是 Account / Market / Content。 -2. 是否确认继续使用 operation-level approval,不把 `meta` 加入 domain-level completed allowlist。 -3. 是否确认用户批准前不得修改 scanner、coverage report、coverage gate 或 Meta 业务代码。 -4. 是否确认真实 Flyway `_test` 是剩余 5 个 operation completed approval 的硬门槛。 diff --git a/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md b/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md deleted file mode 100644 index 8a5b8954..00000000 --- a/docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md +++ /dev/null @@ -1,590 +0,0 @@ -# P1R Meta Remaining 5 Completed Approval 执行版 - -日期:2026-06-10 - -## 结论 - -本执行版只为 Meta 剩余 5 个 operation 的 completed approval 提供可执行门禁方案。当前阶段只允许进入 fresh execution review,不允许实现,不允许修改 scanner / coverage report / OpenAPI / 业务代码,不允许推进任何 operation 到 `completed`。 - -审批对象只包含: - -```text -meta:listProtectionNodes -meta:getProtectionNode -meta:listFunctionChains -meta:previewFunctionChainImpact -meta:activateFunctionChainVersion -``` - -推荐继续使用 operation-level approval: - -```text -APPROVED_COMPLETED_OPERATIONS += remaining 5 Meta operations -APPROVED_COMPLETED_DOMAINS 不加入 meta -``` - -目标状态只允许从: - -```text -total=233 completed=112 needsVerification=121 incomplete=0 genericPersistence=0 ssePlaceholder=0 -``` - -推进到: - -```text -total=233 completed=117 needsVerification=116 incomplete=0 genericPersistence=0 ssePlaceholder=0 -``` - -Account 33、Market 32、Content 51 必须继续保持 `dedicated / needs_verification`。本执行版通过 review 不等于用户批准推进 completed。 - -## 前置事实 - -### 工作区 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- 当前 HEAD:`16e9f4e test(p1r): 收口 Meta completed approval 门禁`。 -- 当前 branch tracking 与 `origin/dev/1.0.0` 对齐;这不等于 working tree clean,因为当前已有本阶段文档改动。 - -### 已通过 review - -审阅版: - -```text -docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md -``` - -fresh review 结果: - -- Hegel scope review:PASS,无阻塞项。 -- Feynman feasibility review:PASS,无阻塞项。 - -已采纳非阻塞建议: - -- 本执行版避免把 branch up-to-date 与 working tree clean 混用。 -- 本执行版把批准前禁止修改与批准后 allowed diff 分阶段写清。 - -### 当前 coverage - -```text -totalOperations 233 -completedOperations 112 -needsVerificationOperations 121 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -Meta 当前状态: - -```text -meta dedicated/completed:11 -meta dedicated/needs_verification:5 -``` - -仍未 completed 的 5 个 operation: - -| operation key | method/path | -| --- | --- | -| `meta:listProtectionNodes` | `GET /admin-api/muse/governance/protection-nodes` | -| `meta:getProtectionNode` | `GET /admin-api/muse/governance/protection-nodes/{nodeKey}` | -| `meta:listFunctionChains` | `GET /admin-api/muse/governance/function-chains` | -| `meta:previewFunctionChainImpact` | `POST /admin-api/muse/governance/function-chains/{chainKey}/impact-preview` | -| `meta:activateFunctionChainVersion` | `POST /admin-api/muse/governance/function-chains/{chainKey}/versions/{version}/activate` | - -## 范围 - -### 目标 - -1. 用真实门禁证明剩余 5 个 Meta operation 可进入用户批准点。 -2. 在用户批准后,只把这 5 个 operation 加入 operation-level completed approval。 -3. 保持 MetaSchema 11 个 completed 不变。 -4. 保持 Account / Market / Content 仍为 `dedicated / needs_verification`。 -5. 保持总 P1R 不进入 completed。 - -### 非目标 - -- 不修改任何 OpenAPI。 -- 不把 `meta` 加入 `APPROVED_COMPLETED_DOMAINS`。 -- 不推进 Account / Market / Content。 -- 不推进总 P1R。 -- 不用 MetaSchema 11 个 operation 的 evidence 替代剩余 5 个 operation 的 evidence。 -- 不把 `previewFunctionChainImpact` 的 `externalRuntime=pending` 误写成真实外部 runtime completed。 - -## 阶段化允许改动 - -### 当前阶段:执行版 review 前 - -只允许: - -```text -docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md -docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md -docs/agent-specs/.agent -``` - -不得修改: - -```text -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/*.java -muse-cloud/muse-module-meta/** -docs/api-contracts/** -``` - -### 执行版双 PASS 且用户明确批准后 - -允许最小修改: - -```text -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/P1rMetaRealApiGateTest.java -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaFlywayMigrationIT.java -docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md -docs/agent-specs/.agent -docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md -``` - -当前 legacy P1R mixed gate 中,以下 4 个文件硬编码了 `completedOperations=112` 或 Meta `11 completed / 5 needs_verification`。如果用户只批准剩余 5 个 operation completed,但不批准同步这些 legacy gate 口径,implementation 必须停止,不能修改 scanner/report,也不能跳过 mixed gate。 - -只有在用户明确批准“同步 legacy P1R mixed gate 口径”后,才允许最小修改以下 4 个测试文件,修改范围仅限把 summary 和 Meta 状态断言同步到 `completedOperations=117`、`needsVerificationOperations=116`、Meta `16 completed / 0 needs_verification`,并继续断言 Account / Market / Content 不被连带推进: - -```text -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 -``` - -以下非目标 domain gate 文件只允许运行和读取 XML,不预置为本轮可修改范围: - -```text -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/P1rAccountRealApiGateTest.java -``` - -如果 implementation 发现还必须修改 `P1rContentRealApiGateTest`、`P1rAccountRealApiGateTest` 或其它任何非目标文件,必须先停下并说明原因,得到用户明确批准后才能扩大 allowed diff。 - -## Protected Diff Gate - -任何阶段都必须保护 7 个 OpenAPI: - -```bash -git -c core.quotePath=false diff --name-only -- \ - docs/api-contracts/account/openapi.yaml \ - docs/api-contracts/ai/openapi.yaml \ - docs/api-contracts/content/openapi.yaml \ - docs/api-contracts/events/openapi.yaml \ - docs/api-contracts/knowledge/openapi.yaml \ - docs/api-contracts/market/openapi.yaml \ - docs/api-contracts/meta/openapi.yaml - -git -c core.quotePath=false diff --cached --name-only -- \ - docs/api-contracts/account/openapi.yaml \ - docs/api-contracts/ai/openapi.yaml \ - docs/api-contracts/content/openapi.yaml \ - docs/api-contracts/events/openapi.yaml \ - docs/api-contracts/knowledge/openapi.yaml \ - docs/api-contracts/market/openapi.yaml \ - docs/api-contracts/meta/openapi.yaml -``` - -输出必须为空。 - -执行版双 PASS 且用户批准前,还必须保护 scanner/report: - -```bash -git -c core.quotePath=false diff --name-only -- \ - muse-cloud/scripts/p1r-audit-api-coverage.py \ - docs/superpowers/reports/p1r-api-coverage.json \ - docs/superpowers/reports/p1r-api-coverage.md - -git -c core.quotePath=false diff --cached --name-only -- \ - muse-cloud/scripts/p1r-audit-api-coverage.py \ - docs/superpowers/reports/p1r-api-coverage.json \ - docs/superpowers/reports/p1r-api-coverage.md -``` - -输出必须为空。 - -## Allowed Diff Gate - -最终 allowed diff 必须覆盖 unstaged / staged / untracked,并使用 `core.quotePath=false`,避免中文路径被转义导致误判。 - -批准前 allowed set: - -```bash -allowed='^(docs/agent-specs/\\.agent|docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版\\.md|docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版\\.md)$' -{ - 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 -} | sed '/^$/d' | sort -u | while IFS= read -r path; do - if ! printf '%s\n' "$path" | grep -Eq "$allowed"; then - echo "unexpected_diff=$path" - exit 1 - fi -done -echo "allowed_diff_gate=PASS" -``` - -用户批准后 allowed set 必须扩展到“阶段化允许改动”中的批准后文件;若用户同时明确批准 legacy P1R mixed gate 口径同步,allowed set 才能包含 `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`。任何额外路径都必须停止。 - -## Scanner 修改策略 - -只允许在用户批准后修改: - -```text -muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -允许新增: - -```python -"meta:listProtectionNodes", -"meta:getProtectionNode", -"meta:listFunctionChains", -"meta:previewFunctionChainImpact", -"meta:activateFunctionChainVersion", -``` - -禁止: - -```python -APPROVED_COMPLETED_DOMAINS = {"ai", "knowledge", "meta"} -``` - -completed 仍必须满足: - -- `implementationStatus == "dedicated"`。 -- operation key 在 approved domain 或 approved operation allowlist 内。 -- 非真实实现状态不能 completed。 - -## TDD 与状态推进顺序 - -### RED - -先修改 gate 预期,不改 scanner/report,确认失败: - -目标失败应来自旧 report 仍是: - -```text -completed=112 -needsVerification=121 -remaining Meta 5 = needs_verification -``` - -命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -预期: - -```text -BUILD FAILURE -``` - -失败断言应指向 summary `117/116` 或剩余 5 个 operation expected completed。 - -### GREEN - -用户批准后,修改 scanner allowlist 并重建 report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -预期 summary: - -```text -233 117 116 0 0 0 -``` - -Meta 预期: - -```text -meta dedicated/completed:16 -``` - -非目标 domain 预期: - -```text -account dedicated/needs_verification:33 -content dedicated/needs_verification:51 -market dedicated/needs_verification:32 -``` - -## 真实 Flyway `_test` - -新增测试文件: - -```text -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaFlywayMigrationIT.java -``` - -目标: - -- clean 隔离 PostgreSQL `_test` 数据库。 -- 使用 `filesystem:sql/muse`。 -- target 至少 V10。 -- 执行 V1-V10 SQL migrations。 -- 证明 FunctionChain / ProtectionNode schema 在真实 PostgreSQL 可用。 - -安全要求: - -- 只允许 JDBC database name 以 `_test` 结尾。 -- JDBC URL query 不允许携带 password/token/secret/api_key。 -- 密码只能从 `P1R_FLYWAY_PASSWORD` 或 `MUSE_POSTGRES_PASSWORD` 环境变量读取。 -- Surefire XML 中必须脱敏 URL / user。 - -最低断言: - -```text -target_schema_version=10 -successful_migration_count >= 10 -muse_meta_function_chain_version exists -muse_meta_function_chain_slot exists -muse_meta_function_chain_node exists -muse_meta_protection_node exists -uk_muse_meta_function_chain_version exists -uk_muse_meta_function_chain_version_active exists -trg_muse_meta_function_chain_version_updated_at exists -trg_muse_meta_function_chain_slot_updated_at exists -trg_muse_meta_function_chain_node_updated_at exists -ProtectionNode 结构化字段存在 -重复 function chain version 被拒绝 -重复 active function chain version 被拒绝 -缺少必填字段被拒绝 -updated_at trigger 可执行 -``` - -运行命令: - -```bash -cd muse-cloud -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rMetaFlywayMigrationIT \ - -Dp1r.flyway.url="$MUSE_POSTGRES_URL_FOR_META_REMAINING_5_TEST" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USER" \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dflyway.postgresql.transactional.lock=false \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -如果本地没有 `_test` 连接配置,不能伪造通过;必须记录 blocker。 - -## Focused Tests - -批准后至少运行: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server,muse-server -am \ - -Dtest=ProtectionNodeServiceTest,FunctionChainServiceTest,AdminProtectionNodeControllerTest,AdminFunctionChainControllerTest,FunctionChainGuardTest,MetaProtectionNodeGuardTest,P1rMetaMigrationSqlTest,P1rMetaFlywayMigrationIT \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -最低 XML 类清单: - -```text -ProtectionNodeServiceTest tests>=3 -FunctionChainServiceTest tests>=12 -AdminProtectionNodeControllerTest tests>=7 -AdminFunctionChainControllerTest tests>=7 -FunctionChainGuardTest tests>=4 -MetaProtectionNodeGuardTest tests>=5 -P1rMetaMigrationSqlTest tests>=3 -P1rMetaFlywayMigrationIT tests>=1 -``` - -每个 XML 必须: - -```text -failures=0 -errors=0 -skipped=0 -``` - -## P1R Mixed Gates - -批准后至少运行: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMetaRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rAccountRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -最低断言: - -- `P1rApiCoverageReportTest` 断言 completed summary 为 117。 -- `P1rMetaRealApiGateTest` 断言 Meta 16 个 operation 全部 `dedicated / completed`。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest` 若获用户批准同步口径,只能同步 summary 和 Meta 16/0 断言,并继续证明非目标 domain 不被推进。 -- Account / Market / Content gate 继续断言各自 `dedicated / needs_verification`。 -- AI / Knowledge / Events 既有 completed 状态不被破坏。 - -## `previewFunctionChainImpact` 口径 - -当前 `FunctionChainServiceImpl` 对外部 runtime 影响使用: - -```text -affectedAIRuntimeTasks=0 -externalRuntime=pending -``` - -执行时必须证明这是 Meta owner 自有 FunctionChain 影响预览的明确边界,而不是伪造外部 runtime 成功。 - -最低要求: - -- 测试证明 preview 持久化 `externalErrorSummary` 或等价摘要包含 pending 边界。 -- 测试证明 preview 只声明 Meta owner 可计算的 slot / protection node changes。 -- 文档或 memory 明确说明本轮不证明 AI runtime / New-API / SSE 外部影响闭环。 - -如果 review 或实现发现 `previewFunctionChainImpact` 的 completed 标准必须包含真实外部 runtime 影响,则该 operation 必须保持 `needs_verification`,本轮只能推进其余 operation。 - -## XML 防空跑 - -必须读取 surefire XML,不接受 Maven exit 0 单独作为通过证据。 - -建议命令: - -```bash -python3 - <<'PY' -from pathlib import Path -from xml.etree import ElementTree as ET - -required = { - "ProtectionNodeServiceTest": 3, - "FunctionChainServiceTest": 12, - "AdminProtectionNodeControllerTest": 7, - "AdminFunctionChainControllerTest": 7, - "FunctionChainGuardTest": 4, - "MetaProtectionNodeGuardTest": 5, - "P1rMetaMigrationSqlTest": 3, - "P1rMetaFlywayMigrationIT": 1, - "P1rApiCoverageReportTest": 1, - "P1rMetaRealApiGateTest": 1, - "P1rEventsRealApiGateTest": 1, - "P1rAiRealApiGateTest": 1, - "P1rKnowledgeRealApiGateTest": 1, - "P1rMarketRealApiGateTest": 1, - "P1rContentRealApiGateTest": 1, - "P1rAccountRealApiGateTest": 1, -} - -roots = [ - Path("muse-cloud/muse-module-meta/muse-module-meta-server/target/surefire-reports"), - Path("muse-cloud/muse-server/target/surefire-reports"), -] - -for class_name, minimum in required.items(): - matches = [] - for root in roots: - matches.extend(root.glob(f"TEST-*{class_name}.xml")) - if not matches: - raise SystemExit(f"missing_xml={class_name}") - element = ET.parse(matches[0]).getroot() - tests = int(element.attrib.get("tests", "0")) - failures = int(element.attrib.get("failures", "0")) - errors = int(element.attrib.get("errors", "0")) - skipped = int(element.attrib.get("skipped", "0")) - if tests < minimum or failures or errors or skipped: - raise SystemExit( - f"bad_xml={class_name} tests={tests} failures={failures} errors={errors} skipped={skipped}" - ) - print(f"{class_name}\t{tests}\tPASS") -PY -``` - -## Coverage 验证 - -批准后 scanner 必须真实重建 report: - -```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=="meta") | [.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 117 116 0 0 0 -``` - -Meta 16 行均为: - -```text -dedicated completed -``` - -Account / Market / Content 聚合必须保持: - -```text -account dedicated/needs_verification:33 -market dedicated/needs_verification:32 -content dedicated/needs_verification:51 -``` - -## Memory 留痕 - -批准后实现完成且验证通过,新增: - -```text -docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md -``` - -必须记录: - -- 推进前 summary:`233/112/121/0/0/0`。 -- 推进后 summary:`233/117/116/0/0/0`。 -- 新增 completed 仅限剩余 5 个 Meta operation。 -- 没有修改 OpenAPI。 -- 没有把 `meta` 加入 domain-level completed allowlist。 -- Account / Market / Content 未推进。 -- 真实 Flyway `_test`、focused tests、P1R mixed gates、XML 防空跑、protected diff、allowed-diff 证据。 - -## Rollback - -如果实现后发现证据不足或 review FAIL: - -1. 从 `APPROVED_COMPLETED_OPERATIONS` 移除剩余 5 个 operation。 -2. 重跑 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check`,恢复 summary 到 `233/112/121/0/0/0`。 -3. 恢复 `P1rApiCoverageReportTest` 与 `P1rMetaRealApiGateTest` 对剩余 5 个 operation `needs_verification` 的断言。 -4. 删除或保留但禁用新增 `P1rMetaFlywayMigrationIT` 需由用户决定;不能让失败 `_test` 留在 required gate 中。 -5. memory 记录 rollback 原因。 -6. 不提交失败状态。 - -## 完成条件 - -执行版当前阶段完成条件: - -1. 本文件写入。 -2. `.agent` 追加执行版状态。 -3. `git diff --check` 通过。 -4. OpenAPI / scanner / coverage report 无 diff。 -5. fresh execution spec/scope review PASS。 -6. fresh execution quality/feasibility review PASS。 - -双 PASS 后仍需用户明确批准以下事项,才允许实现: - -1. 只审批剩余 5 个 Meta operation。 -2. 继续 operation-level approval,不把 `meta` 加入 domain-level allowlist。 -3. 允许按本执行版修改 scanner、coverage report、P1R gate tests、新增 Flyway `_test` 和 memory。 -4. 允许同步 4 个 legacy P1R mixed gate 文件的 summary / Meta 状态断言:`P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`。未批准该项时不得启动实现。 diff --git a/docs/agent-specs/2026-06-10-P1RRemainingDomainCompletedApproval审阅版.md b/docs/agent-specs/2026-06-10-P1RRemainingDomainCompletedApproval审阅版.md deleted file mode 100644 index b22a18cb..00000000 --- a/docs/agent-specs/2026-06-10-P1RRemainingDomainCompletedApproval审阅版.md +++ /dev/null @@ -1,427 +0,0 @@ -# P1R Remaining Domain Completed Approval 审阅版 - -日期:2026-06-10 - -## 结论 - -推荐把 P1R 下一阶段定义为: - -```text -P1R Remaining Domain Completed Approval -``` - -该阶段不是继续 P1R-7,也不是直接进入一个已经存在的 P1R-8 实现阶段;它的目标是为仍处于 `dedicated / needs_verification` 的四个业务域建立 completed approval 总方案: - -- Meta 16 operations -- Account 33 operations -- Market 32 operations -- Content 51 operations - -本审阅版只冻结总路线、审批边界、证据标准和推荐推进顺序。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务代码,也不把任何剩余业务域推进 `completed`。 - -推荐推进顺序: - -```text -Meta -> Account -> Market -> Content -``` - -理由是 Meta 体量最小、外部依赖最少,适合作为 domain-level completed approval 模板;Account 和 Market 分别牵涉 New-API/FileService、授权/安装/治理投影;Content 最大且跨 AI/Knowledge/Meta/FileService/Export/Import/Parse/Planning 等 owner,适合最后按模板分片验收。 - -## 审批主线 - -```mermaid -flowchart TB - Start["当前 HEAD
P1R-7 Events completed approval 已推送"] --> Remaining["剩余 needs_verification domains
Meta / Account / Market / Content"] - Remaining --> Review["总审阅版
冻结证据标准与顺序"] - Review --> DomainSpec{"按域单独审阅?"} - DomainSpec -->|Meta| Meta["P1R-Meta Completed Approval
16 operations"] - DomainSpec -->|Account| Account["P1R-Account Completed Approval
33 operations"] - DomainSpec -->|Market| Market["P1R-Market Completed Approval
32 operations"] - DomainSpec -->|Content| Content["P1R-Content Completed Approval
51 operations"] - Meta --> DomainGate["每域执行版 + fresh 双 review
用户批准后才改 coverage"] - Account --> DomainGate - Market --> DomainGate - Content --> DomainGate - DomainGate --> Scanner["operation/domain allowlist
scanner + report + gates"] - Scanner --> Done{"四域均完成?"} - Done -->|否| NextDomain["继续下一个 domain"] - Done -->|是| P1RApproval["总 P1R completed approval 候选
仍需单独审批"] -``` - -## 已验证事实 - -### 工作区与远端 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 当前分支:`dev/1.0.0`。 -- 写入本文档前已执行: - -```bash -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -``` - -结果: - -```text -## dev/1.0.0...origin/dev/1.0.0 -85c5422 test(p1r): 收口 P1R-7 completed approval 门禁 -Already up to date. -``` - -### 当前 coverage 状态 - -当前 `docs/superpowers/reports/p1r-api-coverage.json` summary: - -```text -totalOperations 233 -completedOperations 101 -needsVerificationOperations 132 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -missingOperations 0 -blockedOperations 0 -``` - -当前按 domain 聚合: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/completed:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -已 completed 的范围: - -- AI 41 operations。 -- Knowledge 59 operations。 -- Events `streamEvents / GET /app-api/muse/events` 1 operation。 - -仍未 completed 的范围: - -- P1R-1 Content Real API:51 operations。 -- P1R-2 Meta Real API:16 operations。 -- P1R-3 Account Real API:33 operations。 -- P1R-6 Market Real API:32 operations。 - -### 既有阶段结论 - -P1R 总规格定义的 completed 标准不是“路由存在”或“dedicated Controller 存在”,而是“真实业务行为、状态机、持久化、外部闭环、审计、测试与真实环境验收证据”。 - -P1R-1 Content 留痕明确: - -- Content 51 operations 已退出 catch-all / generic persistence。 -- 当前状态只是 `dedicated / needs_verification`。 -- 仍缺真实 PostgreSQL/Flyway 集成、外部 owner facade 与端到端验收。 - -P1R-2 Meta 留痕明确: - -- Meta 16 operations 已进入 dedicated Controller / Service。 -- 当前状态只是 `dedicated / needs_verification`。 -- 当时未运行真实 PostgreSQL / Flyway 迁移验证,外部 runtime owner 仍 pending。 - -P1R-3 Account 留痕明确: - -- Account 33 operations 已进入 dedicated route / service / DTO / DAL。 -- PostgreSQL / Flyway V11 已在同实例 test 库验证。 -- New-API runtime、FileService、导出下载真实交付与跨域 runtime 仍未验收,因此不能 completed。 - -P1R-6 Market 留痕明确: - -- Market 32 operations 已从 generic persistence 推进到 `dedicated / needs_verification`。 -- Market completed 不等于治理事件 propagation completed。 -- 仍需证明购买、安装、handoff、授权消费、治理与账户聚合等真实用户流。 - -P1R-7 completed approval 留痕明确: - -- 只把 `events:streamEvents` 从 `dedicated / needs_verification` 推进为 `dedicated / completed`。 -- 没有把 Market / Account / Content / Meta 推进 completed。 -- 没有把总 P1R 写成 completed。 - -## 推断 - -- 当前主线瓶颈已经从“事件流是否可见”转为“剩余业务域是否具备真实业务 completed 证据”。 -- 下一阶段不应再扩展 P1R-7 source owner propagation;P1R-7 已完成的是 Events SSE operation-level approval。 -- 剩余四域不能用一次总批准直接推进 completed,因为它们的外部依赖、状态机、数据面和用户旅程差异较大。 -- 最稳妥路线是先冻结总标准,再按 domain 分批写审阅版 / 执行版 / fresh review / 用户批准 / scanner-report-gate 修改。 - -## 假设 - -- 用户希望继续 P1R 主线,而不是切换到前端 P2/P3 或新功能开发。 -- 用户仍要求 coverage completed 必须基于真实证据和单独批准,不能由 dedicated gate、review PASS 或文档推断自动推进。 -- 后续每个 domain 的 completed approval 都允许修改 scanner、coverage report 和 coverage gate,但必须先通过该 domain 的执行版 review,并在用户明确批准后执行。 - -## 推荐方案 - -### 方案 A:按 domain 分批 completed approval - -每个剩余业务域单独走: - -1. 审阅版:冻结该 domain 的 operation 列表、证据缺口、非目标、审批边界。 -2. fresh spec/scope review + fresh quality/feasibility review。 -3. 执行版:列出可验证证据、scanner/report/gate 修改方式、allowed-diff、rollback。 -4. fresh execution spec review + fresh execution quality review。 -5. 用户明确批准该 domain completed 状态推进。 -6. 修改 scanner / coverage gate / report。 -7. 运行 focused tests、P1R gates、真实环境或 `_test` gate、protected diff、allowed-diff。 -8. fresh implementation 双 review。 -9. 用户批准后提交和 push。 - -推荐选择该方案。 - -理由: - -- 保持最小审批面。 -- 每个 domain 的缺口可以独立验证。 -- 不会因为某个 domain 证据充分而误推进其它 domain。 -- 与 AI / Knowledge / Events 的既有 approval 模型一致。 - -### 方案 B:一次性总 P1R completed approval - -一次性把 Content / Meta / Account / Market 全部纳入一个总执行版,并统一推进 completed。 - -不推荐。 - -理由: - -- 132 个 remaining operation 风险面过大。 -- Content / Account / Market 的外部链路差异明显,容易出现证据稀释。 -- 一次性 scanner/report 修改难以证明每个 operation 的证据归属。 -- 任何一个 domain 的 blocker 都会阻塞整批推进。 - -### 方案 C:按 operation 极细粒度推进 - -对剩余 132 个 operation 逐个 operation-level approval。 - -暂不推荐作为主线默认方案。 - -理由: - -- 最精确,但成本过高。 -- 对 Meta 这种小域会造成过度流程开销。 -- 更适合在某个 domain 内存在少数高风险 operation 不能与其它 operation 一起批准时局部采用。 - -## 推荐推进顺序 - -### 第一阶段:Meta Completed Approval - -候选范围: - -```text -meta 16 dedicated/needs_verification -``` - -推荐先做 Meta,因为: - -- operation 数量最少。 -- 主要围绕 MetaSchema、保护节点、功能链治理。 -- 外部依赖比 Account / Market / Content 少。 -- 更适合建立 completed approval 的 domain 模板。 - -Meta 审阅版必须重点证明: - -- V10 或后续 Meta migration 在真实 `_test` 库可执行。 -- MetaSchema draft / validate / impact preview / publish / activate / rollback / deprecate / gray-rules 有真实状态机、版本约束和审计证据。 -- 保护节点不能被降级为用户可替换槽位。 -- 管理端权限、tenant 隔离和错误码路径可验。 -- 没有把 Content / Account / Market 连带推进。 - -### 第二阶段:Account Completed Approval - -候选范围: - -```text -account 33 dedicated/needs_verification -``` - -Account 必须在 completed 前补足: - -- New-API binding / recheck / quota request / integration call 的真实 runtime 或明确验收替代边界。 -- call attribution 基于真实 integration call / correlation 的查询与归因证据。 -- FileService 或对象存储导出下载真实交付证据。 -- security event / acknowledge / profile / entitlement / usage / purchase / license / publish projection 的 owner 与 tenant 隔离。 -- P1R-7e Account quota adjustment Events propagation 只能作为事件可见证据,不能替代 Account API completed 证据。 - -### 第三阶段:Market Completed Approval - -候选范围: - -```text -market 32 dedicated/needs_verification -``` - -Market 必须在 completed 前补足: - -- 资产发布、审核、驳回、下架、召回、申诉的真实状态机和审计。 -- 购买、安装、bind-precheck、handoff、目标 owner 授权消费链路。 -- Account projection 与 Market installation / purchase / license 的一致性。 -- P1R-7d Market governance Events propagation 只能作为治理事件可见证据,不能替代 Market API completed 证据。 -- `needs_recheck` 仍不能作为 SourceStatus 值误用。 - -### 第四阶段:Content Completed Approval - -候选范围: - -```text -content 51 dedicated/needs_verification -``` - -Content 放最后,因为它覆盖面最大: - -- 作品、章节、Block、结构编辑、来源归因。 -- planning、style check、candidate confirm/discard。 -- import / parse / batch confirm / parse retry。 -- export / export task / download。 -- meta projection。 -- suggestion merge。 -- admin risk action / import task / export task / work governance。 - -Content completed 前必须证明: - -- 核心写命令、幂等、revision、owner guard、审计和 DTO 响应。 -- import / parse / export / download 的真实文件或对象存储闭环。 -- AI suggestion / Knowledge draft / Meta projection / FileService 等外部 owner 边界。 -- P1R-7f `saveBlock` source owner propagation 只覆盖一个事件切片,不能替代全部 Content 51 operations completed。 - -## 非目标 - -- 不修改 `docs/api-contracts/**/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.md`。 -- 不修改业务实现代码。 -- 不把 Meta / Account / Market / Content 任何 operation 推进 `completed`。 -- 不把总 P1R 标记为 completed。 -- 不把 P1R-7 source owner propagation evidence 当作业务域 completed 证据。 - -## 全域 anti-fake-green 基线 - -后续任一 domain completed approval 都必须继承以下硬基线。执行版不得把这些基线留给 implementer 临场决定。 - -### XML 防空跑 - -每个 domain 执行版必须列出所有 required test class,并为每个 class 指定最低 `tests` 数。 - -最终验证必须读取对应 Surefire XML,逐类断言: - -```text -tests > 0 -failures = 0 -errors = 0 -skipped = 0 -``` - -如果新增测试类、拆分测试类或修改 `-Dtest` 列表,执行版必须同步更新 XML 计数下限。不得只依赖 Maven exit 0 或 `surefire.failIfNoSpecifiedTests=false`。 - -### Allowed diff gate - -每个 domain 执行版必须列出允许修改的文件清单,并用同一个 gate 覆盖: - -```bash -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 -``` - -最终 `git status --short --untracked-files=all` 只能出现 allowed 路径。中文路径必须使用 `core.quotePath=false`,避免八进制转义导致误判。 - -### Protected diff gate - -在用户明确批准真实 coverage 状态推进前,以下文件 staged 与 unstaged diff 必须为空: - -```text -docs/api-contracts/account/openapi.yaml -docs/api-contracts/ai/openapi.yaml -docs/api-contracts/content/openapi.yaml -docs/api-contracts/events/openapi.yaml -docs/api-contracts/knowledge/openapi.yaml -docs/api-contracts/market/openapi.yaml -docs/api-contracts/meta/openapi.yaml -muse-cloud/scripts/p1r-audit-api-coverage.py -docs/superpowers/reports/p1r-api-coverage.json -docs/superpowers/reports/p1r-api-coverage.md -``` - -执行版必须内联 `git diff --quiet -- ...` 与 `git diff --cached --quiet -- ...` 命令。若某个 domain 的 completed approval 需要修改 scanner/report/gate,必须先在执行版写明允许路径、修改时机和回滚策略,并在用户批准后才允许真实 worktree 修改。 - -### Scanner 与 coverage report 时机 - -执行版 fresh 双 review 前,只允许在 `/tmp` 隔离副本运行 coverage scanner: - -```text -/tmp/p1r--completed-approval-scan.* -``` - -隔离 scanner 只能用来预验证目标 summary、目标 operation/domain 状态和负向保护。真实 worktree 运行 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 并改写 `docs/superpowers/reports/**`,必须同时满足: - -1. domain 执行版 fresh spec/scope review PASS。 -2. domain 执行版 fresh quality/feasibility review PASS。 -3. 用户明确批准该 domain 的 completed 状态推进。 -4. allowed-diff gate 已把 scanner/report/test 允许路径列明。 - -### 真实 `_test` / 外部验收最低标准 - -每个 domain 审阅版必须先说明 completed 证据所需的真实运行态最低标准: - -- 如果涉及新增或既有 migration,必须指定真实 PostgreSQL `_test` Flyway 验证,包含目标 schema version、关键表、索引、约束、trigger 和非法 insert 拒绝。 -- 如果涉及外部服务,例如 New-API、RAGFlow、FileService、对象存储或导出下载,必须指定成功路径、失败路径、超时、重试或 fail-closed 边界。 -- 如果外部服务暂不可用,必须把该 operation 或该切片保持 `needs_verification`,不能以 mock、bare fake、空成功或固定样例数据替代 completed 证据。 -- 如果某个 domain 只能部分 operation 满足 completed 证据,执行版必须采用 operation-level approval,而不是 domain-level approval。 - -### Implementation review 复核项 - -每个 domain 的 implementation review 必须复核: - -- scanner approval 模型没有扩大到非目标 domain 或未来 operation。 -- report 中新增 completed 的 operation 与用户批准范围完全一致。 -- 非目标 domain 仍保持原状态。 -- OpenAPI 与业务实现 diff 符合执行版 allowed list。 -- focused tests、P1R gates、XML 防空跑、真实 `_test` 或外部验收、allowed-diff、protected diff 均有 fresh 输出。 - -## 后续审阅版必须包含 - -每个 domain 的 completed approval 审阅版都必须包含: - -1. 当前 operation 清单和 coverage 状态。 -2. 已有 dedicated 实现证据。 -3. 已有外部或端到端证据。 -4. 明确缺口:数据库、外部服务、状态机、权限、审计、失败路径、重试补偿。 -5. 非目标 operation 或跨域链路。 -6. 推荐 approval 粒度:domain-level 或 operation-level。 -7. scanner/report/gate 的修改边界。 -8. protected 文件清单,必须覆盖 7 个 OpenAPI: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/content/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/meta/openapi.yaml` -9. review gate: - - fresh spec/scope review - - fresh quality/feasibility review -10. 用户批准点:执行版双 PASS 后,仍必须由用户单独批准真实 coverage 状态推进。 -11. 本文“全域 anti-fake-green 基线”的继承方式;如需例外,必须在审阅版列出理由和替代验证。 - -## 验收标准 - -本总审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-10-P1RRemainingDomainCompletedApproval审阅版.md`。 -2. `.agent` 记录下一阶段状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report 无 diff。 -5. 文档只定义总路线,不进入执行版或实现。 - -## 待确认项 - -1. 是否确认下一阶段正式命名为 `P1R Remaining Domain Completed Approval`。 -2. 是否确认推荐顺序为 `Meta -> Account -> Market -> Content`。 -3. 是否确认下一步先写 Meta completed approval 审阅版,而不是直接进入执行版或实现。 -4. 是否确认每个 domain 的 completed 状态推进仍需要单独用户批准。 diff --git a/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md b/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md deleted file mode 100644 index 2baf4520..00000000 --- a/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md +++ /dev/null @@ -1,410 +0,0 @@ -# P1R Account Completed Approval 审阅版 - -日期:2026-06-11 - -## 结论 - -不建议把 Account 33 个 operation 一次性整域推进 `completed`。 - -推荐把 Account Completed Approval 拆成 operation-level 或证据切片推进:先审批 Account 本域已经形成闭环的读写与审计切片;New-API、FileService/download、call attribution 外部来源、Market purchase/license/publish projection 等跨 owner 链路继续保持 `dedicated / needs_verification`,直到有真实 runtime 或明确验收替代边界。 - -本审阅版只冻结 Account completed approval 的范围判断、证据缺口、推荐审批粒度和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,也不把 Account 或其它 domain 推进 `completed`。 - -```mermaid -flowchart TB - Start["当前 coverage
Account 33 dedicated / needs_verification"] --> Review["Account completed approval 审阅版
冻结证据标准"] - Review --> Split{"是否整域 33/33 completed?"} - Split -->|否,推荐| OpLevel["按证据切片 operation-level approval"] - Split -->|是,不推荐| DomainLevel["domain-level approval
需 New-API/FileService/Market/Attribution 全闭环"] - OpLevel --> Exec["执行版
列出可审批 operation 与保留 needs_verification operation"] - Exec --> FreshReview["fresh spec/scope review
fresh quality/feasibility review"] - FreshReview --> UserApproval{"用户明确批准
Account 状态推进?"} - UserApproval -->|否| Stay["保持 Account 33 needs_verification"] - UserApproval -->|是| Change["最小修改 scanner/report/gates
运行 focused + P1R + _test + XML 防空跑"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支与远端同步,HEAD 为: - -```text -df26bd9 test(p1r): 收口 Meta remaining 5 completed approval 门禁 -``` - -当前 coverage summary: - -```text -total=233 -completed=117 -needsVerification=116 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前按 domain 聚合: - -```text -account total=33 completed=0 needsVerification=33 -ai total=41 completed=41 needsVerification=0 -content total=51 completed=0 needsVerification=51 -events total=1 completed=1 needsVerification=0 -knowledge total=59 completed=59 needsVerification=0 -market total=32 completed=0 needsVerification=32 -meta total=16 completed=16 needsVerification=0 -``` - -Account 33 个 operation 当前全部为 `dedicated / needs_verification`: - -| operationId | Method | Path | 当前状态 | -|---|---|---|---| -| `adminCreateCallAttributionJob` | `POST` | `/admin-api/muse/account/call-attribution-jobs` | `dedicated / needs_verification` | -| `adminGetCallAttributionJob` | `GET` | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `dedicated / needs_verification` | -| `adminGetIntegrationCallByCorrelation` | `GET` | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `dedicated / needs_verification` | -| `adminListNewApiBindings` | `GET` | `/admin-api/muse/account/new-api-bindings` | `dedicated / needs_verification` | -| `adminListPurchaseRecords` | `GET` | `/admin-api/muse/account/purchase-records` | `dedicated / needs_verification` | -| `adminListUsageRecords` | `GET` | `/admin-api/muse/account/usage-records` | `dedicated / needs_verification` | -| `adminListAccountUsers` | `GET` | `/admin-api/muse/account/users` | `dedicated / needs_verification` | -| `adminGetBalanceSnapshots` | `GET` | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `dedicated / needs_verification` | -| `adminGetUserEntitlements` | `GET` | `/admin-api/muse/account/users/{userId}/entitlements` | `dedicated / needs_verification` | -| `adminCreateNewApiBinding` | `POST` | `/admin-api/muse/account/users/{userId}/new-api-binding` | `dedicated / needs_verification` | -| `adminListQuotaAdjustments` | `GET` | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `dedicated / needs_verification` | -| `adminCreateQuotaAdjustment` | `POST` | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `dedicated / needs_verification` | -| `adminCreateQuotaRequest` | `POST` | `/admin-api/muse/account/users/{userId}/quota-requests` | `dedicated / needs_verification` | -| `getAppBalanceSnapshots` | `GET` | `/app-api/muse/account/balance-snapshots` | `dedicated / needs_verification` | -| `appDownloadExport` | `GET` | `/app-api/muse/account/downloads/{credentialId}` | `dedicated / needs_verification` | -| `getAppEntitlements` | `GET` | `/app-api/muse/account/entitlements` | `dedicated / needs_verification` | -| `appCreateExportTask` | `POST` | `/app-api/muse/account/export-tasks` | `dedicated / needs_verification` | -| `appGetExportTask` | `GET` | `/app-api/muse/account/export-tasks/{taskId}` | `dedicated / needs_verification` | -| `appGetIntegrationCallByCorrelation` | `GET` | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `dedicated / needs_verification` | -| `appListLicenses` | `GET` | `/app-api/muse/account/licenses` | `dedicated / needs_verification` | -| `getAppNewApiBinding` | `GET` | `/app-api/muse/account/new-api-binding` | `dedicated / needs_verification` | -| `appRecheckNewApiBinding` | `POST` | `/app-api/muse/account/new-api-binding/recheck` | `dedicated / needs_verification` | -| `appListPublishRecords` | `GET` | `/app-api/muse/account/publish-records` | `dedicated / needs_verification` | -| `appListPurchases` | `GET` | `/app-api/muse/account/purchases` | `dedicated / needs_verification` | -| `appCreateQuotaRequest` | `POST` | `/app-api/muse/account/quota-requests` | `dedicated / needs_verification` | -| `appGetQuotaRequest` | `GET` | `/app-api/muse/account/quota-requests/{requestId}` | `dedicated / needs_verification` | -| `appListSecurityEvents` | `GET` | `/app-api/muse/account/security-events` | `dedicated / needs_verification` | -| `appGetSecurityEvent` | `GET` | `/app-api/muse/account/security-events/{eventId}` | `dedicated / needs_verification` | -| `appAcknowledgeSecurityEvent` | `POST` | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `dedicated / needs_verification` | -| `getAppUsage` | `GET` | `/app-api/muse/account/usage` | `dedicated / needs_verification` | -| `getCurrentUser` | `GET` | `/app-api/muse/me` | `dedicated / needs_verification` | -| `getProfile` | `GET` | `/app-api/muse/profile` | `dedicated / needs_verification` | -| `updateProfile` | `PATCH` | `/app-api/muse/profile` | `dedicated / needs_verification` | - -## 已验证实现证据 - -### Account owner 与入口 - -Account owner 当前位于 `muse-module-member` 的 Account 子域,没有独立 `muse-module-account`。 - -现有 dedicated Controller 覆盖 Account 33 个 operation: - -- `AdminAccountNewApiController` -- `AdminAccountUserController` -- `AdminAccountUsageController` -- `AdminAccountPurchaseController` -- `AppAccountNewApiController` -- `AppAccountEntitlementController` -- `AppAccountMarketRecordController` -- `AppAccountExportController` -- `AppAccountSecurityController` -- `AppAccountProfileController` - -这些 Controller 均通过 `AccountApiVersionGuard.requireVersion(...)` 校验 `X-API-Version`,并调用 Account 子域 Service,不再走旧 `MuseContractPersistenceService` 兜底。 - -管理端入口使用 `@PreAuthorize` 绑定 `muse:account:*` 权限;App 端入口使用 `getLoginUserId()` 作为当前 owner。 - -### Account 本域服务与持久化 - -Account 已有本域 Service / Guard / DAL: - -- `AccountProfileServiceImpl` -- `AccountEntitlementServiceImpl` -- `AccountQuotaServiceImpl` -- `AccountUsageServiceImpl` -- `AccountNewApiBindingServiceImpl` -- `AccountAttributionServiceImpl` -- `AccountMarketRecordServiceImpl` -- `AccountExportServiceImpl` -- `AccountSecurityServiceImpl` -- `AccountCommandServiceImpl` -- `AccountAuditServiceImpl` -- `AccountEventPublishOutboxServiceImpl` -- `AccountEventPublishWorker` -- `AccountApiVersionGuard` -- `AccountOwnerGuard` -- `AccountDownloadGuard` -- `AccountQuotaGuard` -- Account DO / Mapper / Convert 文件 - -P1R-3 已把 Account 从 `generic_persistence / incomplete` 推进为 `dedicated / needs_verification`,并删除旧 Account 合同兜底 Controller。 - -### 数据库与迁移证据 - -V11 Account Real API schema 已存在: - -- `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql` - -V11 追加 Account owner 自有事实,包括: - -- `muse_account_command` -- `muse_account_audit` -- `muse_account_profile` -- `muse_account_balance_snapshot` -- `muse_account_quota_request` -- `muse_account_integration_call` -- `muse_account_call_attribution_job` -- `muse_account_call_attribution_item` -- `muse_account_export_task` -- `muse_account_download_credential` -- `muse_account_security_event_ack` -- `muse_account_record_projection` - -历史留痕记录 V11 已在真实 PostgreSQL test 库通过 Flyway: - -```text -PostgreSQL 17.10 -flyway_success=true -migrations_executed=11 -target_schema_version=11 -flyway_latest=11:extend account real api schema -v11_tables=12 -account_index_count=47 -``` - -V20 Account Events publish outbox 已存在: - -- `muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql` - -历史留痕记录 V20 已在真实 `_test` 库通过 Flyway: - -```text -flyway_success=true -migrations_executed=20 -successful_migration_count=20 -target_schema_version=20 -flyway_latest=20:extend account events publish outbox -v20_table=muse_account_event_publish_outbox -``` - -这些迁移证据证明 DDL / Flyway 可执行性和 outbox schema 约束,不等同于 Account 33 个 API 全部具备 completed-grade runtime 证据。 - -### P1R-7e Account quota alert 证据 - -P1R-7e 已形成 Account quota adjustment terminal fact 到 Events SSE 的 needs-verification evidence: - -```text -AccountQuotaServiceImpl.adminCreateQuotaAdjustment --> muse_member_entitlement_audit_log(change_type=quota_adjustment) --> muse_account_event_publish_outbox --> AccountEventPublishWorker --> EventsPublishApi --> muse_unified_event --> /app-api/muse/events 对 accountUserId 可见 -``` - -历史验证包括: - -- Member focused tests 36/36 pass。 -- P1R mixed executable gates 73/73 pass。 -- Account focused + P1R mixed XML 合计 109 tests,failures/errors/skipped 均为 0。 -- `P1rAccountEventsPublishFlywayMigrationIT` 4/4 pass。 -- Dependency tree gate 证明 Member server 依赖 `muse-module-events-api`,不依赖 `muse-module-events-server`。 - -当前 worktree 可见的 Surefire XML 只覆盖部分 Account P1R gate: - -```text -P1rAccountRealApiGateTest 4/4 -P1rAccountRouteOwnershipTest 4/4 -P1rAccountEventsPublishMigrationSqlTest 3/3 -P1rAccountEventsPublishDependencyTest 2/2 -P1rAccountEventsPublishEndToEndTest 4/4 -``` - -`P1rAccountEventsPublishFlywayMigrationIT` 与 Member focused tests 的 XML 是历史留痕证据,当前 worktree 不能直接引用为 fresh Account completed approval 证据。后续执行版必须重新运行并读取 XML,不能复用历史 XML 口径。 - -该证据可支持后续把 `adminCreateQuotaAdjustment` 作为 Account operation-level completed approval 的第一候选,但不能自动覆盖全部 Account 33 个 operation。 - -## 关键缺口 - -### 缺口 1:New-API runtime 未闭合 - -`NewApiAccountFacade` 定义了绑定和 recheck 的外部边界;默认 `UnavailableNewApiAccountFacade` 抛出 `ACCOUNT_NEW_API_UNAVAILABLE`,注释明确当前 worktree 没有真实 New-API runtime 配置,不能伪造绑定成功或重验任务。 - -因此以下 operation 不能仅凭当前 dedicated 实现直接 completed: - -- `adminListNewApiBindings` -- `adminCreateNewApiBinding` -- `getAppNewApiBinding` -- `appRecheckNewApiBinding` -- `adminCreateQuotaRequest` -- `appCreateQuotaRequest` -- `appGetQuotaRequest` -- `adminGetIntegrationCallByCorrelation` -- `appGetIntegrationCallByCorrelation` - -其中读侧 operation 可能有本地投影读取证据,但 create/recheck/request/correlation 相关 completed approval 必须说明真实 New-API runtime、失败关闭、超时、重试、correlation 与 attribution 口径。 - -### 缺口 2:FileService / 导出下载真实交付未闭合 - -`AccountFileServiceFacade` 默认返回 unavailable;注释明确 Account 只定义任务、凭证和下载校验,不接管真实文件生成或存储。 - -`AccountExportServiceImpl` 已有 fail-closed 行为: - -- 高敏导出缺少可信 step-up proof 时拒绝。 -- FileService 异常收敛为 unavailable。 -- completed 必须有 fileRef、downloadExpiresAt 和 Account HMAC secret。 -- 下载凭证消费具备 owner、过期、撤销、consumed、sourceBlocked 原子门禁。 - -但这仍不能证明真实文件或对象存储交付。因此以下 operation 暂不应 completed: - -- `appCreateExportTask` -- `appGetExportTask` -- `appDownloadExport` - -除非执行版补齐真实 FileService / 对象存储成功路径、失败路径、下载字节、凭证一次性消费和审计证据。 - -### 缺口 3:Call attribution 外部来源未闭合 - -`AccountAttributionServiceImpl` 的 create 路径只建立 job 队列事实,并明确真实归因执行由后续 job/transition 路径接管。外部归因来源不可用时会形成失败或补偿建议,而不是 completed-grade 归因成功。 - -因此以下 operation 不能整批 completed: - -- `adminCreateCallAttributionJob` -- `adminGetCallAttributionJob` - -执行版必须证明真实 attribution source 或明确 operation completed 口径只覆盖“创建 queued job + 可查询失败关闭状态”,否则应保持 `needs_verification`。 - -### 缺口 4:Market purchase/license/publish projection 依赖跨 owner 一致性 - -P1R-3 已固定 Account 视角使用 `muse_account_record_projection(record_type=purchase/license/publish)` 作为查询读模型;`muse_market_account_projection` 只是 Market 侧 sync/outbox 状态,不是 Account 查询读模型。 - -以下 operation 需要 Market 主流程和 Account projection 一致性证据: - -- `adminListPurchaseRecords` -- `appListPurchases` -- `appListLicenses` -- `appListPublishRecords` - -它们不能用 Market 事件传播或 Account projection 表存在来直接 completed。执行版必须证明 purchase/license/publish 上游 owner 写入、projection 同步、tenant/owner 隔离和 stale projection 风险。 - -### 缺口 5:安全事件与高敏导出源事实不足 - -`MemberSecurityEventDO` 有读侧、详情、ack 和脱敏转换;但 P1R-7e 审阅已验证,高敏导出生产安全事件路径当前被 `normalizeRequest` fail-closed 阻断,不能作为第一切片 source fact。 - -以下 operation 若要 completed,必须证明安全事件真实生产路径,而不是只证明读侧和 ack: - -- `appListSecurityEvents` -- `appGetSecurityEvent` -- `appAcknowledgeSecurityEvent` - -### 缺口 6:缺少本轮 fresh Account completed approval gate - -当前 `P1rAccountRealApiGateTest` 仍明确禁止 Account operation 标记为 `completed`,并要求 33 个 operation 保持 `dedicated / needs_verification`。 - -当前 scanner 的 completed 白名单没有包含 Account domain 或 Account operation;已批准范围仍限于 AI / Knowledge domain,以及 Events / Meta 的 operation-level allowlist。未获批准的 Account completed 会被 scanner 拒绝。 - -后续执行版必须先明确用户批准范围,再把 gate 改成审批后口径: - -- 仅允许 approved operation 从 `needs_verification` 变为 `completed`。 -- 未批准 operation 必须继续 `dedicated / needs_verification`。 -- Account 不能自动加入 domain-level completed allowlist。 -- Market / Content 仍不能被连带推进。 - -## 推荐审批粒度 - -### 推荐:Account operation-level approval - -第一批候选不应超过以下本域闭合较强的 operation: - -```text -getCurrentUser -getProfile -updateProfile -adminListAccountUsers -adminGetUserEntitlements -getAppEntitlements -adminGetBalanceSnapshots -getAppBalanceSnapshots -getAppUsage -adminListUsageRecords -adminCreateQuotaAdjustment -adminListQuotaAdjustments -``` - -其中 `adminCreateQuotaAdjustment` 证据最强,因为 P1R-7e 已额外证明 quota adjustment terminal fact 可以进入 Account outbox 并发布到 Events。 - -但上述候选仍必须由执行版逐项验证 Controller、Service、DAL、权限、owner/tenant 隔离、幂等、审计、错误路径、XML 防空跑和真实 `_test` 证据。审阅版不直接批准这些 operation completed。 - -### 不推荐:Account domain-level approval - -除非执行版能一次性补齐: - -- 真实 New-API binding / recheck / quota request runtime。 -- 真实 FileService / 对象存储导出下载。 -- 真实 call attribution source 或明确可验收替代口径。 -- Market purchase/license/publish 到 Account projection 的端到端一致性。 -- security event 生产路径和 ack 的 owner/tenant 证据。 - -否则 Account 33/33 domain-level completed 会把外部闭环缺口包装成已完成。 - -## 非目标 - -- 不修改 7 个 OpenAPI: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/content/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/meta/openapi.yaml` -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.md`。 -- 不修改 Account 业务实现。 -- 不把 Account 33 个 operation 推进 `completed`。 -- 不把 Account 加入 domain-level completed allowlist。 -- 不推进 Market / Content / 总 P1R completed。 -- 不把 P1R-7e quota alert source owner evidence 当作 Account 整域 completed 证据。 - -## 后续执行版必须包含 - -1. Account 33 operation 的完整清单、当前状态和目标审批状态。 -2. 第一批候选 operation 的逐项 evidence map:Controller、Service、DAL、权限、版本头、owner/tenant 隔离、审计、幂等、失败路径。 -3. 明确哪些 operation 继续 `needs_verification`,以及原因。 -4. scanner approval 模型:只允许 operation-level allowlist,不允许 Account domain-level allowlist,除非执行版证明 33/33 全闭环。 -5. `P1rAccountRealApiGateTest` 从禁止 completed 到审批后 partial completed gate 的修改策略。 -6. `P1rApiCoverageReportTest` summary 期望变更策略,必须只提升用户批准的 Account operation 数。 -7. Account focused tests 的 required class 清单与最低 XML tests 数;执行版必须 fresh rerun `P1rAccountEventsPublishFlywayMigrationIT` 与 Member focused tests,不能只引用历史留痕。 -8. P1R mixed gates:包含 Account gate、coverage report gate,以及用于证明非目标 Market / Content 不被推进的 gate。 -9. 真实 PostgreSQL `_test`:至少覆盖 V1-V20 clean migrate;如新增 Account completed approval Flyway IT,应验证 V11/V20 表、索引、约束、trigger、非法 insert 拒绝。 -10. 外部 runtime 验收:New-API / FileService / Market projection / attribution source 对应 operation 若不具备真实验收,必须保持 `needs_verification`。 -11. protected diff gate:7 个 OpenAPI 在批准前后都必须保持空 diff。 -12. allowed-diff gate:覆盖 unstaged、staged、untracked,并使用 `git -c core.quotePath=false`。 -13. rollback 策略:撤回 scanner allowlist、coverage report、gate test 和新增 memory,恢复目标 operation 为 `dedicated / needs_verification`。 - -## 验收标准 - -本审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md`。 -2. `.agent` 记录 Account completed approval 审阅版状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report 无 diff。 -5. 文档只定义审阅路线,不进入执行版或实现。 -6. fresh spec/scope review PASS。 -7. fresh quality/feasibility review PASS。 - -## 待确认项 - -1. 是否确认 Account 第一轮不做 domain-level 33/33 completed approval。 -2. 是否确认优先走 operation-level approval,并从本域闭合较强的 profile / entitlement / usage / quota adjustment 切片开始。 -3. 是否确认 New-API、FileService/download、call attribution external source、Market projection、安全事件生产路径在没有真实 runtime 证据前继续保持 `needs_verification`。 -4. 是否确认审阅版 fresh 双 review PASS 后,再写 Account completed approval 执行版。 diff --git a/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md b/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md deleted file mode 100644 index ef55960d..00000000 --- a/docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md +++ /dev/null @@ -1,506 +0,0 @@ -# P1R Account 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-11 - -## 结论 - -本执行版推荐第一轮只做 Account operation-level completed approval,不做 Account domain-level 33/33 completed。 - -第一轮推荐审批 10 个 Account 本域闭合较强的 operation: - -```text -account:getCurrentUser -account:getProfile -account:updateProfile -account:adminListAccountUsers -account:adminGetUserEntitlements -account:getAppEntitlements -account:adminGetBalanceSnapshots -account:getAppBalanceSnapshots -account:adminCreateQuotaAdjustment -account:adminListQuotaAdjustments -``` - -审批后目标 coverage summary 为: - -```text -total=233 -completed=127 -needsVerification=106 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Account 域目标状态为: - -```text -account total=33 -completed=10 -needsVerification=23 -``` - -本执行版只定义实现前置批准项、允许变更范围、TDD 顺序、验证命令、XML 防空跑和回滚策略。当前不得直接实现,不得修改 OpenAPI、scanner、coverage report、业务实现或 gate test。 - -```mermaid -flowchart TB - Current["当前 Account
33 dedicated / needs_verification"] --> Exec["执行版
锁定第一批 10 个 operation"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/feasibility review"] - Review --> Approval{"用户明确批准 4 项
operation list / operation-level / allowed diff / legacy gates"} - Approval -->|否| Stay["保持当前 coverage
233/117/116"] - Approval -->|是| Red["TDD RED
先改 Account/Coverage gates
旧 report 必须失败"] - Red --> Green["TDD GREEN
scanner operation allowlist + regenerate report"] - Green --> Verify["focused + P1R mixed + Flyway _test
XML 防空跑 + protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -df26bd9 test(p1r): 收口 Meta remaining 5 completed approval 门禁 -``` - -当前 coverage summary: - -```text -233 117 116 0 0 0 -``` - -当前 Account 事实: - -- Account 33 个 operation 全部为 `dedicated / needs_verification`。 -- `APPROVED_COMPLETED_DOMAINS` 当前只包含 `ai`、`knowledge`。 -- `APPROVED_COMPLETED_OPERATIONS` 当前只包含 `events:streamEvents` 与 Meta 16 个 operation。 -- `P1rAccountRealApiGateTest` 当前明确禁止 Account operation 标记为 `completed`。 -- `P1rApiCoverageReportTest` 当前断言 summary `completed=117`,并断言 Account 33 个 operation 继续 `needs_verification`。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest` 仍硬编码当前 summary 或 Account needs_verification 口径;实现前必须把这些 legacy mixed gate 同步列入单独批准项。 - -## 执行边界 - -### 必须先获用户明确批准 - -实现前必须同时获得以下 4 项批准: - -1. 批准第一轮 Account completed operation 精确清单为本执行版列出的 10 个 operation。 -2. 批准只走 operation-level approval,不把 `account` 加入 domain-level completed allowlist。 -3. 批准按本执行版修改 scanner、coverage report、Account/Coverage gate、必要的 legacy mixed gate 和 memory。 -4. 批准同步 4 个 legacy mixed gate 文件的 summary / Account 状态断言: - - `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` - -未获 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/P1rAccountRealApiGateTest.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/P1rAccountEventsPublishFlywayMigrationIT.java`,仅当需要扩展 V11 Account 表/索引/约束 fresh assertion 时允许修改。 -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md` -- `docs/memorys/2026-06-11-P1RAccount状态推进.md` - -如 implementation 发现必须修改 Account 业务实现、OpenAPI、Content/Market/Meta gate、数据库迁移或其它文件,必须停下说明原因并重新取得用户批准。 - -### 本轮禁止变更 - -- 不修改 7 个 OpenAPI。 -- 不修改 Account 业务实现来掩盖 coverage 缺口。 -- 不新增或修改 Account domain-level completed allowlist。 -- 不推进 `getAppUsage`、`adminListUsageRecords`。 -- 不推进 New-API、FileService/download、call attribution external source、Market projection、安全事件生产路径相关 operation。 -- 不推进 Market、Content 或总 P1R completed。 - -## 第一轮审批清单 - -| operation key | Method | Path | 目标状态 | 完成证据边界 | -|---|---|---|---|---| -| `account:getCurrentUser` | `GET` | `/app-api/muse/me` | `dedicated / completed` | App 当前登录用户 owner、Member user + Account profile 读模型、缺 profile fallback、无写入副作用 | -| `account:getProfile` | `GET` | `/app-api/muse/profile` | `dedicated / completed` | App 当前登录用户 owner、Profile 读模型、缺 profile fallback、敏感联系字段不泄露 | -| `account:updateProfile` | `PATCH` | `/app-api/muse/profile` | `dedicated / completed` | `X-API-Version`、`X-Command-Id`、command replay、expectedVersion、审计、profile optimistic update | -| `account:adminListAccountUsers` | `GET` | `/admin-api/muse/account/users` | `dedicated / completed` | 管理端权限、分页、用户查询、Account DTO 转换 | -| `account:adminGetUserEntitlements` | `GET` | `/admin-api/muse/account/users/{userId}/entitlements` | `dedicated / completed` | 管理端权限、指定 userId、entitlement + balance 本域读模型 | -| `account:getAppEntitlements` | `GET` | `/app-api/muse/account/entitlements` | `dedicated / completed` | App 当前 owner、entitlement + balance 本域读模型 | -| `account:adminGetBalanceSnapshots` | `GET` | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `dedicated / completed` | 管理端权限、指定 userId、balance snapshot 分页 | -| `account:getAppBalanceSnapshots` | `GET` | `/app-api/muse/account/balance-snapshots` | `dedicated / completed` | App 当前 owner、balance snapshot 分页 | -| `account:adminCreateQuotaAdjustment` | `POST` | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `dedicated / completed` | 管理端权限、`X-Command-Id`、quota guard、command replay、entitlement audit log、Account outbox、P1R-7e Events publish 证据 | -| `account:adminListQuotaAdjustments` | `GET` | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `dedicated / completed` | 同一 quota ledger 读侧、changeType allowlist、分页、非法 source filter 拒绝 | - -## 保持 needs_verification 的 Account operation - -以下 23 个 operation 必须保持 `dedicated / needs_verification`: - -```text -account:adminCreateCallAttributionJob -account:adminGetCallAttributionJob -account:adminGetIntegrationCallByCorrelation -account:adminListNewApiBindings -account:adminListPurchaseRecords -account:adminListUsageRecords -account:adminCreateNewApiBinding -account:adminCreateQuotaRequest -account:appDownloadExport -account:appCreateExportTask -account:appGetExportTask -account:appGetIntegrationCallByCorrelation -account:appListLicenses -account:getAppNewApiBinding -account:appRecheckNewApiBinding -account:appListPublishRecords -account:appListPurchases -account:appCreateQuotaRequest -account:appGetQuotaRequest -account:appListSecurityEvents -account:appGetSecurityEvent -account:appAcknowledgeSecurityEvent -account:getAppUsage -``` - -其中 `getAppUsage` 与 `adminListUsageRecords` 虽有 dedicated 实现和 focused tests,但仍依赖 `muse_account_integration_call` / attribution source 的上游事实质量;本轮先不把 usage 读模型算作 completed,避免绕过 New-API / attribution runtime 缺口。 - -## 证据矩阵 - -| 证据类型 | 必跑或必查内容 | 目的 | -|---|---|---| -| Account focused tests | Profile、Entitlement、Quota、Audit、Command、Guard、Controller tests | 证明 10 个 operation 的本域读写、owner、权限、审计、幂等、失败路径 | -| P1R Account gates | `P1rAccountRealApiGateTest`、`P1rApiCoverageReportTest` | 证明 only 10 Account operation completed,23 个继续 needs_verification | -| P1R mixed gates | Events、AI、Knowledge、Market、Content、Meta、Account、Coverage gates | 防止非目标 domain 被连带推进 | -| Flyway `_test` | `P1rAccountEventsPublishFlywayMigrationIT` fresh rerun,必要时扩展 V11 assertion | 证明 V1-V20 clean migrate 和 Account V11/V20 schema 可执行 | -| Coverage scanner | `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` | 证明 scanner/report 与 allowlist 口径一致 | -| XML 防空跑 | 逐个读取 surefire XML,检查 tests、failures、errors、skipped | 防止 Maven 未跑目标类或旧 XML 假绿 | -| Protected diff | OpenAPI staged/unstaged diff 必须为空 | 证明没有通过改合同掩盖缺口 | -| Allowed diff | staged、unstaged、untracked 均必须落在允许清单 | 证明没有顺手扩大 scope | - -## TDD 执行步骤 - -### Task 0:实现前现场确认 - -- [ ] 确认 worktree: - -```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 117 116 0 0 0 -``` - -- [ ] 确认 Account 33 个 operation 仍全部是 `dedicated / needs_verification`: - -```bash -jq -r '.operations[] | select(.domain=="account") | [.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -- [ ] 确认 OpenAPI 与业务实现未在实施前有相关 diff: - -```bash -git diff -- docs/api-contracts -git diff -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member -``` - -### Task 1:TDD RED,先改 gate 期望 - -获批后先修改: - -- `P1rApiCoverageReportTest.java` -- `P1rAccountRealApiGateTest.java` -- 必要的 4 个 legacy mixed gate - -RED 期望: - -- `P1rApiCoverageReportTest` 期望 completed `127`,但旧 report 仍是 `117`,必须失败。 -- `P1rAccountRealApiGateTest` 期望 10 个 Account operation 为 `completed`,但旧 report 仍是 `needs_verification`,必须失败。 -- 4 个 legacy mixed gate 期望 `completed=127`、`needsVerification=106`、Account `completed=10`、Account `needsVerification=23`,旧 report 下必须失败。 - -运行: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -预期:至少一个失败点来自旧 report 的 `117/116` 或 Account 10 个 operation 尚未 completed。若直接通过,必须停下排查假绿。 - -### Task 2:TDD GREEN,最小 scanner/report 状态推进 - -只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`: - -- 不修改 `APPROVED_COMPLETED_DOMAINS`。 -- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加 10 个 `account:*` operation key。 -- 保持 completed 必须仍是 `dedicated` 的校验。 - -生成 report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -预期 summary: - -```text -total=233 -completed=127 -needsVerification=106 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -预期 Account: - -```text -completed=10 -needsVerification=23 -``` - -### Task 3:Account focused verification - -运行 Account 本域 focused tests: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-member/muse-module-member-server,muse-server -am \ - -Dtest=AccountProfileServiceTest,AccountEntitlementServiceTest,AccountQuotaServiceTest,AccountAuditServiceTest,AccountCommandServiceTest,AccountOwnerGuardTest,AccountApiVersionGuardTest,AccountQuotaGuardTest,AdminAccountUserControllerTest,AppAccountProfileControllerTest,AppAccountEntitlementControllerTest,P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rApiCoverageReportTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML 并检查 `failures=0`、`errors=0`、`skipped=0`: - -| Test class | 最低 tests | -|---|---:| -| `AccountProfileServiceTest` | 19 | -| `AccountEntitlementServiceTest` | 8 | -| `AccountQuotaServiceTest` | 17 | -| `AccountAuditServiceTest` | 7 | -| `AccountCommandServiceTest` | 15 | -| `AccountOwnerGuardTest` | 3 | -| `AccountApiVersionGuardTest` | 3 | -| `AccountQuotaGuardTest` | 4 | -| `AdminAccountUserControllerTest` | 22 | -| `AppAccountProfileControllerTest` | 7 | -| `AppAccountEntitlementControllerTest` | 9 | -| `P1rAccountEventsPublishMigrationSqlTest` | 3 | -| `P1rAccountEventsPublishDependencyTest` | 2 | -| `P1rAccountEventsPublishEndToEndTest` | 4 | -| `P1rAccountRealApiGateTest` | 4 | -| `P1rApiCoverageReportTest` | 6 | - -如实现修改了 Account gate 测试数量,XML 检查的最低 tests 可高于上表,但不能低于上表。 - -### Task 4:真实 PostgreSQL `_test` Flyway gate - -fresh rerun `P1rAccountEventsPublishFlywayMigrationIT`。使用独立 `_test` 库,不能使用生产库或非 `_test` 库: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_TEST_DB=muse_p1r_account_completed_approval_test - -psql "postgresql://$MUSE_POSTGRES_USERNAME:$MUSE_POSTGRES_PASSWORD@$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/postgres" \ - -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_ACCOUNT_TEST_DB'" | grep -q 1 || \ -psql "postgresql://$MUSE_POSTGRES_USERNAME:$MUSE_POSTGRES_PASSWORD@$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/postgres" \ - -c "CREATE DATABASE $P1R_ACCOUNT_TEST_DB" - -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_ACCOUNT_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 | -|---|---:| -| `P1rAccountEventsPublishFlywayMigrationIT` | 4 | - -如果 reviewer 认为 V11 Account schema 对本轮 10 个 operation 的 evidence 不足,允许在该 IT 内补充 V11 表、索引、约束 assertion,但不得新增迁移或修改业务实现。 - -### Task 5:P1R mixed gate - -运行 mixed gates: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML: - -| Test class | 最低 tests | -|---|---:| -| `P1rApiCoverageReportTest` | 6 | -| `P1rAccountRealApiGateTest` | 4 | -| `P1rEventsRealApiGateTest` | 5 | -| `P1rAiRealApiGateTest` | 7 | -| `P1rKnowledgeRealApiGateTest` | 8 | -| `P1rMarketRealApiGateTest` | 6 | -| `P1rContentRealApiGateTest` | 5 | -| `P1rMetaRealApiGateTest` | 4 | - -每个 XML 均必须 `failures=0`、`errors=0`、`skipped=0`。 - -### Task 6:报告与 diff gate - -检查 summary: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 127 106 0 0 0 -``` - -检查 Account 10/23: - -```bash -python3 - <<'PY' -import json -from collections import Counter -from pathlib import Path -data = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -counter = Counter(op["completionStatus"] for op in data["operations"] if op["domain"] == "account") -print(counter) -PY -``` - -预期: - -```text -Counter({'needs_verification': 23, 'completed': 10}) -``` - -检查 OpenAPI protected diff: - -```bash -git diff --quiet -- docs/api-contracts -git diff --cached --quiet -- docs/api-contracts -``` - -检查 business implementation 不被修改: - -```bash -git diff --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java -git diff --cached --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java -``` - -检查 whitespace: - -```bash -git diff --check -``` - -检查 allowed diff: - -```bash -git -c core.quotePath=false status --short -``` - -输出只能包含本执行版允许路径。若出现 OpenAPI、业务实现、迁移或非批准 gate 文件,必须停止。 - -### Task 7:memory 与 `.agent` - -实现通过后新增: - -- `docs/memorys/2026-06-11-P1RAccount状态推进.md` - -内容必须记录: - -- 批准的 10 个 Account operation。 -- 保持 `needs_verification` 的 23 个 Account operation 及原因。 -- scanner/report/gate 修改范围。 -- 所有验证命令、结果、XML 计数、Flyway `_test` 库名。 -- protected diff 与 allowed diff 结果。 -- 明确说明不代表 Account 33/33 completed,不代表 Market/Content/总 P1R completed。 - -同时更新 `docs/agent-specs/.agent`,但只能记录事实,不写未验证结论。 - -### Task 8:fresh implementation review - -实现、验证和文档留痕后,必须派发两类 fresh reviewer: - -- spec/correctness reviewer:检查 10 个 operation 是否严格匹配本执行版,23 个 operation 是否仍 needs_verification,Account domain allowlist 是否未新增。 -- quality/data-integrity/testing reviewer:检查 scanner/report/gate、XML 防空跑、Flyway `_test`、protected diff、allowed diff、rollback 是否可靠。 - -双 PASS 前不得提交、push 或宣称 Account completed approval 已收口。 - -## 回滚策略 - -如实现后需要撤回: - -1. 从 `APPROVED_COMPLETED_OPERATIONS` 移除 10 个 `account:*` key。 -2. 重新运行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -3. 恢复 gate 期望: - -```text -summary completed=117 -summary needsVerification=116 -account completed=0 -account needsVerification=33 -``` - -4. 删除或修订本轮 memory,保留失败原因。 -5. 重新运行 Account/P1R focused gates 和 `git diff --check`。 - -## 验收标准 - -执行版本身可以进入实现前批准点的条件: - -1. 本文件已写入 `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md`。 -2. `.agent` 记录执行版当前状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report、业务实现当前无新增 diff。 -5. fresh execution spec/scope review PASS。 -6. fresh execution quality/feasibility review PASS。 - -实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。 diff --git a/docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md b/docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md deleted file mode 100644 index 7e16ba23..00000000 --- a/docs/agent-specs/2026-06-11-P1RContentCompletedApproval审阅版.md +++ /dev/null @@ -1,450 +0,0 @@ -# P1R Content Completed Approval 审阅版 - -日期:2026-06-11 - -## 结论 - -不建议把 Content 51 个 operation 一次性整域推进 `completed`。 - -推荐把 Content Completed Approval 拆成 operation-level 或证据切片推进。第一候选是用户端核心作品/章节/Block 读链路,加上已经在 P1R-7f 形成 source owner propagation evidence 的 `saveBlock` 与 `getBlockSourceAttribution`。第一批建议限定为 8 个 operation: - -```text -listWorks -getWork -listChapters -getChapter -listBlocks -getBlock -saveBlock -getBlockSourceAttribution -``` - -本审阅版只冻结 Content completed approval 的范围判断、证据缺口、推荐审批粒度和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,也不把 Content 或其它 domain 推进 `completed`。 - -```mermaid -flowchart TB - Start["当前 coverage
Content 51 dedicated / needs_verification"] --> Review["Content completed approval 审阅版
冻结证据标准"] - Review --> Split{"是否整域 51/51 completed?"} - Split -->|否,推荐| OpLevel["按证据切片 operation-level approval"] - Split -->|是,不推荐| Domain["domain-level approval
需 CRUD / source / admin / import / export / parse / planning / meta 全闭环"] - OpLevel --> First["第一候选 8 ops
work/chapter/block read + saveBlock + attribution read"] - First --> Contract{"是否触碰创建/更新/删除合同?"} - Contract -->|否| Exec["执行版
只写 operation allowlist + real HTTP/DB gate"] - Contract -->|是| ContractFix["先单独审批 OpenAPI/report commandId 合同修正"] - Exec --> FreshReview["fresh spec/scope review
fresh quality/feasibility review"] - FreshReview --> UserApproval{"用户明确批准
Content 状态推进?"} - UserApproval -->|否| Stay["保持 Content 51 needs_verification"] - UserApproval -->|是| Change["最小修改 scanner/report/gates
运行 focused + P1R + _test + XML 防空跑"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支与远端同步,HEAD 为: - -```text -0032774 test(p1r): 收口 Market 第一批 completed approval 门禁 -``` - -当前 coverage summary: - -```text -total=233 -completed=131 -needsVerification=102 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前按 domain 聚合: - -```text -account total=33 completed=10 needsVerification=23 -ai total=41 completed=41 needsVerification=0 -content total=51 completed=0 needsVerification=51 -events total=1 completed=1 needsVerification=0 -knowledge total=59 completed=59 needsVerification=0 -market total=32 completed=4 needsVerification=28 -meta total=16 completed=16 needsVerification=0 -``` - -Content 51 个 operation 当前全部为 `dedicated / needs_verification`: - -| operationId | Method | Path | 当前状态 | -|---|---|---|---| -| `adminListExportTasks` | `GET` | `/admin-api/muse/content/export-tasks` | `dedicated / needs_verification` | -| `adminListImportTasks` | `GET` | `/admin-api/muse/content/import-tasks` | `dedicated / needs_verification` | -| `adminListWorks` | `GET` | `/admin-api/muse/content/works` | `dedicated / needs_verification` | -| `adminGetWork` | `GET` | `/admin-api/muse/content/works/{workId}` | `dedicated / needs_verification` | -| `adminListChapters` | `GET` | `/admin-api/muse/content/works/{workId}/chapters` | `dedicated / needs_verification` | -| `adminRiskAction` | `POST` | `/admin-api/muse/content/works/{workId}/risk-actions` | `dedicated / needs_verification` | -| `confirmChapterParseResult` | `POST` | `/app-api/muse/chapter-parse-results/{resultId}/confirm` | `dedicated / needs_verification` | -| `rejectChapterParseResult` | `POST` | `/app-api/muse/chapter-parse-results/{resultId}/reject` | `dedicated / needs_verification` | -| `downloadExportPackage` | `GET` | `/app-api/muse/downloads/{credentialId}` | `dedicated / needs_verification` | -| `getExportTask` | `GET` | `/app-api/muse/export-tasks/{taskId}` | `dedicated / needs_verification` | -| `getImportTask` | `GET` | `/app-api/muse/import-tasks/{taskId}` | `dedicated / needs_verification` | -| `getParseJob` | `GET` | `/app-api/muse/parse-jobs/{jobId}` | `dedicated / needs_verification` | -| `listParseJobChapters` | `GET` | `/app-api/muse/parse-jobs/{jobId}/chapters` | `dedicated / needs_verification` | -| `batchConfirmChapters` | `POST` | `/app-api/muse/parse-jobs/{jobId}/chapters/batch-confirm` | `dedicated / needs_verification` | -| `retryParseJob` | `POST` | `/app-api/muse/parse-jobs/{jobId}/retry` | `dedicated / needs_verification` | -| `listWorks` | `GET` | `/app-api/muse/works` | `dedicated / needs_verification` | -| `createWork` | `POST` | `/app-api/muse/works` | `dedicated / needs_verification` | -| `deleteWork` | `DELETE` | `/app-api/muse/works/{workId}` | `dedicated / needs_verification` | -| `getWork` | `GET` | `/app-api/muse/works/{workId}` | `dedicated / needs_verification` | -| `updateWork` | `PUT` | `/app-api/muse/works/{workId}` | `dedicated / needs_verification` | -| `deleteBlock` | `DELETE` | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated / needs_verification` | -| `getBlock` | `GET` | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated / needs_verification` | -| `saveBlock` | `PUT` | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated / needs_verification` | -| `mergeBlocks` | `POST` | `/app-api/muse/works/{workId}/blocks/{blockId}/merge` | `dedicated / needs_verification` | -| `getBlockSourceAttribution` | `GET` | `/app-api/muse/works/{workId}/blocks/{blockId}/source-attribution` | `dedicated / needs_verification` | -| `splitBlock` | `POST` | `/app-api/muse/works/{workId}/blocks/{blockId}/split` | `dedicated / needs_verification` | -| `mergeBlockSuggestion` | `POST` | `/app-api/muse/works/{workId}/blocks/{blockId}/suggestion-merges` | `dedicated / needs_verification` | -| `listChapters` | `GET` | `/app-api/muse/works/{workId}/chapters` | `dedicated / needs_verification` | -| `createChapter` | `POST` | `/app-api/muse/works/{workId}/chapters` | `dedicated / needs_verification` | -| `deleteChapter` | `DELETE` | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated / needs_verification` | -| `getChapter` | `GET` | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated / needs_verification` | -| `updateChapter` | `PUT` | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated / needs_verification` | -| `listBlocks` | `GET` | `/app-api/muse/works/{workId}/chapters/{chapterId}/blocks` | `dedicated / needs_verification` | -| `createBlock` | `POST` | `/app-api/muse/works/{workId}/chapters/{chapterId}/blocks` | `dedicated / needs_verification` | -| `reorderChapters` | `PUT` | `/app-api/muse/works/{workId}/chapters/{chapterId}/reorder` | `dedicated / needs_verification` | -| `validateDynamicFields` | `POST` | `/app-api/muse/works/{workId}/dynamic-fields/validate` | `dedicated / needs_verification` | -| `exportWork` | `POST` | `/app-api/muse/works/{workId}/export` | `dedicated / needs_verification` | -| `createExportTask` | `POST` | `/app-api/muse/works/{workId}/export-tasks` | `dedicated / needs_verification` | -| `createImportTask` | `POST` | `/app-api/muse/works/{workId}/import-tasks` | `dedicated / needs_verification` | -| `listMetaProjections` | `GET` | `/app-api/muse/works/{workId}/meta-projections` | `dedicated / needs_verification` | -| `getMetaProjection` | `GET` | `/app-api/muse/works/{workId}/meta-projections/{projectionKey}` | `dedicated / needs_verification` | -| `createParseJob` | `POST` | `/app-api/muse/works/{workId}/parse-jobs` | `dedicated / needs_verification` | -| `getPlanning` | `GET` | `/app-api/muse/works/{workId}/planning` | `dedicated / needs_verification` | -| `listPlanningCandidates` | `GET` | `/app-api/muse/works/{workId}/planning/candidates` | `dedicated / needs_verification` | -| `createPlanningCandidate` | `POST` | `/app-api/muse/works/{workId}/planning/candidates` | `dedicated / needs_verification` | -| `getPlanningCandidate` | `GET` | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}` | `dedicated / needs_verification` | -| `confirmPlanningCandidate` | `POST` | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}/confirm` | `dedicated / needs_verification` | -| `discardPlanningCandidate` | `POST` | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}/discard` | `dedicated / needs_verification` | -| `createStyleCheck` | `POST` | `/app-api/muse/works/{workId}/planning/style-checks` | `dedicated / needs_verification` | -| `getStyleCheckResult` | `GET` | `/app-api/muse/works/{workId}/planning/style-checks/{jobId}` | `dedicated / needs_verification` | -| `savePlanningItem` | `PUT` | `/app-api/muse/works/{workId}/planning/{sectionKey}` | `dedicated / needs_verification` | - -## 已验证实现证据 - -### 子代理只读盘点结论 - -本轮并行只读复核得到两类结论: - -- 核心 Content explorer 支持先做 owned work / chapter / block / source 小批量,并从实现证据角度把 `createWork`、`updateWork`、`deleteWork`、`createChapter`、`updateChapter`、`deleteChapter`、`createBlock` 等也列为强候选。 -- 外部依赖 explorer 明确导入、导出、解析确认、Meta projection、AI planning candidate / style check / suggestion merge 都不应进入第一批;admin read、正式 planning section 可作为后续早批候选,但应独立切片。 - -本审阅版没有采纳 15-operation 首批方案。原因不是这些实现一定不可用,而是当前只读盘点已发现若干创建/更新/删除写命令存在 OpenAPI / coverage `requiresCommandId` 口径与服务端 VO 不一致的风险。第一批若纳入这些 operation,就会把合同修正夹带进 completed approval。更稳的路线是先审批 8 个不需要修改 OpenAPI 的核心读 + `saveBlock` + source attribution 闭环,把写命令合同修正放到后续单独审阅。 - -### Content owner 与入口 - -Content owner 位于 `muse-module-content/muse-module-content-server`。 - -现有 dedicated Controller 覆盖 Content 51 个 operation: - -- `AppContentController` -- `AppContentStructureController` -- `AppContentSourceController` -- `AppContentExportController` -- `AppContentImportParseController` -- `AppContentPlanningController` -- `AppContentMetaProjectionController` -- `AdminContentController` - -`P1rContentRealApiGateTest` 当前明确要求 Content 51 个 operation 全部保持 `dedicated / needs_verification`。这证明 P1R-1 已退出 catch-all / generic persistence / SSE placeholder,不证明任何 Content operation 已达到 completed 口径。 - -### Content 本域核心服务 - -用户端核心作品/章节/Block 链路由 `ContentAppServiceImpl` 承载: - -- `listWorks` / `getWork`:读取 owner 下作品。 -- `listChapters` / `getChapter`:先校验 work owner,再读取章节和 Block 摘要。 -- `listBlocks` / `getBlock`:先校验 work / chapter / block 归属,再读取 Block。 -- `saveBlock`:校验 commandId、owner、expectedRevision,更新 Block,写 `muse_content_block_source_attribution(source_status=active)`,创建 Content event publish outbox,并记录审计。 - -来源归因读链路由 `ContentSourceServiceImpl.getBlockSourceAttribution` 承载,基于当前 Block revision 查询 `muse_content_block_source_attribution` 并返回来源摘要。 - -### P1R-7f saveBlock 证据 - -P1R-7f 已形成以下 `needs_verification` evidence: - -```text -ContentAppServiceImpl.saveBlock --> muse_content_block_source_attribution(source_status=active) --> muse_content_event_publish_outbox --> ContentEventPublishWorker --> EventsPublishApi --> muse_unified_event --> /app-api/muse/events 对 owner 可见 -``` - -该证据覆盖 saveBlock 首次成功路径、command replay 不重复创建 outbox、revision conflict 不更新 Block / 不写 source attribution / 不创建 outbox、payload allowlist、worker retry/dead_letter、V21 Flyway `_test` 和 dependency tree gate。 - -但它仍只是 source owner propagation evidence,不等同于 `saveBlock` API operation completed。Content completed approval 仍必须补 fresh HTTP/API + 真实 PostgreSQL `_test` + scanner/report/gate 同步证据。 - -## 推荐第一候选 - -推荐第一批候选限定为 8 个 operation: - -| operationId | 推荐原因 | 必补 completed-grade 证据 | -|---|---|---| -| `listWorks` | 本域读侧,依赖 owner 过滤和分页,不需要外部 owner | MockMvc HTTP + 真实 DB seed,验证 owner 可见/不可见、分页和响应 DTO | -| `getWork` | 本域读侧,owner guard 明确 | HTTP + DB 验证存在、跨 owner forbidden/not found、DTO 字段 | -| `listChapters` | 本域章节读侧,先校验 work owner | HTTP + DB 验证章节排序、blockCount/wordCount、跨 owner 拒绝 | -| `getChapter` | 本域章节详情,返回 blocks | HTTP + DB 验证章节详情、Block 列表、章节不属 work 拒绝 | -| `listBlocks` | 本域 Block 列表,先校验 chapter 从属 | HTTP + DB 验证列表、排序、跨 work/chapter 拒绝 | -| `getBlock` | 本域 Block 详情,owner/work 归属明确 | HTTP + DB 验证 DTO、跨 owner 拒绝 | -| `saveBlock` | 本域核心写路径,已有 P1R-7f source/event evidence | HTTP + DB 验证 commandId、revision CAS、source attribution、outbox、replay、conflict no-write | -| `getBlockSourceAttribution` | 与 saveBlock 形成读后验证闭环 | HTTP + DB 验证当前 revision 来源、空来源、跨 owner 拒绝 | - -推荐理由: - -- 范围只覆盖 Content 本域核心 owned work / chapter / block,不进入 AI / Knowledge / Meta / FileService 等外部 owner。 -- 读链路可以用测试种子建立作品、章节、Block,避免把 `createWork` / `createChapter` / `createBlock` 的合同缺口带入第一批。 -- `saveBlock` 是 Content 当前最强证据链路,已有 source attribution 和 Events outbox 真实链路基础,但仍需补 API operation completed 级别的 fresh HTTP/DB gate。 -- `getBlockSourceAttribution` 与 `saveBlock` 组成可验证闭环:写入当前 revision 来源事实,再读取同一 revision 的来源摘要。 - -替代方案是扩大到 15 个用户端 work / chapter / block CRUD operation。该方案只有在用户明确批准“先修 Content 写命令 OpenAPI / coverage commandId 合同,再做 completed approval”时才应进入执行版;默认不采用。 - -如果执行版按 8 个 operation 推进,获批后的目标值只能是: - -```text -total=233 -completed=139 -needsVerification=94 -content completed=8 -content needsVerification=43 -``` - -该目标值只是后续获批后的执行目标,不是本审阅版当前状态。 - -## 暂不推荐第一批的切片 - -### create/update/delete 与结构编辑写命令 - -以下 operation 暂不纳入第一批: - -- `createWork` -- `updateWork` -- `deleteWork` -- `createChapter` -- `updateChapter` -- `deleteChapter` -- `createBlock` -- `reorderChapters` -- `splitBlock` -- `mergeBlocks` -- `deleteBlock` - -主要原因: - -- 只读盘点已发现部分写接口的 OpenAPI / coverage `requiresCommandId` 口径与服务端 VO 不一致。例如 `WorkCreateReqVO`、`WorkUpdateReqVO`、`ChapterCreateReqVO` 服务端要求 `commandId`,但当前 coverage 对 `createWork`、`updateWork`、`createChapter` 仍显示 `requiresCommandId=false`,OpenAPI 对 `createWork` / `updateWork` / `createChapter` 的 request body 也没有把 commandId 和 expectedRevision 等写命令字段完整列入 required。 -- `deleteWork` 当前 OpenAPI 路径缺写命令 request body,而服务端 controller 使用 `RevisionCommandReqVO`。 -- 结构编辑写命令会影响 work/chapter/block revision、排序、sourceSnapshot、审计和幂等 replay,适合在合同修正审阅后单独切片。 - -执行版不得为了把这些 operation completed 而顺手改 OpenAPI 或 scanner。若用户希望第二批处理写命令,必须先做 Content 写命令合同一致性审阅版。 - -### AI suggestion 合并 - -`mergeBlockSuggestion` 暂不推荐第一批。 - -它依赖 `ContentAiSuggestionFacade` 查询 AI owner 投影,当前默认 `UnavailableContentAiSuggestionFacade` 明确返回 unavailable。服务端要求 suggestion status、revision、quality result、source status、authorization snapshot 和 license restriction 全部可用,不能在 AI owner 未闭合时伪造成功。 - -### 导入 / 解析 / Knowledge 草稿 - -以下 operation 暂不推荐第一批: - -- `createImportTask` -- `getImportTask` -- `createParseJob` -- `getParseJob` -- `retryParseJob` -- `listParseJobChapters` -- `confirmChapterParseResult` -- `rejectChapterParseResult` -- `batchConfirmChapters` -- `adminListImportTasks` - -主要缺口: - -- `ContentFileFacade` 默认 unavailable,导入文件扫描、存储引用和失败阶段归 FileService owner。 -- `ContentParseJobFacade` 默认 unavailable,Parse Job、章节解析结果和重试事实归 AI owner。 -- `ContentKnowledgeDraftFacade` 默认 unavailable,确认解析结果后创建知识草稿归 Knowledge owner。 -- 确认/批量确认链路需要 Knowledge draft 创建与 AI parse decision record 双 owner 成功,不能只凭 Content 编排服务 completed。 - -### 导出 / 下载 - -以下 operation 暂不推荐第一批: - -- `exportWork` -- `createExportTask` -- `getExportTask` -- `downloadExportPackage` -- `adminListExportTasks` - -主要缺口: - -- `ContentFileFacade.createExportPackage` 和 `downloadExportPackage` 默认 unavailable。 -- 导出包、下载凭证、packageRef、字节内容和过期校验归 FileService owner,Content 当前只能 fail-closed。 -- `getExportTask` / admin export read 可在后续通过 DB seed 单独审批,但不应与创建/下载闭环混为一批。 - -### Meta projection 与动态字段 - -以下 operation 暂不推荐第一批: - -- `listMetaProjections` -- `getMetaProjection` -- `validateDynamicFields` - -主要缺口: - -- `ContentMetaFacade` 默认 unavailable。 -- MetaSchema 解释、字段校验和写入路由建议归 Meta owner,Content 服务明确不复制 Meta 规则、不写 Content 事实表。 - -### Planning candidate / style check - -以下 operation 暂不推荐第一批: - -- `createPlanningCandidate` -- `listPlanningCandidates` -- `getPlanningCandidate` -- `confirmPlanningCandidate` -- `discardPlanningCandidate` -- `createStyleCheck` -- `getStyleCheckResult` - -主要缺口: - -- Candidate 生成、候选投影、丢弃决策和 style check 运行事实归 AI owner。 -- `ContentPlanningCandidateFacade` 与 `ContentStyleCheckFacade` 默认 unavailable。 -- `confirmPlanningCandidate` 会把 AI candidate 写入正式 planning,必须验证 candidate source、quality result、authorization snapshot 和 revision,不适合混入核心 Block 第一批。 - -### Admin governance - -以下 operation 可作为后续候选,但不推荐第一批: - -- `adminListWorks` -- `adminGetWork` -- `adminListChapters` -- `adminRiskAction` - -理由: - -- admin read 是本域读侧,但包含 RBAC、治理摘要、risk flags、word count 汇总和 admin DTO,不应与用户端核心编辑首批混在一起。 -- `adminRiskAction` 是治理写路径,涉及 `expectedVersion`、行锁、targetScope、targetIds 归属、幂等 envelope 和治理动作状态,适合单独做 Admin governance completed approval。 - -### Planning 正式 section - -以下 operation 可作为后续本域候选: - -- `getPlanning` -- `savePlanningItem` - -理由: - -- 它们主要依赖 Content 本域 `muse_content_planning_section`,不是外部 AI candidate runtime。 -- 但 planning section 有 schemaVersion、projectionVersion、sourceSnapshot、upsert CAS 与 command replay 语义,建议作为第二批本域 planning 切片,不混入 Block 第一批。 - -## 后续执行版硬条件 - -如用户批准进入执行版,执行版必须先通过 fresh spec/scope review 与 fresh quality/feasibility review,且至少满足以下硬条件。 - -### 允许的最小 diff - -默认允许: - -- 新增/修改 Content completed approval 执行版文档。 -- 更新 `docs/agent-specs/.agent` 的阶段状态。 -- 新增 Content 第一批 completed approval 的真实 `_test` gate。 -- 在 scanner 中只追加用户批准的 `content:` operation-level allowlist,不加入 `content` domain-level allowlist。 -- 重新生成 coverage report。 -- 同步 P1R gate tests 中的 summary / Content 状态断言,以及 Account / Market / Meta 等非目标域防回退断言。 -- 增强既有 Content events publish gate 仅限测试证据安全边界,例如 `P1rContentEventsPublishFlywayMigrationIT` 的 `p1r.flyway.url/user` 保存恢复与组合顺序验证;不得改变业务断言口径来掩盖回退。 -- 新增 `docs/memorys/YYYY-MM-DD-P1RContent状态推进.md`。 - -默认禁止: - -- 修改 Content OpenAPI。 -- 修改 Content 业务实现来掩盖 coverage 缺口。 -- 修改 SQL migration。 -- 把 `content` 加入 domain-level completed allowlist。 -- 把 create/update/delete/import/export/parse/planning/meta/admin operation 顺手推进 completed。 - -如果执行中发现必须修改 OpenAPI、业务实现或 SQL,必须停下说明原因并取得用户明确批准。 - -### 必须同步的 gate 文件 - -执行版必须枚举所有读取全局 summary、Content 状态,或作为 `saveBlock` source/event 前置证据的 P1R gate。当前至少包括: - -- `P1rApiCoverageReportTest` -- `P1rContentRealApiGateTest` -- `P1rEventsRealApiGateTest` -- `P1rAiRealApiGateTest` -- `P1rKnowledgeRealApiGateTest` -- `P1rMarketRealApiGateTest` -- `P1rAccountRealApiGateTest` -- `P1rMetaRealApiGateTest` -- `P1rContentEventsPublishMigrationSqlTest` -- `P1rContentEventsPublishDependencyTest` -- `P1rContentEventsPublishEndToEndTest` -- `P1rContentEventsPublishFlywayMigrationIT` - -目标值必须公式化为: - -```text -completed = 131 + N -needsVerification = 102 - N -Content completed = N -Content needsVerification = 51 - N -``` - -其中 `N` 只能来自用户明确批准的 Content operation。按本审阅版推荐第一批,`N=8`。 - -### 必须新增或增强的 runtime evidence - -执行版必须包含类似 `P1rContentCoreCompletedApprovalIT` 的真实运行证据: - -- 使用 WebApplicationContext / MockMvc 走 HTTP controller 入口。 -- 使用真实 PostgreSQL `_test` 数据库,执行 Flyway V1-V21。 -- 明确断言 Flyway current=V21、成功 SQL migration 数量为 21、V21 Content event outbox 表/索引/约束/trigger 存在,并验证 invalid outbox insert 被拒绝。 -- V9 planning schema 不属于本第一批 8 个 operation 的业务验收范围;执行版不得为了本批去扩 V9 planning 业务 schema 断言。 -- 使用真实 mapper / service 写读 `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_block_source_attribution` 和 `muse_content_event_publish_outbox`。 -- 覆盖 tenant 与 owner 双隔离:至少 seed 两个 tenant,验证同 owner 不同 tenant 不可见、跨 tenant `saveBlock` 不写 block/source/outbox,并验证本批写入行的 `tenant_id` 正确。 -- 覆盖 `saveBlock` 首次成功、command replay、revision conflict、source attribution、outbox no-duplicate / conflict no-write。 -- 覆盖 `getBlockSourceAttribution` 当前 revision 读模型。 -- 为 8 个 operation 建立 shadow-path matrix,不能只测 happy path: - - `listWorks`:有数据分页、空作品页、跨 tenant 同 owner 不可见。 - - `getWork`:存在、ID 不存在、跨 owner / 跨 tenant 拒绝。 - - `listChapters`:有章节、空章节列表、work 不存在或跨 owner / 跨 tenant 拒绝。 - - `getChapter`:存在并返回 blocks、chapter 不存在、chapter 不属于 path work、跨 owner / 跨 tenant 拒绝。 - - `listBlocks`:有 Block、空 Block 列表、chapter 不属于 path work、跨 owner / 跨 tenant 拒绝。 - - `getBlock`:存在、block 不存在、block 不属于 path work、跨 owner / 跨 tenant 拒绝。 - - `saveBlock`:happy、command replay、revision conflict、缺 `commandId`、缺 `expectedRevision`、缺 `sourceSnapshot`、block 属于同 owner 的另一个 work、跨 owner / 跨 tenant 拒绝;所有失败路径均断言 block/source/outbox/command no-write 或事务回滚后的行数不变。 - - `getBlockSourceAttribution`:当前 revision 有来源、当前 revision 空来源、block 不存在、block 属于同 owner 的另一个 work、跨 owner / 跨 tenant 拒绝,且 path mismatch 不泄露 attribution。 - - 每个路径必须断言 HTTP 状态、CommonResult 体、关键 DTO 字段和 DB 行数。 -- 把 `P1rContentEventsPublishMigrationSqlTest`、`P1rContentEventsPublishDependencyTest`、`P1rContentEventsPublishEndToEndTest`、`P1rContentEventsPublishFlywayMigrationIT` 纳入必跑和 XML 防空跑,作为 `saveBlock` completed approval 的 source/event 前置证据。 -- Content completed IT 与 Content events Flyway IT 如果读取并脱敏 `p1r.flyway.url/user`,必须保存原始 system property 并在 `@AfterAll` 恢复;执行版必须包含普通顺序与 `-Dsurefire.runOrder=reversealphabetical` 或等价反序组合验证,防止同一 Surefire JVM 内污染后续 Flyway IT。 -- 读取并校验 fresh Surefire XML,要求 tests 数量大于 0,failures/errors/skipped 均为 0,且 XML mtime 在本轮运行窗口内。 -- raw Surefire XML 只作为本地验证产物;若需要外发测试报告,必须先清洗 DB host/user,且密码只能来自环境变量,不能通过 JVM system property 或 JDBC URL query 传入。 - -### 必须保留的非目标断言 - -执行版和实现阶段必须继续证明: - -- Account remaining 23 个 operation 仍保持 `dedicated / needs_verification`。 -- Market remaining 28 个 operation 仍保持 `dedicated / needs_verification`。 -- Content 未获批准的 43 个 operation 仍保持 `dedicated / needs_verification`。 -- AI / Knowledge / Events / Meta 已有 completed 口径不回退。 -- OpenAPI protected diff 为空,除非用户单独批准合同修正。 - -## 待确认项 - -1. 是否接受 Content 第一批只审批上述 8 个 operation,不做 Content 51/51。 -2. 是否接受第一批不修改 OpenAPI;创建/更新/删除写命令的 commandId 合同缺口放到后续单独审阅。 -3. 是否接受后续执行版目标值按 `N=8` 计算为 `completed=139`、`needsVerification=94`。 -4. 是否允许执行版把 `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest`、`P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rMetaRealApiGateTest` 的 summary / Content / 非目标域状态防回退断言,以及 `P1rContentEventsPublish*` gate 的必跑与测试证据安全边界修正,列为后续获批 implementation 的 allowed diff。 diff --git a/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md b/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md deleted file mode 100644 index 237342f3..00000000 --- a/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md +++ /dev/null @@ -1,311 +0,0 @@ -# P1R Market Completed Approval 审阅版 - -日期:2026-06-11 - -## 结论 - -不建议把 Market 32 个 operation 一次性整域推进 `completed`。 - -推荐把 Market Completed Approval 拆成 operation-level 或证据切片推进。第一候选是 Marketplace 浏览/收藏切片,但它还不能直接进入实现:`favoriteAsset` / `unfavoriteAsset` 的实现要求 `X-Command-Id`,而当前 OpenAPI 与 coverage report 仍显示这两个 operation `requiresCommandId=false`。这是合同不一致,必须在执行版中单独审批修正或把首批降级为纯读 operation。 - -本审阅版只冻结 Market completed approval 的范围判断、证据缺口、推荐审批粒度和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,也不把 Market 或其它 domain 推进 `completed`。 - -```mermaid -flowchart TB - Start["当前 coverage
Market 32 dedicated / needs_verification"] --> Review["Market completed approval 审阅版
冻结证据标准"] - Review --> Split{"是否整域 32/32 completed?"} - Split -->|否,推荐| Slice["按证据切片 operation-level approval"] - Split -->|是,不推荐| Domain["domain-level approval
需购买/安装/handoff/发布/治理/申诉全闭环"] - Slice --> Candidate["第一候选
Marketplace 浏览/收藏"] - Candidate --> Contract{"favorite/unfavorite 合同一致?"} - Contract -->|否| ReadOnly["降级为纯读候选
或先审批 OpenAPI 合同修正"] - Contract -->|是| Exec["执行版
列出 operation、allowed diff、fresh 验证"] - Exec --> FreshReview["fresh spec/scope review
fresh quality/feasibility review"] - FreshReview --> UserApproval{"用户明确批准
Market 状态推进?"} - UserApproval -->|否| Stay["保持 Market 32 needs_verification"] - UserApproval -->|是| Change["最小修改 scanner/report/gates
运行 focused + P1R + _test + XML 防空跑"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支与远端同步,HEAD 为: - -```text -f18116a test(p1r): 收口 Account 第一批 completed approval 门禁 -``` - -当前 coverage summary: - -```text -total=233 -completed=127 -needsVerification=106 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前按 domain 聚合: - -```text -account total=33 completed=10 needsVerification=23 -ai total=41 completed=41 needsVerification=0 -content total=51 completed=0 needsVerification=51 -events total=1 completed=1 needsVerification=0 -knowledge total=59 completed=59 needsVerification=0 -market total=32 completed=0 needsVerification=32 -meta total=16 completed=16 needsVerification=0 -``` - -Market 32 个 operation 当前全部为 `dedicated / needs_verification`: - -| operationId | Method | Path | 当前状态 | -|---|---|---|---| -| `adminListAppeals` | `GET` | `/admin-api/muse/market/appeals` | `dedicated / needs_verification` | -| `adminGetAppeal` | `GET` | `/admin-api/muse/market/appeals/{appealId}` | `dedicated / needs_verification` | -| `adminResolveAppeal` | `POST` | `/admin-api/muse/market/appeals/{appealId}/resolve` | `dedicated / needs_verification` | -| `adminListMarketAssets` | `GET` | `/admin-api/muse/market/assets` | `dedicated / needs_verification` | -| `adminGetMarketAsset` | `GET` | `/admin-api/muse/market/assets/{assetId}` | `dedicated / needs_verification` | -| `adminDelistAsset` | `POST` | `/admin-api/muse/market/assets/{assetId}/delist` | `dedicated / needs_verification` | -| `adminPreviewGovernanceImpact` | `POST` | `/admin-api/muse/market/assets/{assetId}/governance-impact` | `dedicated / needs_verification` | -| `adminRecallAsset` | `POST` | `/admin-api/muse/market/assets/{assetId}/recall` | `dedicated / needs_verification` | -| `adminListPublishRequests` | `GET` | `/admin-api/muse/market/publish-requests` | `dedicated / needs_verification` | -| `adminApprovePublishRequest` | `POST` | `/admin-api/muse/market/publish-requests/{requestId}/approve` | `dedicated / needs_verification` | -| `adminRejectPublishRequest` | `POST` | `/admin-api/muse/market/publish-requests/{requestId}/reject` | `dedicated / needs_verification` | -| `submitAppeal` | `POST` | `/app-api/muse/marketplace/appeals` | `dedicated / needs_verification` | -| `supplementAppeal` | `POST` | `/app-api/muse/marketplace/appeals/{appealId}/supplements` | `dedicated / needs_verification` | -| `withdrawAppeal` | `POST` | `/app-api/muse/marketplace/appeals/{appealId}/withdraw` | `dedicated / needs_verification` | -| `listMarketplaceAssets` | `GET` | `/app-api/muse/marketplace/assets` | `dedicated / needs_verification` | -| `getMarketplaceAsset` | `GET` | `/app-api/muse/marketplace/assets/{assetId}` | `dedicated / needs_verification` | -| `createBindPrecheck` | `POST` | `/app-api/muse/marketplace/assets/{assetId}/bind-precheck` | `dedicated / needs_verification` | -| `unfavoriteAsset` | `DELETE` | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `dedicated / needs_verification` | -| `favoriteAsset` | `POST` | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `dedicated / needs_verification` | -| `getGovernanceImpact` | `GET` | `/app-api/muse/marketplace/assets/{assetId}/governance-impact` | `dedicated / needs_verification` | -| `installMarketplaceAsset` | `POST` | `/app-api/muse/marketplace/assets/{assetId}/install` | `dedicated / needs_verification` | -| `purchaseAsset` | `POST` | `/app-api/muse/marketplace/assets/{assetId}/purchase` | `dedicated / needs_verification` | -| `listMarketplaceCategories` | `GET` | `/app-api/muse/marketplace/categories` | `dedicated / needs_verification` | -| `createMarketplaceHandoff` | `POST` | `/app-api/muse/marketplace/handoffs` | `dedicated / needs_verification` | -| `getHandoffStatus` | `GET` | `/app-api/muse/marketplace/handoffs/{handoffToken}` | `dedicated / needs_verification` | -| `cancelHandoff` | `POST` | `/app-api/muse/marketplace/handoffs/{handoffToken}/cancel` | `dedicated / needs_verification` | -| `listMyPublishRecords` | `GET` | `/app-api/muse/marketplace/my-publish-records` | `dedicated / needs_verification` | -| `savePublishDraft` | `POST` | `/app-api/muse/marketplace/publish-drafts` | `dedicated / needs_verification` | -| `runPublishCheck` | `POST` | `/app-api/muse/marketplace/publish-drafts/{draftId}/checks` | `dedicated / needs_verification` | -| `submitPublishRequest` | `POST` | `/app-api/muse/marketplace/publish-requests` | `dedicated / needs_verification` | -| `withdrawPublishRequest` | `POST` | `/app-api/muse/marketplace/publish-requests/{requestId}/withdraw` | `dedicated / needs_verification` | -| `listMarketplaceRecommendations` | `GET` | `/app-api/muse/marketplace/recommendations` | `dedicated / needs_verification` | - -## 已验证实现证据 - -### Market owner 与入口 - -Market owner 当前位于 `muse-module-market`,负责市场来源侧事实、授权、安装、handoff、发布、治理、申诉、浏览和收藏。 - -现有 dedicated Controller 覆盖 Market 32 个 operation: - -- `AppMuseMarketplaceAssetController` -- `AppMuseMarketLicenseController` -- `AppMuseMarketHandoffController` -- `AppMuseMarketPublishController` -- `AppMuseMarketAppealController` -- `AdminMuseMarketAssetController` -- `AdminMuseMarketReviewController` -- `AdminMuseMarketAppealController` - -`P1rMarketRouteOwnershipTest` 已证明 Market 路由由 dedicated controller 持有,不再走 `MuseContractPersistenceService` 旧合同兜底。 - -### 数据库与迁移证据 - -V15 Market Real API schema 已存在: - -- `muse-cloud/sql/muse/V15__extend_market_real_api_schema.sql` - -历史留痕记录 V15 已在真实 PostgreSQL `_test` 库通过 Flyway: - -```text -flyway_success=true -migrations_executed=15 -successful_migration_count=15 -target_schema_version=15 -flyway_latest=15:extend market real api schema -schema_version=15 -``` - -历史 Market focused tests 记录为 191/191 pass,P1R Market gates 记录为 24/24 pass。但这些证据只证明 P1R-6 已把 Market 32 个 operation 收口到 `dedicated / needs_verification`,不能作为本轮 completed approval 的 fresh runtime 证据。 - -### 子代理只读盘点结论 - -本轮按 Market 业务切片并行只读复核,结论如下: - -- Marketplace 浏览/收藏 6 个 operation 是第一批 operation-level completed 候选,但仍缺合同一致性和真实 HTTP + DB 闭环。 -- purchase / install / handoff / bind-precheck / governanceImpact 7 个 operation 不适合作为第一批;缺目标 owner 消费 handoff token、回写 completed、Account 读侧 E2E 和跨 owner governance 影响闭环。 -- publish / review / governance 13 个 operation 的实现和测试证据较强,可作为后续候选切片;但当前仍缺审阅版/执行版冻结、fresh completed 级验证和 Market 整域覆盖,不应直接推进 completed。 -- appeal / events / projection 6 个申诉相关 operation 不适合作为第一批;申诉事件是 `muse_market_appeal_event` 审计事实,不是统一 Events publish outbox;`restore` 治理 action 当前不进入 MarketEventPublishOutbox;MarketAccountProjectionProvider 不覆盖申诉。 - -## 推荐第一候选 - -推荐第一批候选限定为 Marketplace 浏览/收藏切片: - -```text -listMarketplaceAssets -getMarketplaceAsset -listMarketplaceCategories -listMarketplaceRecommendations -favoriteAsset -unfavoriteAsset -``` - -推荐理由: - -- Controller / Service / Mapper 链路相对短,主要停留在 Market 本域。 -- 浏览读侧依赖资产、版本、收藏状态和可见性,不需要真实支付、安装、handoff 或目标 owner 消费。 -- 收藏写侧已有 command replay、owner 登录校验、可见资产校验、`muse_market_favorite` active/inactive 审计事实。 -- 风险面小于 purchase/install/handoff、publish/review/governance、appeal/projection 切片。 - -但该候选不能直接批准。执行版必须先解决以下硬条件: - -1. `favoriteAsset` / `unfavoriteAsset` 的 `X-Command-Id` 合同不一致:实现和 controller test 要求 header,OpenAPI 与 coverage report 仍显示 `requiresCommandId=false`。 -2. `listMarketplaceRecommendations` 当前是可解释 fallback 排序,不是独立推荐系统读模型;执行版必须明确 MVP completed 口径接受 fallback,或把该 operation 留在 `needs_verification`。 -3. 缺少真实 Spring context + 测试数据库的 HTTP/DB 闭环:上架资产种子、列表/详情/分类/推荐、收藏/取消、DB 状态、回放、冲突和不可见资产拒绝。 -4. 列表/推荐当前存在逐项取 currentVersion / favorite 的 N+1 查询形态;执行版必须给出分页上限、SQL/mapper 验证或明确的 MVP 接受边界。 -5. 当前 `P1rMarketRealApiGateTest` 明确要求 Market 32 个 operation 保持 `dedicated / needs_verification`;未获用户批准前不能修改 gate。 - -如果不允许在 Market completed approval 中修改 OpenAPI 合同,第一批应降级为纯读候选: - -```text -listMarketplaceAssets -getMarketplaceAsset -listMarketplaceCategories -``` - -`listMarketplaceRecommendations` 是否纳入纯读候选,取决于用户是否接受 fallback recommendation 作为当前 completed 口径。 - -## 暂不推荐的切片 - -### purchase / install / handoff / governanceImpact - -以下 operation 暂不推荐第一批 completed: - -- `purchaseAsset` -- `installMarketplaceAsset` -- `createBindPrecheck` -- `createMarketplaceHandoff` -- `getHandoffStatus` -- `cancelHandoff` -- `getGovernanceImpact` - -主要缺口: - -- `purchaseAsset` 只证明本地授权、purchase fact 和 Account projection 写入,不证明真实支付或 Account 读侧 E2E。 -- `installMarketplaceAsset` 明确不写目标 owner canonical facts,`targetFactsWritten=false`。 -- handoff token 只落 hash,缺真实目标 owner 消费 token、创建 owner precheck、回写 owner confirm result 或推进 handoff completed 的公开链路。 -- `MarketTargetOwnerFacade` 当前只生成目标页并做 owner/action 白名单,接口边界明确不消费 token、不写目标 owner facts。 -- `getGovernanceImpact` 当前主要聚合 Market 自有 authorization / installation / handoff / governance facts,不证明 AI / Knowledge / Content 目标 owner 的真实影响闭环。 - -### publish / review / governance - -以下 operation 可作为后续强候选,但不推荐直接作为第一批: - -- `savePublishDraft` -- `runPublishCheck` -- `submitPublishRequest` -- `withdrawPublishRequest` -- `listMyPublishRecords` -- `adminListPublishRequests` -- `adminApprovePublishRequest` -- `adminRejectPublishRequest` -- `adminListMarketAssets` -- `adminGetMarketAsset` -- `adminPreviewGovernanceImpact` -- `adminDelistAsset` -- `adminRecallAsset` - -已有证据显示它们具备草稿、检查、提交、审核、上架、治理 preview、delist/recall 和部分 Events outbox 链路。但它们仍不应直接 completed: - -- 当前没有 Market Completed Approval 执行版冻结这 13 个 operation 的审批范围和 allowed diff。 -- 仍缺 fresh focused tests、P1R mixed gates、真实 `_test`、XML 防空跑和必要的 HTTP/API 或跨 owner runtime 验收。 -- `MarketEventPublishOutboxService` 当前 allowlist 是 `delist / recall`,不能把所有发布/审核/治理操作都等同于 Events 可见闭环。 -- 该切片不能代表 Market 32/32 domain completed。 - -### appeals / restore / projection - -以下 operation 暂不推荐 first batch: - -- `submitAppeal` -- `supplementAppeal` -- `withdrawAppeal` -- `adminListAppeals` -- `adminGetAppeal` -- `adminResolveAppeal` - -主要缺口: - -- `submit/supplement/withdraw/resolve` 写入的是 `muse_market_appeal_event` 审计事实,不是统一 Events publish outbox。 -- `adminResolveAppeal` 的 restore / partially_restored 路径写 `actionType=restore`,当前 outbox service 会跳过。 -- `MarketAccountProjectionProvider` 只支持 `purchase / license / publish`,不覆盖申诉。 -- 因此不能把申诉审计、治理 outbox 和 Account projection 混成一个 completed 闭环。 - -## 非目标 - -- 不修改 7 个 OpenAPI: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/content/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/meta/openapi.yaml` -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.json`。 -- 不修改 `docs/superpowers/reports/p1r-api-coverage.md`。 -- 不修改 Market 业务实现。 -- 不把 Market 32 个 operation 推进 `completed`。 -- 不把 Market 加入 domain-level completed allowlist。 -- 不推进 Account remaining 23 / Content / 总 P1R completed。 -- 不把 P1R-7d Market governance event propagation evidence 当作 Market 整域 completed 证据。 - -## 后续执行版必须包含 - -1. Market 32 operation 的完整清单、当前状态和目标审批状态。 -2. 第一批候选 operation 的逐项 evidence map:Controller、Service、Mapper、权限、版本头、owner/tenant 隔离、幂等、审计、失败路径。 -3. 明确哪些 operation 继续 `needs_verification`,以及原因。 -4. `favoriteAsset` / `unfavoriteAsset` 合同处理方案:要么审批 OpenAPI `X-Command-Id` 修正,要么从第一批移除。 -5. `listMarketplaceRecommendations` fallback completed 口径:要么明确接受 fallback 推荐,要么保持 `needs_verification`。 -6. scanner approval 模型:只允许 operation-level allowlist,不允许 Market domain-level allowlist。 -7. `P1rMarketRealApiGateTest` 从禁止 completed 到审批后 partial completed gate 的修改策略。 -8. `P1rApiCoverageReportTest` summary 期望变更策略,必须只提升用户批准的 Market operation 数。 -9. legacy P1R mixed gate 同步策略:执行版必须枚举所有读取全局 summary 或 Market 状态的 gate,至少包括 `P1rApiCoverageReportTest`、`P1rMarketRealApiGateTest`、`P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`;需要同步断言的文件必须列入单独用户批准项、allowed diff 和 required XML 防空跑清单。 -10. Market partial completed 目标值必须公式化:若用户批准 `N` 个 Market operation,则 summary 只能变为 `completed=127+N`、`needsVerification=106-N`,Market 只能变为 `completed=N / needs_verification=32-N`;`N` 只能来自用户批准的 Market operation,不能由 Market domain-level allowlist 推导。 -11. Market focused tests 的 required class 清单与最低 XML tests 数;执行版必须 fresh rerun,不能只引用历史留痕。 -12. P1R mixed gates:必须覆盖 Market gate、coverage report gate、上述 legacy summary/Market gate,以及用于证明 Account remaining 23 / Content 不被推进的 gate;未列入 allowed diff 的非目标 gate 只允许运行和读取 XML,不能顺手改。 -13. 真实 PostgreSQL `_test`:至少覆盖 V1-V15 clean migrate;如新增 Market completed approval Flyway IT,应验证 V15 Market 表、索引、约束、trigger、favorite unique 和非法 insert 拒绝。 -14. 真实 HTTP + DB 或等价集成验收:覆盖上架资产可见性、publisher-only 非公开可见性、收藏 active/inactive、command replay、missing/invisible asset 拒绝。 -15. protected diff gate:7 个 OpenAPI 在批准前必须保持空 diff;若执行版要修正 market OpenAPI,必须把该文件列入单独用户批准和 allowed diff。 -16. allowed-diff gate:覆盖 unstaged、staged、untracked,并使用 `git -c core.quotePath=false`。 -17. rollback 策略:撤回 scanner allowlist、coverage report、gate test、可选 OpenAPI 修正和新增 memory,恢复目标 operation 为 `dedicated / needs_verification`。 - -## 验收标准 - -本审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md`。 -2. `.agent` 记录 Market completed approval 审阅版状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report 无 diff。 -5. 文档只定义审阅路线,不进入执行版或实现。 -6. fresh spec/scope review PASS。 -7. fresh quality/feasibility review PASS。 - -## 待确认项 - -1. 是否确认 Market 第一轮不做 domain-level 32/32 completed approval。 -2. 是否确认优先走 operation-level approval,并从 Marketplace 浏览/收藏切片开始。 -3. 是否允许后续执行版把 `docs/api-contracts/market/openapi.yaml` 的 `favoriteAsset` / `unfavoriteAsset` `X-Command-Id` 合同修正列为单独批准项。 -4. 是否接受 `listMarketplaceRecommendations` 当前 fallback recommendation 作为 MVP completed 口径;如果不接受,该 operation 第一轮继续 `needs_verification`。 -5. 是否确认 purchase / install / handoff / governanceImpact / appeals 在没有真实跨 owner 闭环前继续保持 `needs_verification`。 -6. 是否确认审阅版 fresh 双 review PASS 后,再写 Market completed approval 执行版。 diff --git a/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md b/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md deleted file mode 100644 index eb373803..00000000 --- a/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md +++ /dev/null @@ -1,738 +0,0 @@ -# P1R Market 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-11 - -## 结论 - -本执行版推荐第一轮只做 Market operation-level completed approval,不做 Market domain-level 32/32 completed。 - -第一轮推荐审批 4 个 Marketplace 详情/分类/收藏 operation: - -```text -market:getMarketplaceAsset -market:listMarketplaceCategories -market:favoriteAsset -market:unfavoriteAsset -``` - -审批后目标 coverage summary 为: - -```text -total=233 -completed=131 -needsVerification=102 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Market 域目标状态为: - -```text -market total=32 -completed=4 -needsVerification=28 -``` - -本执行版只定义实现前置批准项、允许变更范围、TDD 顺序、验证命令、XML 防空跑和回滚策略。当前不得直接实现,不得修改 OpenAPI、scanner、coverage report、业务实现或 gate test。 - -```mermaid -flowchart TB - Current["当前 Market
32 dedicated / needs_verification"] --> Exec["执行版
锁定第一批 4 个 operation"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/feasibility review"] - Review --> Approval{"用户明确批准
4 ops / operation-level / OpenAPI commandId / allowed diff"} - Approval -->|否| Stay["保持当前 coverage
233/127/106"] - Approval -->|是| Red["TDD RED
先改 Market/Coverage/legacy gates
旧 report 必须失败"] - Red --> Green["TDD GREEN
Market OpenAPI commandId + scanner operation allowlist + regenerate report"] - Green --> Verify["focused + P1R mixed + Flyway _test
XML 防空跑 + allowed/protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -f18116a test(p1r): 收口 Account 第一批 completed approval 门禁 -``` - -当前 coverage summary: - -```text -233 127 106 0 0 0 -``` - -当前 Market 事实: - -- Market 32 个 operation 全部为 `dedicated / needs_verification`。 -- `APPROVED_COMPLETED_DOMAINS` 当前只包含 `ai`、`knowledge`。 -- `APPROVED_COMPLETED_OPERATIONS` 当前只包含 `events:streamEvents`、Meta 16 个 operation、Account 第一批 10 个 operation。 -- `P1rMarketRealApiGateTest` 当前明确禁止 Market operation 标记为 `completed`。 -- `P1rApiCoverageReportTest` 当前断言 summary `completed=127`,并断言 Market 32 个 operation 继续 `needs_verification`。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest` 仍硬编码当前 summary 或 Market needs_verification 口径;实现前必须把这些 legacy mixed gate 同步列入单独批准项。 -- `favoriteAsset` / `unfavoriteAsset` 的 Java Controller 和 Service 强制校验 `X-Command-Id`,但 `docs/api-contracts/market/openapi.yaml` 当前未声明该 header,coverage report 因此显示 `requiresCommandId=false`。 -- `listMarketplaceRecommendations` 当前是 `fallback_popular` / `fallback_newest` 可解释排序,不是独立个性化推荐系统读模型。 - -## 执行边界 - -### 必须先获用户明确批准 - -实现前必须同时获得以下 4 项批准: - -1. 批准第一轮 Market completed operation 精确清单为本执行版列出的 4 个 operation。 -2. 批准只走 operation-level approval,不把 `market` 加入 domain-level completed allowlist。 -3. 批准只在 `docs/api-contracts/market/openapi.yaml` 的 `favoriteAsset` / `unfavoriteAsset` 增加 required `X-Command-Id` header 合同;不修改其它 OpenAPI。 -4. 批准按本执行版修改 scanner、coverage report、Market/Coverage gate、必要 legacy mixed gate、Market focused tests、新增 Market HTTP+DB `_test` IT、Flyway assertion、memory 和 `.agent`。 - -未获 4 项批准前,不得实施本执行版。 - -如果用户不批准第 3 项,必须把 `favoriteAsset` / `unfavoriteAsset` 移出第一批,并重写执行版为纯读候选。 - -`market:listMarketplaceAssets` 与 `market:listMarketplaceRecommendations` 本轮明确不进入 completed approval;前者需要先补 pageNo/pageSize 1..100 边界证据,后者需要先解决 OpenAPI 个性化推荐描述与当前 fallback 排序实现的合同不一致。 - -### 本轮允许变更 - -获批后只允许修改以下路径: - -- `docs/api-contracts/market/openapi.yaml`,仅限为 `favoriteAsset` / `unfavoriteAsset` 增加 required `X-Command-Id` header。 -- `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/P1rMarketRealApiGateTest.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/P1rMarketDiscoveryFavoriteCompletedApprovalIT.java`,新增 Market 详情/分类/收藏 HTTP + 真实 PostgreSQL `_test` 证据。 -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketFlywayMigrationIT.java`,仅限补充 V6/V8/V15 Market 表、索引、约束、trigger 和 favorite 非法 insert/update assertion。 -- `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/controller/app/muse/AppMuseMarketplaceAssetControllerTest.java` -- `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketAssetQueryServiceTest.java` -- `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/application/muse/MarketFavoriteServiceTest.java` -- `muse-cloud/muse-module-market/muse-module-market-server/src/test/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketAssetMapperTest.java` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-11-P1RMarketCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md` -- `docs/memorys/2026-06-11-P1RMarket状态推进.md` - -如 implementation 发现必须修改 Market 业务实现、其它 OpenAPI、数据库迁移、Account/Content/Meta gate 或其它文件,必须停下说明原因并重新取得用户批准。 - -### 本轮禁止变更 - -- 不修改除 `docs/api-contracts/market/openapi.yaml` 之外的 OpenAPI。 -- 不修改 Market 业务实现来掩盖 coverage 缺口。 -- 不新增或修改 Market domain-level completed allowlist。 -- 不推进 Market purchase / install / handoff / governanceImpact。 -- 不推进 Market publish / review / governance 管理切片。 -- 不推进 Market appeals / restore / projection 切片。 -- 不推进 Account remaining 23、Content 或总 P1R completed。 - -## 第一轮审批清单 - -| operation key | Method | Path | 目标状态 | 完成证据边界 | -|---|---|---|---|---| -| `market:getMarketplaceAsset` | `GET` | `/app-api/muse/marketplace/assets/{assetId}` | `dedicated / completed` | App 资产详情、可见性、missing/invisible asset 拒绝、license/category/governance status DTO | -| `market:listMarketplaceCategories` | `GET` | `/app-api/muse/marketplace/categories` | `dedicated / completed` | 分类聚合、fallback recommendation slot、exposure summary unavailable 明示 | -| `market:favoriteAsset` | `POST` | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `dedicated / completed` | required `X-Command-Id`、登录 owner、可见资产、command replay、favorite active、唯一约束和审计事实 | -| `market:unfavoriteAsset` | `DELETE` | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `dedicated / completed` | required `X-Command-Id`、登录 owner、可见资产、command replay、favorite inactive、不删除审计事实 | - -## 保持 needs_verification 的 Market operation - -以下 28 个 operation 必须保持 `dedicated / needs_verification`: - -```text -market:listMarketplaceAssets -market:listMarketplaceRecommendations -market:adminListAppeals -market:adminGetAppeal -market:adminResolveAppeal -market:adminListMarketAssets -market:adminGetMarketAsset -market:adminDelistAsset -market:adminPreviewGovernanceImpact -market:adminRecallAsset -market:adminListPublishRequests -market:adminApprovePublishRequest -market:adminRejectPublishRequest -market:submitAppeal -market:supplementAppeal -market:withdrawAppeal -market:createBindPrecheck -market:getGovernanceImpact -market:installMarketplaceAsset -market:purchaseAsset -market:createMarketplaceHandoff -market:getHandoffStatus -market:cancelHandoff -market:listMyPublishRecords -market:savePublishDraft -market:runPublishCheck -market:submitPublishRequest -market:withdrawPublishRequest -``` - -保留原因: - -- `listMarketplaceAssets` 当前缺少 pageNo/pageSize 1..100 边界证据;实现层会透传 pageSize,底层 `PAGE_SIZE_NONE=-1` 可能变成全量查询,不能在未获业务实现批准前标为 completed。 -- `listMarketplaceRecommendations` 当前 OpenAPI 描述是个性化推荐,实际实现是 `fallback_popular` / `fallback_newest` 排序;合同与实现语义不闭合,不能在本轮作为 completed。 -- purchase / install / handoff / governanceImpact 缺目标 owner 消费闭环、Account 读侧 E2E 和跨 owner governance 影响闭环。 -- publish / review / governance 证据较强,但仍需单独执行版冻结 13 个 operation 的审批范围、Events outbox 边界和 fresh completed 级验证。 -- appeals / restore / projection 写入的是申诉审计、治理 action 或 Account projection 子集,不能混成统一 completed 闭环。 - -## 证据矩阵 - -| 证据类型 | 必跑或必查内容 | 目的 | -|---|---|---| -| Market focused tests | `AppMuseMarketplaceAssetControllerTest`、`MarketAssetQueryServiceTest`、`MarketFavoriteServiceTest`、`MuseMarketAssetMapperTest` | 证明 4 个 operation 的 HTTP contract、版本头、commandId、owner、可见性、幂等和 favorite active/inactive | -| Market HTTP+DB `_test` | 新增 `P1rMarketDiscoveryFavoriteCompletedApprovalIT` | 证明 4 个 operation 经 HTTP 入口访问真实 PostgreSQL `_test` 数据,覆盖 detail/category/favorite/unfavorite 的持久化和错误路径 | -| P1R Market gates | `P1rMarketRealApiGateTest`、`P1rMarketRouteOwnershipTest`、`P1rMarketMigrationSqlTest` | 证明 only 4 Market operation completed,28 个继续 needs_verification,路由仍 dedicated | -| P1R mixed gates | Coverage、Market、Events、AI、Knowledge、Account、Content、Meta gates | 防止非目标 domain 被连带推进,并同步 legacy summary / Market 状态断言 | -| Flyway `_test` | `P1rMarketFlywayMigrationIT` fresh rerun | 证明 V1-V15 clean migrate、Market V6/V8/V15 表/索引/约束/trigger、favorite unique/command 约束和非法 insert 拒绝 | -| Coverage scanner | `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` | 证明 scanner/report 与 operation-level allowlist、OpenAPI commandId 口径一致 | -| XML 防空跑 | 逐个读取 surefire XML,检查 tests、failures、errors、skipped | 防止 Maven 未跑目标类或旧 XML 假绿 | -| OpenAPI allowed diff | 只允许 `market/openapi.yaml` 增加 favorite/unfavorite required `X-Command-Id` | 修正合同与实现不一致,不扩大其它合同 | -| Business protected diff | Market 业务实现 staged/unstaged diff 必须为空 | 证明没有通过改实现掩盖 coverage 缺口 | -| Allowed diff | staged、unstaged、untracked 均必须落在允许清单 | 证明没有顺手扩大 scope | - -说明:standalone MockMvc、mock Service、mapper focused tests 只能作为支持证据,不能单独支撑 `favoriteAsset` / `unfavoriteAsset` completed。实现阶段必须新增 Market HTTP + 真实 PostgreSQL `_test` harness;若现有 Spring 测试结构无法稳定做到 WebApplicationContext/MockMvc + real mapper + `_test` DB,必须停下修订执行版,不能用 mock 组合替代。 - -## TDD 执行步骤 - -### Task 0:实现前现场确认 - -- [ ] 确认 worktree: - -```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 127 106 0 0 0 -``` - -- [ ] 确认 Market 32 个 operation 仍全部是 `dedicated / needs_verification`: - -```bash -jq -r '.operations[] | select(.domain=="market") | [.operationId,.implementationStatus,.completionStatus,.requiresCommandId] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -- [ ] 确认实施前只有文档 diff,没有 OpenAPI、scanner、coverage report、业务实现 diff: - -```bash -git -c core.quotePath=false status --short --branch -git diff -- docs/api-contracts muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports -git diff -- muse-cloud/muse-module-market/muse-module-market-server/src/main/java -``` - -### 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,不要求 `failures=0`。 - -推荐校验脚本: - -```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 期望 - -获批后先修改: - -- `P1rApiCoverageReportTest.java` -- `P1rMarketRealApiGateTest.java` -- `P1rEventsRealApiGateTest.java` -- `P1rAiRealApiGateTest.java` -- `P1rKnowledgeRealApiGateTest.java` - -RED 期望: - -- summary 期望 `completed=131`、`needsVerification=102`,但旧 report 仍是 `127/106`,必须失败。 -- Market 4 个 approved operation 期望 `completed`,但旧 report 仍是 `needs_verification`,必须失败。 -- Market 28 个未批准 operation 继续期望 `needs_verification`,其中必须包含 `listMarketplaceAssets` 与 `listMarketplaceRecommendations`。 -- `favoriteAsset` / `unfavoriteAsset` 期望 `requiresCommandId=true`,但旧 report 仍是 `false`,必须失败。 - -运行: - -```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.P1rMarketRealApiGateTest.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 -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMarketRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -预期:至少一个失败点来自旧 report 的 `127/106`、Market 4 个 operation 尚未 completed,或 favorite/unfavorite `requiresCommandId=false`。若直接通过,必须停下排查假绿。 - -### Task 2:OpenAPI commandId 合同修正 - -只修改 `docs/api-contracts/market/openapi.yaml`: - -- 在 `favoriteAsset` parameters 中追加 required header `X-Command-Id`。 -- 在 `unfavoriteAsset` parameters 中追加 required header `X-Command-Id`。 -- 不修改其它 Market operation。 -- 不修改其它 OpenAPI 文件。 - -推荐写法: - -```yaml - - name: X-Command-Id - in: header - required: true - schema: - type: string - description: 幂等命令 ID,收藏和取消收藏必须传入。 -``` - -修正后用 scanner 验证 report 中: - -```text -market:favoriteAsset requiresCommandId=true -market:unfavoriteAsset requiresCommandId=true -``` - -### Task 3:TDD GREEN,最小 scanner/report 状态推进 - -只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`: - -- 不修改 `APPROVED_COMPLETED_DOMAINS`。 -- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加 4 个 `market:*` operation key:`market:getMarketplaceAsset`、`market:listMarketplaceCategories`、`market:favoriteAsset`、`market:unfavoriteAsset`。 -- 保持 completed 必须仍是 `dedicated` 的校验。 - -生成 report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -预期 summary: - -```text -total=233 -completed=131 -needsVerification=102 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -预期 Market: - -```text -completed=4 -needsVerification=28 -favoriteAsset requiresCommandId=true -unfavoriteAsset requiresCommandId=true -``` - -### Task 4:Market focused verification - -运行 Market 本域 focused tests: - -```bash -cd muse-cloud -export RUN_START_EPOCH=$(date +%s) -rm -f \ - muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.controller.app.muse.AppMuseMarketplaceAssetControllerTest.xml \ - muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.application.muse.MarketAssetQueryServiceTest.xml \ - muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.application.muse.MarketFavoriteServiceTest.xml \ - muse-module-market/muse-module-market-server/target/surefire-reports/TEST-cn.iocoder.muse.module.market.dal.mysql.muse.MuseMarketAssetMapperTest.xml \ - muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketMigrationSqlTest.xml \ - muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rMarketRouteOwnershipTest.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.P1rApiCoverageReportTest.xml -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-market/muse-module-market-server,muse-server -am \ - -Dtest=AppMuseMarketplaceAssetControllerTest,MarketAssetQueryServiceTest,MarketFavoriteServiceTest,MuseMarketAssetMapperTest,P1rMarketMigrationSqlTest,P1rMarketRouteOwnershipTest,P1rMarketRealApiGateTest,P1rApiCoverageReportTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML 并检查 `failures=0`、`errors=0`、`skipped=0`: - -| Test class | 最低 tests | -|---|---:| -| `AppMuseMarketplaceAssetControllerTest` | 11 | -| `MarketAssetQueryServiceTest` | 6 | -| `MarketFavoriteServiceTest` | 7 | -| `MuseMarketAssetMapperTest` | 2 | -| `P1rMarketMigrationSqlTest` | 9 | -| `P1rMarketRouteOwnershipTest` | 3 | -| `P1rMarketRealApiGateTest` | 6 | -| `P1rApiCoverageReportTest` | 6 | - -必须补齐或确认以下断言: - -- MockMvc `getMarketplaceAsset` 覆盖 visible asset、missing asset、invisible asset。 -- MockMvc `listMarketplaceCategories` 覆盖分类聚合、fallback slot、exposure summary unavailable。 -- MockMvc 缺少 `X-Command-Id` 的 `favoriteAsset` 返回 `MARKET_COMMAND_ID_REQUIRED`。 -- MockMvc 缺少 `X-Command-Id` 的 `unfavoriteAsset` 返回 `MARKET_COMMAND_ID_REQUIRED`。 -- Service 空白 commandId 的 `favoriteAsset` 返回 `MARKET_COMMAND_ID_REQUIRED`。 -- Service 空白 commandId 的 `unfavoriteAsset` 返回 `MARKET_COMMAND_ID_REQUIRED`。 -- `unfavoriteAsset` 只能把 favorite fact 置为 inactive,不允许 delete。 -- `listMarketplaceAssets` 与 `listMarketplaceRecommendations` 可保留现有回归测试,但不得作为本轮 completed evidence;P1R gate 必须继续断言它们是 `needs_verification`。 - -如测试数量因补断言增加,XML 检查的最低 tests 可高于上表,但不能低于上表。 - -### Task 5:Market HTTP + 真实 PostgreSQL `_test` completed gate - -新增 `P1rMarketDiscoveryFavoriteCompletedApprovalIT`,使用独立 `_test` 数据库和真实 mapper/service 数据,不能使用生产库或非 `_test` 库: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_MARKET_COMPLETED_APPROVAL_TEST_DB=muse_p1r_market_discovery_favorite_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_MARKET_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_MARKET_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.P1rMarketDiscoveryFavoriteCompletedApprovalIT.xml -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rMarketDiscoveryFavoriteCompletedApprovalIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_MARKET_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 必须至少覆盖以下 8 个测试,XML 最低 tests 为 8: - -| Test class | 最低 tests | -|---|---:| -| `P1rMarketDiscoveryFavoriteCompletedApprovalIT` | 8 | - -必须证明: - -- `GET /app-api/muse/marketplace/assets/{assetId}` 对 `_test` 库中的 visible listed asset 返回真实详情、版本、license、category、governance status 和 favorite 状态。 -- `GET /app-api/muse/marketplace/assets/{assetId}` 对 missing 或 invisible asset 返回错误,不泄露非公开资产。 -- `GET /app-api/muse/marketplace/categories` 从真实资产/分类数据生成分类聚合,并明确 exposure summary unavailable / fallback slot。 -- `POST /favorite` 缺少 `X-Command-Id` 返回 `MARKET_COMMAND_ID_REQUIRED`;带 commandId 时写入 active favorite fact 和 command fact。 -- 同一 commandId 重放 `POST /favorite` 不产生重复 favorite fact,返回 replay 语义。 -- `DELETE /favorite` 带 commandId 后把 favorite fact 置为 inactive,不物理删除审计事实;同一 commandId 重放不重复写入。 -- `DELETE /favorite` 缺少 `X-Command-Id` 返回 `MARKET_COMMAND_ID_REQUIRED`,且不写 command fact、不改变 favorite fact。 -- `POST /favorite` 和 `DELETE /favorite` 对 missing 或 invisible asset 返回错误,并断言不写 command fact、不写或不改变 favorite fact。 - -该 IT 必须在测试启动时校验库名以 `_test` 结尾;密码只能从 `P1R_MARKET_COMPLETED_PASSWORD`、`P1R_FLYWAY_PASSWORD` 或 `MUSE_POSTGRES_PASSWORD` 环境变量读取,不能通过 JVM system property 传入;测试必须断言拒绝 `p1r.market.completed.password` 或任何 password system property;日志和断言不得输出明文数据库密码。 - -### Task 6:真实 PostgreSQL `_test` Flyway gate - -fresh rerun `P1rMarketFlywayMigrationIT`。使用独立 `_test` 库,不能使用生产库或非 `_test` 库: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_MARKET_TEST_DB=muse_p1r_market_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_MARKET_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_MARKET_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.P1rMarketFlywayMigrationIT.xml -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rMarketFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_MARKET_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 | -|---|---:| -| `P1rMarketFlywayMigrationIT` | 4 | - -必须在该 IT 内新增或补齐以下 assertion,但不得新增迁移或修改业务实现: - -- `muse_market_asset`、`muse_market_asset_version`、`muse_market_favorite` 存在。 -- `uk_muse_market_favorite_user_asset`、`uk_muse_market_favorite_command`、`idx_muse_market_favorite_user` 存在。 -- `trg_muse_market_favorite_updated_at` 存在。 -- 同一 `(tenant_id, user_id, asset_id)` 不能重复 active favorite fact。 -- 同一 `(tenant_id, command_id)` 不能重复 command fact。 -- favorite status 更新为 inactive 后 `updated_at` trigger 可执行。 - -### Task 7:P1R mixed gate - -运行 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.P1rMarketRealApiGateTest.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.P1rAccountRealApiGateTest.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.P1rMetaRealApiGateTest.xml -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMarketRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rAccountRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML: - -| Test class | 最低 tests | -|---|---:| -| `P1rApiCoverageReportTest` | 6 | -| `P1rMarketRealApiGateTest` | 6 | -| `P1rEventsRealApiGateTest` | 6 | -| `P1rAiRealApiGateTest` | 7 | -| `P1rKnowledgeRealApiGateTest` | 8 | -| `P1rAccountRealApiGateTest` | 4 | -| `P1rContentRealApiGateTest` | 5 | -| `P1rMetaRealApiGateTest` | 4 | - -每个 XML 均必须 `failures=0`、`errors=0`、`skipped=0`。 - -这些 gate 必须共同证明: - -- summary 只从 `127/106` 推进到 `131/102`。 -- Market 只从 `0/32` 推进到 `4/28`。 -- Account 仍保持 `10 completed / 23 needs_verification`。 -- Content 仍保持 `0 completed / 51 needs_verification`。 -- Meta、AI、Knowledge、Events 状态不变。 - -### Task 8:报告与 diff gate - -检查 summary: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 131 102 0 0 0 -``` - -检查 Market 4/28: - -```bash -python3 - <<'PY' -import json -from collections import Counter -from pathlib import Path -data = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -counter = Counter(op["completionStatus"] for op in data["operations"] if op["domain"] == "market") -print(counter) -for op in data["operations"]: - if op["domain"] == "market" and op["operationId"] in {"favoriteAsset", "unfavoriteAsset"}: - print(op["operationId"], op["requiresCommandId"], op["completionStatus"]) -PY -``` - -预期: - -```text -Counter({'needs_verification': 28, 'completed': 4}) -favoriteAsset True completed -unfavoriteAsset True completed -``` - -检查 OpenAPI diff 只限 Market: - -```bash -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/meta/openapi.yaml -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/content/openapi.yaml docs/api-contracts/events/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/meta/openapi.yaml -git diff -- docs/api-contracts/market/openapi.yaml -``` - -检查 Market 业务实现不被修改: - -```bash -git diff --quiet -- muse-cloud/muse-module-market/muse-module-market-server/src/main/java -git diff --cached --quiet -- muse-cloud/muse-module-market/muse-module-market-server/src/main/java -``` - -检查 whitespace: - -```bash -git diff --check -``` - -检查 allowed diff: - -```bash -git -c core.quotePath=false status --short -``` - -输出只能包含本执行版允许路径。若出现非批准 OpenAPI、业务实现、迁移或非批准 gate 文件,必须停止。 - -### Task 9:memory 与 `.agent` - -实现通过后新增: - -- `docs/memorys/2026-06-11-P1RMarket状态推进.md` - -内容必须记录: - -- 批准的 4 个 Market operation。 -- 保持 `needs_verification` 的 28 个 Market operation 及原因,必须点名 `listMarketplaceAssets` 与 `listMarketplaceRecommendations`。 -- OpenAPI commandId 合同修正范围。 -- `listMarketplaceRecommendations` 未进入本轮 completed,原因是个性化推荐 OpenAPI 描述与 fallback 排序实现未闭合。 -- `listMarketplaceAssets` 未进入本轮 completed,原因是 pageNo/pageSize 边界证据未闭合。 -- scanner/report/gate 修改范围。 -- 所有验证命令、结果、XML 计数、Market HTTP+DB `_test` 库名、Flyway `_test` 库名。 -- protected diff 与 allowed diff 结果。 -- 明确说明不代表 Market 32/32 completed,不代表 Account remaining 23 / Content / 总 P1R completed。 - -同时更新 `docs/agent-specs/.agent`,但只能记录事实,不写未验证结论。 - -### Task 10:fresh implementation review - -实现、验证和文档留痕后,必须派发两类 fresh reviewer: - -- spec/correctness reviewer:检查 4 个 operation 是否严格匹配本执行版,28 个 operation 是否仍 needs_verification,Market domain allowlist 是否未新增,OpenAPI commandId 修正是否只限 favorite/unfavorite。 -- quality/data-integrity/testing reviewer:检查 scanner/report/gate、legacy mixed gate 同步、HTTP+DB `_test`、XML 防空跑、Flyway `_test`、allowed/protected diff、rollback 是否可靠。 - -双 PASS 前不得提交、push 或宣称 Market completed approval 已收口。 - -## 回滚策略 - -如实现后需要撤回: - -1. 从 `APPROVED_COMPLETED_OPERATIONS` 移除 4 个 `market:*` key。 -2. 处理 `docs/api-contracts/market/openapi.yaml` 中 favorite/unfavorite 的 required `X-Command-Id` header: - - 如果用户要求完整撤回本轮合同修正,则移除 header。 - - 如果用户单独决定保留实现与 OpenAPI 一致的 commandId 合同,则保留 header,只撤回 completed approval。 -3. 重新运行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -4. 恢复 gate 期望。完整撤回 OpenAPI header 时: - -```text -summary completed=127 -summary needsVerification=106 -market completed=0 -market needsVerification=32 -favoriteAsset requiresCommandId=false -unfavoriteAsset requiresCommandId=false -``` - -如果保留 OpenAPI commandId 合同,则 gate 必须恢复为: - -```text -summary completed=127 -summary needsVerification=106 -market completed=0 -market needsVerification=32 -favoriteAsset requiresCommandId=true -unfavoriteAsset requiresCommandId=true -``` - -5. 修订本轮 memory 并保留失败原因;除非用户明确要求,不直接删除失败留痕。 -6. 重新运行 Market/P1R focused gates 和 `git diff --check`。 - -## 验收标准 - -执行版本身可以进入实现前批准点的条件: - -1. 本文件已写入 `docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md`。 -2. `.agent` 记录执行版当前状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report、业务实现当前无新增 diff;`market/openapi.yaml` 也必须在用户批准前保持无 diff。 -5. fresh execution spec/scope review PASS。 -6. fresh execution quality/feasibility review PASS。 - -实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。 diff --git a/docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md b/docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md deleted file mode 100644 index 8a21ee56..00000000 --- a/docs/agent-specs/2026-06-12-P1RContentCompletedApproval执行版.md +++ /dev/null @@ -1,857 +0,0 @@ -# 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: - -```text -content:listWorks -content:getWork -content:listChapters -content:getChapter -content:listBlocks -content:getBlock -content:saveBlock -content:getBlockSourceAttribution -``` - -审批后目标 coverage summary 为: - -```text -total=233 -completed=139 -needsVerification=94 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Content 域目标状态为: - -```text -content total=51 -completed=8 -needsVerification=43 -``` - -本执行版只定义实现前置批准项、允许变更范围、TDD 顺序、验证命令、XML 防空跑和回滚策略。当前不得直接实现,不得修改 OpenAPI、scanner、coverage report、业务实现或 gate test。 - -```mermaid -flowchart TB - Current["当前 Content
51 dedicated / needs_verification"] --> Exec["执行版
锁定第一批 8 个 operation"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/feasibility review"] - Review --> Approval{"用户明确批准
8 ops / operation-level / allowed diff / legacy gates"} - Approval -->|否| Stay["保持当前 coverage
233/131/102"] - Approval -->|是| Red["TDD RED
先改 Content/Coverage/legacy gates
旧 report 必须失败"] - Red --> Green["TDD GREEN
scanner operation allowlist + regenerate report"] - Green --> Runtime["Content HTTP+DB _test
V21 Flyway + source/event gates"] - Runtime --> Verify["focused + P1R mixed + XML 防空跑
allowed/protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -0032774 test(p1r): 收口 Market 第一批 completed approval 门禁 -``` - -当前 coverage summary: - -```text -233 131 102 0 0 0 -``` - -当前 Content 事实: - -- Content 51 个 operation 全部为 `dedicated / needs_verification`。 -- `APPROVED_COMPLETED_DOMAINS` 当前只包含 `ai`、`knowledge`。 -- `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`。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rMetaRealApiGateTest` 读取全局 summary 或非目标域状态;实现前必须把这些 mixed gate 同步列入单独批准项。 -- P1R-7f 已证明 `saveBlock -> source attribution active -> Content outbox -> Events -> SSE owner visible` 的 `needs_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、跨 tenant,DTO 字段完整 | -| `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,详情含 blocks,missing、wrong work、跨 owner/tenant | -| `content:listBlocks` | `GET` | `/app-api/muse/works/{workId}/chapters/{chapterId}/blocks` | `dedicated / completed` | chapter 属于 path work,Block 排序,有数据、空列表、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`: - -```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: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 | `ContentAppServiceTest`、`ContentSourceServiceTest`、`ContentEventPublishOutboxServiceTest`、`ContentEventPublishWorkerTest`、`AppContentControllerTest`、`AppContentSourceControllerTest` | 支撑本域读写、source attribution、outbox、controller contract 与错误码 | -| Content HTTP+DB `_test` | 新增 `P1rContentCoreCompletedApprovalIT` | 证明 8 个 operation 经 MockMvc HTTP 入口访问真实 PostgreSQL `_test` 数据,覆盖 shadow-path matrix | -| Content source/event gates | `P1rContentEventsPublishMigrationSqlTest`、`P1rContentEventsPublishDependencyTest`、`P1rContentEventsPublishEndToEndTest`、`P1rContentEventsPublishFlywayMigrationIT` | 证明 `saveBlock` 的 source attribution / Content outbox / Events 前置证据仍成立 | -| P1R Content gates | `P1rContentRealApiGateTest`、`P1rApiCoverageReportTest` | 证明 only 8 Content operation completed,43 个继续 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: - -```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 131 102 0 0 0 -``` - -- [ ] 确认 Content 51 个 operation 仍全部是 `dedicated / needs_verification`: - -```bash -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: - -```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,不要求 `failures=0`。 - -推荐校验脚本: - -```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 期望 - -获批后先修改: - -- `P1rApiCoverageReportTest.java` -- `P1rContentRealApiGateTest.java` -- 必要的 6 个 mixed gate: - - `P1rEventsRealApiGateTest.java` - - `P1rAiRealApiGateTest.java` - - `P1rKnowledgeRealApiGateTest.java` - - `P1rMarketRealApiGateTest.java` - - `P1rAccountRealApiGateTest.java` - - `P1rMetaRealApiGateTest.java` - -RED 期望: - -- summary 期望 `completed=139`、`needsVerification=94`,但旧 report 仍是 `131/102`,必须失败。 -- Content 8 个 approved operation 期望 `completed`,但旧 report 仍是 `needs_verification`,必须失败。 -- Content 43 个未批准 operation 继续期望 `needs_verification`,其中必须包含 `createWork`、`updateWork`、`createChapter`、`mergeBlockSuggestion`、`exportWork`、`getPlanning`、`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 的 `131/102` 或 Content 8 个 operation 尚未 completed。若直接通过,必须停下排查假绿。 - -### Task 2:TDD GREEN,最小 scanner/report 状态推进 - -只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`: - -- 不修改 `APPROVED_COMPLETED_DOMAINS`。 -- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加 8 个 `content:*` operation key。 -- 保持 completed 必须仍是 `dedicated` 的校验。 - -生成 report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -预期 summary: - -```text -total=233 -completed=139 -needsVerification=94 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -预期 Content: - -```text -completed=8 -needsVerification=43 -``` - -预期 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: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 3:Content focused verification - -运行 Content 本域 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.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_EPOCH`、`failures=0`、`errors=0`、`skipped=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 4:Content HTTP + 真实 PostgreSQL `_test` completed gate - -新增 `P1rContentCoreCompletedApprovalIT`,使用独立 `_test` 数据库和真实 mapper/service 数据,不能使用生产库或非 `_test` 库: - -```bash -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_outbox`、`tenant_id`、owner、blockRevision、sourceStatus、notificationType 和 commandId/outbox 唯一事实。 -- 该 IT 必须在测试启动时校验库名以 `_test` 结尾;密码只能从 `P1R_CONTENT_COMPLETED_PASSWORD`、`P1R_FLYWAY_PASSWORD` 或 `MUSE_POSTGRES_PASSWORD` 环境变量读取,不能通过 JVM system property 传入;日志和断言不得输出明文数据库密码。 -- 该 IT 如果读取并脱敏 `p1r.flyway.url/user`,必须保存原始 system property 并在 `@AfterAll` 恢复。 - -### Task 5:真实 PostgreSQL `_test` Flyway gate - -fresh rerun 并增强 `P1rContentEventsPublishFlywayMigrationIT`。使用独立 `_test` 库,不能使用生产库或非 `_test` 库: - -```bash -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_type`、`source_status=deleted`、非法 `publish_status`、非法 owner/block/resource/attempt。 -- 保存原始 `p1r.flyway.url/user`,脱敏后在 `@AfterAll` 恢复,避免同一 Surefire JVM 中污染其它 Flyway IT。 - -### Task 6:Content completed IT 与 Content events Flyway IT 组合顺序验证 - -为证明 system property 脱敏不会污染同 JVM 后续测试,必须用同一个 `_test` 库做普通顺序与反序组合验证。 - -```bash -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 7:P1R mixed gate - -运行 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 \ - -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_EPOCH`、`failures=0`、`errors=0`、`skipped=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: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 139 94 0 0 0 -``` - -检查 Content 8/43: - -```bash -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: - -```bash -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: - -```bash -git diff --check -``` - -检查 allowed diff,必须覆盖 unstaged / staged / untracked: - -```bash -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 9:memory 与 `.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 10:fresh implementation review - -实现、验证和文档留痕后,必须派发两类 fresh reviewer: - -- spec/correctness reviewer:检查 8 个 operation 是否严格匹配本执行版,43 个 operation 是否仍 needs_verification,Content 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. 重新运行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -3. 恢复 gate 期望: - -```text -summary completed=131 -summary needsVerification=102 -content completed=0 -content needsVerification=51 -``` - -4. 删除或修订本轮 memory,保留失败原因。 -5. 重新运行 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。 - -实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。 diff --git a/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md b/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md deleted file mode 100644 index a4a8a1df..00000000 --- a/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval审阅版.md +++ /dev/null @@ -1,291 +0,0 @@ -# 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
8 completed / 43 needs_verification"] --> Choose["选择下一批 evidence slice"] - Choose --> Planning["推荐:formal planning section
getPlanning + savePlanningItem"] - Choose --> Wider["不推荐:剩余 Content 43 全量推进"] - Planning --> Boundary{"是否依赖外部 owner?"} - Boundary -->|否| Evidence["Content 本域 DB evidence
muse_content_planning_section"] - Boundary -->|AI/File/Meta| Defer["保持 needs_verification"] - Evidence --> Review["fresh spec/scope review
fresh quality/feasibility review"] - Review --> Exec["双 PASS 后写执行版"] - Exec --> Approval{"用户明确批准后才实现"} - Approval -->|否| Stay["保持 233/139/94"] - Approval -->|是| Target["目标 233/141/92
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。 diff --git a/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md b/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md deleted file mode 100644 index 874a93a6..00000000 --- a/docs/agent-specs/2026-06-12-P1RContentPlanningCompletedApproval执行版.md +++ /dev/null @@ -1,690 +0,0 @@ -# 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
8 completed / 43 needs_verification"] --> Exec["执行版
锁定 Planning 2 ops"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/feasibility review"] - Review --> Approval{"用户明确批准
2 ops / operation-level / allowed diff / mixed gates"} - Approval -->|否| Stay["保持当前 coverage
233/139/94"] - Approval -->|是| Red["TDD RED
先改 gate 期望
旧 report 必须失败"] - Red --> Green["TDD GREEN
scanner operation allowlist
regenerate report"] - Green --> Runtime["Planning HTTP + real DB _test
V9 schema + command/audit/CAS"] - Runtime --> Verify["focused gates + P1R mixed gates
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。 diff --git a/docs/agent-specs/2026-06-13-P0-止血冻结令.md b/docs/agent-specs/2026-06-13-P0-止血冻结令.md new file mode 100644 index 00000000..34fff8a1 --- /dev/null +++ b/docs/agent-specs/2026-06-13-P0-止血冻结令.md @@ -0,0 +1,66 @@ +# oh-my-muse · P0 止血冻结令 + +> **版本** v1.0 · **日期** 2026-06-13 · **读者** 全体 agent / 工程师 · **状态** 立即生效 +> **边界**:本令冻结"刷完成度台账"这条假绿回路,并记录 P0 已落地的代码改动。 +> **依据**:对抗复盘 `2026-06-13-目标达成对抗复盘.md` §三(P0-1/P0-2)、§四(R1/R5);现状基线 `2026-06-13-项目目标与模块现状基线.md`。 + +--- + +## 0. 一句话 + +**停止"刷完成度台账"这台假绿发动机**:冻结 operation completed approval 收口;完成度的唯一新定义 = **自动化绿的用户旅程**,不是台账里的 completed 数。 + +--- + +## 1. 背景(为什么冻结) + +对抗复盘裁决:失控根因是 **「假绿」**,而非仅 churn—— + +- 完成度信号可手工拨:门禁测试硬编码 `assertEquals(147, completed)`,与 `p1r-api-coverage.json` 互相对着写死; +- CI 用 `-Dmaven.test.skip=true` **跳过全部测试**,且矩阵是 JDK 8/11/17(项目需 21,根本编不过); +- 233 条 operation 的 `implementationStatus` **全部自填 `dedicated`**,从未与代码机械核对; +- 近 15 个提交清一色 `test(p1r): 收口 X completed approval`,边际价值趋零(后端覆盖已 147/233,对"用户能不能用"零贡献)。 + +--- + +## 2. 冻结内容(立即生效) + +- 🚫 **停止**再产 `test(p1r): 收口…completed approval` 类提交;不再人工把 `needs_verification` 拨成 `completed`。 +- 🚫 **不再**以"completed 数 / 合并完成度%"作为对内或对外的完成度口径(R4)。 +- 🧊 `p1r-api-coverage.json` 的 completed 快照**即日冻结**。`P1rApiCoverageReportTest` 中既有的 per-operation / per-domain 断言此后充当**冻结护栏**:任何进一步 promote 都会让测试变红,提示"这不是定义完成的方式"。 + +--- + +## 3. P0 已落地的代码改动(本次) + +| 文件 | 改动 | 作用 | +|---|---|---| +| `mise.toml`(新增) | 锁 `java = "21"` | 本地工具链对齐项目 Java 21(此前本机默认 17) | +| `muse-cloud/.github/workflows/maven.yml` | 矩阵 `8/11/17 → 21`;删除 `-Dmaven.test.skip=true`;开启 `pull_request` 触发 | CI 在**正确 JDK** 下**真正编译并运行测试**,并对 PR 设门(此前=假绿) | +| `…/framework/api/P1rApiCoverageReportTest.java` | 删 `assertEquals(147, completed)` → 改为 `summary.completedOperations` **自洽校验**;新增 `should_back_completed_operations_with_existing_source_files`:completed 必须引用**磁盘真实存在**的 controller/service 文件 | 拆掉自证回路 + 加最小"文件存在性"牙齿(`implementationStatus 由扫描判定`的最小版) | + +> ⚠️ 牙齿边界:文件存在性**只**证明"引用的源码在",**不**证明端到端可用。真实可用性由 P1 自动化用户旅程承担。serviceFiles 当前是"整模块 service 清单"(宽松),收紧到"实际实现该 operation 的文件"留待 P1。 + +--- + +## 4. 完成的新定义(替代台账数字) + +- **完成 = 一条用户旅程在自动化下绿**(关 mock + 真后端 + 自动化端到端)。 +- 对外/对内只报**三口径**(对抗复盘 P1-2),**禁用合并 %**: + 1. 代码实现度(读代码); + 2. 自动化验证度(CI 实跑通过的 operation/旅程数); + 3. 端到端可用度(真实形态跑通的用户旅程数)。 + +--- + +## 5. 解冻条件 + +当 **P1 黄金旅程**(登录 → AI 生成候选 → 前端 Accept → 后端 `mergeBlockSuggestion` 落库+归因 → SSE 回流)在**自动化下绿**、且 CI 真实运行测试成为常态后:完成度口径切换到"旅程绿",覆盖台账退役为历史参考。 + +--- + +## 6. 关联文档 + +- 现状基线:`2026-06-13-项目目标与模块现状基线.md` +- 对抗复盘:`2026-06-13-目标达成对抗复盘.md` +- agent 基建迁移(governance 层,已按对抗结论**降级到 P3-2**,且须挂 CI 校验):`2026-06-13-agent开发基建迁移-review.md` diff --git a/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md b/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md deleted file mode 100644 index 7ee173e0..00000000 --- a/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md +++ /dev/null @@ -1,258 +0,0 @@ -# P1R Account Security Events Completed Approval 审阅版 - -日期:2026-06-13 - -## 结论 - -推荐 Account 下一批只推进 2 个用户安全事件只读 operation-level completed approval: - -```text -account:appListSecurityEvents -account:appGetSecurityEvent -``` - -暂不推进 `account:appAcknowledgeSecurityEvent`。原因是 OpenAPI 当前仍承诺 `session_revoked` 会联动 session 管理并使相关会话失效,但当前实现返回“未接入 session 管理服务”的 fail-closed 文案。operation-level completed 不能排除单个 action 子路径;在 OpenAPI 或真实 session revoke 未闭合前,把整个 acknowledge operation 标为 completed 会制造 fake completed。 - -本切片 completed 口径只覆盖:对既有 `muse_member_security_event` 安全事件事实的当前登录用户可见读取、详情脱敏、latest ack 合并、owner/tenant 隔离、API version 和 read no-write 闭环。 - -本审阅版只冻结候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。 - -```mermaid -flowchart TB - Current["当前 Account
10 completed / 23 needs_verification"] --> Candidate["下一批候选
Security Events read 2 ops"] - Candidate --> Read["list / detail
owner 可见 + 脱敏 + latest ack"] - Candidate --> Exclude["暂缓 acknowledge
session_revoked 合同不闭合"] - Read --> Review["fresh spec/scope review
fresh quality/testing review"] - Exclude --> Keep["继续 needs_verification"] - Review --> Exec["双 PASS 后进入审批点"] - Exec --> Approval{"用户明确批准后才实现"} - Approval -->|"否"| Stay["保持 233/145/88"] - Approval -->|"是"| Target["目标 233/147/86
Account 12 completed / 21 needs_verification"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁 -``` - -当前 coverage summary: - -```text -total=233 -completed=145 -needsVerification=88 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=14 needsVerification=37 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -events completed=1 needsVerification=0 -``` - -目标 operation 当前状态: - -```text -appListSecurityEvents dedicated needs_verification requiresCommandId=false GET /app-api/muse/account/security-events -appGetSecurityEvent dedicated needs_verification requiresCommandId=false GET /app-api/muse/account/security-events/{eventId} -``` - -获批后的目标值: - -```text -total=233 -completed=147 -needsVerification=86 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 - -account total=33 -completed=12 -needsVerification=21 -``` - -## 推荐候选 - -| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence | -|---|---|---|---|---| -| `account:appListSecurityEvents` | `GET` | `/app-api/muse/account/security-events` | 读当前登录用户安全事件摘要,支持 severity 筛选和 latest ack 合并,无写入副作用 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 owner/tenant 隔离、severity filter、分页、latest ack、敏感字段不泄露、missing API version 拒绝、read no-write | -| `account:appGetSecurityEvent` | `GET` | `/app-api/muse/account/security-events/{eventId}` | 读当前登录用户安全事件详情,服务端按 eventId + accountUserId 查询并脱敏 IP / User-Agent | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 detail 脱敏、跨 owner/跨 tenant/missing 不可见、ack detail 合并、非数字 eventId 404、read no-write | - -推荐理由: - -- 2 个 operation 都属于 Account 本域 `AccountSecurityServiceImpl`,不需要调用 New-API、FileService、Market、Content、AI 或 Knowledge 外部 owner。 -- App 端 Controller 使用 `getLoginUserId()`,不接受请求参数覆盖 owner,适合用 HTTP + DB 证明 owner 隔离。 -- OpenAPI 的 list/detail 合同与当前实现方向一致:当前账户可见、安全事件详情、敏感值脱敏。 -- 读侧需要合并 `muse_account_security_event_ack` latest ack,但不需要证明 ack 写命令 completed。 - -## 不纳入本批的 Account operation - -以下 21 个 Account operation 必须继续 `dedicated / needs_verification`: - -```text -account:adminCreateCallAttributionJob -account:adminGetCallAttributionJob -account:adminGetIntegrationCallByCorrelation -account:adminListNewApiBindings -account:adminListPurchaseRecords -account:adminListUsageRecords -account:adminCreateNewApiBinding -account:adminCreateQuotaRequest -account:appDownloadExport -account:appCreateExportTask -account:appGetExportTask -account:appGetIntegrationCallByCorrelation -account:appListLicenses -account:getAppNewApiBinding -account:appRecheckNewApiBinding -account:appListPublishRecords -account:appListPurchases -account:appCreateQuotaRequest -account:appGetQuotaRequest -account:appAcknowledgeSecurityEvent -account:getAppUsage -``` - -排除原因: - -- `appAcknowledgeSecurityEvent` 的 `session_revoked` OpenAPI 合同承诺与当前实现 fail-closed 文案不闭合;必须单独处理合同或实现后再审批。 -- New-API binding、recheck、quota request 和 integration correlation 仍依赖真实 New-API runtime 或外部调用归因闭环。 -- FileService / export download 仍缺真实文件交付、下载字节、对象存储和凭证消费端到端 evidence。 -- call attribution job 当前 create 路径只创建 queued job 事实,真实归因执行仍由后续 owner/facade 接入;可作为后续独立切片,但不能夹入本批。 -- Market purchase / license / publish 仍缺 Market 主流程到 Account projection 的跨 owner 一致性 evidence。 -- usage summary / admin usage 仍受 `muse_member_usage_record` 和 attribution source 质量影响,本批不绕过上游事实缺口。 - -## 已有实现证据 - -### OpenAPI 合同 - -OpenAPI 当前包含 2 个本批候选读 operation: - -```text -GET /app-api/muse/account/security-events -GET /app-api/muse/account/security-events/{eventId} -``` - -本切片不需要修改 `docs/api-contracts/account/openapi.yaml`。但 OpenAPI 中 acknowledge 的 `session_revoked` 文案当前是阻塞项,因此本批明确不推进 `appAcknowledgeSecurityEvent`。 - -### Controller 入口 - -`AppAccountSecurityController` 当前链路: - -```text -GET /muse/account/security-events -GET /muse/account/security-events/{eventId} -AccountApiVersionGuard.requireVersion(apiVersion) -getLoginUserId() -AccountSecurityService -``` - -说明: - -- Controller-local mapping 是 `/muse/account/...`;对外 OpenAPI 路径仍以 `/app-api/muse/...` 为准。 -- 执行版必须用当前项目既有 app MockMvc / Web 测试上下文证明最终 app API 路径可访问。 -- `list` / `detail` 都必须证明缺少 `X-API-Version` 时不调用 service。 - -### Service 读链路 - -`AccountSecurityServiceImpl.appListSecurityEvents`: - -1. `requireUser(accountUserId)`。 -2. normalize severity,只允许 `info / warning / critical`。 -3. `MemberSecurityEventMapper.selectPageByAccountUserIdAndSeverity(...)`。 -4. 批量读取当前 owner 最新 ack。 -5. 转换摘要 VO,敏感字段不直接外泄。 - -`AccountSecurityServiceImpl.appGetSecurityEvent`: - -1. `requireUser(accountUserId)`。 -2. 解析 eventId,非数字或空值按 not found 处理。 -3. `MemberSecurityEventMapper.selectByIdAndAccountUserId(...)`。 -4. 读取当前 owner 最新 ack。 -5. 转换详情 VO,脱敏 IP / userAgent。 - -### Schema 与测试前置 - -P1R SQL 当前定义目标事实表: - -```text -V2: muse_member_security_event -V11: muse_account_security_event_ack -``` - -但 `AccountSecurityServiceImpl.requireUser()` 会通过 `MemberUserMapper.selectById` 读取 `member_user`,而 `sql/muse` 当前 V1-V21 不创建 `member_user`。执行版必须明确:HTTP+DB `_test` 可以在隔离库完成 V1-V21 clean migrate 后创建 test-local `member_user` auth fixture 表并 seed 登录用户,用于满足认证前置;该 fixture 不计入 P1R Account schema evidence,不能修改 production SQL,也不能替代目标安全事件表/ack 表证据。 - -执行版必须 fresh 证明: - -- V1-V21 clean migrate 成功,且新 HTTP+DB IT 使用当前 live migration baseline。 -- 目标表 `muse_member_security_event`、`muse_account_security_event_ack` 的列、索引、trigger 和 JSONB 字段真实存在。 -- test-local `member_user` fixture 只在 `_test` 库、Flyway migrate 后创建,并在测试内记录为 auth fixture;DDL 至少覆盖 `MemberUserDO`、`TenantBaseDO`、`BaseDO` 当前映射列:`id`、`mobile`、`password`、`status`、`register_ip`、`register_terminal`、`login_ip`、`login_date`、`nickname`、`avatar`、`name`、`sex`、`birthday`、`area_id`、`mark`、`point`、`tag_ids`、`level_id`、`experience`、`group_id`、`tenant_id`、`creator`、`create_time`、`updater`、`update_time`、`deleted`。 - -## 风险与取舍 - -1. `appAcknowledgeSecurityEvent` 暂缓是必须的。 - - 当前 OpenAPI 对 `session_revoked` 承诺真实 session revoke。 - - 当前实现只返回 fail-closed 文案。 - - 在合同或实现未闭合前,不能把整个 operation 标为 completed。 - -2. `member_user` 是测试 harness 前置,不是目标业务证据。 - - 不能 mock `MemberUserMapper` 来掩盖真实 service 依赖。 - - 也不能修改 `sql/muse` 迁移补这个表。 - - 允许在隔离 `_test` 库、V1-V21 migrate 后创建 test-local fixture,并明确不计入 P1R schema evidence。 - - fixture DDL 必须覆盖真实 `MemberUserMapper.selectById` 会读取的当前实体映射列,不能只建 `id/nickname` 等最小列。 - -3. 安全事件生产路径不是本批完成条件。 - - 已知 `AccountExportServiceImpl.insertSensitiveExportEvent` 会写安全事件 source fact。 - - 但本批不证明高敏导出文件交付或所有安全事件来源 completed。 - -4. App HTTP + real DB gate 必须避免假绿。 - - 不能只靠 Mockito service/controller tests。 - - 必须读取新鲜 Surefire XML,检查 tests>0、failures=0、errors=0、skipped=0。 - -## 后续执行版必须包含 - -1. 2 个目标 operation 的审批清单、当前状态和目标状态。 -2. 保持 `needs_verification` 的 21 个 Account operation 及原因。 -3. TDD RED:先改 P1R gate 期望,旧 report 必须失败。 -4. TDD GREEN:只把 2 个 `account:*` operation 加入 operation-level allowlist,不加 `account` domain allowlist。 -5. 新增 `P1rAccountSecurityEventsCompletedApprovalIT`,使用 MockMvc HTTP + 真实 PostgreSQL `_test`。 -6. App 端登录用户上下文、tenant interceptor、API version、真实 mapper、test-local `member_user` auth fixture 的组合方式。 -7. schema gate:新 HTTP+DB IT 必须执行 V1-V21 clean migrate;V2/V11 安全事件表、ack 表、唯一约束、索引、trigger、JSONB 字段断言;Account 专属 `P1rAccountEventsPublishFlywayMigrationIT` 可继续作为 V20 outbox 证据,但不能替代新 IT 的 V21 baseline。 -8. runtime matrix:happy、empty page、severity filter、latest ack、detail 脱敏、cross owner、cross tenant、missing、invalid eventId、read no-write。 -9. P1R mixed gates:至少覆盖 Account、Coverage、Events、AI、Knowledge、Market、Content、Meta。 -10. XML 防空跑、protected diff、allowed diff 和 rollback 策略。 -11. `.agent` 与 `docs/memorys` 留痕要求。 - -## 验收标准 - -本审阅版可以视为完成的条件: - -1. 文件写入 `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md`。 -2. `.agent` 记录 Account Security Events completed approval 审阅版状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report、业务实现、SQL migration 当前无新增 diff。 -5. fresh spec/scope review PASS。 -6. fresh quality/feasibility/testing review PASS。 - -## 待确认项 - -1. 是否确认 Account 下一批只做这 2 个 security events read operation-level completed approval。 -2. 是否确认 `appAcknowledgeSecurityEvent` 因 `session_revoked` 合同不闭合继续保持 `needs_verification`。 -3. 是否确认本批不把 `account` 加入 domain-level completed allowlist。 -4. 是否确认允许 HTTP+DB `_test` 在隔离 `_test` 库内创建 test-local `member_user` auth fixture,但不修改 production SQL、不把该 fixture 计入 P1R schema evidence。 diff --git a/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md b/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md deleted file mode 100644 index 9876f835..00000000 --- a/docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md +++ /dev/null @@ -1,700 +0,0 @@ -# P1R Account Security Events Completed Approval 执行版 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task only after fresh execution review double PASS and explicit user approval. Steps use checkbox syntax for tracking. - -日期:2026-06-13 - -## 结论 - -本执行版只锁定 Account 用户安全事件只读 2 个 operation-level completed approval: - -```text -account:appListSecurityEvents -account:appGetSecurityEvent -``` - -本轮不把 `account` 加入 domain-level completed allowlist,不推进 Account 33/33 completed,不修改 OpenAPI,不修改 Account 业务实现,不修改 SQL migration。`account:appAcknowledgeSecurityEvent` 因 `session_revoked` 公开合同与当前实现不闭合,继续保持 `needs_verification`。 - -获批后目标 coverage summary 为: - -```text -total=233 -completed=147 -needsVerification=86 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Account 域目标状态为: - -```text -account total=33 -completed=12 -needsVerification=21 -``` - -当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。 - -```mermaid -flowchart TB - Current["当前 Account
10 completed / 23 needs_verification"] --> Exec["执行版
锁定 Security Events read 2 ops"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/testing review"] - Review --> Approval{"用户明确批准
2 ops / operation-level / allowed diff / mixed gates"} - Approval -->|否| Stay["保持当前 coverage
233/145/88"] - Approval -->|是| Red["TDD RED
先改 gate 期望
旧 report 必须失败"] - Red --> Runtime["HTTP + real DB _test
owner + tenant + latest ack + no-write"] - Runtime --> Green["scanner operation allowlist
regenerate report"] - Green --> Verify["focused tests + P1R mixed gates
XML 防空跑 + protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁 -``` - -当前 coverage summary: - -```text -233 145 88 0 0 0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=14 needsVerification=37 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -events completed=1 needsVerification=0 -``` - -当前目标 operation 状态: - -```text -appListSecurityEvents dedicated needs_verification false GET /app-api/muse/account/security-events -appGetSecurityEvent dedicated needs_verification false GET /app-api/muse/account/security-events/{eventId} -``` - -必须保持 `needs_verification` 的相关写命令: - -```text -appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge -``` - -已验证的只读实现事实: - -- `AppAccountSecurityController` 使用 `AccountApiVersionGuard.requireVersion(...)` 和 `getLoginUserId()`。 -- `AccountSecurityServiceImpl.appListSecurityEvents` 按当前 owner + severity 查询安全事件,并合并最新 ack。 -- `AccountSecurityServiceImpl.appGetSecurityEvent` 按 eventId + owner 查询详情,并通过 `AccountConvert` 脱敏 IP / userAgent。 -- `AccountSecurityServiceImpl.requireUser()` 依赖 `MemberUserMapper.selectById`;P1R `sql/muse` 当前 V1-V21 不创建 `member_user`。 -- `SecurityEventAcknowledgeReqVO` 与 OpenAPI 当前包含 `session_revoked`,但 OpenAPI 文案承诺真实 session revoke,当前实现只返回 fail-closed 文案;本执行版不推进 acknowledge。 - -## 执行边界 - -### 必须先获用户明确批准 - -实现前必须同时获得以下 4 项批准: - -1. 批准本轮只审批 `account:appListSecurityEvents`、`account:appGetSecurityEvent` 两个 operation。 -2. 批准继续使用 operation-level approval,不把 `account` 加入 domain-level completed allowlist。 -3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Security Events HTTP+DB `_test`、memory 和 `.agent`。 -4. 批准同步 mixed gate 的 summary / Account / 非目标域防回退断言,目标为 `233/147/86/0/0/0` 与 Account `12 completed / 21 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/P1rAccountRealApiGateTest.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/P1rContentRealApiGateTest.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/P1rAccountSecurityEventsCompletedApprovalIT.java` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md` - -`AccountSecurityServiceTest`、`AppAccountSecurityControllerTest` 以及既有 mapper/convert focused tests 只允许作为验证命令和 XML 防空跑证据被运行、读取,不列入本轮可修改路径。如 implementation 发现必须修改 Account 业务实现、OpenAPI、SQL migration、既有 focused tests、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。 - -### 本轮禁止变更 - -- 不修改 7 个 OpenAPI。 -- 不修改 Account main/java 业务实现来掩盖 coverage 缺口。 -- 不新增或修改 SQL migration。 -- 不把 `account` 加入 domain-level completed allowlist。 -- 不推进 Account 33/33 completed。 -- 不推进 `account:appAcknowledgeSecurityEvent`。 -- 不推进 Account remaining 21、Market remaining 28、Content remaining 37 或总 P1R completed。 -- 不推进 New-API binding / recheck / quota request、FileService/export/download、call attribution、usage attribution、Market purchase/license/publish projection、integration-call 查询。 -- 不把安全事件读证据写成安全事件所有生产来源 completed。 -- 不通过 mock `MemberUserMapper` 或 production SQL 修改绕过 `member_user` 前置依赖。 -- 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。 - -## 审批清单 - -| operation key | Method | Path | 目标状态 | 完成证据边界 | -|---|---|---|---|---| -| `account:appListSecurityEvents` | `GET` | `/app-api/muse/account/security-events` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 owner/tenant、severity、分页、latest ack、敏感字段不泄露、API version、read no-write | -| `account:appGetSecurityEvent` | `GET` | `/app-api/muse/account/security-events/{eventId}` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 detail 脱敏、跨 owner/跨 tenant/missing 不可见、非数字 eventId、ack 合并、read no-write | - -## 保持 needs_verification 的 Account operation - -以下 21 个 operation 必须保持 `dedicated / needs_verification`: - -```text -account:adminCreateCallAttributionJob -account:adminGetCallAttributionJob -account:adminGetIntegrationCallByCorrelation -account:adminListNewApiBindings -account:adminListPurchaseRecords -account:adminListUsageRecords -account:adminCreateNewApiBinding -account:adminCreateQuotaRequest -account:appDownloadExport -account:appCreateExportTask -account:appGetExportTask -account:appGetIntegrationCallByCorrelation -account:appListLicenses -account:getAppNewApiBinding -account:appRecheckNewApiBinding -account:appListPublishRecords -account:appListPurchases -account:appCreateQuotaRequest -account:appGetQuotaRequest -account:appAcknowledgeSecurityEvent -account:getAppUsage -``` - -## 证据分层 - -| Evidence | 文件 / 测试 | 目的 | -|---|---|---| -| Operation inventory / coverage gate | `P1rApiCoverageReportTest`、`P1rAccountRealApiGateTest` 和 mixed gates | 证明只推进获批 2 个 operation,非目标 domain 不回退 | -| Security Events HTTP+DB `_test` | 新增 `P1rAccountSecurityEventsCompletedApprovalIT` | 证明 2 个 read operation 经 MockMvc HTTP 入口访问真实 service / mapper / PostgreSQL `_test` 数据 | -| Tenant / schema gate | `P1rAccountSecurityEventsCompletedApprovalIT` 内验证 V1-V21 clean migrate 与 V2/V11 安全事件依赖 schema | 证明安全事件表、ack 表、索引、trigger、JSONB 和 tenant 字段在当前 live migration baseline 下真实可用 | -| Auth fixture gate | `P1rAccountSecurityEventsCompletedApprovalIT` 内创建 test-local `member_user` fixture | 满足 `requireUser()` 的真实 mapper 前置,但不计入 P1R schema evidence | -| App security context gate | `P1rAccountSecurityEventsCompletedApprovalIT` 内设置 app LoginUser 与 tenant context | 证明 owner 不能由请求参数覆盖,跨 owner / 跨 tenant 不可见 | -| No-write gate | `P1rAccountSecurityEventsCompletedApprovalIT` 对 read happy/error 前后做表快照 | 证明 list/detail 不写 security event、ack、command、audit 或 outbox | -| Focused unit/controller tests | `AccountSecurityServiceTest`、`AppAccountSecurityControllerTest`、mapper/convert tests | 支撑 service/controller contract,不替代 HTTP+DB `_test` | - -## TDD / 实施步骤 - -### Task 1:Preflight 与 RED gate - -- [ ] 确认当前 worktree: - -```bash -git -c core.quotePath=false status --short --branch -``` - -期望: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -允许存在本执行版和审阅版文档 diff;不得存在 OpenAPI、业务实现、SQL、scanner、coverage report 或 gate test 的未授权 diff。 - -- [ ] 确认当前 coverage: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="account" and (.operationId=="appListSecurityEvents" or .operationId=="appGetSecurityEvent" or .operationId=="appAcknowledgeSecurityEvent")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring),.method,.path] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -期望: - -```text -233 145 88 0 0 0 -appListSecurityEvents dedicated needs_verification false GET /app-api/muse/account/security-events -appGetSecurityEvent dedicated needs_verification false GET /app-api/muse/account/security-events/{eventId} -appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge -``` - -- [ ] TDD RED:先修改 P1R gate 期望值和 2 个 read operation completed 断言,但不修改 scanner/report。 - -RED 目标: - -- `P1rApiCoverageReportTest` 期望 summary `completed=147 / needsVerification=86`。 -- `P1rAccountRealApiGateTest` 期望 Account `12 completed / 21 needs_verification`,2 个 read operation completed,`appAcknowledgeSecurityEvent` 继续 `needs_verification`。 -- 7 个 mixed gate 中所有硬编码 Account `10/23` 或 summary `145/88` 的断言同步为 `12/21` 或 `147/86`。 - -RED 命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望:BUILD FAILURE。失败点必须来自旧 report 仍是 `233/145/88` 或 2 个 read operation 仍为 `needs_verification`;不得来自编译错误或无关测试失败。 - -### Task 2:新增 Security Events HTTP+DB `_test` - -- [ ] 新增: - -```text -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountSecurityEventsCompletedApprovalIT.java -``` - -测试上下文要求: - -- 使用 `@SpringBootTest(webEnvironment = MOCK)` + `WebApplicationContext` + `MockMvcBuilders.webAppContextSetup(...)`。 -- 真实 `/app-api/muse/**` 前缀进入 `AppAccountSecurityController`。 -- 导入 `MuseWebAutoConfiguration`,并提供 test-local `ApiErrorLogCommonApi` stub,返回 `CommonResult.success(true)`,满足 `GlobalExceptionHandler` 依赖。 -- 设置 app `LoginUser` 与 tenant context;请求参数中的 `userId` 或其它伪 owner 参数不得改变 owner。 -- 使用真实 `AccountSecurityServiceImpl`、真实 mapper 和真实 PostgreSQL `_test` 数据库;不得 mock `MemberSecurityEventMapper` 或 `AccountSecurityEventAckMapper`。 -- `MemberUserMapper` 必须使用真实 mapper。由于 `sql/muse` 不包含 `member_user`,测试可以在隔离 `_test` 库完成 Flyway migrate 后创建 test-local `member_user` fixture 表并 seed 登录用户;该 fixture 只能作为 `requireUser()` auth 前置,不计入 P1R schema evidence,不允许修改 production SQL。 -- test-local `member_user` DDL 必须覆盖 `MemberUserDO`、`TenantBaseDO`、`BaseDO` 当前映射列,至少包含:`id`、`mobile`、`password`、`status`、`register_ip`、`register_terminal`、`login_ip`、`login_date`、`nickname`、`avatar`、`name`、`sex`、`birthday`、`area_id`、`mark`、`point`、`tag_ids`、`level_id`、`experience`、`group_id`、`tenant_id`、`creator`、`create_time`、`updater`、`update_time`、`deleted`。不得只建 `id/nickname` 等最小列,也不得通过修改 mapper 或 production SQL 回避真实 `MemberUserMapper.selectById`。 -- 注册真实 MyBatis tenant interceptor:`TenantLineInnerInterceptor` + `TenantDatabaseInterceptor(new TenantProperties())`。 -- `TARGET_VERSION="21"`,执行 V1-V21 clean migrate 后再创建 test-local auth fixture;Account 专属 `P1rAccountEventsPublishFlywayMigrationIT` 可以继续作为 V20 Account outbox 迁移证据复跑,但不能替代本 IT 的当前 live V21 baseline。 -- 数据库密码只能来自环境变量,禁止 JVM system property 或 JDBC query 参数传入。 -- 保存并在 `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user` system property,避免污染同一 Surefire JVM 后续 Flyway 测试。 - -建议测试库: - -```text -muse_p1r_account_security_events_completed_approval_test -``` - -建议环境变量优先级: - -```text -P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD -P1R_ACCOUNT_COMPLETED_PASSWORD -P1R_FLYWAY_PASSWORD -MUSE_POSTGRES_PASSWORD -``` - -standalone 建库与运行前置: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB=muse_p1r_account_security_events_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_ACCOUNT_SECURITY_EVENTS_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_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB" -``` - -必测矩阵: - -1. schema gate: - - V1-V21 clean migrate 成功,`migrationsExecuted=21`。 - - `muse_member_security_event`、`muse_account_security_event_ack` 存在。 - - `muse_member_security_event` 有 tenant_id、account_user_id、device_info JSONB、ip_address、acknowledged、acknowledged_at。 - - `muse_account_security_event_ack` 有 tenant_id、event_id、account_user_id、action、command_id、risk_summary JSONB。 - - `idx_muse_member_security_user`、`trg_muse_member_security_updated_at` 存在。 - - `uk_muse_account_security_event_ack_command`、`idx_muse_account_security_event_ack_user`、`idx_muse_account_security_event_ack_event`、`trg_muse_account_security_event_ack_updated_at` 存在。 - - test-local `member_user` fixture 表存在,覆盖真实实体映射列,并只用于 seed 登录用户。 -2. `appListSecurityEvents`: - - 同 tenant / same owner 返回 list。 - - severity filter 只返回目标严重度。 - - 只合并当前 owner 最新 ack。 - - 返回摘要不泄露原始 IP / device_info。 - - 跨 owner、跨 tenant 不可见。 - - 缺少 `X-API-Version` 不调用 service、不写库。 - - read no-write:请求前后 `muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_command`、`muse_account_audit`、`muse_account_event_publish_outbox` 全表快照一致。 - - no-write 快照必须使用 `row_to_json(t)::text` 级别内容比对并按稳定键排序,至少按 `tenant_id, id` 排序;不能退化成只比较 row count。 -3. `appGetSecurityEvent`: - - detail 返回脱敏 sourceIp、deviceInfo.userAgent。 - - 合并最新 ack 的 acknowledgedAt / acknowledgedAction。 - - missing、非数字 eventId、跨 owner、跨 tenant 都返回 not found 语义且不泄露事件存在性。 - - read no-write,使用与 list 相同的 `row_to_json(t)::text` 全表快照集合。 - -最低 XML tests 数:建议 `P1rAccountSecurityEventsCompletedApprovalIT` >= 8。 - -### Task 3:TDD GREEN,最小 scanner/report 状态推进 - -只修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`: - -- 不修改 `APPROVED_COMPLETED_DOMAINS`。 -- 只向 `APPROVED_COMPLETED_OPERATIONS` 追加: - -```text -account:appListSecurityEvents -account:appGetSecurityEvent -``` - -- 保持 completed 必须仍是 `dedicated` 的校验。 - -生成 report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -预期 summary: - -```text -total=233 -completed=147 -needsVerification=86 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -预期 Account: - -```text -completed=12 -needsVerification=21 -``` - -### Task 4:Account focused verification - -运行 Account security focused tests: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB=muse_p1r_account_security_events_completed_approval_test -export P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB}" - -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_ACCOUNT_SECURITY_EVENTS_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_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_TEST_DB" - -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-member/muse-module-member-server,muse-server -am \ - -Dtest=AccountSecurityServiceTest,AppAccountSecurityControllerTest,AccountSecurityEventAckMapperTest,MemberSecurityEventMapperTest,AccountConvertTest,P1rAccountSecurityEventsCompletedApprovalIT,P1rAccountRealApiGateTest,P1rApiCoverageReportTest \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.account.security-events.completed-approval.jdbc-url="$P1R_ACCOUNT_SECURITY_EVENTS_COMPLETED_APPROVAL_JDBC_URL" \ - -Dp1r.account.security-events.completed-approval.jdbc-user="$MUSE_POSTGRES_USERNAME" \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML 并检查 `tests>0`、`failures=0`、`errors=0`、`skipped=0`: - -| Test class | 最低 tests | -|---|---:| -| `AccountSecurityServiceTest` | 8 | -| `AppAccountSecurityControllerTest` | 6 | -| `AccountSecurityEventAckMapperTest` | 4 | -| `MemberSecurityEventMapperTest` | 2 | -| `AccountConvertTest` | 8 | -| `P1rAccountSecurityEventsCompletedApprovalIT` | 8 | -| `P1rAccountRealApiGateTest` | 5 | -| `P1rApiCoverageReportTest` | 6 | - -`AccountSecurityServiceTest`、`AccountSecurityEventAckMapperTest` 中涉及 ack 或 `session_revoked` 的既有断言,只作为现状回归和 XML 防空跑证据,不计入本轮 completed evidence;`appAcknowledgeSecurityEvent` 仍必须保持 `needs_verification`。 - -如实现修改了 Account gate 测试数量,XML 检查的最低 tests 可高于上表,但不能低于上表。 - -建议使用以下 XML 防空跑脚本检查 Task 4、Task 5 和 Task 6 的目标 XML: - -```bash -python3 - <<'PY' -from pathlib import Path -import sys -import xml.etree.ElementTree as ET - -expected = { - "TEST-cn.iocoder.muse.module.member.application.account.AccountSecurityServiceTest.xml": 8, - "TEST-cn.iocoder.muse.module.member.controller.app.account.AppAccountSecurityControllerTest.xml": 6, - "TEST-cn.iocoder.muse.module.member.dal.mysql.account.AccountSecurityEventAckMapperTest.xml": 4, - "TEST-cn.iocoder.muse.module.member.dal.mysql.account.MemberSecurityEventMapperTest.xml": 2, - "TEST-cn.iocoder.muse.module.member.convert.account.AccountConvertTest.xml": 8, - "TEST-cn.iocoder.muse.server.framework.api.P1rAccountSecurityEventsCompletedApprovalIT.xml": 8, - "TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishFlywayMigrationIT.xml": 4, - "TEST-cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest.xml": 6, - "TEST-cn.iocoder.muse.server.framework.api.P1rAccountRealApiGateTest.xml": 5, - "TEST-cn.iocoder.muse.server.framework.api.P1rEventsRealApiGateTest.xml": 5, - "TEST-cn.iocoder.muse.server.framework.api.P1rAiRealApiGateTest.xml": 7, - "TEST-cn.iocoder.muse.server.framework.api.P1rKnowledgeRealApiGateTest.xml": 8, - "TEST-cn.iocoder.muse.server.framework.api.P1rMarketRealApiGateTest.xml": 6, - "TEST-cn.iocoder.muse.server.framework.api.P1rContentRealApiGateTest.xml": 5, - "TEST-cn.iocoder.muse.server.framework.api.P1rMetaRealApiGateTest.xml": 4, -} - -roots = [ - Path("muse-module-member/muse-module-member-server/target/surefire-reports"), - Path("muse-server/target/surefire-reports"), -] -failures = [] -for name, minimum in expected.items(): - xml_path = next((root / name for root in roots if (root / name).exists()), None) - if xml_path is None: - failures.append(f"missing {name}") - continue - suite = ET.parse(xml_path).getroot() - tests = int(suite.attrib.get("tests", "0")) - failed = int(suite.attrib.get("failures", "0")) - errors = int(suite.attrib.get("errors", "0")) - skipped = int(suite.attrib.get("skipped", "0")) - if tests < minimum or failed or errors or skipped: - failures.append(f"{name}: tests={tests}, failures={failed}, errors={errors}, skipped={skipped}, minimum={minimum}") - -if failures: - print("\n".join(failures)) - sys.exit(1) -print("P1R Account Security Events XML gate passed") -PY -``` - -### Task 5:Account Flyway `_test` gate - -fresh rerun `P1rAccountEventsPublishFlywayMigrationIT`。使用独立 `_test` 库,不能使用生产库或非 `_test` 库: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_TEST_DB=muse_p1r_account_security_events_flyway_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_ACCOUNT_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_ACCOUNT_TEST_DB" - -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_ACCOUNT_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 | -|---|---:| -| `P1rAccountEventsPublishFlywayMigrationIT` | 4 | - -### Task 6:P1R mixed gate - -运行 mixed gates: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML: - -| Test class | 最低 tests | -|---|---:| -| `P1rApiCoverageReportTest` | 6 | -| `P1rAccountRealApiGateTest` | 5 | -| `P1rEventsRealApiGateTest` | 5 | -| `P1rAiRealApiGateTest` | 7 | -| `P1rKnowledgeRealApiGateTest` | 8 | -| `P1rMarketRealApiGateTest` | 6 | -| `P1rContentRealApiGateTest` | 5 | -| `P1rMetaRealApiGateTest` | 4 | - -每个 XML 均必须 `failures=0`、`errors=0`、`skipped=0`。 - -### Task 7:报告与 diff gate - -检查 summary: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 147 86 0 0 0 -``` - -检查 Account 12/21: - -```bash -python3 - <<'PY' -import json -from collections import Counter -from pathlib import Path -data = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -counter = Counter(op["completionStatus"] for op in data["operations"] if op["domain"] == "account") -print(counter) -PY -``` - -预期: - -```text -Counter({'needs_verification': 21, 'completed': 12}) -``` - -检查目标 operation: - -```bash -jq -r '.operations[] | select(.domain=="account" and (.operationId=="appListSecurityEvents" or .operationId=="appGetSecurityEvent" or .operationId=="appAcknowledgeSecurityEvent")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring)] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -appListSecurityEvents dedicated completed false -appGetSecurityEvent dedicated completed false -appAcknowledgeSecurityEvent dedicated needs_verification true -``` - -检查 OpenAPI protected diff: - -```bash -git diff --quiet -- docs/api-contracts -git diff --cached --quiet -- docs/api-contracts -``` - -检查 business implementation 不被修改: - -```bash -git diff --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java -git diff --cached --quiet -- muse-cloud/muse-module-member/muse-module-member-server/src/main/java -``` - -检查 SQL migration 不被修改: - -```bash -git diff --quiet -- muse-cloud/sql/muse -git diff --cached --quiet -- muse-cloud/sql/muse -``` - -检查 whitespace: - -```bash -git diff --check -``` - -检查 allowed diff: - -```bash -git -c core.quotePath=false status --short -``` - -输出只能包含本执行版允许路径。若出现 OpenAPI、业务实现、迁移或非批准 gate 文件,必须停止。 - -### Task 8:memory 与 `.agent` - -实现通过后新增: - -```text -docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md -``` - -内容必须记录: - -- 批准的 2 个 Account operation。 -- 保持 `needs_verification` 的 21 个 Account operation 及原因。 -- `appAcknowledgeSecurityEvent` 暂缓原因:`session_revoked` 合同与实现不闭合。 -- scanner/report/gate 修改范围。 -- 所有验证命令、结果、XML 计数、Flyway `_test` 库名。 -- protected diff 与 allowed diff 结果。 -- 明确说明不代表 Account 33/33 completed,不代表 Market/Content/总 P1R completed。 - -同时更新 `docs/agent-specs/.agent`,但只能记录事实,不写未验证结论。 - -### Task 9:fresh implementation review - -实现、验证和文档留痕后,必须派发两类 fresh reviewer: - -- spec/correctness reviewer:检查 2 个 operation 是否严格匹配本执行版,21 个 operation 是否仍 needs_verification,Account domain allowlist 是否未新增。 -- quality/data-integrity/testing reviewer:检查 HTTP+DB `_test`、test-local auth fixture、scanner/report/gate、XML 防空跑、Flyway `_test`、protected diff、allowed diff、rollback 是否可靠。 - -双 PASS 前不得提交、push 或宣称 Account Security Events completed approval 已收口。 - -## 回滚策略 - -如实现后需要撤回: - -1. 从 `APPROVED_COMPLETED_OPERATIONS` 移除 2 个 `account:*` key。 -2. 重新运行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -3. 恢复 gate 期望: - -```text -summary completed=145 -summary needsVerification=88 -account completed=10 -account needsVerification=23 -``` - -4. 删除或修订本轮 memory,保留失败原因。 -5. 重新运行 Account/P1R focused gates 和 `git diff --check`。 - -## 验收标准 - -执行版本身可以进入实现前批准点的条件: - -1. 本文件已写入 `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md`。 -2. `.agent` 记录执行版当前状态。 -3. `git diff --check` 通过。 -4. OpenAPI、scanner、coverage report、业务实现、SQL migration 当前无新增 diff。 -5. fresh execution spec/scope review PASS。 -6. fresh execution quality/feasibility/testing review PASS。 - -实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。 diff --git a/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md b/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md deleted file mode 100644 index 26ab8c27..00000000 --- a/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md +++ /dev/null @@ -1,350 +0,0 @@ -# P1R Content Admin Read Completed Approval 审阅版 - -日期:2026-06-13 - -## 结论 - -推荐 Content 下一批只推进管理后台只读模型 3 个 operation 的 operation-level completed approval: - -```text -content:adminListWorks -content:adminGetWork -content:adminListChapters -``` - -不推荐把 Content 剩余 41 个 operation 一次性推进 completed,也不推荐把 `content` 加入 domain-level completed allowlist。 - -这 3 个 operation 是 admin 侧读模型,只读取作品摘要、章节摘要和治理摘要,不写 command / audit / outbox,不依赖 FileService、AI candidate、Meta projection 或导入导出产物。它们的 completed approval 应独立成一个小切片,用真实 PostgreSQL `_test`、MockMvc HTTP 入口、admin RBAC、tenant 隔离、治理摘要聚合和正文不泄露证据闭合。 - -本审阅版只冻结候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。 - -```mermaid -flowchart TB - Current["当前 Content
10 completed / 41 needs_verification"] --> Choose["选择下一批 evidence slice"] - Choose --> AdminRead["推荐:admin read
adminListWorks + adminGetWork + adminListChapters"] - Choose --> Wider["不推荐:Content 剩余 41 全量推进"] - AdminRead --> Boundary{"是否写入业务事实?"} - Boundary -->|否| Evidence["只读 evidence
RBAC + tenant + no body leak + no-write"] - Boundary -->|是| Defer["保持 needs_verification"] - Evidence --> Review["fresh spec/scope review
fresh quality/feasibility review"] - Review --> Exec["双 PASS 后写执行版"] - Exec --> Approval{"用户明确批准后才实现"} - Approval -->|否| Stay["保持 233/141/92"] - Approval -->|是| Target["目标 233/144/89
Content 13 completed / 38 needs_verification"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -2692af3 test(p1r): 收口 Content Planning completed approval 门禁 -``` - -当前分支状态: - -```text -dev/1.0.0...origin/dev/1.0.0 -``` - -当前 coverage summary: - -```text -total=233 -completed=141 -needsVerification=92 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=10 needsVerification=41 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -``` - -本审阅版推荐的下一批目标值只是后续获批后的执行目标: - -```text -total=233 -completed=144 -needsVerification=89 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 - -content total=51 -completed=13 -needsVerification=38 -``` - -## 推荐候选 - -| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence | -|---|---|---|---|---| -| `content:adminListWorks` | `GET` | `/admin-api/muse/content/works` | admin 作品列表读模型,支持 status / keyword / riskFlag,返回治理摘要,不返回正文 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 API version、admin RBAC、分页过滤、riskFlag true/false、治理摘要、tenant 隔离、正文不泄露、纯读 no-write | -| `content:adminGetWork` | `GET` | `/admin-api/muse/content/works/{workId}` | admin 作品详情读模型,返回作品元信息、章节摘要、异常摘要和治理历史,不返回正文全文 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖存在、missing、cross tenant、治理历史、章节摘要聚合、正文不泄露、纯读 no-write | -| `content:adminListChapters` | `GET` | `/admin-api/muse/content/works/{workId}/chapters` | admin 章节摘要列表读模型,按章节顺序返回 blockCount / wordCount / riskFlags,不返回 block 正文 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖章节排序、blockCount、wordCount 聚合、治理 risk flags、missing、cross tenant、正文不泄露、纯读 no-write | - -推荐理由: - -- 3 个 operation 全部是 `GET`,当前 coverage `requiresCommandId=false`,与只读语义一致。 -- Controller 已有 `@PreAuthorize("@ss.hasPermission('muse:content:query')")`,后续 completed evidence 应证明 admin RBAC 仍是入口门槛。 -- Service 读模型只调用 Work / Chapter / Block / GovernanceAction mapper,不写 command、audit、outbox 或业务表。 -- OpenAPI 已声明 admin read 不返回用户私有正文全文,现有 controller / service 单测也已有“不返回 content/body/contentText”方向的证据。 -- 这 3 个 operation 可与 `adminRiskAction` 完全切开,避免把治理写命令的 commandId、expectedVersion、幂等、审计和行锁要求混入只读切片。 - -## 不纳入本批的 operation - -以下 38 个 Content operation 必须继续 `dedicated / needs_verification`: - -```text -content:adminListExportTasks -content:adminListImportTasks -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 -``` - -排除原因: - -- `adminRiskAction` 是治理写命令,当前 `requiresCommandId=true`,服务实现包含 command reserve、expectedVersion 校验、行锁、治理事实 insert、audit 和幂等 replay,必须单独做写路径切片。 -- `adminListImportTasks` / `adminListExportTasks` 虽是 admin read,但读的是任务域,证据应覆盖导入导出任务状态、产物和 work 关联,不应夹入本轮作品/章节读模型。 -- 创建、更新、删除、重排、拆分、合并等 Content 写命令仍有独立 OpenAPI / commandId / revision / no-write 负路径闭合风险。 -- 导入、导出、解析、下载仍缺 FileService、任务产物和真实下载字节闭环 evidence。 -- Meta projection、AI planning candidate、style check、suggestion 等能力依赖 Meta / AI / Knowledge 外部 owner closure 和运行时证据。 - -## 已有实现证据 - -### Controller 入口 - -`AdminContentController` 当前提供 3 个 admin read route: - -```text -GET /muse/content/works -GET /muse/content/works/{workId} -GET /muse/content/works/{workId}/chapters -``` - -以上是 controller-local mapping;对外 OpenAPI 路径仍以 `/admin-api/muse/...` 为准。执行版必须通过当前项目既有 admin MockMvc / Web 测试上下文证明最终 admin API 路径可访问。 - -三者均调用 `requireApiVersion()`,并使用: - -```text -@PreAuthorize("@ss.hasPermission('muse:content:query')") -``` - -这能作为后续执行版的入口证据来源,但不能替代 HTTP + real DB completed evidence。 - -### Service 读模型 - -`ContentAdminServiceImpl.listWorks`: - -- `riskFlag=true` 时先按当前 tenant 查询 governance action 的 distinct work ids。 -- 通过 `WorkMapper.selectAdminPage` 按 status、keyword、riskWorkIds 分页。 -- 查询当前页作品的 governance actions,转换为 risk flags 和最后治理时间。 - -`ContentAdminServiceImpl.getWork`: - -- 通过 `requireWork(workId)` 读取作品。 -- 复用 `listChapters(workId)` 返回章节摘要。 -- 查询治理动作,返回 exception summary 和 governance history。 -- 返回作品元信息、ownerId、wordCount、chapterCount、createdAt、updatedAt,不返回 block 正文。 - -`ContentAdminServiceImpl.listChapters`: - -- 通过 `requireWork(workId)` 确认作品存在。 -- 查询 governance actions。 -- 按 `ChapterMapper.selectListByWorkId` 顺序返回章节摘要。 -- 通过 `BlockMapper.selectCountByChapterId` 和 `selectWordCountSumByChapterId` 聚合 block 数和字数,不返回正文。 - -### OpenAPI 合同 - -OpenAPI 当前 operationId: - -```text -adminListWorks -adminGetWork -adminListChapters -``` - -OpenAPI 对 admin read 的核心约束是: - -- 管理员查询作品列表和治理摘要。 -- 管理员查看作品元信息、章节摘要、异常摘要。 -- 管理员查看作品章节列表,不含正文全文。 -- 默认不返回用户私有正文全文,确需查看必须另有合规访问设计。 - -本切片不需要修改 `docs/api-contracts/content/openapi.yaml`。 - -### 当前单元测试证据 - -现有 focused tests 可作为执行版辅助证据: - -- `AdminContentControllerTest.should_requireRbacAnnotationsForAdminContentApis` -- `AdminContentControllerTest.should_rejectMissingApiVersion_when_listWorks` -- `AdminContentControllerTest.should_exposeAdminReadRoutesWithoutPrivateBody` -- `ContentAdminServiceTest.should_listWorksWithGovernanceSummary` -- `ContentAdminServiceTest.should_getWorkDetailWithoutPrivateBody` -- `ContentAdminServiceTest.should_listChaptersWithoutBody` -- `ContentAdminServiceTest.should_listChaptersWithAggregatedWordCountAndKeepBlockCount` - -这些测试不能直接等同 completed。completed approval 仍需要新增或扩展 HTTP + real PostgreSQL `_test` 证据,证明 route、mapper、tenant 拦截、真实 schema 和 no-write 一起成立。 - -## 必补证据标准 - -后续执行版必须至少覆盖以下矩阵。 - -### Admin List Works - -- 有 `X-API-Version` 的成功请求。 -- 缺 `X-API-Version` 的统一错误响应。 -- admin 权限 `muse:content:query` 存在,并有缺权限拒绝证据。 -- `status` 过滤。 -- `keyword` 过滤。 -- `riskFlag=false` 返回普通作品分页。 -- `riskFlag=true` 只返回存在治理动作的作品。 -- `riskFlag=true` 且当前 tenant 无治理动作时返回空页。 -- risk flags、lastGovernanceActionAt、ownerId、wordCount、chapterCount 等摘要字段正确。 -- cross tenant 数据不可见。 -- JSON 响应不含 block 正文、`contentText`、`content`、`body` 等私有正文字段。 -- 请求前后 command、audit、outbox、governance action、work/chapter/block 行数不发生写入变化。 - -### Admin Get Work - -- 存在作品时返回作品元信息、章节摘要、异常摘要、治理历史。 -- missing work 返回统一错误。 -- cross tenant work 返回不可见或 not found 语义,不泄露其它 tenant 数据。 -- 章节摘要包含 order、blockCount、wordCount。 -- governance history 按时间或实现定义顺序返回,risk summary 与治理动作一致。 -- JSON 响应不含 block 正文、`contentText`、`content`、`body` 等私有正文字段。 -- 纯读 no-write。 - -### Admin List Chapters - -- 存在作品时按 `orderNo` 升序返回章节摘要。 -- missing work 返回统一错误。 -- cross tenant work 返回不可见或 not found 语义。 -- blockCount 与真实 block 行数一致。 -- wordCount 聚合使用当前 tenant 数据,空章节或 NULL 字数时返回 0。 -- risk flags 与治理动作 target scope 语义一致。 -- JSON 响应不含章节/Block 正文。 -- 纯读 no-write。 - -### Flyway / schema - -执行版不需要新增 migration,但必须证明当前 `_test` 库能从 V1 到当前目标版本 clean migrate,并且 admin read 依赖的表和索引真实存在: - -```text -muse_content_work -muse_content_chapter -muse_content_block -muse_content_governance_action -``` - -如果复用 Content 既有 Flyway IT,执行版必须明确说明复用关系、目标 schema version、XML 防空跑方式和本切片新增断言;不能把历史 XML 当 fresh evidence。 - -## 风险和取舍 - -1. admin read 不是 app owner read。 - - 本切片不能要求 `owner_user_id = loginUserId`。正确边界是 admin RBAC、tenant SQL 拦截、API version 和不泄露正文。执行版如果把 app owner guard 套到 admin read 上,会改变产品语义。 - -2. `requireWork(workId)` 依赖 mapper 和 tenant 拦截器。 - - Service 内 `requireWork` 当前调用 `workMapper.selectById(workId)`,不是显式 owner 查询。completed evidence 必须在真实 MyBatis + tenant interceptor 环境里证明 cross tenant 不可见,而不是只用 mock service。 - -3. riskFlag 证据不能扩写成 risk action completed。 - - `riskFlag` 只证明 governance action 读聚合;不证明 `adminRiskAction` 的 commandId、expectedVersion、幂等、行锁、审计和写入路径。 - -4. 正文字段泄露是本切片的硬风险。 - - admin read 合同明确默认不返回私有正文全文。执行版必须用 JSON path / JSON 字符串断言正文相关字段不存在,不能只检查 DTO 类名。 - -5. 只读 no-write 必须跨表断言。 - - 这 3 个 operation 是 read completed approval。执行版必须对 command、audit、outbox、governance action、work、chapter、block 的写入污染做基线计数或关键字段快照断言。 - -## 后续执行版要求 - -后续执行版必须包含: - -1. 精确目标 operation 清单:只包括 `adminListWorks`、`adminGetWork`、`adminListChapters`。 -2. operation-level approval:不把 `content` 加入 domain allowlist。 -3. 实施前 RED:旧 report 下 P1R coverage gate 必须因目标 3 ops 尚未 completed 而失败。 -4. HTTP + real PostgreSQL `_test` gate:新增或独立扩展 `P1rContentAdminReadCompletedApprovalIT`。 -5. Web/MyBatis/DataSource 测试上下文:必须启用真实 tenant interceptor、真实 mapper、真实 service 和 controller。 -6. RBAC / API version / CommonResult 错误响应证据。 -7. `adminListWorks`、`adminGetWork`、`adminListChapters` 的 happy、missing、cross tenant、正文不泄露、no-write matrix。 -8. focused tests:运行 `ContentAdminServiceTest`、`AdminContentControllerTest` 并做 XML 防空跑。 -9. P1R mixed gates:同步所有读取全局 summary、Content 状态或非目标 domain 防回退的 gate。 -10. scanner/report 更新策略:只在用户批准后追加 3 个 `content:*` operation-level allowlist。 -11. protected diff:OpenAPI、Content main/java、SQL migration 默认不得修改。 -12. `.agent` 和 `docs/memorys` 留痕。 -13. rollback:撤回 scanner operation keys、coverage report、P1R gate 目标值和新增 `_test`,恢复 3 个 operation 为 `needs_verification`。 - -## 审批前置条件 - -进入执行版前需要 fresh review 双 PASS: - -- fresh spec/scope review:确认只覆盖 3 个 admin read operation,不夹带 `adminRiskAction`、import/export task、Content 写命令或 Content domain-level completed。 -- fresh quality/feasibility/testing review:确认 admin RBAC、tenant 拦截、正文不泄露、riskFlag 聚合、no-write、XML 防空跑和 mixed gate 同步可落地。 - -执行版双 PASS 后,仍必须由用户明确批准以下事项后才允许实现: - -1. 只审批 `content:adminListWorks`、`content:adminGetWork`、`content:adminListChapters`。 -2. 继续 operation-level approval,不把 `content` 加入 domain allowlist。 -3. 允许按执行版修改 scanner、coverage report、P1R gate tests、新增 HTTP+DB `_test` 和 memory。 -4. 允许同步 P1R mixed gate 的 summary / Content / 非目标域防回退断言。 - -## 本审阅版验收标准 - -- 候选 operation 和目标值可由当前 report 机械推导。 -- 非目标 38 个 Content operation 明确保持 `needs_verification`。 -- admin read 与 app owner read、risk action 写命令、import/export task 读模型的边界清晰。 -- evidence 标准包含 admin RBAC、tenant 隔离、正文不泄露、riskFlag 聚合、no-write 和 XML 防空跑。 -- 未修改 OpenAPI、scanner、coverage report、业务实现、SQL migration 或 P1R gate。 diff --git a/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md b/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md deleted file mode 100644 index 476dfd11..00000000 --- a/docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md +++ /dev/null @@ -1,690 +0,0 @@ -# P1R Content Admin Read Completed Approval 执行版 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task only after fresh execution review double PASS and explicit user approval. Steps use checkbox syntax for tracking. - -日期:2026-06-13 - -## 结论 - -本执行版只锁定 Content 管理后台只读模型 3 个 operation-level completed approval: - -```text -content:adminListWorks -content:adminGetWork -content:adminListChapters -``` - -本轮不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51 completed,不修改 OpenAPI,不修改 Content 业务实现,不修改 SQL migration。 - -获批后目标 coverage summary 为: - -```text -total=233 -completed=144 -needsVerification=89 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Content 域目标状态为: - -```text -content total=51 -completed=13 -needsVerification=38 -``` - -当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。 - -```mermaid -flowchart TB - Current["当前 Content
10 completed / 41 needs_verification"] --> Exec["执行版
锁定 Admin Read 3 ops"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/testing review"] - Review --> Approval{"用户明确批准
3 ops / operation-level / allowed diff / mixed gates"} - Approval -->|否| Stay["保持当前 coverage
233/141/92"] - Approval -->|是| Red["TDD RED
先改 gate 期望
旧 report 必须失败"] - Red --> Green["TDD GREEN
scanner operation allowlist
regenerate report"] - Green --> Runtime["Admin Read HTTP + real DB _test
RBAC + tenant + no body leak + no-write"] - Runtime --> Verify["focused tests + P1R mixed gates
XML 防空跑 + protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -2692af3 test(p1r): 收口 Content Planning completed approval 门禁 -``` - -当前分支: - -```text -dev/1.0.0...origin/dev/1.0.0 -``` - -当前 coverage summary: - -```text -233 141 92 0 0 0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=10 needsVerification=41 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -``` - -当前 Content 状态: - -- Content total 为 51。 -- 已有 10 个 operation 为 `dedicated / completed`:`listWorks`、`getWork`、`listChapters`、`getChapter`、`listBlocks`、`getBlock`、`saveBlock`、`getBlockSourceAttribution`、`getPlanning`、`savePlanningItem`。 -- 本执行版目标 3 个 operation 当前为 `dedicated / needs_verification / requiresCommandId=false`。 -- `content:adminRiskAction` 当前为 `dedicated / needs_verification / requiresCommandId=true`,不属于本执行版。 -- `APPROVED_COMPLETED_DOMAINS` 当前只允许 `ai`、`knowledge`。 -- `APPROVED_COMPLETED_OPERATIONS` 当前包含 Events streamEvents、Meta 16、Account 第一批 10、Market 第一批 4、Content 第一批 8、Content Planning 2。 - -已验证的只读实现事实: - -- `AdminContentController` 三个 admin read method 均使用 `@PreAuthorize("@ss.hasPermission('muse:content:query')")`。 -- `AdminContentController` 三个 admin read method 均调用 `requireApiVersion()` 读取 `X-API-Version`。 -- Controller-local mapping 为 `/muse/...`;对外 OpenAPI 路径为 `/admin-api/muse/...`,执行版必须用当前 Web 测试上下文证明最终 admin API 路径。 -- `ContentAdminServiceImpl.listWorks` 通过 `riskFlag` 读取 governance action work ids,再按 status / keyword / riskWorkIds 分页读取 work,并聚合 risk flags。 -- `ContentAdminServiceImpl.getWork` 读取 work、章节摘要、exception summary 和 governance history,不返回 block 正文。 -- `ContentAdminServiceImpl.listChapters` 读取章节列表,聚合 `blockCount` 与 `wordCount`,不返回正文。 -- `requireWork(workId)` 当前调用 `workMapper.selectById(workId)`,不是 app owner guard;admin read 的正确安全边界是 admin RBAC + tenant SQL interceptor + 正文不泄露。 -- 现有 OpenAPI 明确 admin read 默认不返回用户私有正文全文。 -- 现有 `AdminContentControllerTest` / `ContentAdminServiceTest` 只可作为辅助证据,不能替代 completed-grade HTTP + real PostgreSQL `_test`。 - -## 执行边界 - -### 必须先获用户明确批准 - -实现前必须同时获得以下 4 项批准: - -1. 批准本轮只审批 `content:adminListWorks`、`content:adminGetWork`、`content:adminListChapters` 三个 operation。 -2. 批准继续使用 operation-level approval,不把 `content` 加入 domain-level completed allowlist。 -3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Admin Read HTTP+DB `_test`、memory 和 `.agent`。 -4. 批准同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为 `233/144/89/0/0/0` 与 Content `13 completed / 38 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/P1rContentAdminReadCompletedApprovalIT.java` -- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAdminServiceTest.java`,仅限补充本执行版缺失的 focused behavior 证据。 -- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/admin/AdminContentControllerTest.java`,仅限补充本执行版缺失的 controller contract 证据。 -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md` - -如 implementation 发现必须修改 Content 业务实现、OpenAPI、SQL migration、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。 - -### 本轮禁止变更 - -- 不修改 7 个 OpenAPI。 -- 不修改 Content main/java 业务实现来掩盖 coverage 缺口。 -- 不新增或修改 SQL migration。 -- 不把 `content` 加入 domain-level completed allowlist。 -- 不推进 Content 51/51 completed。 -- 不推进 Account remaining 23、Market remaining 28、Content remaining 38 或总 P1R completed。 -- 不推进 `content:adminRiskAction`、admin import/export task、Content 写命令、AI planning candidate、style check、Meta projection、dynamic fields、FileService、import/export/parse operation。 -- 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。 - -## 审批清单 - -| operation key | Method | Path | 目标状态 | 完成证据边界 | -|---|---|---|---|---| -| `content:adminListWorks` | `GET` | `/admin-api/muse/content/works` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 API version、method security RBAC、分页、status / keyword / riskFlag true/false、riskFlag 空结果、治理摘要、tenant 隔离、正文不泄露、纯读 no-write | -| `content:adminGetWork` | `GET` | `/admin-api/muse/content/works/{workId}` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖存在、missing、cross tenant、章节摘要、异常摘要、治理历史、正文不泄露、纯读 no-write | -| `content:adminListChapters` | `GET` | `/admin-api/muse/content/works/{workId}/chapters` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖章节排序、blockCount、wordCount 聚合、空章节 / NULL wordCount、governance target scope、missing、cross tenant、正文不泄露、纯读 no-write | - -## 保持 needs_verification 的 Content operation - -以下 38 个 operation 必须保持 `dedicated / needs_verification`: - -```text -content:adminListExportTasks -content:adminListImportTasks -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 -``` - -## 证据分层 - -| Evidence | 文件 / 测试 | 目的 | -|---|---|---| -| Operation inventory / coverage gate | `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest` 和 mixed gates | 证明只推进获批 3 个 operation,非目标 domain 不回退 | -| Admin HTTP+DB `_test` | 新增 `P1rContentAdminReadCompletedApprovalIT` | 证明 3 个 operation 经 MockMvc HTTP 入口访问真实 PostgreSQL `_test` 数据 | -| Tenant / schema gate | `P1rContentAdminReadCompletedApprovalIT` 内验证 V1-V21 clean migrate 与 admin read 依赖表 | 证明 Work / Chapter / Block / GovernanceAction schema 和 tenant interceptor 真实可用 | -| Method security gate | `P1rContentAdminReadCompletedApprovalIT` 内启用 method security 与 `ss` bean | 证明 admin RBAC 缺权限拒绝不是注解反射假证据 | -| Focused unit/controller tests | `ContentAdminServiceTest`、`AdminContentControllerTest` | 支撑 service/controller contract,不替代 HTTP+DB `_test` | - -## 共同验证规则 - -每个 Maven 验证任务必须: - -- 先 `rm -f` 目标 surefire XML,避免读旧 XML。 -- 设置 `RUN_START_EPOCH=$(date +%s)`。 -- Maven 完成后读取目标 XML,检查 `mtime >= RUN_START_EPOCH`。 -- 检查 `tests >= 最低 tests`、`failures=0`、`errors=0`、`skipped=0`。 -- 如果命令失败,保留失败输出和 XML,不得通过跳过测试或降低最低 tests 继续。 - -XML 防空跑脚本模板: - -```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 = [ - # ("path/to/TEST-Example.xml", min_tests), -] -failed = False -for path, min_tests in checks: - p = Path(path) - if not p.exists(): - print(f"missing_xml={path}") - failed = True - continue - mtime = int(p.stat().st_mtime) - if mtime < run_start: - print(f"stale_xml={path} mtime={mtime} run_start={run_start}") - failed = True - root = ET.parse(p).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=144`、`needsVerification=89`,但旧 report 仍是 `141/92`,必须失败。 -- Content 13 个 approved operation 期望 `completed`,但 `adminListWorks`、`adminGetWork`、`adminListChapters` 在旧 report 仍是 `needs_verification`,必须失败。 -- Content 38 个未批准 operation 继续期望 `needs_verification`,其中必须包含 `adminRiskAction`、`adminListImportTasks`、`adminListExportTasks`、`createWork`、`updateWork`、`createChapter`、`mergeBlockSuggestion`、`exportWork`、`listPlanningCandidates`、`createStyleCheck`。 -- 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 \ - -DfailIfNoTests=false \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -预期:至少一个失败点来自旧 report 的 `141/92` 或目标 3 个 admin read operation 尚未 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=144 -needsVerification=89 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -预期 Content: - -```text -completed=13 -needsVerification=38 -``` - -预期 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:adminListWorks", "content:adminGetWork", "content:adminListChapters"]: - assert key in source -print("content_admin_read_operation_level_allowlist=PASS") -PY -``` - -### Task 3:Focused Admin unit/controller verification - -运行 Content admin 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.ContentAdminServiceTest.xml \ - muse-module-content/muse-module-content-server/target/surefire-reports/TEST-cn.iocoder.muse.module.content.controller.admin.AdminContentControllerTest.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=ContentAdminServiceTest,AdminContentControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -必须读取以下 XML 并检查 `mtime >= RUN_START_EPOCH`、`failures=0`、`errors=0`、`skipped=0`: - -| Test class | 最低 tests | -|---|---:| -| `ContentAdminServiceTest` | 10 | -| `AdminContentControllerTest` | 6 | - -Focused tests 必须至少支撑: - -- `listWorks` status / keyword / governance summary。 -- `getWork` 不返回正文。 -- `listChapters` 不返回正文,且 blockCount / wordCount 聚合正确。 -- Controller 层 `X-API-Version`、CommonResult route、`@PreAuthorize` 注解合同。 - -Focused tests 只作为补充证据,不替代 Task 4 的 HTTP + real DB `_test`;尤其缺权限拒绝不能只靠 `@PreAuthorize` 反射断言。 - -### Task 4:Admin Read HTTP + 真实 PostgreSQL `_test` completed gate - -新增 `P1rContentAdminReadCompletedApprovalIT`,使用独立 `_test` 数据库、MockMvc HTTP 入口、真实 mapper/service、真实 PostgreSQL 数据。 - -测试上下文要求: - -- 只导入 admin read 必需 controller/service/mapper 与基础 Web/MyBatis/DataSource 自动配置,不做 Content 全包扫描。注意 `ContentAdminServiceImpl` 字段注入还包含 `ImportTaskMapper`、`ExportTaskMapper`、`ContentCommandService`、`ContentAuditService`;即使本切片不调用 import/export/risk 写路径,测试上下文也必须保证这些 `@Resource` 依赖可启动,避免 Spring 启动失败。 -- 最小 Spring 配置必须复用 `P1rContentCoreCompletedApprovalIT` / `P1rContentPlanningCompletedApprovalIT` 的 Web/MyBatis/DataSource 模式:`JacksonAutoConfiguration`、`HttpMessageConvertersAutoConfiguration`、`DataSourceAutoConfiguration`、`DataSourceTransactionManagerAutoConfiguration`、`JdbcTemplateAutoConfiguration`、`TransactionAutoConfiguration`、`RestTemplateAutoConfiguration`、`WebMvcAutoConfiguration`、`MuseDataSourceAutoConfiguration`、`MuseMybatisAutoConfiguration`、`MybatisPlusAutoConfiguration`、`MybatisPlusJoinAutoConfiguration`、`MuseWebAutoConfiguration`。 -- 导入 `AdminContentController`、`ContentAdminServiceImpl`。 -- `ContentAdminServiceImpl` 字段依赖中包含 `ContentCommandService` 与 `ContentAuditService`。本切片只走读路径,不应调用它们;测试上下文必须提供真实 bean 或 fail-fast test stub 使 Spring 可启动,并在所有 admin read 用例后断言 fail-fast stub 未被调用。 -- 提供 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 数据碰巧不可见。 -- 启用 method security。可选方案是导入 `MuseWebSecurityConfigurerAdapter` 并提供最小安全依赖,或在 test configuration 上使用 `@EnableMethodSecurity(securedEnabled = true)`。 -- 提供 `@Bean("ss")` 的 `SecurityFrameworkService` test double,按测试用例可控返回 `true/false`,并记录收到的 permission;授权成功用例必须断言调用了 `muse:content:query`。缺权限用例必须返回拒绝响应、不泄露 seed 数据,并保持 command/outbox/governance/work/chapter/block no-write;如要证明 service 未执行,可使用 spy bean 或 fail-fast wrapper,但不得把该证明建立在 `@PreAuthorize` 反射断言上。 -- 使用 `WebApplicationContext + MockMvc` 访问 `/admin-api/muse/content/works`、`/admin-api/muse/content/works/{workId}`、`/admin-api/muse/content/works/{workId}/chapters`。 -- 同时断言 controller-local `/muse/...` 不是 completed evidence 的唯一入口;最终证据必须来自 `/admin-api/muse/...`。 -- 每个测试启动时设置 admin login user、tenant context,并在测试结束清理 `SecurityContextHolder` 与 `TenantContextHolder`。 -- 每个测试前 `TRUNCATE muse_content_governance_action, muse_content_block, muse_content_chapter, muse_content_work, muse_content_command_log, muse_content_event_publish_outbox RESTART IDENTITY CASCADE`,再 seed 必需数据。Content audit snapshot 当前保存在 `muse_content_command_log.result_snapshot` 内,不存在独立 Content audit 表,执行版不得新增该表或把它作为 no-write 断言目标。 - -建库与运行: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_READ_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_admin_read_completed_approval_test - -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_CONTENT_ADMIN_READ_COMPLETED_APPROVAL_TEST_DB'" | grep -q 1 || \ -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -c "CREATE DATABASE $P1R_CONTENT_ADMIN_READ_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.P1rContentAdminReadCompletedApprovalIT.xml -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rContentAdminReadCompletedApprovalIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_ADMIN_READ_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 | -|---|---:| -| `P1rContentAdminReadCompletedApprovalIT` | 10 | - -该 IT 必须覆盖以下 10 个测试: - -1. 拒绝 JVM password system property 和 JDBC credential query,库名必须以 `_test` 结尾。 -2. V1-V21 clean migrate 后验证 admin read 依赖 schema:`muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_governance_action` 表、tenant_id、deleted、work/chapter/block 关联列、基础索引或查询所需列存在。 -3. Method security 成功路径:`ss.hasPermission("muse:content:query")` 返回 true 时,`GET /admin-api/muse/content/works` 成功,并记录 permission 调用。 -4. Method security 拒绝路径:`ss.hasPermission("muse:content:query")` 返回 false 时,admin read 请求被拒绝,不泄露 seed 数据,并保持 command/outbox/governance/work/chapter/block no-write;该证据必须来自启用 method security 的 HTTP 请求,不能只靠 `@PreAuthorize` 反射断言。 -5. `adminListWorks` happy path:`status`、`keyword`、`riskFlag=false` 返回当前 tenant 普通分页、ownerId、wordCount、chapterCount、risk flags 和 lastGovernanceActionAt,且不泄露正文。 -6. `adminListWorks` risk path:`riskFlag=true` 只返回当前 tenant 有 governance action 的 work;当前 tenant 无治理动作时返回空页;cross tenant governance action 不影响当前 tenant。 -7. `adminGetWork` happy path:返回作品元信息、章节摘要、exception summary、governance history;不返回 `contentText`、`content`、`body` 等正文字段。 -8. `adminGetWork` missing / cross tenant:返回统一错误或 not found 语义,不泄露其它 tenant 数据,且 no-write。 -9. `adminListChapters` happy path:按 `orderNo` 升序返回章节摘要,blockCount 与真实 block 行数一致,wordCount 聚合当前 tenant 数据,空章节或 NULL wordCount 返回 0,governance target scope 对章节 risk flags 生效。 -10. `adminListChapters` missing / cross tenant / missing API version:缺 `X-API-Version` 返回 CommonResult 错误;missing/cross tenant 不泄露数据;三类负路径均 no-write。 - -No-write assertion 必须至少覆盖: - -```text -muse_content_command_log -muse_content_event_publish_outbox -muse_content_governance_action -muse_content_work -muse_content_chapter -muse_content_block -``` - -正文不泄露 assertion 必须至少覆盖 JSON 字符串或 JSON path 中不存在: - -```text -contentText -content -body -正文全文 -``` - -数据库与安全要求: - -- 密码只能从 `P1R_CONTENT_ADMIN_READ_COMPLETED_PASSWORD`、`P1R_CONTENT_COMPLETED_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。 - -### Task 5:Admin Read + existing Content DB gates 组合验证 - -运行新增 Admin Read IT 与既有 Content DB gates,防止 Flyway system property、tenant interceptor 或 data source 配置互相污染: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_READ_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_admin_read_combined_test - -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_CONTENT_ADMIN_READ_COMPLETED_APPROVAL_TEST_DB'" | grep -q 1 || \ -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -c "CREATE DATABASE $P1R_CONTENT_ADMIN_READ_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.P1rContentAdminReadCompletedApprovalIT.xml \ - 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.P1rContentPlanningCompletedApprovalIT.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=P1rContentAdminReadCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentPlanningCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_ADMIN_READ_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 | -|---|---:| -| `P1rContentAdminReadCompletedApprovalIT` | 10 | -| `P1rContentCoreCompletedApprovalIT` | 12 | -| `P1rContentPlanningCompletedApprovalIT` | 11 | -| `P1rContentEventsPublishFlywayMigrationIT` | 4 | - -总计最低 tests 为 37。 - -### Task 6:P1R mixed gates - -重新运行并 XML 防空跑: - -```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=144`、`needsVerification=89`;approved Content completed set 改为 13;断言 `adminListWorks`、`adminGetWork`、`adminListChapters` completed;仍断言 `adminRiskAction`、`adminListImportTasks`、`adminListExportTasks`、`createWork`、`updateWork`、`createChapter`、`mergeBlockSuggestion`、`exportWork`、`listPlanningCandidates`、`createStyleCheck` 为 `needs_verification`。 -- `P1rContentRealApiGateTest`:approved Content completed set 改为 13;Content count 改为 `13 completed / 38 needs_verification`;代表性未批准 operation 必须包含 `adminRiskAction`、admin import/export task、AI planning candidate / style check 相关 operation。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rMetaRealApiGateTest`:同步 summary 文案和 Content count 到 `13/38`,并继续校验 Account `10/23`、Market `4/28`、Meta `16/0`、AI/Knowledge/Events 不回退。 - -### Task 7: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,.requiresCommandId] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -预期: - -```text -233 144 89 0 0 0 -``` - -Content 必须精确为 13 completed / 38 needs_verification,且 `APPROVED_COMPLETED_DOMAINS` 仍不包含 `content`。 - -### Task 8:Diff gate 与文档留痕 - -- [ ] 更新 `.agent`,记录 implementation evidence、测试命令、XML 计数、protected diff、review 状态和 remaining boundary。 -- [ ] 新增或更新 `docs/memorys/2026-06-13-P1RContentAdminRead状态推进.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 9:Fresh implementation review gate - -实现与验证完成后,必须重新派发两个 fresh reviewer: - -- fresh implementation spec/correctness reviewer。 -- fresh implementation quality/data-integrity/testing reviewer。 - -两个 reviewer 都必须只读复核: - -- 正确 worktree 与 HEAD。 -- coverage summary `233/144/89/0/0/0`。 -- Content 精确为 `13 completed / 38 needs_verification`。 -- `APPROVED_COMPLETED_DOMAINS` 未加入 `content`。 -- scanner 只追加 `content:adminListWorks`、`content:adminGetWork`、`content:adminListChapters` 三个 operation-level allowlist。 -- `adminRiskAction`、admin import/export task、Content 写命令、AI planning candidate / style check 仍为 `needs_verification`。 -- OpenAPI、Content main/java、SQL migration diff 为空。 -- `P1rContentAdminReadCompletedApprovalIT` HTTP + real DB `_test` 证据覆盖 admin RBAC、tenant interceptor、正文不泄露、riskFlag 聚合、missing/cross tenant、no-write。 -- P1R mixed gates XML 防空跑成立。 - -fresh implementation review 双 PASS 前,不得提交、push 或宣称 `adminListWorks`、`adminGetWork`、`adminListChapters` completed approval 已收口。 - -## 回滚策略 - -如任一 gate 失败: - -- 保留失败输出和 XML 事实,先定位是否是执行版遗漏、测试假设错误、实现缺口或环境问题。 -- 不通过修改 OpenAPI、Content main/java 或 SQL migration 来绕过本轮 coverage 缺口。 -- 如缺口证明当前实现不足以支撑 completed approval,回退本轮 scanner/report/gate 状态推进,只保留审阅版和执行版文档事实,向用户汇报阻塞。 - -如需要回退已实施的 operation-level approval: - -- 从 `APPROVED_COMPLETED_OPERATIONS` 移除 `content:adminListWorks`、`content:adminGetWork`、`content:adminListChapters`。 -- 重新运行 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check`,恢复 summary `233/141/92/0/0/0`。 -- 恢复 P1R gate 对 Content `10 completed / 41 needs_verification` 的断言。 -- 保留 memory 中的失败事实和回退原因,避免后续重复推进。 - -## 自检清单 - -- [ ] 本执行版只覆盖 `content:adminListWorks`、`content:adminGetWork`、`content:adminListChapters`。 -- [ ] 本执行版未要求把 `content` 加入 domain-level completed allowlist。 -- [ ] 本执行版未要求修改 OpenAPI、Content main/java 或 SQL migration。 -- [ ] 38 个保留 `needs_verification` operation 与审阅版一致。 -- [ ] TDD RED 在 scanner/report 修改前发生。 -- [ ] HTTP+DB `_test` 覆盖 admin RBAC、tenant interceptor、API version、admin list/detail/chapter read、riskFlag、正文不泄露和 no-write。 -- [ ] Method security 缺权限拒绝不只靠 `@PreAuthorize` 反射断言。 -- [ ] Mixed gates 明确包含 8 个类名,并覆盖 Account、Market、Meta 防回退。 -- [ ] XML 防空跑有明确 mtime、tests、failures、errors、skipped 规则。 -- [ ] 实现完成后仍需 fresh implementation review 双 PASS,才允许请求提交/push。 diff --git a/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md b/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md deleted file mode 100644 index 7171313d..00000000 --- a/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md +++ /dev/null @@ -1,417 +0,0 @@ -# P1R Content Admin RiskAction Completed Approval 审阅版 - -日期:2026-06-13 - -## 结论 - -推荐 Content 下一批只推进 1 个管理后台治理写命令的 operation-level completed approval: - -```text -content:adminRiskAction -``` - -不推荐把 Content 剩余 38 个 operation 一次性推进 completed,也不推荐把 `content` 加入 domain-level completed allowlist。 - -`adminRiskAction` 与刚收口的 Admin Read 3 个只读 operation 相邻,但它不是读模型。它是治理写路径,必须证明 `commandId`、`expectedVersion`、行锁、目标归属、治理事实写入、command/audit envelope、幂等 replay、冲突拒绝、RBAC、API version、tenant 隔离和事务回滚全部闭合后,才允许进入 completed approval。 - -本审阅版只冻结候选范围、证据标准、风险边界和后续执行版要求。不修改 OpenAPI,不修改 scanner,不修改 coverage report,不修改业务实现,不推进任何 operation completed。 - -```mermaid -flowchart TB - Current["当前 Content
13 completed / 38 needs_verification"] --> Candidate["下一包候选
adminRiskAction"] - Candidate --> Kind{"路径类型"} - Kind -->|"POST 写命令"| Evidence["必须补 completed-grade write evidence"] - Evidence --> E1["RBAC + API version"] - Evidence --> E2["commandId + replay + conflict"] - Evidence --> E3["expectedVersion + row lock"] - Evidence --> E4["targetScope / targetIds 归属"] - Evidence --> E5["governance fact + audit snapshot"] - Evidence --> E6["错误路径 no-write / rollback"] - Evidence --> Review["fresh spec/scope review
fresh quality/testing review"] - Review --> Exec["双 PASS 后写执行版"] - Exec --> Approval{"用户明确批准后才实现"} - Approval -->|"否"| Stay["保持 233/144/89"] - Approval -->|"是"| Target["目标 233/145/88
Content 14 completed / 37 needs_verification"] -``` - -## 当前事实状态 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支状态: - -```text -dev/1.0.0...origin/dev/1.0.0 -``` - -当前 HEAD: - -```text -b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁 -``` - -当前 coverage summary: - -```text -total=233 -completed=144 -needsVerification=89 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=13 needsVerification=38 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -``` - -目标 operation 当前状态: - -```text -adminRiskAction dedicated needs_verification requiresCommandId=true POST /admin-api/muse/content/works/{workId}/risk-actions -``` - -本审阅版推荐的下一批目标值只是后续获批后的执行目标: - -```text -total=233 -completed=145 -needsVerification=88 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 - -content total=51 -completed=14 -needsVerification=37 -``` - -## 推荐候选 - -| operation key | Method | Path | 推荐原因 | 必补 completed-grade evidence | -|---|---|---|---|---| -| `content:adminRiskAction` | `POST` | `/admin-api/muse/content/works/{workId}/risk-actions` | admin 治理写命令,当前实现已有 command reserve、expectedVersion、行锁、治理事实和 audit envelope,但仍缺 HTTP + 真实 DB completed approval 证据 | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 RBAC、API version、commandId、expectedVersion、replay、command conflict、revision conflict、targetScope / targetIds 归属、治理事实、audit snapshot、tenant 隔离和 no-write / rollback | - -推荐理由: - -- 当前 coverage report 已识别 `adminRiskAction.requiresCommandId=true`,说明它天然属于写命令切片,不能和 admin read 混在一起。 -- Controller 使用 `@PreAuthorize("@ss.hasPermission('muse:content:risk-action')")`,权限点与 Admin Read 的 `muse:content:query` 不同,必须单独证明。 -- Service 使用 `@Transactional(rollbackFor = Exception.class)`,写路径应以事务内证据证明成功写入和失败回滚。 -- Service 先 `reserveCommand`,再 `selectByIdForUpdate` 锁定作品行并校验 `expectedVersion`,适合单独验证幂等、冲突和并发边界。 -- OpenAPI 已声明 `RiskActionRequest.required=[commandId, action, reason, expectedVersion]`,当前不需要为了本切片修改合同。 - -## 不纳入本批的 operation - -以下 37 个 Content operation 必须继续 `dedicated / needs_verification`: - -```text -content:adminListExportTasks -content:adminListImportTasks -content:confirmChapterParseResult -content:rejectChapterParseResult -content:downloadExportPackage -content:getExportTask -content:getImportTask -content:getParseJob -content:listParseJobChapters -content:batchConfirmChapters -content:retryParseJob -content:createWork -content:deleteWork -content:updateWork -content:deleteBlock -content:mergeBlocks -content:splitBlock -content:mergeBlockSuggestion -content:createChapter -content:deleteChapter -content:updateChapter -content:createBlock -content:reorderChapters -content:validateDynamicFields -content:exportWork -content:createExportTask -content:createImportTask -content:listMetaProjections -content:getMetaProjection -content:createParseJob -content:listPlanningCandidates -content:createPlanningCandidate -content:getPlanningCandidate -content:confirmPlanningCandidate -content:discardPlanningCandidate -content:createStyleCheck -content:getStyleCheckResult -``` - -排除原因: - -- `adminListImportTasks` / `adminListExportTasks` 是任务读模型,证据应覆盖导入导出任务状态、产物和 work 关联,不应夹入治理写命令。 -- 创建、更新、删除、重排、拆分、合并等 Content 写命令有各自的 revision、source snapshot、outbox 和结构一致性边界,应独立切片。 -- 导入、导出、解析、下载仍缺 FileService、任务产物和真实下载字节闭环 evidence。 -- Meta projection、AI planning candidate、style check、suggestion 等能力依赖 Meta / AI / Knowledge 外部 owner closure 和运行时证据。 - -## 已有实现证据 - -### OpenAPI 合同 - -OpenAPI 当前 operation: - -```text -POST /admin-api/muse/content/works/{workId}/risk-actions -operationId: adminRiskAction -security: adminBearerAuth -``` - -请求体 `RiskActionRequest`: - -```text -required: commandId, action, reason, expectedVersion -action enum: warn, restrict, block_content, force_archive, notify_owner -targetScope enum: work, chapter, block -targetIds: chapter 或 block 粒度必填 -expectedVersion: 作品当前版本号,用于乐观锁 -duration: restrict 动作限制时长 -``` - -响应体 `RiskActionResult`: - -```text -actionId -status: executed | pending_notification -affectedScope -auditLogId -``` - -本切片不需要修改 `docs/api-contracts/content/openapi.yaml`。 - -### Controller 入口 - -`AdminContentController.adminRiskAction`: - -```text -POST /muse/content/works/{workId}/risk-actions -@PreAuthorize("@ss.hasPermission('muse:content:risk-action')") -requireApiVersion() -contentAdminService.adminRiskAction(getLoginUserId(), workId, reqVO) -``` - -说明: - -- Controller-local mapping 是 `/muse/content/...`;对外 OpenAPI 路径仍以 `/admin-api/muse/...` 为准。 -- 执行版必须用当前项目既有 admin MockMvc / Web 测试上下文证明最终 admin API 路径可访问。 -- 权限证据必须来自启用 method security 的 HTTP 请求,不能只靠反射检查注解。 - -### Service 写链路 - -`ContentAdminServiceImpl.adminRiskAction` 当前链路: - -1. 校验 `expectedVersion` 非空。 -2. 用请求体和 `workId` 构造 scoped request hash。 -3. `ContentCommandService.reserveCommand(commandId, "adminRiskAction", operatorUserId, "work", workId, requestHash)`。 -4. 如果 command 已存在且 hash / target 匹配,返回 `resultSnapshot` replay。 -5. `WorkMapper.selectByIdForUpdate(workId, tenantId)` 锁定作品行。 -6. 校验 `work.revision == expectedVersion`。 -7. 校验 action、targetScope、targetIds。 -8. 对 chapter / block target 沿 `targetIds -> chapter/block -> work` 校验从属关系。 -9. 插入 `muse_content_governance_action`。 -10. 构造 `RiskActionResultRespVO`。 -11. `ContentCommandAuditService.recordSucceededWithAudit(...)` 记录 command/audit/result snapshot。 - -关键语义: - -- `notify_owner` 结果状态为 `pending_notification`。 -- 其它当前支持 action 结果状态为 `executed`。 -- `affectedScope` 对 work 粒度为 `work:{workId}`;chapter/block 粒度为 `{targetScope}:{targetIds}`。 -- 成功审计 target 仍绑定业务目标 work,不能改写成治理动作 id,否则 command replay 会被判为跨目标冲突。 - -### Schema 证据来源 - -V9 当前定义: - -```text -muse_content_governance_action -tenant_id -work_id -operator_user_id -action -reason -target_scope default work -target_ids jsonb -expected_version not null -status -audit_log_id -command_id not null -uk_muse_content_governance_action_command unique (tenant_id, command_id) -idx_muse_content_governance_action_work -trg_muse_content_governance_action_updated_at -``` - -执行版不需要新增 migration,但必须 fresh 证明 `_test` 库从 V1 到当前版本 clean migrate。当前 Content 已有 V21 event publish outbox,因此本切片的真实 schema gate 应至少证明 V1-V21 clean migrate 成功,并断言治理动作表、唯一约束、work 索引、JSONB `target_ids`、`tenant_id` / `expected_version` / `command_id` 关键列、updated_at trigger 真实存在。 - -## 当前测试证据与缺口 - -已有 focused tests 可作为辅助证据: - -- `AdminContentControllerTest.should_requireRbacAnnotationsForAdminContentApis` -- `AdminContentControllerTest.should_rejectRiskActionMissingReason` -- `AdminContentControllerTest.should_rejectRiskActionMissingExpectedVersionAtValidationLayer` -- `AdminContentControllerTest.should_createRiskActionWithCommonResult` -- `ContentAdminServiceTest.should_rejectRiskAction_when_expectedVersionMismatch` -- `ContentAdminServiceTest.should_rejectRiskAction_when_workMissing` -- `ContentAdminServiceTest.should_createRiskActionWithAudit` -- `ContentAdminServiceTest.should_rejectRiskAction_when_actionUnsupported` -- `ContentAdminServiceTest.should_rejectRiskAction_when_targetScopeUnsupported` -- `ContentAdminServiceTest.should_rejectRiskAction_when_chapterTargetIdsBelongToOtherWork` -- `ContentAdminServiceTest.should_rejectRiskAction_when_blockTargetIdsBelongToOtherWork` -- `ContentAdminServiceTest.should_replayRiskActionCommand` - -这些测试不能直接等同 completed。缺口包括: - -- 没有 HTTP + 真实 PostgreSQL `_test` 的端到端证据。 -- 缺 method security 真实拒绝路径证据。 -- 缺真实 tenant interceptor 下的 cross tenant 不可见 / no-write 证据。 -- 缺 command table、governance action、Content outbox、work/chapter/block 的全表快照或关键字段 no-write / rollback 证据。 -- 缺 V1-V21 clean migrate 后 `muse_content_governance_action` / `muse_content_command_log` schema、唯一键、JSONB、trigger、tenant 关键列证据与 XML 防空跑。 -- 缺 command replay / command conflict 的真实 DB 证据,尤其同 commandId 不同 action、reason、targetScope、targetIds、workId、expectedVersion。 -- 缺并发/行锁语义的可接受代理证据,例如 `FOR UPDATE` SQL 与真实事务 revision stale / conflict 场景。 -- 缺 P1R scanner/report/gate 在获批后从 `233/144/89` 推进到 `233/145/88` 的 RED/GREEN 证据。 - -## 必补证据标准 - -后续执行版必须至少覆盖以下矩阵。 - -### HTTP / 权限 / 版本 - -- 有 `X-API-Version` 的成功请求。 -- 缺或不支持 `X-API-Version` 的统一错误响应。 -- 有 `muse:content:risk-action` 权限时允许进入 service。 -- 缺 `muse:content:risk-action` 权限时 403,且不写 command、governance action、outbox 或业务表。 -- 登录 operator user id 必须进入 `operator_user_id` 和 command/audit 事实。 -- command/audit snapshot 必须显式断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId=workId`、`status=succeeded`、`reason`、`requestSummary.action`、`requestSummary.targetScope`、`requestSummary.targetIds`、`requestSummary.expectedVersion` 和 `result.actionId/status/affectedScope/auditLogId`。 - -### 成功写路径 - -- work 粒度 happy path:写入 1 条 governance action,写入 1 条 command/audit envelope,返回 actionId/status/affectedScope/auditLogId。 -- chapter 粒度 happy path:targetIds 必须属于 URL work,返回 chapter affected scope。 -- block 粒度 happy path:targetIds 必须沿 block -> chapter -> work 归属到 URL work。 -- `notify_owner` 返回 `pending_notification`。 -- `block_content` / `warn` / `restrict` / `force_archive` 返回 `executed`。 -- 结果 snapshot 可被 replay 解析,重复同 commandId / 同请求返回同一结果,不重复插入治理事实。 - -### command / revision / target 负路径 - -- 缺 `commandId` validation 失败,不写任何事实。 -- 缺 `expectedVersion` validation 失败,不写任何事实。 -- `expectedVersion` 与 work revision 不一致,返回 revision conflict,不写治理事实和 succeeded audit。 -- 同 commandId 不同 action、reason、targetScope、targetIds、workId 或 expectedVersion 返回 command conflict,不写第二条治理事实。 -- 同 commandId 不同 workId 返回 command conflict 或目标冲突,不泄露目标数据。 -- unsupported action 返回统一错误并 no-write。 -- unsupported targetScope 返回统一错误并 no-write。 -- chapter targetIds 为空、含 null、missing、属于其它 work、属于其它 tenant,均拒绝并 no-write。 -- block targetIds 为空、含 null、missing、属于其它 work、章节链路不匹配、属于其它 tenant,均拒绝并 no-write。 -- missing work / cross tenant work 返回不可见或 not found 语义,不写任何事实。 - -### no-write / rollback - -错误路径必须对以下表做全表快照或关键字段快照,不只比较行数: - -```text -muse_content_work -muse_content_chapter -muse_content_block -muse_content_governance_action -muse_content_command_log -muse_content_event_publish_outbox -``` - -说明: - -- 成功写路径允许 governance action 与 command/audit 变化。 -- 错误路径必须证明上述表没有 INSERT / DELETE / UPDATE 污染。 -- 如果测试自身需要 seed 或清理,必须在 no-write baseline 采样之前完成。 - -### Flyway / schema - -执行版必须 fresh 证明: - -- `_test` 库只使用测试库名,禁止连接非 `_test`。 -- 密码只从环境变量读取,禁止通过 JVM system property 或 JDBC query 传入。 -- V1-V21 clean migrate 成功。 -- `muse_content_governance_action` 表、关键列、JSONB `target_ids`、unique command 约束、work 索引和 updated_at trigger 存在。 -- duplicate `(tenant_id, command_id)` 被数据库唯一约束拒绝。 - -## 风险和取舍 - -1. `adminRiskAction` 不是 Admin Read 的延伸证据。 - - Admin Read 已证明治理事实可读;它不能证明治理事实写入、command replay、expectedVersion、行锁和 audit snapshot。 - -2. `selectByIdForUpdate` 是本切片的核心边界。 - - 执行版必须证明 expectedVersion 校验发生在同一事务的锁定作品行之后;否则 completed 证据不足以支撑并发写路径。 - -3. `targetIds` 归属不能只查目标 id 存在。 - - chapter 必须属于 URL work;block 必须同时满足 block.workId 与 block.chapterId 对应 chapter.workId 都属于 URL work。 - -4. command replay 不能只证明不报错。 - - 必须证明 replay 返回原 `resultSnapshot`,且没有重复插入治理事实、command/audit 或 outbox。 - -5. 不应新增 Content audit 表。 - - 当前 Content audit snapshot 保存在 `muse_content_command_log.result_snapshot` / command audit envelope 中。执行版不得为了测试方便新增表或修改 schema。 - -6. 不把生成 DTO 当成唯一合同证据。 - - OpenAPI 已声明 `expectedVersion` required,服务端 `RiskActionReqVO` 也有 `@NotNull` validation。后续执行版如果引用生成物或客户端 DTO,必须说明其新鲜度和同步关系;completed 证据应以 OpenAPI、服务端 VO validation、HTTP 请求和真实 DB 结果闭合。 - -## 后续执行版要求 - -后续执行版必须包含: - -1. 精确目标 operation 清单:只包括 `content:adminRiskAction`。 -2. operation-level approval:不把 `content` 加入 domain allowlist。 -3. 实施前 RED:旧 report 下 P1R coverage gate 必须因 `adminRiskAction` 尚未 completed 而失败。 -4. HTTP + real PostgreSQL `_test` gate:新增独立 `P1rContentAdminRiskActionCompletedApprovalIT` 或等价命名。 -5. Web/MyBatis/DataSource 测试上下文:必须启用真实 tenant interceptor、真实 mapper、真实 service、真实 controller 和 method security。 -6. RBAC / API version / CommonResult 错误响应证据。 -7. happy / replay / command conflict / revision conflict / validation / missing / cross tenant / target mismatch / no-write matrix。 -8. focused tests:运行 `ContentAdminServiceTest`、`AdminContentControllerTest` 并做 XML 防空跑。 -9. P1R mixed gates:同步所有读取全局 summary、Content 状态或非目标 domain 防回退的 gate。 -10. scanner/report 更新策略:只在用户批准后追加 1 个 `content:adminRiskAction` operation-level allowlist。 -11. protected diff:OpenAPI、Content main/java、SQL migration 默认不得修改。 -12. `.agent` 和 `docs/memorys` 留痕。 -13. rollback:撤回 scanner operation key、coverage report、P1R gate 目标值和新增 `_test`,恢复 `adminRiskAction` 为 `needs_verification`。 - -## 审批前置条件 - -进入执行版前需要 fresh review 双 PASS: - -- fresh spec/scope review:确认只覆盖 `content:adminRiskAction`,不夹带 admin import/export task、Content CRUD 写命令、Content domain-level completed 或其它 domain。 -- fresh quality/feasibility/testing review:确认 RBAC、tenant 拦截、command replay、expectedVersion、targetScope / targetIds、no-write / rollback、schema gate、XML 防空跑和 mixed gate 同步可落地。 - -执行版双 PASS 后,仍必须由用户明确批准以下事项后才允许实现: - -1. 只审批 `content:adminRiskAction`。 -2. 继续 operation-level approval,不把 `content` 加入 domain allowlist。 -3. 允许按执行版修改 scanner、coverage report、P1R gate tests、新增 HTTP+DB `_test` 和 memory。 -4. 允许同步 P1R mixed gate 的 summary / Content / 非目标域防回退断言。 - -## 本审阅版验收标准 - -- 候选 operation 和目标值可由当前 report 机械推导。 -- 非目标 37 个 Content operation 明确保持 `needs_verification`。 -- `adminRiskAction` 与 Admin Read、admin import/export task、Content CRUD 写命令的边界清晰。 -- evidence 标准包含 RBAC、tenant 隔离、commandId、expectedVersion、行锁、target 归属、governance action、audit snapshot、replay、conflict、no-write / rollback 和 XML 防空跑。 -- 未修改 OpenAPI、scanner、coverage report、业务实现、SQL migration 或 P1R gate。 diff --git a/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md b/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md deleted file mode 100644 index b7ff4a04..00000000 --- a/docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md +++ /dev/null @@ -1,615 +0,0 @@ -# P1R Content Admin RiskAction Completed Approval 执行版 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task only after fresh execution review double PASS and explicit user approval. Steps use checkbox syntax for tracking. - -日期:2026-06-13 - -## 结论 - -本执行版只锁定 Content 管理后台治理写命令 1 个 operation-level completed approval: - -```text -content:adminRiskAction -``` - -本轮不把 `content` 加入 domain-level completed allowlist,不推进 Content 51/51 completed,不修改 OpenAPI,不修改 Content 业务实现,不修改 SQL migration。 - -获批后目标 coverage summary 为: - -```text -total=233 -completed=145 -needsVerification=88 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Content 域目标状态为: - -```text -content total=51 -completed=14 -needsVerification=37 -``` - -当前阶段只允许写执行方案并进入 fresh execution review。执行版 review 双 PASS 后,仍必须由用户再次明确批准审批清单、operation-level 边界、allowed diff 和 mixed gate 同步范围,才允许实施。 - -```mermaid -flowchart TB - Current["当前 Content
13 completed / 38 needs_verification"] --> Exec["执行版
锁定 adminRiskAction 1 op"] - Exec --> Review["fresh execution spec/scope review
fresh execution quality/testing review"] - Review --> Approval{"用户明确批准
1 op / operation-level / allowed diff / mixed gates"} - Approval -->|否| Stay["保持当前 coverage
233/144/89"] - Approval -->|是| Red["TDD RED
先改 gate 期望
旧 report 必须失败"] - Red --> Runtime["HTTP + real DB _test
RBAC + tenant + command + audit + rollback"] - Runtime --> Green["scanner operation allowlist
regenerate report"] - Green --> Verify["focused tests + P1R mixed gates
XML 防空跑 + protected diff"] - Verify --> FreshReview["fresh implementation review"] -``` - -## 当前事实 - -正确 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前 HEAD: - -```text -b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁 -``` - -当前分支: - -```text -dev/1.0.0...origin/dev/1.0.0 -``` - -当前 coverage summary: - -```text -233 144 89 0 0 0 -``` - -当前 domain 状态: - -```text -account completed=10 needsVerification=23 -content completed=13 needsVerification=38 -market completed=4 needsVerification=28 -meta completed=16 needsVerification=0 -``` - -当前目标 operation 状态: - -```text -adminRiskAction dedicated needs_verification true POST /admin-api/muse/content/works/{workId}/risk-actions -``` - -已验证的只读实现事实: - -- OpenAPI `RiskActionRequest.required` 已包含 `commandId`、`action`、`reason`、`expectedVersion`。 -- `AdminContentController.adminRiskAction` 使用 `@PreAuthorize("@ss.hasPermission('muse:content:risk-action')")`,并调用 `requireApiVersion()`。 -- Controller-local mapping 为 `/muse/content/works/{workId}/risk-actions`;对外 OpenAPI 路径为 `/admin-api/muse/content/works/{workId}/risk-actions`。 -- `ContentAdminServiceImpl.adminRiskAction` 使用 `@Transactional(rollbackFor = Exception.class)`。 -- Service 先通过 `ContentCommandService.reserveCommand(...)` 处理 commandId 和 replay,再通过 `WorkMapper.selectByIdForUpdate(workId, tenantId)` 锁定作品行。 -- Service 在锁定作品行后校验 `expectedVersion`、action、targetScope、targetIds,并校验 chapter / block target 归属 URL work。 -- 成功路径插入 `muse_content_governance_action`,并通过 `ContentCommandAuditService.recordSucceededWithAudit(...)` 写 command/audit/result snapshot。 -- 当前实现不写 `muse_content_event_publish_outbox`;本切片不证明 Events propagation。 -- 现有 `AdminContentControllerTest` / `ContentAdminServiceTest` 只能作为 focused 辅助证据,不能替代 completed-grade HTTP + real PostgreSQL `_test`。 - -## 执行边界 - -### 必须先获用户明确批准 - -实现前必须同时获得以下 4 项批准: - -1. 批准本轮只审批 `content:adminRiskAction` 一个 operation。 -2. 批准继续使用 operation-level approval,不把 `content` 加入 domain-level completed allowlist。 -3. 批准按本执行版修改 scanner、coverage report、P1R gates、新增 Admin RiskAction HTTP+DB `_test`、memory 和 `.agent`。 -4. 批准同步 mixed gate 的 summary / Content / 非目标域防回退断言,目标为 `233/145/88/0/0/0` 与 Content `14 completed / 37 needs_verification`。 - -未获上述批准前,不得实施本执行版。 - -### 本轮允许变更 - -获批后只允许修改以下路径: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminRiskActionCompletedApprovalIT.java` -- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentAdminServiceTest.java`,仅限补充本执行版缺失的 focused behavior 证据。 -- `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/controller/admin/AdminContentControllerTest.java`,仅限补充本执行版缺失的 controller contract 证据。 -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md` - -如 implementation 发现必须修改 Content 业务实现、OpenAPI、SQL migration、非批准 gate 或其它文件,必须停下说明原因并重新取得用户批准。 - -### 本轮禁止变更 - -- 不修改 7 个 OpenAPI。 -- 不修改 Content main/java 业务实现来掩盖 coverage 缺口。 -- 不新增或修改 SQL migration。 -- 不把 `content` 加入 domain-level completed allowlist。 -- 不推进 Content 51/51 completed。 -- 不推进 Account remaining 23、Market remaining 28、Content remaining 37 或总 P1R completed。 -- 不推进 admin import/export task、Content CRUD 写命令、AI planning candidate、style check、Meta projection、dynamic fields、FileService、import/export/parse operation。 -- 不把 Admin Read 的 riskFlag 聚合证据当成 `adminRiskAction` completed 证据。 -- 不把 dedicated gate PASS、review PASS、文档结论或已有 Mockito 单测直接等同 completed。 - -## 审批清单 - -| operation key | Method | Path | 目标状态 | 完成证据边界 | -|---|---|---|---|---| -| `content:adminRiskAction` | `POST` | `/admin-api/muse/content/works/{workId}/risk-actions` | `dedicated / completed` | MockMvc HTTP + 真实 PostgreSQL `_test`,覆盖 API version、method security RBAC、commandId、replay、command conflict、expectedVersion、row lock、targetScope / targetIds 归属、governance action、command/audit snapshot、tenant 隔离、schema gate、no-write / rollback | - -## 保持 needs_verification 的 Content operation - -以下 37 个 operation 必须保持 `dedicated / needs_verification`: - -```text -content:adminListExportTasks -content:adminListImportTasks -content:confirmChapterParseResult -content:rejectChapterParseResult -content:downloadExportPackage -content:getExportTask -content:getImportTask -content:getParseJob -content:listParseJobChapters -content:batchConfirmChapters -content:retryParseJob -content:createWork -content:deleteWork -content:updateWork -content:deleteBlock -content:mergeBlocks -content:splitBlock -content:mergeBlockSuggestion -content:createChapter -content:deleteChapter -content:updateChapter -content:createBlock -content:reorderChapters -content:validateDynamicFields -content:exportWork -content:createExportTask -content:createImportTask -content:listMetaProjections -content:getMetaProjection -content:createParseJob -content:listPlanningCandidates -content:createPlanningCandidate -content:getPlanningCandidate -content:confirmPlanningCandidate -content:discardPlanningCandidate -content:createStyleCheck -content:getStyleCheckResult -``` - -## 证据分层 - -| Evidence | 文件 / 测试 | 目的 | -|---|---|---| -| Operation inventory / coverage gate | `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest` 和 mixed gates | 证明只推进获批 1 个 operation,非目标 domain 不回退 | -| Admin RiskAction HTTP+DB `_test` | 新增 `P1rContentAdminRiskActionCompletedApprovalIT` | 证明 `adminRiskAction` 经 MockMvc HTTP 入口访问真实 service / mapper / PostgreSQL `_test` 数据 | -| Tenant / schema gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内验证 V1-V21 clean migrate 与 governance action 依赖 schema | 证明 Work / Chapter / Block / GovernanceAction / CommandLog / Outbox schema 和 tenant interceptor 真实可用 | -| Method security gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内启用 method security 与 `ss` bean | 证明 admin RBAC 缺权限拒绝不是注解反射假证据 | -| Command / audit gate | `P1rContentAdminRiskActionCompletedApprovalIT` 内读取 `muse_content_command_log.result_snapshot` | 证明 operationId、side、target、requestSummary、result snapshot 和 replay 语义真实落库 | -| Focused unit/controller tests | `ContentAdminServiceTest`、`AdminContentControllerTest` | 支撑 service/controller contract,不替代 HTTP+DB `_test` | - -## TDD / 实施步骤 - -### Task 1:Preflight 与 RED gate - -- [ ] 确认当前 worktree: - -```bash -git -c core.quotePath=false status --short --branch -``` - -期望: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -允许存在本执行版和审阅版文档 diff;不得存在 OpenAPI、业务实现、SQL、scanner、coverage report 或 gate test 的未授权 diff。 - -- [ ] 确认当前 coverage: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="content" and .operationId=="adminRiskAction") | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring),.method,.path] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -期望: - -```text -233 144 89 0 0 0 -adminRiskAction dedicated needs_verification true POST /admin-api/muse/content/works/{workId}/risk-actions -``` - -- [ ] TDD RED:先修改 P1R gate 期望值和 `adminRiskAction` completed 断言,但不修改 scanner/report。 - -RED 目标: - -- `P1rApiCoverageReportTest` 期望 summary `completed=145 / needsVerification=88`。 -- `P1rContentRealApiGateTest` 期望 Content `14 completed / 37 needs_verification`,`adminRiskAction` completed。 -- 7 个 mixed gate 中所有硬编码 Content `13/38` 或 summary `144/89` 的断言同步为 `14/37` 或 `145/88`。 - -RED 命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rAccountRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望:BUILD FAILURE。失败点必须来自旧 report 仍是 `233/144/89` 或 `adminRiskAction` 仍为 `needs_verification`;不得来自编译错误或无关测试失败。 - -### Task 2:新增 Admin RiskAction HTTP+DB `_test` - -- [ ] 新增: - -```text -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminRiskActionCompletedApprovalIT.java -``` - -测试上下文要求: - -- 使用 `@SpringBootTest(webEnvironment = MOCK)` + `WebApplicationContext` + `MockMvcBuilders.webAppContextSetup(...)`。 -- 真实 `/admin-api/muse/**` 前缀进入 `AdminContentController`。 -- 导入 `MuseWebAutoConfiguration`,并提供 test-local `ApiErrorLogCommonApi` stub,返回 `CommonResult.success(true)`,满足 `GlobalExceptionHandler` 依赖。 -- 启用 `@EnableMethodSecurity(securedEnabled = true)`,并提供 `@Bean("ss")` 的 test-local `SecurityFrameworkService`,权限点必须是 `muse:content:risk-action`。 -- 使用真实 `ContentAdminServiceImpl`、`ContentCommandServiceImpl`、`ContentAuditServiceImpl`、真实 mapper 和真实 PostgreSQL `_test` 数据库。 -- 注册真实 MyBatis tenant interceptor:`TenantLineInnerInterceptor` + `TenantDatabaseInterceptor(new TenantProperties())`。 -- 所有 `ContentAdminServiceImpl` 的 `@Resource` 依赖必须可启动;不得只注册 Work/Chapter/Block/GovernanceAction mapper 后让上下文靠 mock 补洞。 -- Configuration 必须导入所有 ContentAdminServiceImpl 字段依赖需要的 mapper / service / facade stub;如果某个 facade 不属于本切片,应提供 fail-fast stub,并证明本切片未调用它。 -- `TARGET_VERSION="21"`,执行 V1-V21 clean migrate。 -- 数据库密码只能来自环境变量,禁止 JVM system property 或 JDBC query 参数传入。 -- 保存并在 `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user` system property,避免污染同一 Surefire JVM 后续 Flyway 测试。 - -建议测试库: - -```text -muse_p1r_content_admin_risk_action_completed_approval_test -``` - -建议环境变量优先级: - -```text -P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD -P1R_CONTENT_COMPLETED_PASSWORD -P1R_FLYWAY_PASSWORD -MUSE_POSTGRES_PASSWORD -``` - -standalone 建库与运行前置: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_APPROVAL_TEST_DB=muse_p1r_content_admin_risk_action_completed_approval_test - -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_APPROVAL_TEST_DB'" | grep -q 1 || \ -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -c "CREATE DATABASE $P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_APPROVAL_TEST_DB" -``` - -执行版 implementation 必须显式拒绝非 `_test` 数据库名;如果建库变量不是 `_test` 后缀,测试必须在连接前失败。 - -- [ ] 新增基础 seed 与快照工具。 - -seed 要求: - -- 当前 tenant 的 work,revision 固定为 `7`。 -- 当前 tenant 下属于该 work 的 chapter / block。 -- 当前 tenant 下另一个 work、另一个 chapter / block,用于 cross-work target 拒绝。 -- 另一个 tenant 的 work / chapter / block,用于 tenant 隔离。 -- 当前 login user 为 admin user,tenant 与 visitTenant 都设置为当前 tenant。 - -错误路径 no-write 快照必须覆盖: - -```text -muse_content_work -muse_content_chapter -muse_content_block -muse_content_governance_action -muse_content_command_log -muse_content_event_publish_outbox -``` - -快照必须用 `row_to_json(t)::text` 或等价关键字段快照,不能只比对行数。 - -- [ ] mandatory test matrix。 - -最低测试类行为: - -1. 拒绝 unsafe DB 配置:非 `_test` 数据库、JVM password system property、JDBC credential query。 -2. 验证 schema:V1-V21 clean migrate;`muse_content_governance_action` 表、关键列、JSONB `target_ids`、unique command 约束、work 索引、updated_at trigger 存在;duplicate `(tenant_id, command_id)` 返回 SQLState `23505`。 -3. API version / RBAC:缺或不支持 `X-API-Version` no-write;缺 `muse:content:risk-action` 权限返回 403 CommonResult 且 no-write;成功请求必须证明 `ss.hasPermission("muse:content:risk-action")` 被调用。 -4. work scope happy path:成功写 1 条 governance action 和 1 条 command/audit envelope,返回 `actionId/status/affectedScope/auditLogId`;`affectedScope=work:{workId}`;`outbox` 不写。 -5. chapter / block scope happy path:targetIds 属于 URL work 才能成功;chapter 与 block affectedScope 正确。 -6. action status:`notify_owner` 返回 `pending_notification`;`warn` / `restrict` / `block_content` / `force_archive` 返回 `executed`。 -7. command replay:同 commandId 同请求返回同一 `resultSnapshot`,不重复插入 governance action、command/audit 或 outbox。 -8. command conflict:同 commandId 不同 action、reason、targetScope、targetIds、workId 或 expectedVersion 返回 command conflict,且 no-write。 -9. revision conflict:`expectedVersion` 与 work revision 不一致返回 `CONTENT_REVISION_CONFLICT`,且 no-write。 -10. validation / unsupported:缺 commandId、缺 expectedVersion、unsupported action、unsupported targetScope 均拒绝并 no-write。 -11. targetIds invalid:chapter / block targetIds 为空、含 null、missing、cross-work、cross-tenant、block -> chapter -> work 链路不匹配,均拒绝并 no-write。 -12. missing / cross tenant work:返回 not found 或不可见语义,不泄露其它 tenant 数据,且 no-write。 -13. row-lock proxy:至少用 `WorkMapper.selectByIdForUpdate` 源码 / mapper SQL 断言包含 `FOR UPDATE`,并用真实 DB stale expectedVersion 场景证明锁后 revision 校验会拒绝并 rollback。若要做真实并发事务测试,必须保证可重复、无 sleep 竞态和可诊断日志。 -14. command/audit row fact:断言 `muse_content_command_log` 行级字段 `tenant_id`、`command_id`、`command_type=adminRiskAction`、`owner_user_id=admin user`、`target_type=work`、`target_id=workId`、`request_hash` 非空且 replay 时稳定。 -15. governance action row fact:断言 `muse_content_governance_action` 行级字段 `tenant_id`、`work_id`、`operator_user_id=admin user`、`command_id`、`audit_log_id`、`action`、`reason`、`target_scope`、`target_ids`、`expected_version`、`status`。 -16. command/audit snapshot:解析 `muse_content_command_log.result_snapshot`,断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId=workId`、`status=succeeded`、`reason`、`requestSummary.action`、`requestSummary.targetScope`、`requestSummary.targetIds`、`requestSummary.expectedVersion`、`result.actionId/status/affectedScope/auditLogId`。 - -### Task 3:Focused tests - -- [ ] 运行 Content focused tests: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-content/muse-module-content-server,muse-server -am \ - -Dtest=ContentAdminServiceTest,AdminContentControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望: - -```text -ContentAdminServiceTest: pass -AdminContentControllerTest: pass -BUILD SUCCESS -``` - -如执行版实现发现 focused tests 缺少审阅版硬门槛,可只在允许范围内补充测试,不得改 main/java。 - -### Task 4:GREEN scanner / report - -- [ ] scanner 只追加 operation-level key: - -```text -content:adminRiskAction -``` - -禁止修改: - -```text -APPROVED_COMPLETED_DOMAINS -``` - -- [ ] 重新生成 coverage report: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -期望 summary: - -```text -233 145 88 0 0 0 -``` - -期望 Content: - -```text -completed=14 -needsVerification=37 -``` - -期望 operation: - -```text -adminRiskAction dedicated completed true -``` - -仍必须抽样断言这些非目标 operation 保持 `needs_verification`: - -```text -adminListImportTasks -adminListExportTasks -createWork -updateWork -createChapter -mergeBlockSuggestion -exportWork -listPlanningCandidates -createStyleCheck -getStyleCheckResult -``` - -### Task 5:P1R mixed gates - -- [ ] 运行 P1R mixed gates: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rAccountRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -期望: - -- `P1rApiCoverageReportTest` summary 为 `233/145/88/0/0/0`。 -- `P1rContentRealApiGateTest` Content 为 `14/37`,`adminRiskAction` completed。 -- `P1rEventsRealApiGateTest`、`P1rAiRealApiGateTest`、`P1rKnowledgeRealApiGateTest`、`P1rMarketRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rMetaRealApiGateTest` 内所有 Content partial approval 防回退断言同步为 `14/37`。 -- AI 41、Knowledge 59、Events 1、Meta 16、Account 10、Market 4 状态不回退。 - -### Task 6:Content combined DB gate - -- [ ] 使用独立 `_test` 库组合运行 Content completed approval DB gates: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_RISK_ACTION_COMPLETED_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_TEST_DB=muse_p1r_content_admin_risk_action_combined_test - -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -tc "SELECT 1 FROM pg_database WHERE datname = '$P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_TEST_DB'" | grep -q 1 || \ -PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -c "CREATE DATABASE $P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_TEST_DB" - -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rContentAdminRiskActionCompletedApprovalIT,P1rContentAdminReadCompletedApprovalIT,P1rContentPlanningCompletedApprovalIT,P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_CONTENT_ADMIN_RISK_ACTION_COMBINED_TEST_DB" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= -DsocksProxyPort= \ - -Dhttp.proxyHost= -Dhttp.proxyPort= \ - -Dhttps.proxyHost= -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -要求: - -- 所有类 tests > 0。 -- failures=0、errors=0、skipped=0。 -- 证明新增 Admin RiskAction IT 不污染同 JVM 的 Flyway system properties。 -- 如 first run 暴露 password env 缺失或 system property 污染,必须修复后重跑,并记录失败根因与修复。 - -### Task 7:XML 防空跑 - -- [ ] 检查以下 XML 必须存在、mtime 在本轮验证窗口内、tests > 0、failures/errors/skipped 均为 0: - -```text -P1rContentAdminRiskActionCompletedApprovalIT -P1rContentAdminReadCompletedApprovalIT -P1rContentPlanningCompletedApprovalIT -P1rContentCoreCompletedApprovalIT -P1rContentEventsPublishFlywayMigrationIT -ContentAdminServiceTest -AdminContentControllerTest -P1rApiCoverageReportTest -P1rContentRealApiGateTest -P1rEventsRealApiGateTest -P1rAiRealApiGateTest -P1rKnowledgeRealApiGateTest -P1rMarketRealApiGateTest -P1rAccountRealApiGateTest -P1rMetaRealApiGateTest -``` - -防空跑脚本可用 shell / awk / xmllint / existing local pattern,但不得把 XML 旧产物当 fresh evidence。 - -### Task 8:Protected diff / final checks - -- [ ] 运行: - -```bash -git diff --check -``` - -- [ ] 确认 protected diff 为空: - -```bash -git diff --name-only -- docs/api-contracts muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse -``` - -期望:无输出。 - -- [ ] 确认 scanner/report 目标: - -```bash -jq -r '.summary | [.totalOperations,.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain=="content" and (.operationId=="adminRiskAction" or .operationId=="adminListExportTasks" or .operationId=="adminListImportTasks" or .operationId=="createWork" or .operationId=="exportWork" or .operationId=="createStyleCheck")) | [.operationId,.implementationStatus,.completionStatus,(.requiresCommandId|tostring)] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -期望: - -```text -233 145 88 0 0 0 -adminRiskAction dedicated completed true -adminListExportTasks dedicated needs_verification false -adminListImportTasks dedicated needs_verification false -createWork dedicated needs_verification false -exportWork dedicated needs_verification false -createStyleCheck dedicated needs_verification true -``` - -## Review gate - -本执行版写完后必须先进行 fresh execution review: - -1. fresh execution spec/scope review: - - 确认只覆盖 `content:adminRiskAction`。 - - 确认不夹带 admin import/export task、Content CRUD 写命令、Content domain-level completed 或其它 domain。 - - 确认目标值 `233/145/88` 与 Content `14/37` 能从当前 `233/144/89` 与 Content `13/38` 加 1 个 operation 机械推导。 - - 确认 allowed / prohibited diff 自洽。 - -2. fresh execution quality/feasibility/testing review: - - 确认 HTTP+DB `_test` harness 可复用现有 Admin Read / Planning 模式落地。 - - 确认 method security、tenant interceptor、command replay/conflict、expectedVersion/row lock、targetScope / targetIds、governance action、command/audit snapshot、no-write / rollback、schema gate、XML 防空跑和 mixed gate 同步都可验证。 - - 确认不需要改 OpenAPI、Content main/java 或 SQL migration。 - -双 PASS 后,仍不代表 `adminRiskAction` completed。必须由用户明确批准执行边界后才允许进入 implementation。 - -## Rollback - -如果实现后需要回滚本切片: - -1. 从 `APPROVED_COMPLETED_OPERATIONS` 删除 `content:adminRiskAction`。 -2. 重新运行 scanner,恢复 report summary 到 `233/144/89/0/0/0`。 -3. 恢复 `P1rApiCoverageReportTest`、`P1rContentRealApiGateTest` 和 mixed gates 的 summary / Content count / `adminRiskAction` 断言。 -4. 删除 `P1rContentAdminRiskActionCompletedApprovalIT`。 -5. 删除或修订对应 memory / `.agent` 状态推进记录。 -6. 保留 OpenAPI、Content main/java、SQL migration 无 diff。 - -## 完成条件 - -只有同时满足以下条件,才允许汇报本切片具备提交候选条件: - -- 用户已批准执行版四项前置条件。 -- RED gate 先失败且失败点正确。 -- `P1rContentAdminRiskActionCompletedApprovalIT` fresh pass,且 XML 防空跑通过。 -- Content focused tests fresh pass。 -- scanner `--check` fresh pass,summary 为 `233/145/88/0/0/0`。 -- P1R mixed gates fresh pass。 -- Content combined DB gate fresh pass。 -- protected OpenAPI / Content main/java / SQL diff 为空。 -- `git diff --check` 通过。 -- `.agent` 和 `docs/memorys` 留痕明确说明 Content 仍不是 51/51 completed。 -- fresh implementation spec/correctness review + fresh quality/data-integrity/testing review 双 PASS。 diff --git a/docs/agent-specs/2026-06-13-目标达成对抗复盘.md b/docs/agent-specs/2026-06-13-目标达成对抗复盘.md new file mode 100644 index 00000000..b63d1940 --- /dev/null +++ b/docs/agent-specs/2026-06-13-目标达成对抗复盘.md @@ -0,0 +1,204 @@ +# Muse 目标达成对抗复盘(独立评审官 / 最高强度) + +| 项 | 值 | +|---|---| +| 版本 | v1.0 | +| 日期 | 2026-06-13 | +| 立场 | 独立对抗评审官,未参与前序工作;任务是**质疑而非附和** | +| 输入 | 基线 `2026-06-13-项目目标与模块现状基线.md`;迁移评审 `2026-06-13-agent开发基建迁移-review.md` | +| 方法 | 不接受基线结论,逐条对**实际代码 / git / 覆盖率 JSON / CI / 测试 gating** 取证后再裁决 | +| 边界 | 只读核实(未实跑 mvn/npm,与基线同口径);所有挑战均附 evidence,凡证据不足的质疑已自我剔除 | + +--- + +## 0. 结论先行(TL;DR) + +1. **基线大体诚实,但仍系统性高估了"可达成度",并漏掉两个比 churn 更深的根因。** 基线"后端是真实纵向实现、历史声称是低报不是高报"——这两点经核实**基本成立**,我予以确认,不做无证据翻案。但基线把整体完成度报为 **76%**、把跨 BC facade 描述为"诚实留白",**掩盖了两个结构性事实**:① **BC 边界已被代码实际违反**(AI 模块直接 import 并注入 Content 模块的 DAL `WorkMapper/ChapterMapper/BlockMapper` 与 `WorkDO/ChapterDO/BlockDO`),不是"未机械约束",而是"已破墙";② **整套"completed/证据门"在 CI 里根本不运行**(yudao 继承的 `maven.yml` 用 `-Dmaven.test.skip=true`),且门禁测试**硬编码 `assertEquals(147, completed)`**,live 验收 IT 全部 `assumeTrue` 跳过——所谓"147 completed(门禁口径)"本质是**人工自证的数字**,不是任何流水线证明的结果。 + +2. **"失控"根因诊断只对了一半。** churn(34 memorys + 35 agent-specs)是**症状**;迁移评审把根因归为"接口未锁 + 无单一进度源 + 无复利"——这是**治理层根因**,成立但不完整。**被双方都漏掉的更深根因是:验证闭环造假倾向(verification theater)**。证据:近 15 个提交清一色 `test(p1r): 收口 … completed approval 门禁`;coverage JSON `generatedAt=2026-05-25` 已陈旧却被手工把 completed 改到 147;门禁测试自己断言这个 147;CI 跳过所有测试;前端 DEV 无条件挂 MSW、SSE 契约漂移被 mock 喂成假绿。**真正失控的不是"代码写太多",而是"完成度被定义成一个可以手工拨动、且无人自动校验的刻度盘"。** 迁移评审的"软约束优先、机械门禁后置第二期"方向,**恰好会让这个根因继续存活**——这是我对迁移方案的最强烈反对点。 + +3. **目标可达性裁决:目标本身可达,但"现有总体计划 + 现有验证机制"不可达,继续执行会再次失控。** 后端领域逻辑的质量是真实资产,不该推倒;但"完成度"的**计量与验证机制必须先换掉**,否则团队会在一个测不准的刻度盘上继续"刷绿"。**必须先关掉刷分回路,再谈推进功能。** + +4. **资源错配最严重处:正在"刷后端 operation 覆盖度"(已 147/233 且持续收口),而真正卡住产品价值的是——前端用户端断链 + 跨 BC 集成在真实部署形态下未验证 + BC 边界已破。** 后端 operation 门禁的边际价值已趋零(再多收口几个 needs_verification→completed,对"用户能不能用"零贡献);资源应转向"端到端竖切一条真实可跑的用户旅程"。 + +--- + +## 一、前提挑战清单(按严重度) + +> 说明:每条给出"基线/迁移评审的前提 → 我的挑战 → 证据 → 严重度"。**我确认成立的前提单列在 §一.B,不当成靶子打**,以示对抗有节制。 + +### A. 需要挑战的前提(高 → 低) + +#### A1【高】"本仓只承载设计文档,不承载运行时代码"——前提与事实矛盾 +- **基线/CLAUDE.md 前提:** oh-my-muse = `muse-design-docs`,"不承载运行时代码 / CI / 部署"。 +- **挑战:** 这是**事实错误的自我定位**。`muse-cloud/`(4468 个文件被本仓 git 跟踪,无 `.gitmodules`)、`muse-admin/`、`muse-studio/` 三个运行时代码仓**就在同一个 git 工作树里、被同一个仓库版本控制**。所谓"四仓架构 + 设计 SSOT 只读"是**文档里的叙事,不是磁盘上的事实**。这直接动摇基线第三节"四仓协作"表与整个 owner 论述的物理前提。 +- **影响:** 迁移评审基于"单仓 monorepo,治理层放仓根"——这点反而对了;但 CLAUDE.md 写"本仓不承载代码"会让 agent 误判可改动边界(以为不能动代码),与现实冲突,是 churn 的隐性来源之一。 +- **证据:** `git ls-files muse-cloud | wc -l` = 4468;`cat .gitmodules` = No such file;`ls` 顶层同时存在 design-docs + muse-cloud + muse-admin + muse-studio。 +- **严重度:high** + +#### A2【高】"147 completed 是有证据支撑的门禁口径,coverage json 盘中复核未被篡改"——把"人工自证"当成"客观度量" +- **基线前提:** completed=147 是"严格门禁口径(HTTP + 真实 PG + Flyway + 证据)";"coverage json 盘中复核未被篡改";把它当作可信的完成度锚点。 +- **挑战(三连击):** + 1. **JSON 是手工维护、且已陈旧。** `generatedAt=2026-05-25T14:04:44Z`,但基线写于 06-13、completed 已是 147,中间 19 天的"收口"提交不断把数字往上改。所以它不是"生成器实测产物",而是**手工编辑的台账**;"未被篡改"是错误的框——它本来就是被人持续编辑的,谈不上篡改与否,只能谈"是否可信",而它**不可独立复核**。 + 2. **门禁测试硬编码目标数字。** `P1rApiCoverageReportTest.java:287` 写死 `assertEquals(147, completed, "…只能从 145 增至 147")`。这意味着"147"不是统计出来的,是**测试和 JSON 互相对着写死的常量**;每收口一批,人工把 JSON 改大 + 把断言改大。这是**自证循环**,不是门禁。 + 3. **`implementationStatus` 全部 = `dedicated`(233/233)。** "0 catch_all / 0 generic_persistence / 0 sse_placeholder / 0 missing"这条被基线反复引用的"高质量证据",其实只是"有人把所有 233 条都标成了 dedicated"——是 JSON 里的**自填标签**,不是机械判定。 +- **影响:** 基线 §6.1"内部验证门禁(coverage)中(147/233≈63%)"这一整层评分**失去客观地基**。完成度的核心量化锚点是可疑的。 +- **证据:** `docs/superpowers/reports/p1r-api-coverage.json` → `generatedAt=2026-05-25`,`summary.completedOperations=147`,逐条 `implementationStatus` 全为 `dedicated`,`completionStatus` ∈ {completed×147, needs_verification×86};`P1rApiCoverageReportTest.java:42-46`(NON_REAL 集合)、`:287`(硬编码 147)、`:49-53`(APPROVED_COMPLETED_DOMAINS 仅 ai/knowledge,白名单也是手填)。 +- **严重度:high** + +#### A3【高】"完成度 ≈ 76%,内部验证门禁中"——验证机制在 CI 中根本不运行,且 live 验收全跳过 +- **基线/迁移评审前提:** 有一套"证据门",completed 需 MockMvc + 真实 PG + Flyway;迁移评审把"证据门"列为五道软约束护栏之一,认为已在协议层生效。 +- **挑战:** + 1. **CI 跳过全部测试。** `muse-cloud/.github/workflows/maven.yml:30` = `mvn -B package --file pom.xml -Dmaven.test.skip=true`。所有 P1R 门禁测试、覆盖率断言、完成审批 IT——**在 CI 里一个都不跑**。它们只在某人本地手动 `mvn test` 时才有意义。 + 2. **真实外部验收 IT 全部 `assumeTrue` 自跳。** `P1rAiRuntimeEndToEndLiveAcceptanceIT.java:157` / `P1rKnowledgeRuntimeEndToEndLiveAcceptanceIT.java:137` 用 `assumeTrue(externalAcceptanceEnabled(), …)`——默认环境变量不开就**当成通过(跳过=绿)**。 + 3. **没有任何测试证明"完整 muse-server 聚合启动 + 服务一个真实请求"在自动化下成功过。** 基线自己承认"未实跑 mvn/npm"。于是:既无 CI 实跑,又无 live IT,又无人工实跑——"completed/可用"三个口径**全部缺少自动化证据**。 +- **影响:** "76%"是**在没有任何一次绿色流水线背书**的情况下给出的;它表达的是"读代码看起来实现了多少",不是"验证过多少"。基线把它分层表述已是进步,但仍偏高,且"验证门禁=中"严重失真(应为"验证自动化≈0")。 +- **证据:** `maven.yml:30`(skip tests);`P1rAiRuntimeEndToEndLiveAcceptanceIT.java:157`、`P1rKnowledgeRuntimeEndToEndLiveAcceptanceIT.java:137`(assumeTrue 跳过);基线自述"未实跑 mvn/npm"。 +- **严重度:high** + +#### A4【高】"AI 授权包级隔离仅是软约束、被有意后置"——实际是 BC 边界已被代码违反(比未约束更糟) +- **基线前提:** ArchUnit/grant-runtime 包隔离"全仓 0,被显式后置第二期,当前仅软约束";措辞暗示"边界在,只是没机械护栏"。 +- **挑战:** 边界**不是没护栏,是已经破了**。`ContentMuseWorkOwnerFacade`(AI 模块内,`@Primary @ConditionalOnBean({WorkMapper,ChapterMapper,BlockMapper})`)**直接 `import cn.iocoder.muse.module.content.dal.dataobject.{WorkDO,ChapterDO,BlockDO}` 和 `content.dal.mysql.{WorkMapper,ChapterMapper,BlockMapper}` 并 @Resource 注入**。即:AI BC 绕过 facade-api / RPC 契约,**直接读另一个 BC 的私有 DAL 与 DO**。这违反基线第三节反复强调的"逻辑 owner 优先 + 跨 BC 写入走 owner facade + facade-api 契约"。基线不但没发现,还把这块讲成"诚实留白 + 后置 ArchUnit"。 +- **次生事实(对基线的纠偏,双向):** 也正因为 AI 直接抓 Content 的 mapper,在 `muse-server` **单体聚合部署**下(content 与 ai 同 classpath),`ContentMuseWorkOwnerFacade` 这个 `@Primary` real bean **会赢过 `UnavailableMuseContentWorkOwnerFacade`**(后者 `@ConditionalOnMissingBean`)。所以基线"跨 BC facade 多为 Unavailable→运行期端到端阻断"对 **content/security 这两类是过度悲观的**——在真实单体形态它们解析到 real 实现。**结论两面性:基线在"阻断范围"上偏悲观,却在"边界完整性"上偏乐观,两个方向的误判同时存在。** +- **影响:** 这是对"模块化单体 + 清晰 BC 边界"(ADR-001)这一根基的实质背离。一旦未来要拆服务,这种直连 DAL 会让拆分代价爆炸;ArchUnit"后置"会让破墙继续蔓延而无人察觉——**这正是把机械门禁后置的真实代价**。 +- **证据:** `muse-module-ai/.../application/muse/facade/ContentMuseWorkOwnerFacade.java:6-11`(import content DO+Mapper)、`:28-31`(@Primary @ConditionalOnBean + implements MuseContentWorkOwnerFacade);`UnavailableMuseContentWorkOwnerFacade.java:16-17`(@ConditionalOnMissingBean 兜底);AI 模块 main 内跨模块 `content/market/meta/knowledge` 的 Mapper/DO/dal import 共 6 处。 +- **严重度:high** + +#### A5【高】"先软约束、机械门禁列入第二期"——这个决策直接喂养了失控根因 +- **迁移评审前提(本期已定三决策之③):** 先只上软约束(入口必读/契约先行/分流评审/证据门/模块边界),CI/ArchUnit/pre-commit 后置第二期。 +- **挑战:** 把"软约束优先"当解法,是**用产生问题的同一种机制去治理问题**。失控的实证根因(见 A2/A3/A4)全都是"靠自觉的软约束在没有机械校验下被绕过":证据门靠自觉 → CI 跳过测试;契约先行靠自觉 → SSE 契约漂移被 mock 掩盖;模块边界靠自觉 → AI 直连 Content DAL;完成度靠自觉 → 手工拨 147。**已经有充分证据表明这套软约束在本项目失效了**,却把更强的机械门禁继续往后推——这是**已被证伪的赌注又下一次**。 +- **影响:** 若按迁移评审执行,治理层文档会更整齐,但**失控的发动机(可手工拨动且无人校验的完成度)原封不动**。churn 可能因"单一进度源"短期下降,但"假绿"风险上升(进度更集中、更难交叉证伪)。 +- **证据:** A2/A3/A4 全部证据即本条论据;迁移评审 §0.3 / §2.2 / §3.6(机械门禁后置)。 +- **严重度:high** + +#### A6【中】"前端 connectAIStream 契约漂移属中等风险、被 mock 掩盖"——低估了它代表的系统性"假绿" +- **基线前提:** 列为 Top6 第 6 条,严重度"中",描述为单点 AI 流联调阻断。 +- **挑战:** 这不是一个孤立 bug,而是**整条前端验证链造假**的样本。`muse-studio/src/main.tsx:9-11` 在 `import.meta.env.DEV` 下**无条件 `worker.start()`**——所有开发期请求走 MSW;而 `sse.ts:10` onDone 用 `{generationId, candidateId}`、`:25` 按 `data.type` 派发,均偏离契约(契约要求 `{taskId, suggestionId}` 走 SSE `event:` 字段)。mock 照抄错误 shape → 前端测试与联调**永远见不到真后端**。这意味着**没有任何一个开发者通过真实 UI 跑通过任何后端**,基线所有"前端薄/断链"的判断其实更严重:连"已实现的那部分前端"也未对真后端验证过。 +- **影响:** "前端完成度≈30%"里的那 30% 也是 mock 上的 30%,真实可对接比例更低。 +- **证据:** `muse-studio/src/main.tsx:9-11`;`muse-studio/src/lib/sse.ts:10,25,33`;契约 `docs/api-contracts/ai/openapi.yaml` SSEDoneEvent.data required `[taskId,suggestionId]`(基线 §5.6 已引)。 +- **严重度:中** + +#### A7【中】"churn 不是真实交付量,只是过程文档堆积"——部分代码提交也是 churn,基线/迁移评审都只盯文档 churn +- **前提:** 迁移评审把 churn 限定为"34 memorys + 33 agent-specs 过程文档";基线认同。 +- **挑战:** **代码提交本身也在 churn。** 近 200 提交里 `fix(p1r)` 37 次、`test(p1r)` 23 次、`docs(p1r)` 7 次 = 67 次围绕同一批 P1R 的返工/收口,而 `feat(p1r)`+`feat(api)` 合计仅 33 次。即"修 P1R + 收口 P1R"的提交量是"真正新增功能/契约"的 **2 倍**。把 churn 仅归于文档,会**低估返工成本、误以为只要收敛文档就能止血**;实际是"接口未锁"在代码层也持续诱发返工。 +- **影响:** 迁移评审的解法(收敛文档 + 单一进度源)只能止住文档那一半;代码返工那一半需要"契约真正锁定 + 机械校验"才能止。 +- **证据:** `git log --oneline -200` 类型分布:fix(p1r)=37 / test(p1r)=23 / docs(p1r)=7 / feat(p1r)=21 / feat(api)=12;最近 15 提交全为 `test(p1r): 收口…completed approval 门禁`。 +- **严重度:中** + +#### A8【中】"muse-module-ai 是 364 文件的高质量纵向实现"——大半是 yudao 原生 AI/BPM 模块,Muse 自有面小得多 +- **基线前提:** AI 域"364 个 Muse java 文件…真实较高质量实现"。 +- **挑战:** 这 364 里**含大量 wholesale 继承的 yudao 原生模块**(`framework/ai/core`、`service/chat`、`service/workflow`、`service/music`、`service/model`、`AiModelFactoryImpl` 等),它们带着 yudao 作者(`@芋艿`/`@lesan`)的原生 TODO 与 `UnsupportedOperationException`(全仓 `TODO/FIXME/UnsupportedOperation` 222 处,绝大多数在这些继承树里)。**真正 Muse 业务包(`application/muse`、`domain/muse`)里的 TODO/桩只有约 9 处**,且都是基线已诚实披露的 scoped 缺口(如 Task4 owner "not implemented")。 +- **两面性(对基线公平):** 这条**同时支持也削弱**基线——支持"Muse 自有业务代码确实干净"(我确认,见 §一.B);削弱"364 文件高质量"的体量叙事(分母被 yudao 继承码灌水)。 +- **影响:** 用"文件数/LOC"衡量完成度会高估;应只算 Muse 自有 BC 面。 +- **证据:** 全仓 `TODO|FIXME|UnsupportedOperation` 222 处,scope 到 `*/muse/` 业务包且排除 yudao 作者/framework 后仅约 9 处;样例 `MuseKnowledgeBaseService.java:222`("not implemented in Task 4")。 +- **严重度:中** + +#### A9【低】"gateway 保留死路由 + 未接 Muse BC 是高危缺口"——成立,但严重度被高估 +- **基线前提:** Top6 第 4 条"高",担心"误以网关为真实入口部署 → Muse BC 全 404"。 +- **挑战(降级而非翻案):** 事实成立(网关只路由 system/member/bpm/report/pay/mp,无任何 content/ai/knowledge/market/meta/events;且 bpm/pay/report/mp 已从 `muse-server` 注释停用却仍有路由)。但严重度应为**中**:当前部署形态是 `muse-server` 单体聚合直出,网关本就不在真实入口链路上;这是"未来要用网关时的待办",不是"现在阻断了什么"。把它列"高"会与真正阻断价值的前端断链/边界破墙抢优先级。 +- **证据:** `muse-gateway/.../application.yaml:36-119`(仅 system/member/bpm/report/pay/mp 路由,无 Muse BC);`muse-server` pom 注释停用 bpm/pay/report/mp。 +- **严重度:低(基线列高,我下调)** + +### B. 经核实予以确认、不再挑战的前提(对抗有节制) + +- **B1 后端 Muse 业务代码是真实实现,非空壳。** Muse 自有业务包 TODO/桩仅约 9 处且均诚实标注;`RealNewApiMuseAiRuntimeClient`、`HttpRagFlowKnowledgeRuntimeClient` 是真 HTTP 客户端;幂等/乐观锁/审计模式在多模块一致出现。**确认成立,不推倒。** +- **B2 历史"completed"声称是低报而非高报(注水方向相反)。** 各域 memos 反复拒绝宣称 33/33、32/32,把多数 operation 留在 needs_verification。**确认成立**——但这恰恰反衬 A2/A3:诚实的"低报"配上"无人自动校验的刻度盘",仍然不能当可信完成度。 +- **B3 设计层(design-docs)成熟。** 迁移评审此判断与本次只读印象一致,不挑战。 +- **B4 churn 治理需要单一进度源 + 契约锁定。** 方向正确,我只反对"机械门禁后置"(见 A5),不反对"单一进度源"。 + +--- + +## 二、目标可达性裁决 + +**裁决:目标(多角色 AI 创作与资产流通系统,Shadow→Canonical 主权)本身可达;但"现有总体计划 + 现有验证/计量机制"判定为不可达——若不先更换计量与校验机制,继续执行将以更高置信度再次失控("假绿"取代"churn 体感")。** + +分三层: + +| 层 | 裁决 | 依据 | +|---|---|---| +| **目标本身** | ✅ 可达 | 后端领域逻辑是真实资产(B1);设计成熟(B3);硬不变式 Shadow→Canonical 在 content `mergeBlockSuggestion` 等处确有代码强制 | +| **现有"完成度"计量** | ❌ 不可信 | 147 是手工台账 + 硬编码断言 + 全 dedicated 自填标签(A2);CI 跳过测试 + live IT 自跳(A3)。计量盘本身坏了 | +| **现有总体计划(继续收口 operation 门禁 + 软约束治理)** | ❌ 达不成目标 | operation 门禁边际价值趋零(刷的是后端覆盖,不是用户价值);软约束已被实证绕过(A5);BC 边界已破却无机械护栏(A4)。继续执行 = 在坏盘上刷绿 | +| **会在哪里再次失控** | ⚠ 三处 | ① 前端真接后端时,SSE/契约漂移与 mock 假绿集中爆雷(A6);② 拆服务或 ArchUnit 补回时,AI↔Content 直连 DAL 大面积返工(A4);③ "completed=N"被对外当"可用",上线即穿帮(A2/A3) | + +**一句话:** 不是"还差 24% 就到 100%",而是"**衡量到 76% 的那把尺子需要先扔掉**"。先恢复"可信、自动、可证伪"的完成度信号,再谈推进——否则推进越多,真假越难分。 + +--- + +## 三、重新确立的达成路径(排序,每步给 why 与优先级) + +> 排序原则:**先止血(关掉刷分回路)→ 再竖切验证(用一条真实可跑的旅程重建"完成"的定义)→ 再补边界护栏 → 最后才铺面**。与迁移评审最大分歧:**机械门禁不后置,而是与软约束并行的最小集前置**(只上能止血的那几条,不求全套 CI)。 + +### P0(止血,先于一切;1-3 天级) + +**P0-1 打开 CI 测试 + 把门禁测试改为"计算而非硬编码"。** +- **做什么:** `maven.yml` 去掉 `-Dmaven.test.skip=true`(至少对 muse-server 的非 live 单测/IT);`P1rApiCoverageReportTest` 删除 `assertEquals(147, completed)` 这类硬编码常量,改为"从覆盖矩阵**重新统计** completed,并校验每条 completed 必须满足真实判据(有 controller+service 文件、非 NON_REAL)";`implementationStatus` 不再接受 JSON 自填,改由扫描判定(哪怕只判"是否存在 service 文件 + 是否含 generic catch")。 +- **why:** 这是失控发动机的点火开关。只要"147"还能手工拨、CI 还跳过测试,后面任何路径都建在沙上(A2/A3/A5)。 +- **优先级:最高。** + +**P0-2 冻结"operation completed approval 收口"类工作。** +- **做什么:** 停止再产 `test(p1r): 收口 X completed approval` 提交;把人力从"把 needs_verification 拨成 completed"撤出。 +- **why:** 近 15 提交全在做这件零用户价值的事(A7);后端 operation 门禁 147/233 的边际价值已趋零,继续刷只是制造更多假信号与 churn。 +- **优先级:最高。** + +### P1(用一条真实竖切重建"完成"的定义;1-2 周级) + +**P1-1 选定唯一一条端到端"黄金旅程"并真实跑通:`登录 → 打开作品 → AI 生成候选 → 前端 Accept Suggestion → 候选经后端 mergeBlockSuggestion 落库 + 来源归因 → SSE 回流`。** +- **做什么:** 真实启动聚合 `muse-server` + 真实 PG + 真实(或受控)New-API;`muse-studio` 关掉该旅程的 MSW、对真后端;修 `sse.ts` 契约漂移(taskId/suggestionId + event 字段);把 `EditorPage` 的 `demo-work`/`b-${id}`/`revision=1` 演示壳替换为真实 `WorkspacePage` 路径并挂上 AI 候选面板;让 studio 真正调用 `suggestion-merges`(当前真实调用数 = 0)。产出一个**自动化的端到端冒烟**(可 `assumeTrue` 跳过 live New-API,但 PG + 内部链路不许跳)。 +- **why:** ① 这条链是 objective"AI 先审后入"的最小可用证明,基线 Top6 第 1/6 条都压在它上;② 用"一条真能跑的旅程"取代"147 个自证 operation"作为新的完成度锚点——**完成 = 用户旅程在自动化下绿,不是台账数字**;③ 一次性证伪/暴露 A6 的前端假绿。 +- **优先级:最高(P0 之后第一件)。** + +**P1-2 明确区分并对外只报三个口径,禁止用单一"%"。** +- **做什么:** 任何对上汇报用三列:`代码实现度(读代码)` / `自动化验证度(CI 实跑通过的 operation/旅程数)` / `端到端可用度(真实形态跑通的用户旅程数)`。删除"76%"这类合并数。 +- **why:** 基线已指出三者是双关语却仍给了合并 76%;合并数是高估的来源(A2/A3)。 +- **优先级:高。** + +### P2(补边界护栏,趁破墙未蔓延;1 周级,可与 P1 并行) + +**P2-1 上 ArchUnit 最小集(只两三条,不求全):禁止 `module.ai` import `module.content/market/meta/knowledge` 的 `.dal..`(DAL/DO/Mapper)。** +- **做什么:** 加一条 ArchUnit 规则把 A4 的直连 DAL 标红;对已存在的 `ContentMuseWorkOwnerFacade` 直连,要么改为经 content 的 facade-api/对外 API 读,要么显式登记为已知违例并定整改期。 +- **why:** A4 是对 ADR-001/边界根基的实质背离,且会随时间蔓延、拆服务时代价爆炸;"机械门禁后置第二期"(A5)正是让它继续烂的决策。**这一条 ArchUnit 的成本极低、止血价值极高**,是"机械门禁不该全后置"的最小反例。 +- **优先级:高。** + +**P2-2 meta impact-preview 决断:要么补一个 real 生产者,要么显式接受"meta 发布链路本期不可端到端"。** +- **做什么:** `MetaImpactFacade` 当前是接口、唯一实现是 `UnavailableMetaImpactFacade`(确认基线此处正确),导致 publish/activate 因 `requireImpactPreview` 硬门禁必失败。明确选一:① 在 content/knowledge/ai 侧补最小 impact 统计;② 或把 meta 标为"治理写链路本期 design-complete / runtime-blocked",写进单一进度源,不再让它以"completed 覆盖"形式制造可用假象。 +- **why:** 这是少数"基线没冤枉"的真实运行期阻断;放着不决会持续产生"completed≠可用"的认知差。 +- **优先级:中。** + +### P3(才轮到铺面;P0-P2 稳定后) + +**P3-1 按"黄金旅程"模板,逐条竖切其余高价值旅程**(知识库草稿确认闭环 → 市场生产侧发布/上架 → 个人中心权益)。每条都遵循 P1 的"关 mock + 真后端 + 自动化端到端"标准,**完成定义统一为旅程绿,不再是 operation 台账**。 +- **why:** 前端用户端是真实大缺口(基线 §6.1≈30%,且这 30% 还在 mock 上,A6),但只有先有 P0 的可信尺子 + P1 的旅程模板,铺面才不会变成新一轮刷分。 +- **优先级:中。** + +**P3-2 迁移评审的治理层(AGENTS.md/.agents/contracts/进度总账)按"修正版"落地:保留单一进度源,但①进度只承认"自动化绿的旅程",②契约锁定必须配 P0-1 的 CI 校验(openapi-diff 实跑),不是纯文档纪律。** +- **why:** 单一进度源方向对(B4),但若进度源记录的仍是"手工 completed",只是把假信号换了个更集中的地方放(A5)。治理层必须挂在 P0 的机械校验上才有意义。 +- **优先级:中。** + +--- + +## 四、Top 风险 + +| # | 风险 | 触发条件 | 影响 | 缓解 | +|---|---|---|---|---| +| R1 | **"假绿"取代"churn"成为新失控形态** | 按迁移评审执行(软约束 + 机械门禁后置),进度收敛到单一源但仍是手工 completed | 进度更集中、更权威、却更难交叉证伪;上线/对外承诺时集中穿帮 | P0-1/P0-2 先关刷分回路;P1-2 三口径分报 | +| R2 | **AI↔Content 直连 DAL 蔓延,拆服务/补边界时大面积返工** | ArchUnit 继续后置,新功能照抄 `ContentMuseWorkOwnerFacade` 直连套路 | ADR-001 模块化单体边界名存实亡;未来拆分代价爆炸 | P2-1 最小 ArchUnit 立即止血 | +| R3 | **前端真接后端时 mock 假绿集中爆雷** | 任一旅程关 MSW 对真后端 | SSE/契约漂移、字段名不符成片暴露;"已实现前端"实际不可对接 | P1-1 先竖切一条并修 sse.ts;之后逐条关 mock | +| R4 | **"completed=147"被当"可用"对外承诺** | 汇报沿用合并完成度% | 与真实可用度差距在交付节点暴露,信任受损 | P1-2 禁用单一%,只报三口径 | +| R5 | **继续投入后端 operation 收口,挤占前端竖切资源** | P0-2 不执行 | 后端覆盖刷到更高但用户仍不可用,飞轮 UI 层持续断裂 | P0-2 冻结收口 + P1/P3 资源转向旅程竖切 | +| R6 | **本仓"不承载代码"的自我定位让 agent 误判改动边界** | CLAUDE.md 不更正 | agent 不敢/不知可改 muse-cloud 等,绕路产生更多文档 churn | 更正 CLAUDE.md 与 AGENTS.md 对仓库物理结构的描述(A1) | + +--- + +## 附:本次对抗取证索引(关键 evidence) + +- **仓库物理结构(A1):** `git ls-files muse-cloud|wc -l`=4468;无 `.gitmodules`;顶层并存 design-docs + 三代码仓。 +- **完成度计量造假面(A2):** `docs/superpowers/reports/p1r-api-coverage.json`(`generatedAt=2026-05-25`,completed=147,implementationStatus 全 dedicated);`muse-server/.../P1rApiCoverageReportTest.java:42-53`(自填白名单/NON_REAL 集),`:287`(`assertEquals(147, completed)` 硬编码)。 +- **验证不自动(A3):** `muse-cloud/.github/workflows/maven.yml:30`(`-Dmaven.test.skip=true`);`P1rAiRuntimeEndToEndLiveAcceptanceIT.java:157`、`P1rKnowledgeRuntimeEndToEndLiveAcceptanceIT.java:137`(`assumeTrue` 自跳)。 +- **BC 边界已破(A4):** `muse-module-ai/.../application/muse/facade/ContentMuseWorkOwnerFacade.java:6-11,28-31`(import + @Resource content DAL/DO/Mapper,@Primary @ConditionalOnBean);`UnavailableMuseContentWorkOwnerFacade.java:16`(@ConditionalOnMissingBean 兜底,单体下不生效)。 +- **代码层 churn(A7):** `git log --oneline -200` 类型分布 fix(p1r)=37 / test(p1r)=23 / feat(p1r)=21 / feat(api)=12;最近 15 提交全 `收口…completed approval 门禁`。 +- **前端假绿(A6):** `muse-studio/src/main.tsx:9-11`(DEV 无条件 MSW);`src/lib/sse.ts:10,25,33`(generationId/candidateId + data.type,偏离契约);`suggestion-merges` 真实调用数=0。 +- **yudao 继承灌水(A8):** 全仓 TODO/FIXME/UnsupportedOperation=222,scope 到 Muse 业务包后≈9。 +- **meta 运行期阻断(P2-2):** `muse-module-meta/.../MetaImpactFacade.java:12`(接口);唯一实现 `UnavailableMetaImpactFacade`。 +- **gateway 未接 Muse(A9):** `muse-gateway/.../application.yaml:36-119`(仅 system/member/bpm/report/pay/mp)。 +- **确认成立侧(B1/B2):** Muse 业务包桩≈9 且诚实标注;`RealNewApiMuseAiRuntimeClient`、`HttpRagFlowKnowledgeRuntimeClient` 为真 HTTP;各域 memos 拒绝宣称满分(低报方向)。 diff --git a/docs/agent-specs/2026-06-13-项目目标与模块现状基线.md b/docs/agent-specs/2026-06-13-项目目标与模块现状基线.md new file mode 100644 index 00000000..24773ae8 --- /dev/null +++ b/docs/agent-specs/2026-06-13-项目目标与模块现状基线.md @@ -0,0 +1,459 @@ +# Muse(oh-my-muse)项目目标与模块现状基线 + +| 项 | 值 | +|---|---| +| 版本 | v1.0 | +| 日期 | 2026-06-13 | +| 读者 | 决策与执行(管理者 / 工程执行 / 后续 Agent) | +| 性质 | 项目现状基线 SSOT(单一事实源) | +| 用途 | 后续工作据此进行,**避免重复探索**;凡与本文冲突,以经核实的代码与覆盖率台账为准 | +| 边界 | 只汇编"目标设计 + 模块当前真实现状";不重新设计、不下达任务。模块完成度为**只读判断**(未实跑 mvn/npm),已逐项标注证据 | + +--- + +> ⚠️ 引用说明(2026-06-14 补):本文内联引用的 `docs/memorys/*`、`docs/agent-specs/*审阅版/执行版` 过程文档已作为 churn 清理;唯一操作干货已蒸馏入 [`.agents/knowledge/external-deps-and-gotchas.md`](../../.agents/knowledge/external-deps-and-gotchas.md),完整原文见 git 历史。本文作为现状基线快照保留,内联引用路径仅供 git 追溯。 + +## 结论先行(TL;DR) + +1. **后端不是空壳,而是一套真实、较高质量的纵向实现。** 9 个领域 BC 的后端代码普遍是真实业务逻辑(幂等、乐观锁、审计、Shadow→Canonical 硬闸门、真实 HTTP 外部集成),覆盖率台账 233 operation 中 **147 completed / 86 needs_verification,且 catch_all / generic_persistence / sse_placeholder / missing / blocked 均为 0**。 +2. **历史"完成"声称是低报而非高报。** 大量 `CompletedApproval / 状态推进 / 收口` 文档经核实异常诚实、自我设限:"completed" 是**覆盖率门禁批准状态**(需 HTTP 入口 + 真实 PostgreSQL + Flyway + 证据),不等于"功能端到端可用"。文档反复拒绝宣称 33/33、32/32。churn(过程文档反复重开)真实存在(34 memorys + 33 agent-specs,被项目自我诊断),但反映的是"接口未锁 + 无单一进度源"的治理问题,**不是虚假交付量**。 +3. **三类最危险缺口(详见第六节):** + - **前端用户端(muse-studio)严重滞后**:AI 候选闭环断链(后端齐全、studio 未接 `suggestion-merges`)、知识库工作台核心交互(草稿确认/绑定/发布/图谱)完全缺失、市场生产侧(发布/上架/申诉)无 UI、个人中心仅约 21% 端面且跑在 MSW mock 上。**这是 objective"AI 先审后入"在用户可见层的落点,当前不可用。** + - **跨 BC facade 多为 `Unavailable` 默认实现**:meta 的 impact-preview、account 的 New-API/FileService/Market 投影等 facade 仅有 fail-closed 占位,导致大量 operation 运行期返回 `*_UNAVAILABLE`(meta 写链路、account 21/33 端面运行期阻断)。代码诚实留白,但端到端不可用。 + - **设计承诺与机械门禁缺口**:ADR 级"AI 授权包级隔离 + ArchUnit"全仓 0 依赖 0 规则(被显式后置第二期);gateway 路由未接任何 Muse BC 且保留已停用模块死路由;若误以网关为真实入口部署,Muse BC 全部 404。 +4. **真实整体完成度评估:约 76%**(后端实现高、内部验证门禁中、前端用户端低、跨 BC 集成与机械门禁低)。 + +--- + +## 一、项目目标与产品意图 + +### 1.1 目标(objective) + +把 Muse 从"会提取设定的文档助手 + 提示词面板",升级为面向长篇小说的**多角色 AI 创作与资产流通系统**: + +- **管理员**治理系统能力:MetaSchema、系统级智能体、全局知识、质量门控、市场秩序、权限。 +- **普通用户**在低认知负担的作品工作台里完成:写作 / 规划 / 生成 / 知识治理 / 质量感知 / 导入 / 导出;并通过智能体工作台、知识库工作台、市场来管理与流通**作品 / 智能体 / 知识库**资产。 +- **核心硬规则——双轨主权:** AI、外部知识、市场资产一律先进 **Shadow(待审层)**,只有**用户确认或用户保存**才进 **Canonical(规范数据)**;**正文保存不等于知识入库**。 +- **工程底座:** 以 yudao-cloud fork 为底座,按 **9 个领域 BC(逻辑 owner)** 落地;**逻辑 owner 优先于物理表 / 页面入口**。 +- **本仓定位:** `muse-design-docs`(即 oh-my-muse)是设计 SSOT,只定义目标设计与 owner 归属,**不承载运行时代码**。 + +### 1.2 意图(intent) + +让长篇创作者获得"可控、可解释、越用越懂当前作品"的 AI 协作: + +| 价值 | 含义 | +|---|---| +| 用户主权 | AI 不黑盒改稿,先审后入 | +| 长期知识与叙事闭环 | 人物/关系/事件/世界规则沉淀为可确认可检索资产,已确认知识真正回流到下一次生成上下文 | +| 不止守一致性 | 还推进弧线/悬念/节奏/张力(质量门控 + 创作健康度) | +| 资产飞轮 | 作品/智能体/知识库变成可复用、可授权、可流通的资产 | +| 管理员价值 | 系统能力可配置、可回滚、可审计,不散落进创作流程 | + +**非目标(明确排除):** 一键全自动黑盒写作机;通用智能体平台 / 通用 RAG SaaS / 阅读分发社区;市场本阶段不做完整电商支付结算 DRM。 + +--- + +## 二、总体计划(overallPlan) + +两条线并存: + +### 2.1 技术依赖线 S0–S5 +S0 基础设施补齐 → S1 RAGFlow 集成与知识检索基座 → … → AI 编排/质量评测闭合(见 `doc/dev/01-09`,本次未深读,来源为映射表与 ADR 引用)。 + +### 2.2 产品形态 / 信息架构门禁线 IA-0..IA-3 +不重排 S0–S5,是产品与文档对齐门禁: + +| 门禁 | 内容 | +|---|---| +| IA-0 | 角色边界与信息架构重构(管理员/普通用户/个人中心入口与权限边界) | +| IA-1 | 普通用户作品工作台(我的作品 + 作品工作台 + 单作品深层流程) | +| IA-2 | 管理员控制台(配置能力/权限/审计/接口归属) | +| IA-3 | 作品规划台与高质量小说维度(规划维度进入 AI 辅助/上下文/检查链路) | + +### 2.3 工程承接节点("阶段 7") +fork yudao-cloud:保留 `system/infra/framework/gateway/member/pay/bpm/report/mp`,补 `content/knowledge/ai/market/meta` 五个 Muse 业务模块。 + +**作品资产能力默认全部 Feature Gate 关闭**(`template/reference/ai_context=false`),开启前必须先补:ADR + 产品-02C owner + Schema/API + 使用预检 + 来源 lineage + 授权快照 + 导出限制 + 状态机承接。 + +--- + +## 三、系统架构 + +### 3.1 四仓协作 + +| 仓 | 角色 | 技术栈 | +|---|---|---| +| **muse-cloud** | 后端主仓(SSOT 落地为代码);fork YunaiV/yudao-cloud,继承网关/权限/基础设施/文件/任务,补 content/knowledge/ai/market/meta 五业务模块,account 由 member 承载 | Java 21 + Spring Boot 3 + PostgreSQL(JSONB,ADR-003)+ Yudao Cloud + Sa-Token(ADR-013) | +| **muse-admin** | 管理端;只调 `/admin-api/**`,复用 Vben,承载系统治理/MetaSchema/Prompt-Agent/全局知识/市场治理/用户权限/审计/质量观察 | Vue 3 + Vben Admin + TS;fork yudao-ui-admin-vben | +| **muse-studio** | 用户端;只调 `/app-api/**`,承载我的作品/作品工作台/写作台/规划台/智能体工作台/知识库工作台/市场/个人中心;不暴露系统 Prompt/Pipeline/后台配置 | React + Vite(或 Next.js)+ TS;编辑器 Block 级 schema(ADR-004),Block 用 UUID(ADR-007) | +| **muse-design-docs(本仓 oh-my-muse)** | 产品/架构/流程/前端/后端/专题设计的单一事实源;只定义目标设计与 owner 归属,不承载运行时代码/CI/部署 | Markdown + SQL | + +**最高级别不变式:** Shadow→Canonical 双轨主权——AI/市场/外部知识默认不可信,只能产生候选/草稿/快照/任务结果;正式作品事实只能由**用户确认、用户保存或目标 owner 显式规则**写入;正文保存不等于知识入库。横切契约 Source/Authorization 采用**事件驱动 + 各模块自治**(来源 owner 发 Source Status Event,消费方直接决策 active/disabled,无 needs_recheck 中间态)。逻辑 owner 优先于物理表/代码模块/页面入口(ADR-015)。 + +### 3.2 业务域(9 BC) + +| key | 名称 | 物理 owner | 核心职责(摘要) | +|---|---|---|---| +| **ai** | AI Orchestration BC | yudao-module-ai(二开,grant vs runtime 包级隔离 + ArchUnit) | 链路编排/上下文组装/生成/分析/全书解析/质量门控/AI 候选(Suggestion)/规划候选/风险标记/Quality Result/运行记录;承载 Protection Node(grant)与 Quality Policy。只产 Shadow,绝不直写 Canonical;只消费服务端签发的 Runtime Permission Envelope,不能自授权 | +| **content** | Work/Content BC | yudao-module-content | Work/Chapter/Block/正文版本/Block Source Attribution/Import-Export/Planning Canonical/Narrative State。正文 Canonical 唯一写入方;Block 写入须带 expectedRevision 且 revision 单调递增。**Accept Suggestion 是候选进正文的唯一合法入口**,只写正文 + 候选归档 + 来源归因,不写 Local KB | +| **knowledge** | Knowledge BC | yudao-module-knowledge | Local KB / User KB / 全局知识处理 / Knowledge Draft / Knowledge Source Binding / 投影与索引状态。进 Local KB 唯一入口是用户显式确认草稿或手动修正;全书解析章节确认只产/推 Draft,不写正式知识。来源优先级:Local KB > 用户绑定知识 > 市场/全局知识 | +| **market** | Marketplace/Asset BC | yudao-module-market | 市场资产(Work/Agent/KB Asset)/Publish Draft/Publish Check Snapshot/Review/Listing/License/Purchase/Install/Handoff Token/授权摘要/跳转审计/Governance Action/Source Status Event。授权≠所有权转移;市场不是源事实 owner;目标事实回目标 owner。作品资产当前仅阅读/收藏/授权记录,模板化/参考写入/AI 上下文受 Feature Gate 默认关闭 | +| **meta** | MetaSchema BC | yudao-module-meta(独立模块) | 元结构定义:字段类型/校验/枚举/引用/领域/范围/目标类型/可见性/AI 上下文/导出语义;控制 uiVisible/aiContext/userEditable/userSearchable/exportable。admin 写全局定义,用户作品级覆盖扩展字段,业务模块经 facade-api 只读消费投影。**ADR-016 已将其从 Admin/Governance 拆出独立;后端-03 §3 仍残留旧表述,属文档内部待统一点** | +| **events** | Source/Authorization 横切上下文(事件驱动) | 来源对象 owner + outbox 协作(source_snapshot DDL 归 infra) | 贯穿 content/knowledge/agent/ai/market/account/导出/个人中心的横切契约,**非独立 source 模块(ADR-017)**。Source Snapshot/Authorization Snapshot/Source Status Event/Source Propagation Target。各消费模块自治监听并直接决策 active/disabled;传播失败 fail-default 禁用新使用;不自动回滚已确认 Canonical | +| **account** | Account/Usage/Audit BC | yudao-module-member(已决策承载,不新增 account 模块) | Profile/Entitlement-Quota/Usage Record/Personal Center Summary/记录总览/Security Event/New-API Binding/Audit Log/Download Credential。是用户可见权益的聚合**读模型**与跳转入口,**不是消费账本事实源**,不能反写作品/知识/智能体/市场事实。高危审计 append-only + WORM/哈希链,读取与导出本身也被审计 | +| **platform** | 平台底座(Identity/Auth + Admin/Governance + Integration + Yudao 基座) | yudao-module-system/infra/ai.grant/meta + Integration facade + gateway/framework/server | Sa-Token 认证授权;系统功能链路/开放槽位/系统 Prompt/保护节点注册表;Integration(New-API/RAGFlow/GraphRAG/文件/通知外部适配,只存绑定引用/correlationId/idempotencyKey,不复制供应商路由与底层成本日志 authority)。Yudao 基座绝不承载 Muse 创作业务事实;系统任务必须用明确服务身份,不复用用户权限绕过 Shadow→Canonical | + +### 3.3 已确认决策(ADR 摘要) + +| 决策 | 结论 | +|---|---| +| 模块化单体优先(ADR-001) | 默认模块化单体,清晰边界/本地事务/低成本;痛点可量化时才拆服务,但业务事实源与用户确认边界必须回 Muse | +| Shadow→Canonical 数据主权(ADR-003/架构-02) | AI 只产 Shadow;正文保存≠知识入库;**非谈判项,不能被实现绕过** | +| AI 能力经外部受信 Agent 服务(ADR-002) | Muse 负责权限/上下文组装/调用/结果消费/Shadow 写入;Agent 服务不得直接写库 | +| 向量检索用 RAGFlow 替代 pgvector;图查询依赖 RAGFlow GraphRAG 不自建图存储(ADR-005/006) | PG 不存 embedding 作主检索;不引入 Neo4j;需降级与投影重建路径 | +| 管理员控制台独立入口(ADR-008/原则1.2) | 前端 IA/权限/审计按角色分离;管理员配置经配置快照影响生成链路;治理权分层无单一超级权限 | +| Sa-Token 作认证授权基座(ADR-013) | 承担登录/RBAC/鉴权/会话/基础审计;但不决定作品事实是否进 Canonical,业务规则仍由 Muse 控 | +| 阶段7 fork yudao-cloud,逻辑 owner 优先(ADR-014/015) | yudao-cloud 作工程基座与过渡物理落点;逻辑 owner/facade/状态机/写入边界以 BC 为准;跨 BC 写入走 owner facade | +| Governance 按消费者拆散,MetaSchema 独立(ADR-016) | MetaSchema→独立 BC;Protection Node + Quality Policy→AI grant 包;市场治理→Marketplace admin 包 | +| Source 传播=事件驱动各模块自治;去 needs_recheck(ADR-017/018) | 无独立 source 模块与 Candidate Decision Envelope;候选接受时实时对比来源版本 + 同步质量/合规/静态检查;source_snapshot 独立表 DDL 归 infra | +| AI 授权包级隔离(grant vs runtime)+ ArchUnit | runtime 包禁调 grant 写入;工具调用/上下文读取/外发须经服务端 tool broker 校验不可变 envelope。**⚠ 现状:全仓 0 ArchUnit,被显式后置第二期,当前仅软约束** | +| Account 物理落 member,不新增 account 模块 | member = 端侧用户 = Account BC 物理承载,扩 Entitlement/Quota/Usage/Security/Binding;唯一写入 authority | +| New-API 职责边界(ADR-009) | New-API 保供应商路由/限流/成本/原始日志 authority;Muse 只保绑定引用/任务摘要/归属/correlationId/幂等;凭据禁入日志/Prompt/候选/导出/用户错误 | +| 作品资产能力 Feature Gate 默认全关 | `template/reference/ai_context_enabled=false`;开启前须补 ADR + owner + Schema/API + 预检 + lineage + 授权快照 + 导出限制 + 状态机 | + +### 3.4 核心数据模型 + +**双轨三层模型(架构-02 为权威):** + +| 层 | 含义 | 内容 | +|---|---|---| +| **Canonical** | 规范数据(正式事实) | 正文 / 正式规划项 / Local KB / User KB 资料 / 智能体版本 / 市场授权记录 | +| **Shadow** | 待审层(默认不可信) | AI 候选 / 规划候选 / 知识草稿 / 解析结果 / 风险标记 / 质量结果 | +| **Archive** | 归档层(终态历史) | 先同表 + status 过滤,后续按量分离 | + +**进入 Canonical 的合法入口仅 7 类:** 用户保存正文 / 接受候选 / 确认规划候选 / 确认知识草稿 / 维护 User KB / 管理员发布系统配置 / 市场授权安装。**且接受候选不自动确认知识草稿。** + +- **内容骨架:** Work > Chapter > Block(UUID,场景/小节级粒度,expectedRevision 乐观锁);不为 Scene 单独建一级模型(ADR-012);规划维度复用 MetaSchema/Narrative State/Work/Chapter/Block(ADR-011)。 +- **三类知识库:** Global KB(管理员)/ User KB(用户跨作品复用)/ Local KB(单作品正式事实)。 +- **横切 value object:** Source Lineage / Source Snapshot(独立 `source_snapshot` 表)/ Authorization Snapshot(消费方须存快照 id 或不可变指纹,不能只存布尔)/ Block Source Attribution / Agent Runtime Permission Envelope(服务端签发不可变,前端/智能体/AI runtime 自报无效)。 +- 动态属性用 **JSONB + 属性级变更日志**而非 EAV(ADR-003)。 + +--- + +## 四、模块现状总表 + +> 实现层:✅ 已落地 / ❌ 缺失 / ⚠ 受限或薄层。完成度为只读判断,口径见各模块详情。 + +| 模块(BC) | server | api | contract | 前端 | DB迁移 | 测试 | 真实完成度 | 构建 | 契约一致性 | 置信度 | 最关键风险 | +|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|---|:---:|---| +| **AI 编排** (muse-module-ai) | ✅ | ✅ | ✅ | ⚠ | ✅ | ✅ | **72%** | 未实跑/可编译 | 较好有偏差 | high | ADR 级包隔离+ArchUnit 缺失(软约束);前端全 MSW,SSE 疑漂移;真实 New-API 未端到端验收 | +| **作品/编辑器** (muse-module-content) | ✅ | ✅ | ✅ | ⚠ | ✅ | ✅ | **82%** | 未实跑/可编译 | 高度一致 | high | **前端 Accept Suggestion 断链**:后端齐全但 studio 未接 suggestion-merges,AI 候选进正文用户端不可用 | +| **知识库** (muse-module-knowledge) | ✅ | ⚠ | ✅ | ⚠ | ✅ | ✅ | **72%** | 未实跑/可编译 | 高度一致 | high | 前端 2/3 面缺失(草稿确认/绑定/发布/图谱无 hook);Task4 跨 BC owner 未实现;recheck/worker 默认未生效 | +| **市场** (muse-module-market) | ✅ | ⚠ | ✅ | ⚠ | ✅ | ✅ | **82%** | 未实跑/可编译 | 高度一致 | high | studio 仅消费侧(浏览/授权/安装);**生产侧发布/上架/申诉无 UI,飞轮 UI 层断裂** | +| **元治理/MetaSchema** (muse-module-meta) | ✅ | ❌ | ✅ | ⚠ | ✅ | ✅ | **82%** | 未实跑/可编译 | 高度一致 | high | **impact-preview facade 仅 Unavailable→写链路运行期端到端阻断**;facade-api 缺失,跨 BC 投影消费契约未建立 | +| **事件/SSE** (muse-module-events) | ✅ | ✅ | ✅ | ⚠ | ✅ | ✅ | **88%** | 未实跑/可编译 | 后端一致/前端漂移 | high | 前端 connectAIStream 契约漂移(被 mock 掩盖,真实联调会失败);统一事件客户端为孤儿无 UI 消费 | +| **账户/个人中心** (muse-module-member) | ✅ | ❌ | ✅ | ⚠ | ✅ | ✅ | **68%** | 未实跑/可编译 | 端点齐/约2/3运行期降级 | high | 跨 BC facade 全 Unavailable→21/33 端面运行期 `*_UNAVAILABLE`;前端仅约 21% 面且跑 MSW | +| **平台底座** (gateway/system/infra/pay/bpm/mp/report + 两前端接线) | ✅ | ✅ | ❌(设计如此) | ✅ | ❌(沿用 yudao) | ✅ | **80%** | 未实跑/可编译 | 无契约(符合设计) | high | **gateway 路由未接任何 Muse BC + 保留死路由**;pay/bpm/mp/report 显式停用 0 运行覆盖;底座 schema 未 Flyway 化 | + +> 说明:`api ❌` 表示该 BC 的 `-api` 子模块仅 package-info / ErrorCode(meta、account 无对外 facade-api DTO,knowledge 的 api 仅 ErrorCode,market 的 api 刻意薄属 yudao 风格,均非"缺失业务"而是"对外 RPC 契约未建立或无需");`contract ❌` 平台底座属设计上不为 yudao 基座单独定契约。 + +**覆盖率台账(盘中实读 `docs/superpowers/reports/p1r-api-coverage.json`):** + +| 指标 | 值 | +|---|---| +| totalOperations | 233 | +| completedOperations | **147** | +| needsVerificationOperations | **86** | +| incomplete / catchAll / genericPersistence / ssePlaceholder / missing / blocked | **全部 0** | + +**逐域 operation 计数(契约口径,与台账逐项吻合):** account 33 / ai 41 / content 51 / events 1 / knowledge 59 / market 32 / meta 16。台账 domain 仅含这 7 个 BC,**平台底座无"完成"声称**(它是被继承的 yudao 基座)。 + +--- + +## 五、逐模块详情 + +### 5.1 AI 编排(muse-module-ai)— 完成度 72% + +**实现摘要:** 后端 `muse-module-ai-server` 是真实较高质量实现(364 个 Muse java 文件 / 35 DO / 35 Mapper / 14 admin + 6 app Controller / ~30 service+facade,无 TODO/FIXME/stub)。核心链路:`AppMuseAiTaskController`(create/get/SSE/试用 Agent,带 X-API-Version guard + getLoginUserId)→ `MuseAiTaskServiceImpl`(712 行:幂等命令回放 reserveCommand、@Transactional、SourceSnapshot 构建、Agent 解析、Runtime Permission Envelope 签发 + Guard 校验、写 muse_ai_generation/job)→ `MuseAiRuntimeServiceImpl`(22 行薄壳,故意只稳定 contract)→ `RealNewApiMuseAiRuntimeClient`(419 行,Java21 HttpClient 真打 New-API `/v1/chat/completions`,超时/异常分类/token 脱敏/retryable)与 `UnavailableMuseAiRuntimeClient`(fail-closed),由 `AiAutoConfiguration` @Conditional 装配。SSE:`MuseAiTaskStreamServiceImpl`(377 行)已脱离占位,轮询持久化事件 + replay + keepalive + 终态互斥。Suggestion:`MuseSuggestionServiceImpl` 只做 list/get/reject(**accept 正确不在 AI 域,归 content BC**)。DB:V4 + V12/V13/V17,22 张表。测试 ~25 个 Muse 测试。契约 38 path / 41 operation 与后端基本对位。 + +**桩与缺口:** +- `muse-module-ai-contract-server` **整模块空壳**(仅 pom,0 java),且 `muse-server` 已改依赖 `-server`,成为**孤儿 submodule**。 +- **ArchUnit + grant/runtime 包级隔离:全仓 0 依赖 0 规则 0 包目录**,实为类命名约定 + 运行时 Guard 软约束。 +- 真实 New-API 端到端**未验收**(P1R4 smoke qwen3.5/doubao 超时);本地无配置即 fail-closed 全失败。 +- Evaluation run 起 job 但**同步评分/LLM-judge 闭环深度未见**(疑 job 异步延迟)。 +- 前端 agent 特性 **DEV 全程 MSW**(8 端点全 mock),真实连通未验证;`AgentPage` 有 `WORK_ID_FOR_SLOT_PREVIEW` 硬编码;**AI-stream SSE 与契约疑 drift**(有独立待办 chip)。 + +**设计 vs 现实差异:** ADR/CLAUDE.md 反复声称"AI 授权包级隔离 + ArchUnit",现实为软约束(被 2026-06-13 评审文档明确"列入第二期不实施")——**有意后置而非偷工,但设计承诺的硬隔离未达成**。 + +**"完成"声称 vs 现实:** **AI 域是文档低报而非高报。** `2026-05-30-P1R4AI真实API规格计划.md` 全程坚持 completedOperations=0、明确 streamAiTask 曾是 sse_placeholder、记录 New-API smoke 超时拒写"已完成"。代码现实强于其保守声称。唯一名实不符是 ArchUnit/包隔离承诺(被显式后置)。 + +**证据(file:line):** +- `muse-cloud/muse-module-ai/muse-module-ai-contract-server/pom.xml:1`(整模块仅此,0 java) +- `.../application/muse/MuseAiTaskServiceImpl.java:98-128`(幂等+事务+envelope guard) +- `.../application/muse/facade/RealNewApiMuseAiRuntimeClient.java:43-74`(真实 HttpClient + 异常分类) +- `.../application/muse/facade/UnavailableMuseAiRuntimeClient.java:13-18`(fail-closed) +- `.../framework/ai/config/AiAutoConfiguration.java:317-329`(@Conditional 装配) +- `.../application/muse/MuseSuggestionServiceImpl.java:66-127`(reject,无 accept,符合 content owner) +- `.../application/muse/MuseAiTaskStreamServiceImpl.java:57-95,144`(SSE 真实) +- `muse-server/pom.xml:114`(依赖 -server 非 contract-server,证孤儿) +- 全仓 grep archunit/ArchRule = **0 命中** +- `docs/memorys/2026-05-30-P1R4AI真实API规格计划.md:49,115,448`(拒写 completed;smoke 超时) +- `muse-studio/src/main.tsx:9-11` + `src/api/mocks/handlers/ai.ts:130-305`(DEV MSW 全拦截) + +--- + +### 5.2 作品/编辑器(muse-module-content)— 完成度 82% + +**实现摘要:** 后端 159 java 文件,**51 个端点(app 44 + admin 6)精确等于契约 51 个 HTTP method**,全部委派到 Application Service 含真实逻辑:乐观锁(expectedRevision/revision 单调递增 CAS)、commandId 幂等、owner/租户 guard、审计、Shadow→Canonical 硬闸门。核心:split/merge/reorder(临时负序号重排避唯一键 + 双 Block 来源归因)、**Accept Suggestion(mergeBlockSuggestion)严格前置校验**(来源版本/授权快照/许可/合规/静态检查/质量结果版本逐项,外部 owner 不可用即 blocked 不伪造)、Planning 候选确认写 Canonical、Import/Parse Job 状态机、Export + download credential 校验、Meta 投影/动态字段、事件 outbox。DB:V1(7 表)+ V9 + V21。测试 21 类,断言密度高(53-95/类),7 个 app controller MockMvc 测试。前端 `WorkspacePage` 真实消费 + `MuseEditor` 真实自动保存(2s 防抖 + IndexedDB)。 + +**桩与缺口:** +- **前端 Accept Suggestion 断链:** studio 无 `suggestion-merges` 调用,`EditorPage.handleAcceptDiff` 仅本地 `setContent`,纯前端文本替换,不落库、不写来源归因、不带 suggestionId/expectedRevision。 +- **EditorPage 是演示壳:** chapters 硬编码、`workId='demo-work'`、`blockId='b-${id}'`、revision=1 硬编码。 +- `AIPanel` 候选不携带 suggestionId,无法走后端校验闸门。 +- **真实编辑路径(WorkspacePage)未挂 AIPanel/CandidatePanel**,真实路径与 AI 候选路径 UI 未合流。 +- 字数统计 P1R-1 用字符 length 占位;`useChapterDelete` 前端 expectedRevision=1 为 mock。 +- 域级 content **43/51 operation 自评仍 needs_verification**(验收门禁滞后于代码,非代码缺口)。 +- `authorization_snapshot_id` 表为 BIGINT,非数值外部授权快照被静默丢弃(已知表能力限制)。 + +**设计 vs 现实差异:** 后端与设计高度一致;差异集中在前端 AI 候选闭环未对齐专题-01。存在两套编辑器(真实 WorkspacePage 无 AI 面板 / 演示 EditorPage 有面板但全硬编码),易误判已打通。 + +**"完成"声称 vs 现实:** 历史文档**属实且异常诚实**。`2026-05-27-P1R1内容真实API收口.md` 明确"完成口径不是标 completed";`2026-06-12-P1RContent状态推进.md` 自述"本轮未修改 OpenAPI/业务实现/SQL",仅把 8 个 operation 推 completed、列其余 43 仍 needs_verification。**churn 是门禁审批 bookkeeping 反复刷,非反复声称写完代码;声称比现实更谦虚。** + +**证据(file:line):** +- `ContentStructureServiceImpl.java:77-214`(split/merge:CAS + 临时负序号 + 双 Block 归因) +- `ContentSourceServiceImpl.java:73-204`(mergeBlockSuggestion 逐项前置校验,外部不可用 throw EXTERNAL_OWNER_UNAVAILABLE) +- `ContentExportServiceImpl.java:102-163`(download credential 校验) +- `ContentPlanningServiceImpl.java:148-179`(confirmPlanningCandidate 写 Canonical) +- `sql/muse/V1__init_content_schema.sql:13-180`(7 表 + 约束 + 触发器) +- `muse-studio/src/pages/EditorPage.tsx:17-21,114-117,47-55`(演示壳硬编码 + handleAcceptDiff 仅 setContent) +- `muse-studio` grep `suggestion-merges` **仅命中 types/content.ts:358(生成类型),无实际调用** +- `muse-studio/src/pages/WorkspacePage.tsx:31-36`(真实消费,但无 AI 面板) +- `docs/memorys/2026-06-12-P1RContent状态推进.md`(completed=8 needs_verification=43) + +--- + +### 5.3 知识库(muse-module-knowledge)— 完成度 72% + +**实现摘要:** 本仓最完整业务模块之一。**59 端点(app 36 + admin 23)与契约 48 path/59 method 精确对齐**;service 共 6443 行(DocumentService 1068/KnowledgeBaseService 951/DraftService 646)。`confirmKnowledgeDraft` 真把 Shadow 草稿物化为 Canonical 实体/关系(entityMapper/relationMapper.insert),带冲突检测、决策审计、commandId 幂等、sourceVersion 追踪。RAGFlow 是真 HTTP:`HttpRagFlowKnowledgeRuntimeClient`(621 行)调 `/api/v1/datasets`、`/api/v1/retrieval`、`/run_graphrag`、`/knowledge_graph`,Bearer + SSRF 防护 + 超时 + 18 类 FailureClass,并有 `Unavailable*` 优雅降级(ADR-006)。绑定预检存 sourceSnapshotId/authorizationSnapshotId/handoffHash;发布 readiness/snapshot;source-event 传播;outbox worker。DB:V5(7 表)+ V14(18 表)+ V18 ≈ 26 张。测试 38 文件 209 @Test,含真实 RAGFlow 验收 IT。前端 `useKnowledge.ts`(405 行)真调 `/knowledge-bases`、`/installed-knowledge-bases`、`/documents`,但仅覆盖约 1/3 面。 + +**桩与缺口:** +- **跨 BC owner 计数未实现(Task 4):** installed KB owner / document owner count / export_task_owner 以 'unsupported'/'not implemented in Task 4' 占位返回。 +- **recheck 仅记状态**,未做外部来源真实重校验('external source validation is not configured')。 +- linkUrl 材料化 fail-closed(无 SSRF allowlist/隔离抓取前一律拒绝),功能未通。 +- **outbox 发布 worker 默认关闭**,启用需运维配置。 +- **前端缺失核心 Shadow→Canonical 用户面:** 无 drafts confirm/ignore/recheck、无 bindings、无 publish、无 entities/relations、无 graph 的 hook 与页面(约 2/3 前端面缺)。 +- 用户面 `/graph`、`/local-knowledge` 服务的是 **Canonical 实体/关系投影,非 RAGFlow GraphRAG 实时图**(架构自洽,但与"图查询依赖 RAGFlow"直觉读法有差异,需对齐确认)。 +- admin 端知识库视图存在但本次未深核接通度。 + +**设计 vs 现实差异:** 后端与设计高度吻合(Shadow→Canonical/RAGFlow 替代 pgvector/不自建图/事件驱动);若干 scoped 落差多为已注释而非隐藏空壳。**最大落差在前端**:草稿确认/绑定/发布/图谱用户面完全缺失。 + +**"完成"声称 vs 现实:** 非典型 churn,历史 memory 异常克制。06-01 P1R5"只完成规格/计划不进代码";06-06 P1R7c"不能推断已完成、不能推进 completed";06-09 P1R7CompletedApproval 才标 `knowledge 59 dedicated/completed` 并纳入 `APPROVED_COMPLETED_DOMAINS={ai,knowledge}`。**关键校正:该 completed 是 backend operation-level 覆盖门禁口径,未声称前端完整或外部依赖闭合;读作"知识库工作台端到端完成"会高估。** + +**证据(file:line):** +- `MuseKnowledgeDraftService.java:105-145`(confirmKnowledgeDraft 真物化 Canonical entityMapper.insert@302/relationMapper.insert@330) +- `MuseKnowledgeDraftService.java:194`(recheck 'external source validation is not configured') +- `HttpRagFlowKnowledgeRuntimeClient.java:1-621`(真 HttpClient 调四端点 + Bearer) +- `UnavailableRagFlowKnowledgeRuntimeClient.java:9-60`(ADR-006 降级) +- `MuseKnowledgeBaseService.java:221-248`(Task 4 scoped 缺口占位) +- `MuseKnowledgeGraphQueryService.java:41-69`(读 Canonical 投影非实时图) +- `sql/muse/V5/V14/V18`(7+18+outbox 表) +- `muse-studio/src/pages/KnowledgePage.tsx:18,53,266`(仅 KB list + MaterialManager + CreateKBModal;drafts/bindings/publish/graph 无 hook) +- `docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md:53,94`(knowledge 59 completed;DOMAINS={ai,knowledge}) + +--- + +### 5.4 市场(muse-module-market)— 完成度 82% + +**实现摘要:** 后端成熟真实。**32 方法级端点(app 21 + admin 11)与契约 32 operation 完全对齐**。13 service 约 5537 行(AdminMarketReviewServiceImpl 840/MarketPublishServiceImpl 819/AdminMarketGovernanceServiceImpl 805/MarketAppealServiceImpl 770)。覆盖:资产发现/详情/分类/推荐、收藏、授权、安装、bind-precheck、handoff、发布(草稿/检查/提交/撤回)、管理端审核(真实状态机 pending→submitted→reviewing→approved/rejected/listed)、治理(下架/召回/影响预览)、申诉、Account 投影 outbox、Events outbox+worker。DAL 真实 MyBatis-Plus(insertIgnore 幂等、updateByExpectedStatus 乐观锁)。23 张表(V6 + V8/V15/V19)。横切契约真实落地。前端:muse-admin 治理台真实(index.vue 1166 行 + 22 API 函数);muse-studio **仅 MarketBrowse 一个组件**(浏览/分类/授权/安装)。测试 209 @Test、1271 assert、6781 行。 + +**桩与缺口:** +- **studio 用户端仅消费侧:** 发布(publish-drafts/requests)、handoff 跳转、申诉(appeals)、授权记录/license 详情在 studio 无 UI(仅后端 + 契约 + admin 就绪)。 +- **32 op 中 28 仍 needs_verification**(非空壳,缺 MockMvc/真实 DB 正式验收证据);listMarketplaceAssets 缺分页边界证据,recommendations 个性化语义待澄清。 +- `muse_market_favorite` 的 status 无 DB 级 CHECK,asset_id/user_id 无 FK(schema hardening 待办)。 +- recommendations 为**可解释 fallback 排序而非真实个性化**(有意降级非桩)。 + +**设计 vs 现实差异:** 核心不变式已被代码强制:install/handoff `targetFactsWritten=false`、work 资产强制 read-only 且排除出 install(`INSTALLABLE_ASSET_TYPES=Set.of(agent,knowledge_base)`)、授权≠安装≠绑定、事件驱动各模块自治。差距=studio 仅"浏览/授权/安装"闭环,发布/handoff/申诉无前端。 + +**"完成"声称 vs 现实:** **无虚报,代码比文档声称更完整。** `2026-06-11-P1RMarket状态推进.md` 明确 Market 仅 4 completed/28 needs_verification,显式禁止宣称 32/32。盘中校验磁盘 coverage json:market 4 completed + 28 needs_verification,与 memory 完全吻合未被篡改。**真正风险是闸门状态让人"低估"完成度,而非高估。** + +**证据(file:line):** +- `MarketInstallServiceImpl.java:41,62-65,96`(INSTALLABLE_ASSET_TYPES;work 抛 NOT_BINDABLE;targetFactsWritten=false) +- `MarketPublishServiceImpl.java:671-682`(work 强制 read-only) +- `AdminMarketReviewServiceImpl.java:53-62,99-120`(状态机 + approve 幂等回放) +- `sql/muse/V6` + V8/V15/V19(23 表) +- `MarketEventPublishWorker.java:6-46`(EventsPublishApi + 重试) +- `muse-server/pom.xml:74` + `muse-cloud/pom.xml:23`(已装配) +- `muse-studio/src/features/market`(仅 MarketBrowse.tsx + useMarket.ts,无 publish/handoff/appeal/license) +- `muse-admin/.../views/muse/market/index.vue`(1166 行真实治理台) +- `docs/memorys/2026-06-11-P1RMarket状态推进.md`(completed=4/needs_verification=28,禁止宣称 32/32) + +--- + +### 5.5 元治理/MetaSchema(muse-module-meta)— 完成度 82% + +**实现摘要:** 本次核查完成度最高最扎实之一。后端 **16 admin 端点(3 controller)与契约 16 operation、前端 16 API 函数 1:1 对齐**。`MetaSchemaServiceImpl`(1217 行)实现 list/detail/version/saveDraft/publish/activate/rollback/deprecate/gray-rules 完整状态机,含 expectedVersion 乐观锁、validationResultId + impactPreviewId 双引用门禁、快照、灰度、全程审计;`MetaSchemaValidationServiceImpl`(386 行)字段/枚举/relation/保护节点边界(replaceable=false 不可解绑)/兼容性 breaking 真实校验;`FunctionChainServiceImpl`(804 行);`MetaCommandServiceImpl` 真实幂等引擎(SHA-256 + insertIgnore reserve/replay)。DB:V3(6 表)+ V10(8 表 + ALTER)= 14 张。前端 admin 真实接 API(meta-schema.ts 757 行 + detail.vue 1123 行)+ vitest。测试 165 @Test/592 断言/~5449 行,含 route ownership 与 wildcard 退役断言。 + +**桩与缺口:** +- **MetaImpactFacade 唯一实现 `UnavailableMetaImpactFacade` 直接抛 META_EXTERNAL_OWNER_UNAVAILABLE。** 因 publish/activate/rollback/deprecate 均经 `requireImpactPreview` 硬性要求一条 succeeded 的 impact-preview,而 impact-preview 在 Content/Knowledge/AI 真实 facade 接入前必失败——**MetaSchema 治理写链路结构完整但运行期端到端被阻断**(诚实留白,但"可发布"目标当前运行期不可达)。 +- **facade-api 层缺失:** `muse-module-meta-api` 仅 package-info;content/knowledge/ai **0 处引用 meta**,投影消费契约尚未建立(crippled/orphan 风险)。 +- **muse-studio 用户端元引擎/动态表单(前端-03)未实现:** 仅自动生成的 admin 治理类型(types/meta.ts 1560 行),无消费 MetaField 渲染动态表单的组件(与"用户作品级覆盖扩展字段"目标有差距)。 +- 投影 rebuild/invalidate 仅返回 pending job id,不真正驱动 Content/Knowledge 投影执行。 +- FunctionChain impact-preview 的 affectedAgentSlotBindings/affectedAIRuntimeTasks 固定 0(AI runtime 无真实 owner)。 +- `AdminMetaSchemaController` 类级 Javadoc 过时(称写接口"留给后续实现",实际已全实现)。 + +**设计 vs 现实差异:** 三处实质差异均如上(impact-preview 阻断、facade-api 缺失、用户端元引擎缺失),均为"本地逻辑完整、外部依赖/用户侧待接"的透明降级。 + +**"完成"声称 vs 现实:** **是 churn 怀疑的反例**,git 仅 4 次提交触及。memos 高度诚实:05-28"没有把 Meta 标为 completed";06-10 两份是 coverage 簿记"未修改业务实现""APPROVED_COMPLETED_DOMAINS 仍只有 ai/knowledge"(用 operation 级 approval,未把 meta 整域列白名单)+ 新增真实 PG Flyway IT。**唯一名实落差:completed 覆盖含义≠运行期可端到端跑通(因 impact-preview facade 不可用)。memos 本身未声称运行期闭环,不构成虚假完成。** + +**证据(file:line):** +- `AdminMetaSchemaController.java:64-176`(11 端点全实现);`:45-50`(过时 Javadoc) +- `MetaSchemaServiceImpl.java:199-486`(状态机);`:634-646`(requireImpactPreview 硬门禁);`:909-915`(projectionJobId 仅 pending) +- `facade/UnavailableMetaImpactFacade.java:15-24`(唯一实现抛 unavailable) +- `MetaSchemaValidationServiceImpl.java:127-224`(字段/保护节点/兼容性真实校验) +- `MetaCommandServiceImpl.java:36-135,248`(SHA-256 幂等) +- `sql/muse/V3:4-118`(6 表);`V10:4-239`(8 表 + ALTER) +- `muse-module-meta-api/.../package-info.java`(无 facade-api) +- `muse-admin/.../governance/meta-schema/detail.vue`(1123 行) +- `muse-studio/src/types/meta.ts:1-7`(auto-generated,非元引擎组件) +- `docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md`(未改业务实现;DOMAINS 仅 ai/knowledge) + +--- + +### 5.6 事件/SSE(muse-module-events)— 完成度 88% + +**实现摘要:** 后端产线级,无空壳/桩/TODO。垂直链路完整:`AppMuseEventsController`(GET `/app-api/muse/events` SSE)→ `EventsStreamServiceImpl`(游标解析/首连冻结不 replay 历史/心跳/线程池后台轮询/lifecycle 取消/租户+owner 隔离/payload 脱敏/版本与游标 fail-closed 错误事件)→ `UnifiedEventMapper`(可见性查询 + 原生 insertIgnore ON CONFLICT,sequenceNo 由 PG sequence)→ `UnifiedEventDO`(JSONB)→ V16 DDL(sequence + 4 唯一约束 + 2 CHECK + owner 索引 + 触发器)。内部 RPC `EventsPublishApi.publish`:双重幂等(commandId/source tuple)、OpenAPI schema 逐字段校验、secret 检测拒绝。**跨模块集成真实广泛:ai/knowledge/market/member/content 五个 owner 各有 EventPublishWorker + outbox 表(V17-V21),88 处引用 EventsPublishApi**,逐域端到端测试。前端 `connectEventStream` 按 SSE event/id 字段正确解析、含重连退避 + token 注入,契约保真。 + +**桩与缺口:** +- **前端 connectAIStream 契约漂移:** 按 `data.type` 派发(应为 SSE `event:` 字段)且 onDone 用 `generationId/candidateId`(契约为 `taskId/suggestionId`);MSW mock 照搬错误 shape 致测试**假绿、与真实后端无法对接**。 +- **契约保真的统一事件客户端 connectEventStream 为孤儿:** 仅自身测试 import,无任何 UI 组件接入消费(仅 connectAIStream 被 AIPanel 接入)。 +- 后端缺真实 SSE HTTP 集成测试覆盖 controller getLoginUserId() 鉴权端到端路径(仅单测/静态/迁移 IT)。 +- 管理端 muse-admin 无任何 SSE/EventSource 消费(可能有意,契约仅定义 app-api)。 + +**设计 vs 现实差异:** 设计("AI stream 独立 + 事件统一")后端已落地双端点分离。差异集中在前端 AI-stream(非 events 本体):data.type 派发与 done 字段名错误。 + +**"完成"声称 vs 现实:** **非 churn,是本项目最克制、最有证据纪律的一组完成文档。** 06-05 把 streamEvents 仅推到 needs_verification;06-09 仅将单一 operation `events:streamEvents` 推 completed(coverage 100→101/132),附 TDD RED→GREEN、真实 PG/Flyway、118 测试 gate、双 fresh review PASS、空 OpenAPI diff,并显式列"不代表其他域/总 P1R completed"边界。声称未注水。 + +**证据(file:line):** +- `AppMuseEventsController.java:37`(唯一公开 SSE 端点) +- `EventsStreamServiceImpl.java:71-117`(首连冻结/游标/fail-closed);`:282-339`(heartbeat + 轮询 + 脱敏 error) +- `EventsPublishServiceImpl.java:66-96`(双重幂等);`:147-164`(逐字段校验) +- `UnifiedEventMapper.java:36-78`(可见性查询 + insertIgnore ON CONFLICT) +- `sql/muse/V16:4-52`(sequence + 约束 + 触发器);`V17..V21`(五域 outbox) +- `MuseKnowledgeEventPublishWorker.java:100`(真实调用,跨模块 outbox→统一事件) +- `muse-studio/src/lib/sse.ts:10,25-38`(onDone 错误形状 + data.type 派发,偏离契约);`:316-475`(connectEventStream 保真但孤儿) +- `docs/api-contracts/ai/openapi.yaml:3159-3182`(SSEDoneEvent.data required [taskId,suggestionId]) +- `docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md:1-204`(仅 events:streamEvents→completed,双 review PASS,非 churn) + +--- + +### 5.7 账户/个人中心(muse-module-member / Account BC)— 完成度 68% + +**实现摘要:** 后端真实成熟,account 主代码 **9877 LOC、0 TODO、约 291 @Test(34 文件)**。10 controller(admin 4 + app 6),去重约 32 逻辑端点(app 侧每端点拆 X-API-Version 两变体);约 16 应用服务全有真实 Impl;19 DO + 22 Mapper;5 domain guard(Owner/Quota/Download/ApiVersion/UserIdResolver,真实属主等值/配额/下载凭证 expiry-consumed-revoked-sourceBlocked 校验)。核心机制全落地:命令幂等、乐观锁(revision + DuplicateKey→VERSION_CONFLICT)、审计(before/after 快照)、事务性 outbox + @Scheduled worker(claim 租约/重试退避/dead_letter/幂等发布,带 sourceOwner/sourceRevision,符合 ADR-017)。DDL:V2(6 表)+ V11(368 行)+ V20。15 个 ACCOUNT_ 错误码。前端 `useAccount.ts` 真调 `/profile`(带 commandId/expectedVersion)、`/account/entitlements`、`/account/usage`,但运行在 MSW mock 上,仅覆盖 4/约19 app 端点。 + +**桩与缺口:** +- **跨 BC 集成边界全部 Unavailable 默认实现(@ConditionalOnMissingBean):** NewApiAccountFacade / AccountFileServiceFacade / MarketAccountProjectionFacade / AccountAttributionSourceFacade——本地逻辑完整但外部交付未接,运行时抛 `*_UNAVAILABLE`。 +- New-API 绑定/recheck/quota-request 无真实 runtime;导出文件下载持久化齐备但真实字节/对象存储未接(高敏导出 step-up 抛 invalidParam);市场记录(purchases/licenses/publish-records)投影未接;call-attribution 外部来源未接。 +- **Profile 摘要诚实占位:** emailVerified/mfaEnabled 硬编码 false、securityRiskCount=0、defaultEntry/productSpaces 静态默认;admin 用户列表 restricted 返回空集;session_revoked 未接入 session 管理服务。 +- **前端薄层:** 仅 4 app 端点(profile/entitlements/usage)有 UI 且跑 MSW;security-events/export-tasks/downloads/licenses/purchases/publish-records/new-api-binding/balance-snapshots/quota-requests 共约 15 个 app 端点**无前端**。 +- `member-api` 未暴露任何 account DTO/Enum(当前 Account 为自包含 BC,无外部读模型集成)。 + +**设计 vs 现实差异:** 与设计高度对齐(只读聚合 + 记录入口,不反写他域;New-API 只存绑定引用/correlationId 符合 ADR-009)。主要差异是"跨 BC 集成边界尚未接通"和"前端仅起步"——均为透明降级非伪造。 + +**"完成"声称 vs 现实:** **诚实准确,未发现夸大式 churn。** completed 是有严格定义的覆盖率门禁批准状态(需 MockMvc + 真实 PG _test + Flyway V1-V21 + row_to_json no-write 证据)。06-13/06-11 明确 Account 33 op,仅 **12 completed-approved、21 needs_verification**,反复拒绝宣称 33/33。21 个未通过原因(New-API/FileService/call attribution/Market 投影/security event 缺真实上游)与代码 Unavailable facade 边界完全一致。**实现骨架对 33 op 全部存在,21 个因上游 facade 未接通无法通过门禁,故诚实标 needs_verification。** + +**证据(file:line):** +- `AccountQuotaServiceImpl.java:95-181`(adminCreateQuotaAdjustment:幂等+乐观锁+审计+同事务 outbox) +- `AccountEventPublishWorker.java:64-132`(事务性 outbox:claim/退避/dead_letter/幂等,ADR-017) +- `facade/UnavailableNewApiAccountFacade.java:14-23`(仅 Unavailable,抛 ACCOUNT_NEW_API_UNAVAILABLE) +- `autoconfigure/.../MarketAccountProjectionFacadeAutoConfiguration.java:13-22`(@ConditionalOnMissingBean 注册 Unavailable) +- `AccountExportServiceImpl.java:177,199-211`(持久化齐备但文件交付降级 ACCOUNT_FILE_SERVICE_UNAVAILABLE) +- `AccountProfileServiceImpl.java:95-99`(诚实占位 emailVerified/mfaEnabled=false) +- `sql/muse/V2:4,31,57,79,104,124`(6 表);V11 368 行;V20 outbox +- `muse-studio/src/features/account/hooks/useAccount.ts:8-48`(真实 react-query,仅 4 端点) +- `muse-studio/src/api/mocks/handlers/account.ts:95-128`(跑在 MSW 上) +- `docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md:44-51`(total=33/completed=12/needs_verification=21,拒绝宣称 33/33) + +--- + +### 5.8 平台底座(gateway/system/infra/pay/bpm/mp/report + 两前端接线)— 完成度 80% + +**实现摘要:** 平台底座 = 对 yudao-cloud 的干净 fork,8 个核查模块全部真实完整(非空壳):system 32 admin controller/36 Impl/39 测试;infra 13/13/21;pay 19/12(PayOrderServiceImpl 610 行 18 方法);bpm 11/12/18;mp 12/9;report 4 Impl。gateway 17 java(CorsFilter/TokenAuthenticationFilter/GrayLoadBalancer/AccessLogFilter/GlobalExceptionHandler 等真实组件)。`muse-server` 是聚合启动入口,**实际装配 system/infra/content/meta/events/knowledge/market/member/ai;pay/bpm/mp/report 在 pom 中显式注释停用**(故意"默认隐藏",符合后端-02)。包名已整体 fork `cn.iocoder.yudao`→`cn.iocoder.muse` 无残留。前端两仓均真实接线:muse-studio 212 处 `/app-api` 引用 + 真实 client.ts/sse.ts/indexed-db.ts/typed openapi.ts;muse-admin web-antd 真实 `src/api/muse/{ai,knowledge,market,account,governance,audit,newapi,jobs}` 调 `/admin-api/muse/**` 且各带 `__tests__`。 + +**桩与缺口:** +- **gateway/application.yaml 未为任何 Muse BC 配置路由;Muse 业务 API 当前不经网关,走 muse-server 单体——网关对 Muse 业务实际未接线**(设计写应承接 /admin-api + /app-api 路由)。 +- **gateway 保留 bpm/pay/mp/report 死路由**(/admin-api/bpm 等),而这些模块已从装配注释停用,路由指向不存在的服务。 +- pay/bpm/mp/report 代码完整但被显式注释,**0 运行覆盖**(设计内"默认隐藏",但意味当前无运行/测试保活,有上游漂移腐化风险)。 +- **平台底座 8 模块无 Muse 自有 Flyway 迁移**(sql/muse V1-V21 全是 BC schema),其 schema 仍依赖 yudao 遗留静态 SQL dump,未纳入 Flyway 受控迁移。 +- mp 模块测试 0、report 仅 2(继承 yudao 现状)。`MockPayClient` 含 `UnsupportedOperationException`(yudao 原生 mock 设计,非 Muse 业务桩)。 + +**设计 vs 现实差异:** 高度吻合(pay/bpm/report/mp"保留默认隐藏不删依赖"逐条对应),但 **1 处真实缺口=gateway 路由未对齐 Muse**:既保留已停用模块死路由,又完全没有 content/ai/knowledge/market/meta/events 真正 Muse BC 路由。设计写"承接路由",实际未承接 BC。 + +**"完成"声称 vs 现实:** **平台底座根本没有"完成"声称**,它是被继承的 yudao 基座;海量 P1R 文档与 24244 行 P1r* 测试全部针对 BC,与底座无关。对 BC 层这套机制经核查真实且反 churn:`P1rApiCoverageReportTest` 把 catch_all/generic_persistence/sse_placeholder/missing 列为不可标 completed 并断言 completed 恰为 147/233(其余 86 needs_verification,incomplete/blocked/placeholder 均 0);`P1rContentCoreCompletedApprovalIT` 真跑 Flyway(断言 21 迁移)+ 真实 PG 种子 + MockMvc 打真实控制器,断言分页/owner/租户隔离(跨租户 404 vs 跨 owner 403)/读路径不写 outbox;重 IT 用 `assumeTrue` 门控无 DB 时跳过而非伪绿。 + +**证据(file:line):** +- `muse-server/muse-module-server/pom.xml`(显式注释停用 report/bpm/pay/mp,启用其余) +- `muse-cloud/pom.xml:1-20`(groupId=cn.iocoder.cloud,modules 含全部) +- `muse-module-pay/.../PayOrderServiceImpl.java:1-610`(真实 18 方法非空壳) +- `muse-gateway/src/main/resources/application.yaml:34-`(routes 仅 system/infra/member/bpm/report/pay/mp;**无 content/ai/knowledge/market/meta/events**;bpm/report/pay/mp 死路由) +- `docs/api-contracts/openapi-base.yaml`(paths:{} 为空,仅共享组件) +- `docs/superpowers/reports/p1r-api-coverage.json`(totalOperations=233 completed=147 needsVerification=86,catchAll/genericPersistence/ssePlaceholder/missing/blocked=0;domain 无平台底座) +- `muse-server/.../P1rApiCoverageReportTest.java:42-48,287`(NON_REAL 状态不可标 completed;assertEquals(147)) +- `muse-server/.../P1rContentCoreCompletedApprovalIT.java:163,200-243`(真跑 Flyway 21 迁移;MockMvc 断言分页/owner/租户/读不写 outbox) +- `design-docs/后端-02-工程结构与模块职责.md:32,40-61`(gateway"承接路由";pay/bpm/report/mp"默认隐藏不删依赖") + +--- + +## 六、全局结论 + +### 6.1 真实整体完成度 + +**约 76%。** 分层看: + +| 维度 | 评估 | 说明 | +|---|---|---| +| 后端业务实现 | **高(~80%+)** | 9 BC 真实纵向实现,幂等/乐观锁/审计/Shadow→Canonical 硬闸门/真实外部 HTTP 普遍落地,无散落 TODO/stub | +| 内部验证门禁(coverage) | **中(147/233≈63%)** | completed 是严格门禁口径(HTTP+真实 PG+Flyway+证据);其余 86 needs_verification 多为"已实现未走完验证"非"未做" | +| 跨 BC 集成 | **低** | meta/account/部分链路的对端 facade 多为 Unavailable 占位,运行期端到端被阻断 | +| 前端用户端(studio) | **低(~30%)** | AI 候选断链、知识库工作台 2/3 面缺、市场生产侧无 UI、个人中心约 21% 且跑 MSW;管理端(admin)反而较真实 | +| 机械门禁/底座接线 | **低** | ArchUnit 0 落地(后置);gateway 对 Muse BC 未接线 + 死路由;底座 schema 未 Flyway 化 | + +### 6.2 churn 声称 vs 现实差距 + +| 维度 | 结论 | +|---|---| +| 过程文档 churn 是否真实 | **真实存在**(34 memorys + 33 agent-specs,被 `2026-06-13-agent开发基建迁移-review.md` 自我诊断为"接口未锁 + 无单一进度源导致的过程文档 churn,不是真实交付量") | +| "完成"声称是否注水 | **否,系统性低报。** 所有核查域的 memos 一致区分"covered/completed(门禁口径)"与"端到端可用",反复拒绝宣称 33/33、32/32;coverage json 盘中复核未被篡改 | +| 名实不符点 | **仅一处:** ADR 级"AI 包级隔离 + ArchUnit"承诺未兑现(被显式后置第二期)。其余皆为代码强于声称,或诚实标注的 scoped 缺口 | +| 治理风险 | churn 反映"接口未锁、无单一进度源",**本基线文档即是收敛此风险的单一进度源起点** | + +### 6.3 最危险缺口 Top 6 + +| # | 缺口 | 严重度 | 影响 | +|---|---|:---:|---| +| 1 | **前端 AI 候选闭环断链**(content):后端 mergeBlockSuggestion 齐全,studio 未接 suggestion-merges,EditorPage 仅本地 setContent | **高** | objective"AI 不黑盒改稿先审后入"在用户可见层不可用;演示壳本地替换反而绕过双轨主权(虽未落库无脏数据) | +| 2 | **跨 BC facade 多为 Unavailable**(meta impact-preview / account New-API·File·Market 投影):运行期抛 `*_UNAVAILABLE` | **高** | MetaSchema 治理写链路端到端阻断(发布跑不通);account 21/33 端面运行期不可用;"completed"被误读为"可用"风险高 | +| 3 | **前端用户端整体严重滞后**:知识库工作台 2/3 面缺、市场生产侧(发布/上架/申诉)无 UI、个人中心约 21% 且跑 MSW | **高** | 产品-02 多数用户旅程无前端实现;市场飞轮在 UI 层断裂,创作者无法自助上架 | +| 4 | **gateway 对 Muse BC 未接线 + 死路由**:无 content/ai/knowledge/market/meta/events 路由,保留已停用 bpm/pay/mp/report 路由 | **高** | 若误以网关为真实入口部署,Muse BC 全部 404,且死路由打向不存在服务 | +| 5 | **ADR 级 ArchUnit + grant/runtime 包隔离缺失**:全仓 0 依赖 0 规则 0 包目录,仅类命名 + 运行时 Guard 软约束 | **中-高** | 高价值安全边界(grant 写入 vs runtime 只读强隔离)无机械保证;被有意后置但仍是安全缺口 | +| 6 | **前端 connectAIStream 契约漂移**(events):data.type 派发 + done 字段名错误,被 MSW mock 掩盖致测试假绿 | **中** | 真实前后端联调时 done/quality_check 不触发回调,AI 流联调阻断;统一事件客户端 connectEventStream 为孤儿无 UI 消费 | + +**横切观察:** 后端"completed"(门禁口径)与"端到端可用"是双关语,**对外汇报必须明确区分"代码实现完成度(高)"与"内部验证门禁通过度(中)"与"端到端运行可用(受跨 BC facade + 前端拖累偏低)"**,否则极易高估。多数运行期阻断点已被后端正确 fail-safe(不伪造成功),属"功能不可用"而非"产生脏数据"。 + +--- + +## 附:本次来源 + +### A. synthesis.sourcesRead(目标锁定阶段已读) +- `/Users/lili/Project/oh-my-muse/CLAUDE.md` +- `design-docs/00-文档大纲.md`、`design-docs/内容映射表.md` +- `design-docs/产品-01-产品定位与核心价值.md`、`design-docs/产品-02-核心功能与交互边界.md` +- `design-docs/架构-01-系统全貌与边界上下文.md`、`架构-02-核心数据结构与双轨模型.md`、`架构-03-关键决策与原则(ADR).md`、`架构-04-状态机与约束清单.md` +- `design-docs/后端-01-领域模型与聚合设计.md`、`后端-02-工程结构与模块职责.md`、`后端-03-关键流程实现与接口契约.md` + +### B. 模块核实证据索引(各模块 evidence 见第五节 file:line;关键交叉验证点) +- **覆盖率台账(盘中实读):** `docs/superpowers/reports/p1r-api-coverage.json` → totalOperations=233 / completed=147 / needsVerification=86 / 其余指标全 0(本文 §四、§6.1、§5.8 引用) +- **门禁口径定义:** `muse-server/.../P1rApiCoverageReportTest.java:42-48,287`;`P1rContentCoreCompletedApprovalIT.java:163,200-243` +- **各 BC 契约:** `docs/api-contracts/{account,ai,content,events,knowledge,market,meta}/openapi.yaml`;`docs/api-contracts/openapi-base.yaml`(paths 为空) +- **各 BC DB 迁移:** `muse-cloud/sql/muse/V1-V21`(全为 BC schema) +- **自我诊断 churn:** `docs/agent-specs/2026-06-13-agent开发基建迁移-review.md`(34 memorys + 33 agent-specs = 过程文档 churn;ArchUnit/CI 后置第二期) +- **置信度:** 7 个 BC 模块核实均为 **high**(基于只读代码 + 契约 + 迁移 + 测试 + memos 交叉验证;未实跑 mvn/npm) diff --git a/docs/agent-specs/2026-06-14-agent基建六砖交付.md b/docs/agent-specs/2026-06-14-agent基建六砖交付.md new file mode 100644 index 00000000..c549642d --- /dev/null +++ b/docs/agent-specs/2026-06-14-agent基建六砖交付.md @@ -0,0 +1,108 @@ +# Agent 开发基建六砖交付说明(2026-06-14) + +| 项 | 值 | +|---|---| +| 版本 | v1.0 | +| 日期 | 2026-06-14 | +| 读者 | 决策者 / 后续 Agent | +| 性质 | 点状**交付说明**(本轮做了什么、为何、证据);进度活账见 [`docs/mvp/进度总账.md`](../mvp/进度总账.md) | +| 用途 | 留痕与验收;后续同类工作据此复用,**避免重复探索** | +| 边界 | 只记本轮 agent 开发基建交付;模块真实现状权威见 [现状基线](./2026-06-13-项目目标与模块现状基线.md) | + +--- + +## 一、结论(TL;DR) + +1. 按**机械门禁优先**建成 oh-my-muse 的 agent 开发基建:**P0 + 六砖**全部交付。 +2. **承重的两道机械门禁有真实绿证据**(JDK21 实跑,CI 同款 surefire): + - BC 边界门 `BcBoundaryArchTest` 1/0F、契约先行门 `ContractFirstGateTest` 2/0F; + - 合并运行 **Tests run: 3, Failures: 0, BUILD SUCCESS**。 +3. 治理层(knowledge/skills/workflow/总账/per-module `.agent`)以**交叉引用零缺失**验收,其约束力来自所**指向**的真门禁。 +4. 直面失控根因"**假绿**"(完成信号靠人工拨动 + CI 跳过测试 + 自填标签 + mock 假绿):本轮把约束从"自觉文档"升级为"**机械阻断**",坏代码进 PR 即红。 + +--- + +## 二、为什么这样建(根因 → 取舍) + +- 失控根因(见 [对抗复盘](./2026-06-13-目标达成对抗复盘.md))不是缺文档,而是**软约束被绕过**:CI 曾 `-Dmaven.test.skip`、门禁断言硬编码、completed 台账人工对账、前端只对 MSW mock 通过。 +- 取舍:**机械门禁优先**于软约束。`.agents/rules/` 的每条红线**必须配套机械门禁**(CI/ArchUnit/契约校验),否则只是会被绕过的文档。 +- 同源于你的边界原则:权限/安全/数据约束必须落在**可信边界**——类比之,agent 约束必须落在 CI 这道机械边界,而非 prompt 或文档自觉。 + +--- + +## 三、交付全景 + +```mermaid +flowchart TD + A[AGENTS.md\n项目唯一入口] --> B[.agents/ 能力中枢] + B --> K[knowledge/\n定位·架构·现状指针·决策] + B --> R[rules/\n硬约束 + 必配门禁] + B --> S[skills/\n黄金旅程·加BC模块] + B --> W[workflows/\nAI开发协议:承接→…→沉淀] + + R --> G1[BcBoundaryArchTest\nBC不碰他域 DAL] + R --> G2[ContractFirstGateTest\nFlyway卫生 + OpenAPI结构] + R --> G3[CI maven.yml\n真跑测试·JDK21·不跳过] + + G1 & G2 & G3 -.PR/push 机械阻断.-> IMPL[(muse-cloud / muse-admin / muse-studio\n实现代码)] + + W -- 第六步 沉淀(复利) --> B + LED[docs/mvp/进度总账\n单一进度源] --- AG[muse-module-*/.agent\n模块跨会话记忆] + A --> LED +``` + +要点:**入口唯一**(AGENTS.md)→ **中枢四层**(.agents)→ **三道机械门禁**实在地卡在 PR/push → workflow 的"沉淀"把每次成果**写回**中枢形成复利 → 进度只进**总账 + .agent**,杜绝过程文档 churn。 + +--- + +## 四、逐砖交付与验证证据 + +| 砖 | 交付物 | 验证方式 | 证据 | +|---|---|---|---| +| P0 | CI 真跑测试(JDK21、去 `-Dmaven.test.skip`)+ 覆盖台账去硬编码 + [冻结令](./2026-06-13-P0-止血冻结令.md) | 机械 | 既有,绿 | +| ① | [`BcBoundaryArchTest`](../../muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java) + [`bc-boundaries.md`](../../.agents/rules/bc-boundaries.md) | JDK21 实跑 | **1/0F**,clean 全 reactor BUILD SUCCESS | +| ② | [`ContractFirstGateTest`](../../muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java) + [`contract-first.md`](../../.agents/rules/contract-first.md) | JDK21 实跑 | **2/0F**(Flyway 唯一/连续/命名 + OpenAPI 7 域结构) | +| ③ | [`.agents/knowledge/`](../../.agents/knowledge/) 3 份蒸馏 | 链接完整性 | 13/13 引用存在 | +| ④ | [`.agents/skills/`](../../.agents/skills/) 2 份手册 | 锚点完整性 | 13/13 路径/锚点存在 | +| ⑤ | [`ai-development-protocol.md`](../../.agents/workflows/ai-development-protocol.md) | 链接完整性 | 全绿 | +| ⑥ | [`进度总账`](../mvp/进度总账.md) + 7 个 BC `.agent` | 链接完整性 | 16/16 路径存在 | +| 合并 | 两门禁同一 surefire 运行 | JDK21 实跑 | **Tests run: 3, Failures: 0, BUILD SUCCESS** | + +--- + +## 五、三目标达成映射 + +| 原始目标 | 落点 | +|---|---| +| 建 agent 开发基建 | AGENTS.md 入口 + .agents 四层中枢 + 总账 + per-module `.agent` | +| **约束 agent 编码** | **机械门禁**:CI 真跑测试、BC 边界 ArchUnit、契约/迁移卫生——违例=红=阻断(非靠自觉) | +| 持续改进开发环境 | workflow"沉淀" + README"新 rule 必配门禁" + 单一进度源 → 复利写回闭环 | + +--- + +## 六、诚实边界与未做(非阻塞,已如实标 ⏳) + +- ③④⑤⑥ 验收是**链接完整性**而非机械执行;约束力来自其指向的真门禁。 +- 模块现状为 2026-06-13 **只读**判断;`.agent`/总账指向基线、不复制数字,降漂移。 +- **明确未做**: + 1. openapi-diff 破坏性变更 CI(就绪片段见 contract-first §四,**未接入即不宣称已拦截破坏性变更**); + 2. `docs/dev-baseline/global/*` 收敛进 `engineering-conventions` / `security-and-reliability` 两条带门禁 rule; + 3. BC 边界门扩到其它方向(当前只守"AI→他域 DAL"这条已实证违反的方向); + 4. `muse-module-ai-contract-server` 孤儿空壳清理。 + +--- + +## 七、建议下一步 + +1. 接入并跑一次 openapi-diff CI,把契约门从"存在/结构"升到"破坏性变更"拦截。 +2. 收敛 dev-baseline,补齐两条**带门禁**的 rule(工程约定 / 安全可靠)。 +3. 用 [`golden-journey-vertical-slice`](../../.agents/skills/golden-journey-vertical-slice.md) 真跑一条旅程(如 content Accept Suggestion 前端断链),把基建从"约束"带到"产出可用功能"。 + +--- + +## 附:关键文件索引 + +- 入口 / 中枢:[`AGENTS.md`](../../AGENTS.md) · [`.agents/README.md`](../../.agents/README.md) +- 脊柱规则:[`verification-and-anti-false-green.md`](../../.agents/rules/verification-and-anti-false-green.md) +- 进度活账:[`docs/mvp/进度总账.md`](../mvp/进度总账.md) +- 现状基线 / 复盘:[基线](./2026-06-13-项目目标与模块现状基线.md) · [复盘](./2026-06-13-目标达成对抗复盘.md) diff --git a/docs/dev-baseline/muse-design-docs/CLAUDE.md b/docs/dev-baseline/muse-design-docs/CLAUDE.md index b3e7ca36..afbd2968 100644 --- a/docs/dev-baseline/muse-design-docs/CLAUDE.md +++ b/docs/dev-baseline/muse-design-docs/CLAUDE.md @@ -23,7 +23,6 @@ muse-design-docs 是 Muse 四仓架构中的设计文档仓,承担产品、架 | 前端 | 工程结构、编辑器交互、动态表单 | `前端-02-编辑器与影子层交互.md` | | 流程 | 操作流程、系统处理流程 | `流程-01A-管理员操作流程(操作视角).md` | | 专题 | 跨领域专项设计 | `专题-03-AI编排上下文与质量评测实现规范.md` | -| 临时 | 迁移依据,非长期 SSOT | `临时-产品形态阶段化重设计计划.md` | 规则: - 扁平结构,不建子目录(prototypes / memorys 除外) diff --git a/docs/memorys/2026-05-24-muse-studio写作台与AI协作实现.md b/docs/memorys/2026-05-24-muse-studio写作台与AI协作实现.md deleted file mode 100644 index 6f2d0da7..00000000 --- a/docs/memorys/2026-05-24-muse-studio写作台与AI协作实现.md +++ /dev/null @@ -1,38 +0,0 @@ -# muse-studio 写作台与 AI 协作实现留痕 - -## 任务背景 -在 `muse-studio` 前端子项目集成 Tiptap 富文本编辑器,实现本地 IndexedDB 影子层与远程 API 的双向防抖同步,接入 SSE AI 流式生成文本,并实现字符级高亮差异比对及采纳替换的闭环流程。 - -## 核心设计与踩坑总结 - -### 1. 分级 Diff 字符比对设计 (Coarse-to-Fine) -- **挑战**:经典的 DP(动态规划)LCS 算法时间复杂度为 $O(N \times M)$。在长篇小说场景下,对 3000 字以上的内容进行字符级 LCS 对比,计算矩阵元素高达近千万,直接导致浏览器 UI 线程卡死(ANR)。 -- **优化实现**: - 1. **段落级粗筛**:以段落(`\n`)为元素单元,首先运行一遍粗粒度的段落 LCS。 - 2. **局部字符 LCS 细筛**:如果段落被判定为 `modified`(即相邻的一段被删除、一段被新增,视为修改),且单段字数在安全限制(乘积小于 1,000,000)内,才对其进行细粒度的字符级 LCS 比对。 - 3. **非空安全断言**:在 TS 极严模式(`noUncheckedIndexedAccess: true`)下,对二维矩阵 `dp[i]` 级别的读取,使用局部变量承接和非空断言(`!`),消除对象可能为 `undefined` 的编译问题。 -- **效果**:长文本 Diff 运算时长由秒级缩短至 4 毫秒,彻底消除了卡顿风险。 - -### 2. 编辑器双防抖与临别落库机制 -- ** IndexedDB IO 去抖 (500ms)**:打字时内容先进入本地 IndexedDB 去抖保存,避免高频按键对磁盘读写的密集事务挤占,优化 INP 打字卡顿。 -- **API 远程去抖 (2000ms)**:延迟 2 秒调用 `PUT` 接口,同步成功后销毁本地影子草稿以释放磁盘。 -- **临别落库 (Flush)**:为防范打字结束 2 秒内用户突然离开页面导致最新修改丢失,扩展 `debounce` 模块提供 `.cancel()` 与 `.flush()` 能力。在编辑器 `useEffect` 卸载 cleanup 中: - ```typescript - useEffect(() => { - return () => { - debouncedAPISave.flush(); // 立即将挂起的定时任务提交 API 执行 - debouncedAPISave.cancel(); // 释放定时器引用 - }; - }, [blockId]); - ``` - -### 3. Zustand Selector 精准状态更新 -- **挑战**:多栏式写作台包含章节大纲、编辑器、AI 面板。如果解构式订阅 Zustand Store,打字时高频的 `isDirty` 变化会触发整棵组件树重渲染,造成严重输入卡顿。 -- **避坑**:组件必须使用精细 Selector 单独订阅特定状态,严禁解构大对象,阻断不必要的 React Re-render: - ```typescript - const markDirty = useEditorStore((state) => state.markDirty); - ``` - -### 4. 远程 API 同步失败异常捕获 (sync_failed) -- **实现**:如果 `PUT` 同步接口失败,通过 `catch` 捕获异常,立刻将 IndexedDB 对应小节的本地缓存状态覆写为 `status: 'sync_failed'`。 -- **恢复**:编辑器组件初始化挂载时,查询该小节是否存在 `sync_failed` 状态记录,若存在则提示恢复横幅“恢复草稿 / 忽略并覆写”,闭环了离线保存安全网。 diff --git a/docs/memorys/2026-05-24-muse-studio脚手架与基础设施搭建.md b/docs/memorys/2026-05-24-muse-studio脚手架与基础设施搭建.md deleted file mode 100644 index dd56e085..00000000 --- a/docs/memorys/2026-05-24-muse-studio脚手架与基础设施搭建.md +++ /dev/null @@ -1,76 +0,0 @@ -# muse-studio 脚手架与核心基础设施搭建留痕 - -## 任务背景 -在根目录从零初始化前端用户端子项目 `muse-studio`(Vite + React + TS 结构),并完成路由、统一客户端、MSW 2.0 Mock、IndexedDB 及 Zustand 状态库等基础设施建设。 - -## 关键工程实践与避坑指南 - -### 1. TypeScript 6.0 严格模式兼容 - -#### (1) `erasableSyntaxOnly` 限制 -- **现象**:当启用该规则时,TypeScript 6.0 禁用了 parameter properties 语法(如在 constructor 参数中直接写 `public code: string`)。 -- **避坑**:声明异常类或常规类时,需采用标准 ES ES6 属性定义: - ```typescript - export class ApiError extends Error { - public code: string; - public status: number; - constructor(code: string, message: string, status: number) { - super(message); - this.code = code; - this.status = status; - } - } - ``` - -#### (2) `exactOptionalPropertyTypes` 限制 -- **现象**:在对象字面量中如果显式将可选属性设为 `undefined` 会报错。 -- **避坑**:构造请求参数(如 RequestInit 的 `body`)时,避免传入 `body: undefined`。应采用动态属性扩展或先判断再赋值: - ```typescript - const options: RequestInit = { method: 'POST' }; - if (data !== undefined) { - options.body = JSON.stringify(data); - } - ``` - -#### (3) `baseUrl` 被废弃 -- **现象**:TypeScript 6.0+ 将逐渐废弃 `baseUrl`,编译时会报 TS5101 错误。 -- **避坑**:从 `tsconfig.app.json` 中直接移除 `"baseUrl"`,Vite + TS 依然能够通过相对位置正确解析 `"paths"`(别名映射)。 - -### 2. pnpm 包管理构建脚本受阻 - -- **现象**:在新版 pnpm (v10/v11) 中,若存在未显式授权的构建脚本(如 `esbuild`, `msw`),任何 pnpm 命令在运行依赖完整性检查时都会抛出 `[ERR_PNPM_IGNORED_BUILDS]` 并强制中断。 -- **解决方案**: - 1. **本地开发**:在子项目根目录下创建 `.npmrc` 并添加以下白名单配置: - ```ini - only-built-dependencies[]=esbuild - only-built-dependencies[]=msw - ``` - 2. **CI 或自动化编译**:如果依然受到全局白名单阻碍,可使用 `--ignore-scripts` 绕过依赖构建脚本检查: - ```bash - pnpm install --ignore-scripts - ``` - -### 3. MSW 2.0 模拟 SSE (Server-Sent Events) - -- **实现**:MSW 2.0 支持使用 `ReadableStream` 模拟 SSE 流式推送。在 Mock 处理器中可通过 `TextEncoder` 写入符合 EventSource 格式的缓冲行,并在 headers 中指定 `text/event-stream`。 - ```typescript - http.get('/api/ai/stream', () => { - const encoder = new TextEncoder(); - const stream = new ReadableStream({ - async start(controller) { - controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'chunk', content: '数据' })}\n\n`)); - controller.close(); - } - }); - return new HttpResponse(stream, { - headers: { 'Content-Type': 'text/event-stream' } - }); - }) - ``` - -### 4. Zustand 4 严格模式类型推断 -- **避坑**:在 Zustand Store 中,为了避免隐式 `any` 类型报错,建议统一使用柯里化方法传入泛型定义: - ```typescript - import create from 'zustand'; - export const useUIStore = create()((set) => ({ ... })); - ``` diff --git a/docs/memorys/2026-05-24-作品与工作区章节管理.md b/docs/memorys/2026-05-24-作品与工作区章节管理.md deleted file mode 100644 index d105ba2a..00000000 --- a/docs/memorys/2026-05-24-作品与工作区章节管理.md +++ /dev/null @@ -1,77 +0,0 @@ -# 任务留痕:作品与工作区章节管理实现 (2026-05-24) - -## 1. 任务背景与目标 -开发 `muse-studio` 前端创作台子项目中的 **Feature 2: 我的作品 + 工作区章节管理** 功能。要求在对齐 TypeScript 6.0 极严类型约束的基线规范下,实现作品创建、卡片列表、侧边栏大纲管理、行内新建章节、级联物理删除、自动保存字数局部静默更新以及防止切章时编辑器残留的创作环境。 - ---- - -## 2. 关键设计与最佳实践 - -### ① TS 6.0 可选属性编译规避 (`exactOptionalPropertyTypes`) -在开启 `exactOptionalPropertyTypes: true` 下,如果将含有显式 `undefined` 值的字段作为可选参数传给 DTO/API 接口,TS 编译器会报错(不允许传递 `{ genre: undefined }`,只允许直接省略该键)。 -* **解决方案**:编写全局通用过滤函数 `stripUndefined`,在表单提交前采用严格的 `value !== undefined` 判断剔除空属性,成功解决 Zod 表单 transform 可选字段带来的编译难题。 - ```typescript - export function stripUndefined>(obj: T): { - [K in keyof T]: Exclude; - } { - const result = {} as any; - for (const [key, value] of Object.entries(obj)) { - if (value !== undefined) { - result[key] = value; - } - } - return result; - } - ``` - -### ② TS 6.0 数组严格索引防空 (`noUncheckedIndexedAccess`) -在 `noUncheckedIndexedAccess: true` 模式下,直接读取数组 `list[0]` 推导的类型是 `T | undefined`,读取属性会引发编译失败。 -* **解决方案**:在大纲载入自动激活首章时,必须进行显式类型收窄保护: - ```typescript - const firstChapter = chapters[0]; - if (firstChapter && firstChapter.id) { - setActiveChapter(firstChapter.id); - } - ``` - -### ③ MSW 单元测试状态与并发隔离 -由于 MSW 的拦截器在 Node.js 中会以全局单例的形式加载,并发单元测试对 mockDB 内存数据的改动可能会造成竞态污染(Flaky Tests)。 -* **解决方案**: - 1. 在 `content.ts` 拦截器中定义深拷贝初始状态,并导出 `resetMockDb()`。 - 2. 在每个单元测试文件的 `beforeEach` 钩子中,强行调用 `resetMockDb()` 重置 Mock 库状态,保障隔离性。 - 3. 在 Vitest 运行配置中,禁止对涉及 mockDB 修改的 API hooks 开启 `concurrent` 并发测试,保持串行稳健运行。 - -### ④ 编辑器生命周期重载与切章残留防护 -在单页应用中,Tiptap/ProseMirror 编辑器拥有独立的 Document 状态。如果在左侧大纲切换章节时只替换 `content` 变量,可能会导致旧章节的正文内容残留在新的章节内,引发严重的保存冲突 BUG。 -* **解决方案**:在工作区装配编辑器时,为其强制绑定 `key={activeChapterId}`。利用 React 默认的 Key 渲染更替机制,在每次激活的章节 ID 变更时彻底销毁旧的编辑器实例并重装,彻底擦除上一章的 State 缓存。 - -### ⑤ 零网络重绘大纲字数静默回写 -高频打字触发的自动保存(每 2 秒一次)如果直接 `invalidateQueries` 大纲缓存 `['chapterList']`,会导致左侧侧栏每 2 秒就发起 HTTP 请求并全量重新闪烁重绘,产生严重卡顿。但若不刷新,侧栏字数统计又会产生长达 30 秒的滞后。 -* **解决方案**:在编辑器自动保存 API 同步成功的回调中,使用 `queryClient.setQueryData` 局部静默更新当前章节的 `wordCount`,做到侧边栏字数在 0 次网络请求开销下毫秒级同步。 - ```typescript - queryClient.setQueryData( - ['chapterList', workId], - (oldData: any[] | undefined) => { - if (!oldData) return []; - return oldData.map((ch) => - ch.id === id ? { ...ch, wordCount: newWordCount } : ch - ); - } - ); - ``` - -### ⑥ React.memo 与 useCallback 缓存失效规避 -将章节单项提取为 `ChapterItem` 并使用 `React.memo` 优化。但在父组件 `ChapterPanel` 传递给它的 `onSelect`、`onDelete` 回调中若使用匿名内联函数,每次父组件重绘时都会生成新的函数引用,导致 `React.memo` 完全失效。 -* **解决方案**:在父面板中,将所有回传的回调函数严格使用 `useCallback` 强包装,维护引用的一致性,阻断高频打字重渲染。 - ---- - -## 3. 任务交付件索引 -- 全局过滤器: [object.ts](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/lib/object.ts) -- API 交互 Hooks: [useWorks.ts](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/features/editor/hooks/useWorks.ts) -- 动态 Mock DB: [content.ts](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/api/mocks/handlers/content.ts) -- 作品卡片列表: [WorkListPage.tsx](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/features/editor/components/WorkListPage.tsx) -- 创建作品弹窗: [CreateWorkModal.tsx](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/features/editor/components/CreateWorkModal.tsx) -- 大纲侧栏面板: [ChapterPanel.tsx](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/features/editor/components/ChapterPanel.tsx) -- 创作台工作区: [WorkspacePage.tsx](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/pages/WorkspacePage.tsx) -- 单元测试套件: [useWorks.test.tsx](file:///Users/qingse/Sync/local-git/oh-my-muse/muse-studio/src/features/editor/hooks/useWorks.test.tsx) diff --git a/docs/memorys/2026-05-25-muse-admin管理端搭建.md b/docs/memorys/2026-05-25-muse-admin管理端搭建.md deleted file mode 100644 index 191a63f4..00000000 --- a/docs/memorys/2026-05-25-muse-admin管理端搭建.md +++ /dev/null @@ -1,263 +0,0 @@ -# muse-admin 管理端搭建留痕 - -## 任务背景 - -执行 `docs/superpowers/plans/2026-05-24-P3-muse-admin.md` 的 P3 管理端搭建任务,目标是在既有 Vben Admin fork 基础上补齐 Muse 管理后台。 - -本次任务只负责 `muse-admin/`,不修改 `muse-studio/` 和 `muse-cloud/`。如果实现与设计文档不一致,先校准设计文档和 API 契约,再继续实现。 - -## 已完成内容 - -### 1. 设计与契约校准 - -提交: - -- `9df67b2 docs(admin): 校准管理端契约边界` - -完成内容: - -- 将 P3 计划目标从 5 个功能域校准为 MetaSchema、系统治理、AI 配置、市场治理、全局知识管理、Account/New-API、任务监控、日志审计。 -- 明确 Muse 管理端页面目录使用 `muse-admin/apps/web-antd/src/views/muse/**`,不使用早期计划里的根级 `src/views/{governance,ai,...}`。 -- 明确 Account/New-API 只展示治理和对账所需的脱敏摘要、配额调整、网关绑定、余额查询和调用日志归属;用户侧完整账户体验仍归 `产品-02G`。 -- 补齐日志审计双 surface:接口调用日志和业务审计日志。 -- 更新 `docs/api-contracts/ai/openapi.yaml`,新增: - - `GET /admin-api/muse/audit/api-logs` - - `GET /admin-api/muse/audit/api-logs/{logId}` - - `GET /admin-api/muse/audit/business-events/{eventId}` -- 重新生成 `docs/api-contracts/generated/typescript/ai.ts`。 - -### 2. 管理端工程与 API 基座 - -提交: - -- `912ec7d feat(admin): 搭建 Muse 管理端治理页面` - -新增核心路径: - -- `muse-admin/apps/web-antd/src/api/muse/client.ts` -- `muse-admin/apps/web-antd/src/api/muse/**` -- `muse-admin/apps/web-antd/src/api/types/**` -- `muse-admin/apps/web-antd/src/router/routes/modules/muse.ts` -- `muse-admin/apps/web-antd/src/views/muse/**` - -实现约束: - -- 所有 Muse 管理端 API 统一通过 `museAdminApi` 调用。 -- 请求前缀固定为 `/admin-api/muse/**`。 -- 每个请求强制携带 `X-API-Version: 1`。 -- 复用 Vben `requestClient`,不另装 axios,不直接调用 fetch。 -- 响应按 Vben 现有 `code/data/msg` 解包逻辑处理,页面层拿到的是 `data`。 - -### 3. 功能域落地 - -已落地页面和能力: - -- MetaSchema:列表、详情、草稿保存、校验、影响预览、发布、激活、灰度、回滚、废弃。 -- 系统治理 / 功能编排:保护节点、功能链路、开放槽位、影响预览、激活命令。 -- AI 配置:Prompt 模板、系统智能体、Tool Grant、质量策略和评估任务摘要。 -- 全局知识库:全局库列表、详情摘要、访问策略、索引重建、来源传播和影响预览。 -- 市场治理:资产审核、下架、召回、申诉、影响预览、合规原因和处置审计。 -- Account/New-API:用户治理摘要、权益配额、配额调整、网关绑定、余额快照、调用日志归属。 -- 任务治理:任务列表、任务详情、失败重试、取消、来源事件重试和重验清单。 -- 日志审计:接口调用日志、业务审计事件、脱敏详情、append-only 边界提示。 - -## 关键工程实践与避坑 - -### 1. `muse-admin/` 已整仓纳管,但仍要排除构建产物 - -当前 `muse-admin` 目录已经作为本仓工作区的一部分被统一纳管,不再按“独立子仓库”理解;但它仍然需要继续排除 `dist/`、`dist.zip`、`node_modules/`、`test-results/` 这类构建和验证产物,避免误入提交。 - -因此本次按 P3 计划要求,用 `git add -f` 精确加入 P3 相关源码和测试文件: - -- `muse-admin/apps/web-antd/src/api/muse/**` -- `muse-admin/apps/web-antd/src/views/muse/**` -- `muse-admin/apps/web-antd/src/api/types/**` -- `muse-admin/apps/web-antd/src/router/routes/modules/muse.ts` - -初次 P3 提交没有提交 `dist/`、`dist.zip`、`node_modules/` 或其他 `muse-admin` 基线文件。 - -后续完善全量验证时,为保证 `web-antd` 类型检查和单元测试能在当前 checkout 内复现通过,精确纳入了少量 `muse-admin` 既有基线路径修复: - -- Vue ref 模板引用改为显式 `void xxxRef.value`,避免 `vue-tsc` 误判 unused expression。 -- Vben/Ant 表格、选择器和 BPM 配置组件补齐回调参数类型与安全转换。 -- `mp/freePublish` 类型改为复用 `MpDraftApi.NewsItemList`,避免富文本草稿结构被误收窄成字符串。 -- `echarts` 插件去除重复类型导出并显式导出 `ECOption`。 -- `core-user` store 在 `setUserInfo(null)` 时同步清空 `userRoles`,避免登出后旧角色残留。 - -二次完善 E2E 覆盖时发现部分 Muse 页面在真实 Vite 运行时不会自动解析本地未注册的 Ant Design Vue 组件。已在 P3 页面内显式导入实际使用的 Ant 组件,并同步补齐页面测试中的 Ant mock export: - -- `muse-admin/apps/web-antd/src/views/muse/governance/function-orch/index.vue` -- `muse-admin/apps/web-antd/src/views/muse/knowledge/index.vue` -- `muse-admin/apps/web-antd/src/views/muse/market/index.vue` -- `muse-admin/apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts` -- `muse-admin/apps/web-antd/src/views/muse/market/__tests__/index.test.ts` - -审查后继续收紧 E2E 时,发现 `museAdminApi` 原先把 `/admin-api/muse` 直接传给 Vben `requestClient`。但当前 `web-antd` 的 `VITE_GLOB_API_URL` 已经是 `/admin-api`,真实浏览器请求会变成 `/admin-api/admin-api/muse/**`。已修正为: - -- `museAdminApi` 内部只拼接 `/muse/**` 资源路径。 -- 由 Vben `requestClient` baseURL 统一组合成运行时 `/admin-api/muse/**`。 -- `apps/web-antd/src/api/muse/__tests__/client.spec.ts` 同步断言资源路径,避免再次双拼 `/admin-api`。 - -后续需要主代理确认长期 Git 边界:要么让 `muse-admin` 成为真正独立仓库,要么调整根 `.gitignore` 和纳管策略。 - -### 2. 不伪造后端契约 - -Account 角色/权限写接口、任务治理独立重验接口等后端契约仍未完全闭合。本次前端没有自造不存在的 API: - -- 任务重试前在页面保留 owner、来源、授权、幂等重验清单。 -- 独立重验接口缺失时只提示“当前契约无独立任务重验接口”,不提交后端命令。 -- Account 高危治理动作要求原因、复核引用和确认文本,但未伪造角色权限写入接口。 - -### 3. 管理端不能越过用户私有边界 - -页面中明确表达以下边界: - -- 管理员默认不查看或修改用户私有作品正文。 -- 市场下架、召回只影响公共市场对象和后续获取、安装、绑定或生成,不直接修改用户私有副本。 -- 全局知识库停用只改变后续可用性和来源状态,不回滚用户已确认事实。 -- 用户智能体保护节点不可由普通用户替换,管理员页面只治理系统智能体、系统能力和开放槽位规则。 - -## 验证记录 - -执行过的验证命令: - -```bash -npx openapi-typescript docs/api-contracts/ai/openapi.yaml --output docs/api-contracts/generated/typescript/ai.ts -pnpm --dir muse-admin exec vitest run apps/web-antd/src/api/muse/newapi/__tests__/index.spec.ts apps/web-antd/src/api/muse/audit/__tests__/index.spec.ts apps/web-antd/src/api/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/api/muse/governance/__tests__/governance-api.test.ts apps/web-antd/src/api/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/jobs/__tests__/index.spec.ts apps/web-antd/src/api/muse/account/__tests__/index.spec.ts apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/views/muse/ai/__tests__/index.test.ts apps/web-antd/src/views/muse/market/__tests__/index.test.ts --dom -pnpm --dir muse-admin exec oxlint apps/web-antd/src/api/muse apps/web-antd/src/views/muse apps/web-antd/src/router/routes/modules/muse.ts -pnpm --dir muse-admin --filter @vben/web-antd run typecheck -pnpm --dir muse-admin test:unit -pnpm --dir muse-admin --filter @vben/web-antd run build -git diff --check -- design-docs/产品-02B-管理员控制台功能规格.md docs/api-contracts/ai/openapi.yaml docs/api-contracts/generated/typescript/ai.ts docs/superpowers/plans/2026-05-24-P3-muse-admin.md -``` - -完善后新增验证命令: - -```bash -pnpm --dir muse-admin exec playwright install chromium -pnpm --dir muse-admin exec vitest run apps/web-antd/src/api/muse/newapi/__tests__/index.spec.ts apps/web-antd/src/api/muse/audit/__tests__/index.spec.ts apps/web-antd/src/api/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/api/muse/governance/__tests__/governance-api.test.ts apps/web-antd/src/api/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/jobs/__tests__/index.spec.ts apps/web-antd/src/api/muse/account/__tests__/index.spec.ts apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/views/muse/ai/__tests__/index.test.ts apps/web-antd/src/views/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/__tests__/client.spec.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/src/views/muse/__tests__/governance-pages.test.ts --dom -pnpm --dir muse-admin exec playwright test -c playwright.config.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin exec oxlint apps/web-antd/src/api/muse apps/web-antd/src/views/muse apps/web-antd/src/router/routes/modules/muse.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -``` - -结果: - -- OpenAPI TypeScript 生成通过。 -- P3 定向 Vitest 初次实现:10 个测试文件、19 个测试全部通过。 -- P3 完善后定向 Vitest:13 个测试文件、26 个测试全部通过。 -- P3 Playwright E2E 初版:1 个 Chromium 用例通过,覆盖账号、任务、审计三个治理页的真实路由访问。 -- P3 Playwright E2E 二次完善:2 个 Chromium 用例通过,覆盖 MetaSchema、功能编排、AI 配置、全局知识库、市场治理、Account/New-API、任务治理、日志审计全部主要页面的真实路由访问和边界文案。 -- P3 定向 oxlint:35 个文件,0 warning、0 error。 -- `web-antd` typecheck 通过。 -- 全量 `test:unit` 通过:48 个测试文件、336 个测试通过;仍有既有 `use-sortable.test.ts` nested `vi.mock` 警告。 -- `web-antd` build 通过。 -- `git diff --check` 通过。 - -二次完善提交前复核: - -```bash -pnpm --dir muse-admin --filter @vben/web-antd run typecheck -pnpm --dir muse-admin exec playwright test -c playwright.config.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin exec vitest run apps/web-antd/src/api/muse/newapi/__tests__/index.spec.ts apps/web-antd/src/api/muse/audit/__tests__/index.spec.ts apps/web-antd/src/api/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/api/muse/governance/__tests__/governance-api.test.ts apps/web-antd/src/api/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/jobs/__tests__/index.spec.ts apps/web-antd/src/api/muse/account/__tests__/index.spec.ts apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/views/muse/ai/__tests__/index.test.ts apps/web-antd/src/views/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/__tests__/client.spec.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/src/views/muse/__tests__/governance-pages.test.ts --dom -pnpm --dir muse-admin exec oxlint apps/web-antd/src/api/muse apps/web-antd/src/views/muse apps/web-antd/src/router/routes/modules/muse.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin test:unit -pnpm --dir muse-admin --filter @vben/web-antd run build -``` - -结果: - -- `web-antd` typecheck 退出码 0。 -- P3 Playwright E2E:2 个 Chromium 用例通过;Vite dev server 仍输出 `ResizeObserver loop completed with undelivered notifications` 开发噪声,但 Playwright 退出码为 0。 -- P3 定向 Vitest:13 个测试文件、26 个测试通过。 -- P3 定向 oxlint:35 个文件,0 warning、0 error。 -- 全量 `test:unit`:48 个测试文件、336 个测试通过;仍有既有 `use-sortable.test.ts` nested `vi.mock` 警告。 -- `web-antd` build 退出码 0,并已清理生成的 `dist/` 和 `dist.zip`,不纳入提交。 - -审查修复后再次复核: - -```bash -pnpm --dir muse-admin exec playwright test -c playwright.config.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin --filter @vben/web-antd run typecheck -pnpm --dir muse-admin exec vitest run apps/web-antd/src/api/muse/newapi/__tests__/index.spec.ts apps/web-antd/src/api/muse/audit/__tests__/index.spec.ts apps/web-antd/src/api/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/api/muse/governance/__tests__/governance-api.test.ts apps/web-antd/src/api/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/jobs/__tests__/index.spec.ts apps/web-antd/src/api/muse/account/__tests__/index.spec.ts apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/views/muse/ai/__tests__/index.test.ts apps/web-antd/src/views/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/__tests__/client.spec.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/src/views/muse/__tests__/governance-pages.test.ts --dom -pnpm --dir muse-admin exec oxlint apps/web-antd/src/api/muse apps/web-antd/src/views/muse apps/web-antd/src/router/routes/modules/muse.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin test:unit -pnpm --dir muse-admin --filter @vben/web-antd run build -``` - -结果: - -- P3 Playwright E2E:2 个 Chromium 用例通过;E2E 已改为未显式 mock 的 `/admin-api/muse/**` 请求返回 500 并在用例末尾断言无遗漏,同时断言每个主要页面的真实 mock 数据。 -- 审查后删除了 E2E 中会绕开统一 mock 的重复 specific routes,所有 Muse 管理端请求都由同一个 `/admin-api/muse/**` route 处理;Account、Purchase、Jobs、Source Event mock 字段已改为契约字段,并补充关键列断言,避免字段漂移仍然绿。 -- `web-antd` typecheck 退出码 0。 -- P3 定向 Vitest:13 个测试文件、26 个测试通过。 -- P3 定向 oxlint:35 个文件,0 warning、0 error。 -- 全量 `test:unit`:48 个测试文件、336 个测试通过;仍有既有 `use-sortable.test.ts` nested `vi.mock` 警告。 -- `web-antd` build 退出码 0,并已清理生成的 `test-results/`、`dist/` 和 `dist.zip`,不纳入提交。 - -子代理评审修复后最终复核: - -```bash -pnpm --dir muse-admin --filter @vben/web-antd run typecheck -pnpm --dir muse-admin test:unit -pnpm --dir muse-admin --filter @vben/web-antd run build -pnpm --dir muse-admin exec vitest run --dom apps/web-antd/src/api/muse/__tests__/client.spec.ts apps/web-antd/src/api/muse/account/__tests__/index.spec.ts apps/web-antd/src/api/muse/ai/__tests__/jobs.spec.ts apps/web-antd/src/api/muse/audit/__tests__/index.spec.ts apps/web-antd/src/api/muse/governance/__tests__/governance-api.test.ts apps/web-antd/src/api/muse/jobs/__tests__/index.spec.ts apps/web-antd/src/api/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/api/muse/market/__tests__/index.test.ts apps/web-antd/src/api/muse/newapi/__tests__/index.spec.ts apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/src/views/muse/__tests__/governance-pages.test.ts apps/web-antd/src/views/muse/ai/__tests__/index.test.ts apps/web-antd/src/views/muse/governance/meta-schema/__tests__/detail.test.ts apps/web-antd/src/views/muse/knowledge/__tests__/index.test.ts apps/web-antd/src/views/muse/market/__tests__/index.test.ts -pnpm --dir muse-admin exec playwright test -c playwright.config.ts apps/web-antd/e2e/muse-admin-governance.spec.ts -pnpm --dir muse-admin exec oxlint apps/web-antd/e2e/muse-admin-governance.spec.ts apps/web-antd/src/api/muse apps/web-antd/src/views/muse apps/web-antd/src/router/routes/__tests__/muse.test.ts apps/web-antd/src/router/routes/modules/muse.ts -git diff --check -- muse-admin docs/api-contracts docs/memorys .gitignore -``` - -结果: - -- 子代理 review 发现的 Critical 已修复:MetaSchema save/validate/preview 旧响应不能回写新 schema 或新 draft;FunctionChain 预览票据绑定 `chainKey + targetVersion`;Market 预览票据绑定 asset/appeal/action/scope/generation。 -- Market 不再把 `stop_new_acquire` 影响预览票据复用到 `full_delist` 提交;`review_rejection` 已同步到 OpenAPI 和前端 action mapping。 -- E2E 增强为 9 个 Chromium 用例通过,覆盖 retry 只发 `commandId`、cancel/source retry 保留 `reason`、跨资产 preview 不可复用、同资产切换治理范围/动作/申诉后必须重新生成 preview、错误动作重新预览不能提交召回、核心页面真实路由和运行时 console/pageerror 门禁。 -- P3 定向 Vitest:7 个测试文件、14 个测试通过;market API 已覆盖 `assetId/requestId/appealId` 路径参数编码。 -- 全量 `test:unit`:50 个测试文件、345 个测试通过;仍有既有 `use-sortable.test.ts` nested `vi.mock` 警告。 -- `web-antd` typecheck 退出码 0。 -- `web-antd` build 退出码 0,并已清理生成的 `test-results/`、`apps/web-antd/dist/` 和 `apps/web-antd/dist.zip`,不纳入提交。 -- P3 定向 oxlint:37 个文件,0 warning、0 error。 -- `git diff --check` 通过。 - -## 提交与 Hook 说明 - -本次 P3 相关提交: - -```text -9df67b2 docs(admin): 校准管理端契约边界 -912ec7d feat(admin): 搭建 Muse 管理端治理页面 -``` - -完善阶段在前两次提交之后继续补齐: - -- API client header/prefix 测试,验证只走 `/admin-api/muse/**` 且带 `X-API-Version: 1`。 -- Muse 路由注册测试。注意测试文件不能放在 `src/router/routes/modules/**`,该目录会被 Vite 动态路由 glob 当作业务路由加载。 -- Account/New-API、任务治理、审计页面的最小集成测试。 -- Playwright E2E,使用本地 mock 覆盖 Vben 登录态、权限菜单、租户、字典、通知和 P3 API。 -- 二次扩展 Playwright E2E,将 MetaSchema、功能编排、AI 配置、全局知识库、市场治理也纳入真实路由访问,并检查管理员边界文案、影响预览、原因和审计提示。 -- 修复 E2E 暴露的 Ant Design Vue 组件运行时解析问题,Muse 页面改为显式导入实际使用的 Ant 组件。 -- 审查修复后,E2E 对未处理的 Muse 管理端 API 不再返回空分页兜底,避免错误 URL 或漏 mock 仍然绿;同时修正 New-API 真实运行时双 `/admin-api` 前缀问题。 -- 再次审查后,删除 Account/Jobs/Audit 的重复 route 注册,避免 specific route 抢先匹配导致 unified mock 失效;同步修正 E2E mock 字段契约漂移。 -- 子代理评审后继续修复治理命令串票风险:MetaSchema 草稿票据加 save/validate/preview 请求序号;FunctionChain 和 Market 影响预览返回时比对发起时上下文;Market OpenAPI 关键治理 ID/引用统一为 string;E2E 开启 console/pageerror/未 mock API 失败门禁。 - -提交前发现暂存区已有其他会话 staged 的 `P1-execution-prompt.md` 和 `P3-execution-prompt.md`,已仅执行 `git restore --staged`,没有改动文件内容。 - -整理提交阶段补充: - -- `muse-admin/apps/web-antd/dist/`、`dist.zip`、`test-results/` 属于验证产物,提交前必须清理。 -- 暂存区如果混入 `docs/memorys/2026-05-25-muse-cloud后端P1收口.md`,只执行 `git restore --staged` 移出 P3 提交,不删除并行 P1 会话文件。 -- 根目录 `.DS_Store` 曾被仓库跟踪;本轮改为从 Git 索引移除,并保留本地文件由 `.gitignore` 忽略,避免继续进入提交。 -- 根 `.gitignore` 保留 `muse-admin/` 独立子仓库边界,同时补充 `test-results/`、`playwright-report/` 和 `precommit-scan-*.md`,避免验证报告和安全扫描报告误入主提交。 - -提交时按当前项目约定使用: - -```bash -touch /tmp/.opsera-pre-commit-scan-passed -``` - -跳过 Opsera pre-commit 安全扫描。原因是项目现有执行 prompt 记录安全扫描工具缺失时采用该跳过方式。 - -## 后续建议 - -1. 主代理确认 `muse-admin/` 的长期 Git 纳管方式,避免后续继续依赖 `git add -f`。 -2. 后端补齐 Account 角色/权限写接口、任务独立重验接口后,管理端可接入真实命令入口。 -3. P3 已补齐覆盖全部主要页面的最小 E2E;后续可继续扩展 Playwright 覆盖 MetaSchema 发布、市场审核/下架、任务重试和审计详情查看等具体命令流。 diff --git a/docs/memorys/2026-05-25-muse-cloud后端P1收口.md b/docs/memorys/2026-05-25-muse-cloud后端P1收口.md deleted file mode 100644 index d1283f54..00000000 --- a/docs/memorys/2026-05-25-muse-cloud后端P1收口.md +++ /dev/null @@ -1,252 +0,0 @@ -# muse-cloud 后端 P1 收口留痕 - -## 任务背景 - -执行 `docs/superpowers/plans/2026-05-24-P1-muse-cloud.md` 的 P1 后端搭建任务,目标是在 `muse-cloud/` 这个 Yudao Cloud fork 基础上搭建 Muse 后端业务模块。 - -本次任务只负责 `muse-cloud/`,不修改 `muse-studio/`、`muse-admin/` 和 `docs/api-contracts/**`。P2、P3 有并行会话执行,提交时必须严格避免带入其他会话文件。 - -## 已完成内容 - -### 1. 基础环境与远端基础设施 - -已确认当前开发环境使用: - -- Java 21 -- Spring Boot 3 / Yudao Cloud fork -- PostgreSQL 连接来自 `~/.config/muse-repo/infra.env` -- Redis 连接同样来自 `~/.config/muse-repo/infra.env` - -用户确认当前远端 PostgreSQL 15 可接受,不强制补 PostgreSQL 16 实例。远端 Redis 已验证 TCP 端口连通。 - -注意:`~/.config/muse-repo/infra.env` 内有真实凭据,不要输出密码。 - -### 2. DDL / Flyway SQL - -已在 `muse-cloud/sql/muse/` 下形成 V1-V8: - -- `V1__init_content_schema.sql` -- `V2__init_account_schema.sql` -- `V3__init_meta_schema.sql` -- `V4__init_ai_schema.sql` -- `V5__init_knowledge_schema.sql` -- `V6__init_market_schema.sql` -- `V7__add_contract_operation_audit.sql` -- `V8__add_contract_workflow_and_market_interaction.sql` - -关键新增: - -- `muse_domain_operation_record`:统一操作审计与 `commandId` 幂等记录。 -- `muse_domain_workflow_task`:承载预检、导出、重试、发布准备等异步工作流事实。 -- `muse_market_favorite`:市场收藏状态。 -- `muse_market_purchase`:市场购买事实。 - -DDL 口径: - -- 使用 PostgreSQL 语法。 -- 主键使用 `BIGINT GENERATED ALWAYS AS IDENTITY`。 -- 布尔字段使用 `BOOLEAN`。 -- 大文本使用 `TEXT`。 -- 不使用 MySQL `AUTO_INCREMENT`、`BIT`、`MEDIUMTEXT`。 - -### 3. 合同入口持久化 - -新增: - -- `muse-cloud/muse-framework/muse-spring-boot-starter-mybatis/src/main/java/cn/iocoder/muse/framework/mybatis/core/muse/MuseContractPersistenceService.java` - -用途: - -- 统一处理 Meta / Knowledge / Market / AI / Account 合同入口。 -- 先执行 OpenAPI 合同路由和基础字段校验。 -- 写命令先插入 `muse_domain_operation_record` 占用 `commandId`。 -- 再写入领域事实表或统一 workflow task。 -- 最后回写操作记录的 `resource_id` 和响应快照。 -- 重复 `commandId` 返回历史响应,避免重复扣费、重复发布、重复安装。 - -为了让 framework 包下服务能被 `muse-server` 扫描到,已在: - -- `MuseMybatisAutoConfiguration` - -中通过 `@Import(MuseContractPersistenceService.class)` 注册。 - -### 4. 模块接入 - -以下合同 Controller 已从纯 `MuseApiContractSupport.handle(...)` 占位响应,改为注入 `MuseContractPersistenceService`: - -- Meta admin -- Knowledge admin / app -- Market admin / app -- Account admin / app -- AI admin / app - -并为相关模块补齐 `muse-spring-boot-starter-mybatis` 依赖。 - -Content 的核心作品、章节、Block 已有专用业务 Controller / Service,因此没有强行纳入统一 shim。Content 扩展类 API 仍保留 catch-all 合同入口,后续应继续拆专用业务服务。 - -### 5. 写命令覆盖口径 - -通过 `MuseApiContractSupport` 中的 operation 注册表核对: - -- 全部合同 operation:233 -- 全部写命令:118 -- Account 写命令:9/9 已接入持久化 -- AI 写命令:19/19 已接入持久化 -- Knowledge 写命令:36/36 已接入持久化 -- Market 写命令:19/19 已接入持久化 -- Meta 写命令:8/8 已接入持久化 - -Content 写命令不按统一 shim 统计,因为核心 Content 已有专用实现;剩余扩展 Content API 仍需后续继续落专用 Application Service。 - -## 关键工程实践与避坑 - -### 1. 不再返回伪成功 - -早期 Meta / Knowledge / Market / AI / Account 合同入口主要返回占位响应,容易让前端误以为写命令已生效。 - -当前修正为: - -- 已支持的写命令必须落业务表或 workflow task。 -- 未拥有的领域写命令抛 `CONTRACT_NOT_IMPLEMENTED`。 -- Content 写命令由 Content 专用服务负责,不在 `MuseContractPersistenceService` 里伪造成功。 - -### 2. 幂等记录必须先于业务写入 - -并发场景下,如果先写业务表再写幂等记录,两个相同 `commandId` 请求可能同时撞业务唯一键。 - -当前顺序固定为: - -1. 查询是否已有 `commandId` 响应。 -2. 插入 `muse_domain_operation_record` 占用 `commandId`。 -3. 执行业务事实落库。 -4. 回写操作记录响应。 - -### 3. 领域 owner 不能被 admin/app 入口拆散 - -后端按领域模块拆,不按 admin/app 拆。`admin-api` 和 `app-api` 只是入口不同,领域事实只有一个 owner。 - -本次实现中: - -- App 写命令会对作品、知识库、文档、安装记录等做有限 owner guard。 -- Meta / Market 的 `expectedVersion`、`expectedActiveVersion`、`expectedStatus` 会在后端校验。 -- `commandId`、revision / expectedVersion、审计字段不只依赖前端。 - -### 4. 统一 workflow task 是 P1 过渡层 - -预检、导出、重试、发布准备等命令先统一落 `muse_domain_workflow_task`,它解决的是 P1 阶段“必须有后端事实、幂等和审计”的问题。 - -长期方向仍应按领域继续拆: - -- Account quota request / export task -- AI evaluation run / source event retry -- Knowledge publish readiness / export / reindex -- Market bind precheck - -## 验证记录 - -### 1. 目标测试 - -```bash -cd muse-cloud -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ -PATH=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home/bin:$PATH \ -mvn -o test \ - -pl muse-framework/muse-spring-boot-starter-mybatis,muse-server \ - -am \ - -Dtest=MuseContractPersistenceServiceTest,MuseApiContractSupportTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果: - -- `BUILD SUCCESS` -- `MuseContractPersistenceServiceTest` 8 个测试通过 -- `MuseApiContractSupportTest` 4 个测试通过 - -### 2. 全量 Maven 测试 - -```bash -cd muse-cloud -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ -PATH=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home/bin:$PATH \ -mvn test -``` - -最终结果: - -- `BUILD SUCCESS` -- 总耗时约 `01:14 min` - -### 3. PostgreSQL DDL 验收 - -使用远端 PG,通过临时 schema 执行 `V1..V8`,最后 `ROLLBACK`: - -```sql -SELECT current_setting('server_version') AS pg_version, - count(*) AS muse_tables, - count(*) AS muse_triggers; -``` - -结果: - -- PostgreSQL `15.17 (Debian 15.17-1.pgdg13+1)` -- Muse 表数量:`46` -- Muse trigger 数量:`46` -- 已回滚临时 schema,不污染数据库。 - -### 4. Redis 连通性 - -```bash -nc -vz "$MUSE_REDIS_HOST" "$MUSE_REDIS_PORT" -``` - -结果: - -- `Connection to 100.64.0.8 port 6379 succeeded` - -本机缺少 `redis-cli`,因此未执行 AUTH/PING。 - -## 提交记录 - -本次 P1 相关提交: - -- `6410604 feat(muse-cloud): 持久化 Muse 合同入口` - -提交只包含 `muse-cloud/` 下 P1 相关文件。 - -提交后工作树中仍有其他会话文件,不属于 P1: - -- `.DS_Store` -- `muse-admin/**` -- `docs/memorys/2026-05-25-muse-admin管理端搭建.md` -- `docs/superpowers/plans/P1-execution-prompt.md` -- `docs/superpowers/plans/P3-execution-prompt.md` -- `precommit-scan-oh-my-muse-20260524.md` - -后续提交时需要继续避开这些文件,除非对应 owner 明确要求。 - -## 未完成与后续建议 - -### 1. DTO 仍需继续精细化 - -当前读接口很多仍返回通用 JSON 行、操作记录或 workflow task,不等于完全符合 OpenAPI DTO 的最终形态。 - -后续应按领域补齐 Application Query Service 和 response assembler。 - -### 2. Content 扩展 API 仍需专用实现 - -Content 核心作品、章节、Block 已有业务实现;但规划、导入、解析、导出、MetaProjection、SuggestionMerge 等扩展 API 仍应继续拆专用服务。 - -### 3. Workflow task 需要异步执行器 - -`muse_domain_workflow_task` 当前只保证命令事实、幂等和审计。后续需要补: - -- 任务调度 -- 状态流转 -- 失败重试 -- 结果 payload -- 领域事件或 outbox - -### 4. PG16 最终环境仍未实际验收 - -用户已确认可使用 PG15 推进。若后续要严格对齐计划中的 PostgreSQL 16,需要重新在 PG16 实例上跑一次 `V1..V8` 和应用启动验收。 diff --git a/docs/memorys/2026-05-25-muse-studio智能体市场个人中心.md b/docs/memorys/2026-05-25-muse-studio智能体市场个人中心.md deleted file mode 100644 index 5fef2ec1..00000000 --- a/docs/memorys/2026-05-25-muse-studio智能体市场个人中心.md +++ /dev/null @@ -1,192 +0,0 @@ -# muse-studio 智能体、市场、个人中心实现留痕 - -## 任务背景 - -继续执行 `docs/superpowers/plans/2026-05-24-P2-muse-studio.md` 的 P2 用户端搭建任务。接手时,`muse-studio` 已经完成脚手架、基础设施、写作台、作品工作区和知识库工作台;本次重点补齐剩余三个用户端功能域: - -- 智能体工作台 -- 创作市场 -- 个人中心 - -同时修复当前代码基线的 TypeScript / ESLint 阻断,补齐对应 Hook 测试和页面冒烟验证。 - -## 已完成内容 - -### 1. 智能体工作台 - -新增智能体功能域: - -- `src/features/agent/hooks/useAgents.ts` -- `src/features/agent/components/AgentList.tsx` -- `src/features/agent/components/AgentCreateForm.tsx` -- `src/features/agent/components/SlotBindingPanel.tsx` -- `src/features/agent/hooks/useAgents.test.tsx` -- `src/pages/AgentPage.tsx` - -实现能力: - -- 查询系统默认与用户自建智能体列表。 -- 创建用户自建配置型智能体。 -- 在沙盒上下文中试用智能体,结果只进入预览,不写作品事实。 -- 查询作品开放智能体槽位。 -- 执行槽位预检,再消费预检凭证绑定槽位,避免直接写槽位事实。 -- 保护节点只读展示,不允许前端发起替换。 - -对应 Mock API 在 `src/api/mocks/handlers/ai.ts` 中补齐: - -- `GET /app-api/muse/agents` -- `POST /app-api/muse/agents` -- `POST /app-api/muse/agents/:agentId/test` -- `GET /app-api/muse/works/:workId/agent-slots` -- `POST /app-api/muse/works/:workId/agent-slots/:slotKey/prechecks` -- `POST /app-api/muse/works/:workId/agent-slots/:slotKey/bind` - -### 2. 创作市场 - -新增市场功能域: - -- `src/features/market/hooks/useMarket.ts` -- `src/features/market/components/MarketBrowse.tsx` -- `src/features/market/hooks/useMarket.test.tsx` -- `src/pages/MarketPage.tsx` - -实现能力: - -- 按作品、智能体、知识库三类资产筛选市场列表。 -- 支持关键词搜索。 -- 展示分类摘要。 -- 获取市场资产授权。 -- 安装已授权的智能体或知识库到账户可用资产列表。 -- 明确区分“授权获取”和“安装”,不暗示自动绑定作品或写入作品事实。 - -对应 Mock API 在 `src/api/mocks/handlers/market.ts` 中补齐: - -- `GET /app-api/muse/marketplace/assets` -- `GET /app-api/muse/marketplace/categories` -- `GET /app-api/muse/marketplace/assets/:assetId` -- `POST /app-api/muse/marketplace/assets/:assetId/purchase` -- `POST /app-api/muse/marketplace/assets/:assetId/install` - -### 3. 个人中心 - -新增个人中心功能域: - -- `src/features/account/hooks/useAccount.ts` -- `src/features/account/components/PersonalCenter.tsx` -- `src/features/account/components/UsageStats.tsx` -- `src/features/account/hooks/useAccount.test.tsx` -- `src/pages/AccountPage.tsx` - -实现能力: - -- 展示账户资料、账号状态、邮箱和手机号验证状态。 -- 支持保存昵称和公开署名。 -- 展示账户级 Token 用量、待归属数量、计量异常数量。 -- 展示权益、配额和资源使用进度。 -- 展示用量归属分布。 - -对应 Mock API 在 `src/api/mocks/handlers/account.ts` 中补齐: - -- `GET /app-api/muse/profile` -- `PATCH /app-api/muse/profile` -- `GET /app-api/muse/account/entitlements` -- `GET /app-api/muse/account/usage` - -## 关键工程实践与避坑 - -### 1. 以当前 OpenAPI 为准,不沿用旧计划里的简化路径 - -P2 计划中的部分路径仍是早期简化版,例如: - -- `/agents/:agentId/slots` -- `/market/assets` -- `/account/profile` - -当前真实 OpenAPI 已经演进为: - -- `/app-api/muse/agents` -- `/app-api/muse/works/{workId}/agent-slots` -- `/app-api/muse/marketplace/assets` -- `/app-api/muse/profile` -- `/app-api/muse/account/usage` -- `/app-api/muse/account/entitlements` - -后续继续 P2 或 P3 时,应优先以 `docs/api-contracts/generated/typescript/*`、`muse-studio/src/types/*` 和当前产品规格为准,不直接照抄旧 plan 路径。 - -### 2. `exactOptionalPropertyTypes` 下不能显式传 `undefined` - -新增测试和 Mock 时多次遇到该问题: - -- 可选字段如果值可能是 `undefined`,不要写进对象字面量。 -- 需要先构造基础对象,再按条件追加字段。 -- 测试中如果字段必填,应先用 `if (!slot) throw new Error(...)` 做显式收窄,再传 `slot.revision`。 - -典型写法: - -```typescript -const dto: AgentCreateDTO = { name: name.trim() }; -if (trimmedDescription) { - dto.description = trimmedDescription; -} -``` - -### 3. React 新 lint 规则更严格 - -之前 `MuseEditor` 中使用 `useCallback(debounce(...))` 会触发 React Hooks 静态规则。已调整为 `useMemo(() => debounce(...), deps)`。 - -组件文件中导出非组件函数也会触发 Fast Refresh 规则,因此将知识库类型守卫移到: - -- `src/features/knowledge/utils/typeGuards.ts` - -### 4. 市场和智能体必须保持 owner 边界 - -市场获取授权不等于安装,安装不等于作品绑定。智能体槽位绑定必须先预检,再绑定: - -1. 调用 `prechecks` 获取短期 `agentSlotPrecheckId`。 -2. 调用 `bind` 消费该凭证。 -3. Mock 中会校验槽位 revision,避免直接跳过预检写入槽位。 - -这符合产品规格中“市场不拥有最终业务事实,目标 owner 空间最终确认”的边界。 - -## 验证记录 - -本次收口执行过以下验证: - -```bash -pnpm exec tsc -b --pretty false -pnpm exec vitest run -pnpm lint -pnpm build -``` - -结果: - -- TypeScript 编译通过。 -- Vitest:7 个测试文件、23 个测试全部通过。 -- ESLint:退出码 0;仅 `public/mockServiceWorker.js` 生成文件存在 unused eslint-disable warning。 -- Vite build 通过;存在 chunk > 500KB 提示,后续可做路由级 code splitting。 - -还启动过开发服务器: - -```bash -pnpm dev --host 127.0.0.1 -``` - -并使用 Playwright + 本机 Chrome 冒烟访问: - -- `http://127.0.0.1:5173/agents` -- `http://127.0.0.1:5173/market` -- `http://127.0.0.1:5173/account` - -截图检查结论: - -- 页面非空。 -- 主要标题、列表、卡片、用量和权益面板可见。 -- 未发现明显文字重叠、空白页或加载失败状态。 - -## 后续建议 - -1. 如果继续 P2,可补 Playwright E2E 用例,覆盖作品创建、写作台 AI 生成、智能体试用、市场授权安装、个人资料保存。 -2. 当前构建包提示超过 500KB,后续可以按路由拆分 `AgentPage`、`MarketPage`、`AccountPage` 等功能域。 -3. `public/mockServiceWorker.js` 的 lint warning 来自生成文件,可通过 ESLint ignore 或生成文件豁免处理,不建议手改生成文件。 -4. P2 plan 清单仍未同步真实完成状态,后续可以单独更新计划文档,明确 Feature 3 实际已演进为“知识库列表 + 资料管理”形态,而不是旧版“实体/草稿/图谱”。 diff --git a/docs/memorys/2026-05-26-P1R0基线门禁收口.md b/docs/memorys/2026-05-26-P1R0基线门禁收口.md deleted file mode 100644 index f4698023..00000000 --- a/docs/memorys/2026-05-26-P1R0基线门禁收口.md +++ /dev/null @@ -1,235 +0,0 @@ -# P1R0 基线门禁收口留痕 - -## 任务背景 - -本次执行 `docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md`,目标是为 P1R 真实 API 改造建立可重复运行的基线门禁。 - -P1R-0 不负责实现真实业务 API,不修改生产 Controller、Service、Mapper 或 DDL。它只回答: - -- OpenAPI 合同里到底有多少 operation。 -- 当前 `muse-cloud/` 后端对每个 operation 是真实专用入口、catch-all 合同入口、通用持久化入口、SSE 占位入口,还是缺入口。 -- 哪些 operation 绝不能在 P1R-0 阶段被误判为完成。 -- 后续 P1R-1 到 P1R-7 应该基于哪份机器可读矩阵继续推进。 - -## 计划原始目标 - -计划要求新增一套 API 覆盖矩阵门禁: - -- 新增 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- 解析 `docs/api-contracts/*/openapi.yaml`。 -- 扫描 `muse-cloud/` Java 源码。 -- 生成: - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` -- 新增 Maven 测试: - - `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- 保证 P1R-0 不把 catch-all、generic persistence、SSE placeholder、missing 这类非真实实现标记为完成。 - -## 实际完成内容 - -P1R-0 在独立 worktree 中完成: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/feature-p1r-0-baseline-gate -``` - -分支: - -```text -feature/p1r-0-baseline-gate -``` - -关键提交: - -```text -97f1f3a chore(p1r): 生成 API 覆盖基线矩阵 -7d35ce7 fix(p1r): 修正 API 覆盖矩阵识别规则 -4084fce fix(p1r): 收紧 API 覆盖矩阵路由匹配 -0d16c21 fix(p1r): 修正 API 覆盖矩阵必填与入口归属 -9a32e3d fix(p1r): 强化覆盖矩阵完成状态校验 -83d7df1 test(p1r): 增加 API 覆盖矩阵门禁 -19e673c fix(p1r): 对齐基线门禁验证规则 -``` - -实际产物: - -- 审计脚本:`muse-cloud/scripts/p1r-audit-api-coverage.py` -- JSON 报告:`docs/superpowers/reports/p1r-api-coverage.json` -- Markdown 报告:`docs/superpowers/reports/p1r-api-coverage.md` -- Maven 门禁:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- 规格与计划命令同步更新: - - `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` - - `docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md` - -## 关键结果 - -最终报告统计: - -```text -totalOperations=233 -completedOperations=0 -catchAllOperations=36 -genericPersistenceOperations=180 -ssePlaceholderOperations=2 -missingOperations=0 -``` - -`completedOperations=0` 是预期结果。P1R-0 是基线审计,不跑领域真实验收,所以不能宣称任何 operation 已完成。 - -非真实实现状态规则已经统一到脚本、Maven 测试、规格和计划: - -- `catch_all` -- `generic_persistence` -- `sse_placeholder` -- `missing` - -这些 implementationStatus 对应的 completionStatus 只能是: - -- `incomplete` -- `blocked` - -不能是 `completed`,也不能是 `needs_verification`。 - -## Maven 行为结论 - -计划原始命令是: - -```bash -cd muse-cloud -mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -``` - -实测该命令会失败在上游模块,例如 `muse-common`: - -```text -No tests matching pattern "P1rApiCoverageReportTest" were executed! -``` - -根因: - -- `-pl muse-server -am` 会把 `muse-server` 的上游 reactor 模块一起拉入构建。 -- `-Dtest=P1rApiCoverageReportTest` 是全局 surefire 参数。 -- 上游模块没有这个测试类,默认会因为“指定测试不存在”失败。 - -正确行为不是修改父 POM。父 POM 全局关闭 `failIfNoSpecifiedTests` 会改变全仓测试语义,容易掩盖测试名拼写错误。 - -正确的局部命令是: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -含义: - -- 运行环境必须是 Java 21。 -- 上游 reactor 模块没有 `P1rApiCoverageReportTest` 不应失败。 -- 真正需要执行目标门禁测试的是 `muse-server`。 - -## 验证记录 - -执行过的验证: - -```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 -``` - -报告一致性验证: - -```bash -python3 - <<'PY' -import json -from pathlib import Path -report = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -summary = report["summary"] -count = len(report["operations"]) -assert summary["totalOperations"] == count, (summary["totalOperations"], count) -assert summary["completedOperations"] == 0, summary["completedOperations"] -for op in report["operations"]: - assert op["completionStatus"] != "completed", op["operationId"] - if op["implementationStatus"] in {"catch_all", "generic_persistence", "sse_placeholder", "missing"}: - assert op["completionStatus"] in {"incomplete", "blocked"}, op["operationId"] -print("P1R-0 report consistency OK:", count, "operations") -PY -``` - -结果: - -```text -P1R-0 report consistency OK: 233 operations -``` - -脚本负例验证: - -- 将内存中的 non-real operation 临时改成 `needs_verification`。 -- 调用 `validate_report(report)`。 -- 结果按预期失败,说明脚本会拒绝 non-real `needs_verification`。 - -输出: - -```text -P1R-0 negative validate_report OK: adminCreateCallAttributionJob -``` - -Maven 门禁: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果: - -```text -Running cn.iocoder.muse.server.framework.api.P1rApiCoverageReportTest -Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -## 当前集成状态 - -截至本留痕创建时: - -- P1R-0 已在 `feature/p1r-0-baseline-gate` 分支完成并提交。 -- 用户曾要求合并到 `main`,但随后中断该回合并改为“进行总结留痕”。 -- 因此当前记录只确认 P1R-0 分支完成,不确认已经合并到 `main`。 -- 主工作区 `/Users/qingse/Sync/local-git/oh-my-muse` 当时仍在 `feature/p1-muse-cloud-backend-20260524`,且有其他未提交或未跟踪内容,不能当作干净 main 合并面。 - -后续如果要继续合并,需要重新执行: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/feature-p1r-0-baseline-gate -git status --short -git branch --show-current -``` - -确认干净后,再按实际 base 分支执行 merge / PR 流程。 - -## 下个阶段 - -下个阶段是: - -```text -P1R-1 Content Real API spec + plan -``` - -P1R-1 不应该继续做基线扫描,而应该基于 P1R-0 报告开始真实业务 API 改造设计。 - -建议步骤: - -1. 从 `docs/superpowers/reports/p1r-api-coverage.json` 筛出 Content 域 operation。 -2. 分清哪些 Content operation 仍是 `catch_all` 或 `generic_persistence`。 -3. 写 P1R-1 Content Real API 规格,明确真实 Controller、Service、DTO、持久化、幂等、权限和审计闭环。 -4. 写 P1R-1 执行计划。 -5. 再进入实现,不要直接从 catch-all 适配拼接到另一个临时 spec。 - -P1R-1 的核心目标是把 Content 域从“合同可响应”推进到“真实业务 API 可验收”。 diff --git a/docs/memorys/2026-05-27-P1R1内容真实API收口.md b/docs/memorys/2026-05-27-P1R1内容真实API收口.md deleted file mode 100644 index 6820d485..00000000 --- a/docs/memorys/2026-05-27-P1R1内容真实API收口.md +++ /dev/null @@ -1,145 +0,0 @@ -# P1R1 内容真实 API 收口留痕 - -## 任务背景 - -本次执行 `docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md`,目标是从 P1R-0 的覆盖矩阵进入第一个真实业务域改造阶段:把 Content 域 51 个 operation 从合同兜底入口推进到专用 Controller、Service、DTO、持久化、owner guard、幂等、审计和错误响应可验收的状态。 - -P1R-1 的完成口径不是把 operation 标为 `completed`。当前阶段只确认 Content 域已退出 catch-all / generic persistence / SSE placeholder,并进入 `dedicated / needs_verification`,后续仍需要更强的端到端验收、真实外部 owner 接通和数据库集成验证。 - -## 执行位置 - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -## 计划目标 - -P1R-1 要完成: - -- 读取 `docs/superpowers/reports/p1r-api-coverage.json`,筛出 Content 域 51 个 operation。 -- 分清 `catch_all`、`generic_persistence`、`sse_placeholder`、`missing` 等非真实实现状态。 -- 为 Content 域补真实 API 规格与执行计划。 -- 逐组实现专用 app/admin Controller、Application Service、DTO、Mapper/DO、外部 facade 边界、owner guard、commandId 幂等、审计和错误码。 -- 删除 Content 域 catch-all 合同入口。 -- 更新覆盖矩阵和阶段门禁,确保未经验收的 operation 不能误标为 `completed`。 - -## 实际完成内容 - -核心产物: - -- 规格:`docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md` -- 覆盖报告: - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` -- Content 阶段门禁: - - `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java` - -主要代码范围: - -- App Content 专用入口:核心 CRUD、结构编辑、来源归因、planning、import/parse、export/download、meta projection。 -- Admin Content 专用入口:作品列表、详情、章节摘要、导入任务、导出任务、风险治理动作。 -- Application Service:幂等、审计、owner guard、版本校验、状态流转和 facade 编排。 -- Content 持久化:import/export/planning/governance 等 DO、Mapper 和 V9 Flyway 追加脚本。 -- 外部 owner 边界:AI、Meta、Knowledge、FileService 默认不可用时返回 blocked,不伪造成功。 - -已删除的关键兜底入口: - -- `AppMuseContentContractController` -- `AdminMuseContentContractController` - -OpenAPI 例外变更: - -- 用户已批准将 `RiskActionRequest.required` 从 `[commandId, action, reason]` 调整为 `[commandId, action, reason, expectedVersion]`,用于匹配 `adminRiskAction` 的服务端版本冲突校验。 - -## 当前覆盖结果 - -P1R 审计后 Content 域结果: - -```text -51 dedicated needs_verification -``` - -含义: - -- Content 51 个 operation 均已有专用入口。 -- Content 域没有 `catch_all`、`generic_persistence`、`sse_placeholder` 或 `missing` 残留。 -- 没有任何 Content operation 被提前标记为 `completed`。 - -## 验证记录 - -P1R 覆盖脚本: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:通过,Content 51 个 operation 全部为 `dedicated / needs_verification`。 - -Content 模块全量测试: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-content/muse-module-content-server -am \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过,204 tests,0 failures。 - -P1R 门禁测试: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过,10 tests,0 failures。 - -基础卫生检查: - -```bash -git diff --check -git status --short -- muse-cloud/scripts -``` - -结果: - -- `git diff --check` 通过。 -- `muse-cloud/scripts/**` 无改动。 - -## 重要修复点 - -Admin risk action 收口时修复过以下问题: - -- 幂等 envelope 使用 `targetType=work`、`targetId=workId`,不再用未生成的 `actionId` 作为幂等目标。 -- `RiskActionReqVO.expectedVersion` 改为必填,OpenAPI 同步 required。 -- `adminRiskAction` 校验 action、targetScope 和 targetIds。 -- chapter/block target 会校验归属 `workId`。 -- expectedVersion 校验前用 `selectByIdForUpdate` 锁定作品,降低并发 TOCTOU 风险。 -- admin chapter summary 的 `wordCount` 改为聚合 block `word_count`。 - -## 残余风险 - -- 当前状态是 `needs_verification`,不是业务完成态 `completed`。 -- 测试主要覆盖 Mockito/MockMvc 和覆盖矩阵门禁,还没有真实 PostgreSQL/Flyway 集成测试。 -- 外部 owner facade 多数仍是边界合同和 blocked 行为,AI、Meta、Knowledge、FileService 的真实闭环需要后续阶段接入。 -- 没有在本留痕中确认提交或推送;当前只是工作树收口状态。 - -## 下一阶段 - -下一阶段应从 `needs_verification` 推进到更强验收,而不是继续补扫描脚本: - -- 补真实 DB/Flyway 集成测试。 -- 针对 Content 自有事实 operation 做端到端合同验收。 -- 接通或明确排期外部 owner facade。 -- 将符合验收条件的 operation 从 `needs_verification` 推进到后续完成口径。 diff --git a/docs/memorys/2026-05-27-P1R2元治理规格计划.md b/docs/memorys/2026-05-27-P1R2元治理规格计划.md deleted file mode 100644 index 2781d62b..00000000 --- a/docs/memorys/2026-05-27-P1R2元治理规格计划.md +++ /dev/null @@ -1,96 +0,0 @@ -# P1R2 元治理规格计划留痕 - -## 任务背景 - -本次进入 P1R-2 Meta Real API spec + plan 阶段,目标是先完成 Meta 域真实业务 API 的事实盘点、规格和执行计划,不进入代码实现。 - -P1R-2 的核心不是继续增强 `MuseContractPersistenceService`,也不是把通用持久化响应包装成 Meta 业务结果,而是把 Meta 域从“通用持久化可响应”推进到“真实 Meta 治理 API 可验收”的规格和计划阶段。 - -## 执行位置 - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -说明:`/Users/qingse/Sync/local-git/oh-my-muse` 当前不是本阶段真实工作面,核验时位于 `feature/p1-muse-cloud-backend-20260524` 且有未跟踪文件。`dev/1.0.0` 已由上面的 superpowers worktree 占用。 - -## 已验证事实 - -P1R 覆盖矩阵中 Meta 域当前状态: - -```text -16 generic_persistence / incomplete -``` - -Meta 16 个 operation 全部来自 `docs/api-contracts/meta/openapi.yaml`,当前没有 `dedicated / needs_verification`、`catch_all`、`sse_placeholder` 或 `missing`。 - -当前 Meta 模块事实: - -- `muse-module-meta-server` 只有 `AdminMuseMetaContractController`。 -- 该 Controller 使用 wildcard 路由接管 `meta-schemas`、`protection-nodes`、`function-chains`。 -- 当前请求统一委托 `MuseContractPersistenceService.handle(...)`。 -- 当前没有 Meta 专用 Controller、Service、DTO、DO、Mapper、command/audit 服务和状态机。 - -当前 DDL 事实: - -- V3 已有 `muse_meta_schema`、`muse_meta_schema_version`、`muse_meta_field`、`muse_meta_visibility_policy`、`muse_meta_protection_node`、`muse_meta_function_chain`。 -- V3 不足以覆盖 validation result、impact preview、gray rule、FunctionChain version/slot/node relation、Meta command/audit。 -- MetaSchema active 唯一约束不能按 `tenant_id + schema_key` 过度约束,应按 `tenant_id + schema_id`,因为 V3 允许 `(domain, scope, target_type, schema_key)` 组合唯一。 - -## 本阶段产物 - -- 规格:`docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md` - -## Review 结果 - -本阶段使用 fresh subagent 做了两轮文档 review。 - -第一轮 review 未通过,阻塞点包括: - -- `blocked` 状态没有 OpenAPI/coverage 可执行入口。 -- Meta wildcard catch-all 路由退役顺序不清。 -- Task 1 clean worktree 预期未考虑新增 review 文档。 -- DDL active 唯一约束 identity 与 V3 不一致。 -- rollback/deprecate 缺少 OpenAPI required 字段落地要求。 -- `previewMetaSchemaDraftImpact` 缺少等价幂等键。 -- 权限和 `code/data/msg` controller 验收不够明确。 - -修正后第二轮 review 通过: - -- P1R-2 收口状态统一为 `dedicated / needs_verification`,禁止 `completed` 和 coverage `blocked` 收口。 -- 增加 Task 9,专门退役 Meta 合同兜底路由,并要求 route ownership 测试证明 16 个路径不再调用 `MuseContractPersistenceService`。 -- `validateMetaSchemaDraft` / `previewMetaSchemaDraftImpact` 确认为使用 `schemaKey + draftVersion + actor + draftHash` 的等价幂等键,不静默改 OpenAPI。 -- rollback/deprecate/publish/activate/gray/function-chain activate 均补齐 commandId、reason、version、validationResultId、impactPreviewId、权限、同源校验和 audit 要求。 -- 计划补充 `P1rMetaMigrationSqlTest` 聚焦验证 V10 迁移 SQL 和 active 唯一索引边界。 - -## 当前阶段边界 - -本阶段只完成规格和计划,没有实现代码。 - -未修改: - -- `docs/api-contracts/meta/openapi.yaml` -- `docs/superpowers/reports/p1r-api-coverage.*` -- `muse-cloud/scripts/**` -- `muse-cloud/muse-module-meta/**` 实现代码 - -当前不应把任何 Meta operation 标为 `completed`。 - -## 下一步 - -进入 P1R-2 实现前,应从 `docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md` Task 1 开始,按 fresh subagent + 两轮 review 执行。 - -实现阶段完成口径: - -- Meta 16 个 operation 退出 `generic_persistence`。 -- Meta 16 个 operation 全部为 `dedicated / needs_verification`。 -- Content 51 个 operation 保持 `dedicated / needs_verification`。 -- 不修改脚本掩盖缺口。 -- 不把 Meta 标为 `completed`。 diff --git a/docs/memorys/2026-05-28-P1R2元治理真实API收口.md b/docs/memorys/2026-05-28-P1R2元治理真实API收口.md deleted file mode 100644 index 90d6198f..00000000 --- a/docs/memorys/2026-05-28-P1R2元治理真实API收口.md +++ /dev/null @@ -1,106 +0,0 @@ -# P1R-2 元治理真实 API 收口 - -日期:2026-05-28 - -## 背景 - -P1R-2 Meta Real API 的规格和计划已存在: - -- 规格:`docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md` - -本次执行 Task 10:覆盖审计、扫描规则纠偏、报告再生成、Meta gate test 和最终留痕。 - -## Task 4-9 实现摘要 - -- Task 4:新增 `AdminMetaSchemaController`,MetaSchema list/detail/version 进入 dedicated Controller 和 Service。 -- Task 5:MetaSchema draft 保存、校验、影响预览进入 dedicated Controller、Service、DTO 和测试。 -- Task 6:MetaSchema publish/activate/rollback/deprecate/gray-rules 进入 dedicated command 流程,保留 `needs_verification`。 -- Task 7:新增 `AdminProtectionNodeController`,ProtectionNode list/detail 进入 dedicated 查询入口。 -- Task 8:新增 `AdminFunctionChainController`,FunctionChain list/impact-preview/activate 进入 dedicated 入口。 -- Task 9:P1R-2 Meta 合同兜底路由已从完成路径退役,并有 route ownership 测试覆盖。 - -## Task 10 报告状态 - -首次运行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:退出码 0,生成 `docs/superpowers/reports/p1r-api-coverage.json` 和 `.md`。 - -审计脚本输出的 Meta 状态仍为: - -```text -generic_persistence/incomplete 10 -missing/incomplete 6 -``` - -原因:脚本的保守扫描规则只匹配单个注解里的路由字面量,不能合并 Meta Controller 的类级 `@RequestMapping("/muse/governance/...")` 和方法级 `@GetMapping/@PostMapping`。 - -已纠偏:`muse-cloud/scripts/p1r-audit-api-coverage.py` 改为识别类级 `@RequestMapping` base path,并与方法级 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping`、`@RequestMapping(method=...)` 合并后再匹配 OpenAPI path。该变更只是 Spring 路由扫描规则纠偏,不硬编码 Meta 16 个 operation,不把 generic fallback 算 dedicated。 - -最终报告状态: - -```text -Meta 16 dedicated / needs_verification -Content 51 dedicated / needs_verification -``` - -全局 summary: - -```text -totalOperations=233 -completedOperations=0 -needsVerificationOperations=67 -incompleteOperations=166 -genericPersistenceOperations=164 -ssePlaceholderOperations=2 -missingOperations=0 -blockedOperations=0 -``` - -没有把 Meta 或 Content operation 标为 `completed`。 - -## 验证结果 - -扫描规则纠偏验证: - -- 修复前真实 audit 再生成结果:Meta 为 `generic_persistence/incomplete 10`、`missing/incomplete 6`,Content 为 `dedicated/needs_verification 51`,completed 为 `0`。 -- 修复后真实 audit 再生成结果:Meta 为 `dedicated/needs_verification 16`,Content 为 `dedicated/needs_verification 51`,completed 为 `0`。 - -Meta gate TDD 过程: - -- 新增 `P1rMetaRealApiGateTest` 后先运行单类测试,当前报告下按预期失败,失败点为 Meta operation 仍是 `missing`。 -- 报告对账后重跑单类测试通过。 - -P1R 门禁: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过,14 个测试,0 failures,0 errors。 - -Meta 模块全量测试: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过,165 个测试,0 failures,0 errors。 - -## 残余风险 - -- 未运行真实 PostgreSQL / Flyway 迁移验证,V10 仍需要真实数据库验收。 -- 外部 runtime owner 仍 pending,AI / New-API / SSE / Content / Knowledge 等跨 owner 闭环不能标为 completed。 -- `p1r-audit-api-coverage.py` 已做 class+method mapping 扫描规则纠偏;后续若引入更复杂的 Spring 表达式、常量路径或自定义组合注解,仍需要单独评审扫描器覆盖范围。 -- 当前 coverage 状态只是 `needs_verification`,不是业务完成态。 diff --git a/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md b/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md deleted file mode 100644 index 08d79ab1..00000000 --- a/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md +++ /dev/null @@ -1,293 +0,0 @@ -# P1R3 账户真实 API 规格计划 - -日期:2026-05-28 - -## 背景 - -本次进入 P1R-3 Account Real API spec + plan 阶段,只完成 Account 域真实 API 的事实盘点、规格和执行计划,不进入代码实现。 - -P1R-3 的核心不是继续增强 `MuseContractPersistenceService`,也不是把 `generic_persistence` 包装成业务结果,而是把 Account 域从“通用持久化可响应”推进到“真实账户业务 API 可验收”的规格和计划阶段。 - -## 执行位置 - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -本阶段开始和收口时,工作树只包含本阶段新增文档改动,没有实现代码改动。 - -## 已验证事实 - -P1R 覆盖矩阵中 Account 域当前状态: - -```text -33 generic_persistence / incomplete -``` - -同时保持: - -- completed operations = 0 -- Content 51 个 operation 为 `dedicated / needs_verification` -- Meta 16 个 operation 为 `dedicated / needs_verification` - -当前代码事实: - -- 仓库没有 `muse-cloud/muse-module-account/**`。 -- Account 当前真实 owner 应落在 `muse-module-member` 的 Account 子域。 -- 当前 Account 合同入口仍是 `AdminMuseAccountContractController` 和 `AppMuseAccountContractController`。 -- 两个 Controller 仍通过 `MuseContractPersistenceService` 通用持久化处理 Account 路由,不是 P1R 完成证据。 - -当前 DDL 事实: - -- V2 已有 `muse_member_entitlement`、`muse_member_entitlement_audit_log`、`muse_member_quota`、`muse_member_usage_record`、`muse_member_security_event`、`muse_member_new_api_binding`。 -- P1R-3 计划新增 `muse_account_profile`、`muse_account_audit`、`muse_account_command`、`muse_account_balance_snapshot`、`muse_account_quota_request`、`muse_account_integration_call`、`muse_account_call_attribution_job`、`muse_account_call_attribution_item`、`muse_account_export_task`、`muse_account_download_credential`、`muse_account_security_event_ack`、`muse_account_record_projection`。 -- purchase / license / publish records 统一使用 `muse_account_record_projection(record_type=...)` 作为 Account 视角读模型,不在 P1R-3 实现 Market 主流程。 - -## 本阶段产物 - -- 规格:`docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md` - -## 关键设计结论 - -- P1R-3 目标状态只能是 `dedicated / needs_verification`,不能写成 `completed`。 -- Account owner 采用 `muse-module-member/account` 子域,不新增孤立 `muse-module-account`。 -- `muse_account_profile` 承载 profile 扩展事实和 `expectedVersion` 乐观锁,不污染 `member_user`。 -- `muse_account_audit` 与 `muse_account_command` 是 commandId 幂等、requestHash 冲突和审计的必需基础设施。 -- New-API binding / recheck 必须写内部 integration call,并通过 `bindingId` / `jobId` 关联 `correlationId`;现有 OpenAPI 响应不新增 `correlationId`。 -- New-API、call attribution、FileService unavailable 只能通过合同内业务错误、内部 job 状态或 OpenAPI 已声明状态表达,不能返回未声明的 `pending` 等状态,也不能 fake success。 -- `appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream`,错误响应仍走统一 `code/data/msg`。 -- AI runtime / task / SSE、Knowledge / RAGFlow、Market 主流程、统一 Events SSE 均不属于 P1R-3。 - -## Review 结果 - -本阶段使用 fresh subagent 做了两轮文档 review,并按 review 结果修正后复审通过。 - -Spec compliance review 最终结果: - -```text -SPEC REVIEW PASS -``` - -Quality review 最终结果: - -```text -QUALITY REVIEW PASS -``` - -过程中修正的关键问题: - -- 补齐 New-API binding create / recheck inline requestBody 的 typed VO。 -- 明确 unavailable / failed / pending 等内部状态不得进入 OpenAPI 未声明 response status。 -- 将 app quota request DTO 改为 `AppQuotaRequestCreateReqVO`,避免复用 admin DTO。 -- 增加 `UnavailableNewApiAccountFacade`、`UnavailableMarketAccountProjectionFacade`、`UnavailableAccountFileServiceFacade` 的默认边界和测试要求。 -- 移除会诱导创建独立 AI / Knowledge facade 的过宽表述,AI / Knowledge 只通过 attribution source unavailable 边界表达。 -- 移除 `-Dsurefire.failIfNoSpecifiedTests=false`,并要求关键指定测试命令用 `test -f` 防止缺失测试被掩盖。 - -## 当前阶段边界 - -本阶段只完成规格、计划、review 和 memory 留痕,没有实现代码。 - -未修改: - -- `docs/api-contracts/account/openapi.yaml` -- `docs/superpowers/reports/p1r-api-coverage.*` -- `muse-cloud/scripts/**` -- `muse-cloud/muse-module-member/**` 实现代码 -- `muse-cloud/sql/muse/**` - -当前不应把任何 Account operation 标为 `completed`。 - -## 残余风险 - -- 规格计划阶段未运行真实 PostgreSQL / Flyway 验证;实现阶段已在同一 PostgreSQL 实例的 `muse_local_test` 数据库补跑,结果见下文。 -- 未接真实 New-API runtime,binding / recheck / quota request 仍需保持 `needs_verification`。 -- 未接真实 FileService runtime,export task / download credential 仍需保持 `needs_verification`。 -- 外部 Market / Content / AI / Knowledge owner 未闭合时,purchase / license / publish / attribution 只能走 Account 读模型和 unavailable 边界,不能标为 completed。 - -## 下一步(规格计划阶段记录) - -进入 P1R-3 实现前,应从 `docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md` Task 1 开始,按 fresh subagent + spec compliance review + quality review 执行。 - -实现阶段完成口径: - -- Account 33 个 operation 退出 `generic_persistence`。 -- Account 33 个 operation 全部为 `dedicated / needs_verification`。 -- Content 51 个 operation 保持 `dedicated / needs_verification`。 -- Meta 16 个 operation 保持 `dedicated / needs_verification`。 -- 不修改脚本掩盖缺口。 -- 不把 Account 标为 `completed`。 - -## 2026-05-29 实现阶段收口更新 - -### 实现任务范围 - -本轮实现阶段在以下真实 worktree 执行: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -`4d9a242..HEAD` 已覆盖 P1R-3 Task 1-12: - -1. Preflight 与覆盖基线冻结。 -2. Account 基础设施与 V11 追加 DDL。 -3. DTO / DAL / Mapper 骨架。 -4. App `me` / `profile` 与 Admin 用户摘要。 -5. 权益、配额、余额、用量。 -6. New-API binding、recheck、quota request、integration call。 -7. call attribution job。 -8. purchase / license / publish records Account 投影。 -9. security events。 -10. export task / download credential。 -11. Account 合同兜底退役。 -12. P1R-3 覆盖门禁与报告对账。 - -Task 10 最新事实: - -- 已新增 Account export/download Controller、Service、FileService facade、unavailable facade、凭证与导出任务相关 DAL 和测试。 -- 下载凭证边界覆盖 credential hash、owner、expiresAt、revoked、consumed、source blocked。 -- 高敏导出在缺少可信 step-up 证明时 fail closed。 -- `appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream`;错误仍走统一 `code/data/msg`。 -- unavailable FileService 不返回伪造文件流。 - -Task 11 最新事实: - -- 已删除旧 Account 合同兜底 Controller。 -- 已新增 `P1rAccountRouteOwnershipTest`,证明 33 个 P1R-3 Account 路由进入 dedicated Controller,不再回落到 `MuseContractPersistenceService`。 - -Task 12 最新事实: - -- 已新增 `P1rAccountRealApiGateTest`。 -- 已更新 `docs/superpowers/reports/p1r-api-coverage.json` 和 `docs/superpowers/reports/p1r-api-coverage.md`。 -- Account 33 个 operation 统一收口到 `dedicated / needs_verification`,仍保留真实运行态验收缺口。 - -### Coverage 前后状态 - -实现起点 `4d9a242`: - -```text -Account 33 generic_persistence / incomplete -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -``` - -当前 `HEAD`: - -```text -Account 33 dedicated / needs_verification -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -``` - -该状态只证明仓库内 dedicated 入口、覆盖报告和门禁已收口。PostgreSQL / Flyway 迁移链路已在同实例 test 库验证;New-API、FileService 或端到端运行态仍未验收。 - -### Review 结果 - -本轮实现按计划要求在每个实现 Task 后执行: - -1. spec compliance review。 -2. code quality review。 - -已记录的收口事实: - -- Task 10 在修正高敏导出 fail-closed、导出凭证消费边界、HMAC 凭证派生和 FileService 异常处理后,spec compliance review 与 code quality review 均通过。 -- Task 11 的 spec compliance review 与 code quality review 均通过。 -- Task 12 的 spec compliance review 与 code quality review 均通过。 - -这些 review 结果只证明 P1R-3 代码、报告和门禁符合当前 spec / plan,不代表外部 runtime owner 已闭合。 - -### 测试命令和结果 - -本阶段已记录的验证命令与结果: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:通过;覆盖报告保持 Account 33、Content 51、Meta 16 均为 `dedicated / needs_verification`。 - -```bash -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过,22 tests,0 failures。 - -说明:原计划中的 `-pl muse-server -am -Dtest=...` 命令在上游 reactor 模块没有同名测试时会触发 `failIfNoSpecifiedTests` 问题。本轮通过 `test -f` 先校验目标测试存在,再局部追加 `-Dsurefire.failIfNoSpecifiedTests=false` 避免上游模块误失败;这不是全局放宽测试缺失门禁。 - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-member/muse-module-member-server -am -``` - -结果:通过,member-server 272 tests,0 failures。 - -```bash -git diff --check -``` - -结果:通过,无空白错误输出。 - -2026-05-30 追加真实 PostgreSQL / Flyway 验证: - -```bash -# 使用 ~/.config/muse-repo/infra.env 中同一个 PostgreSQL 实例 -# 基于 MUSE_POSTGRES_DATABASE=muse_local 创建固定 test 后缀数据库 -createdb muse_local_test - -# 使用 Flyway API 和 application.yaml 同口径的 filesystem:sql/muse 迁移目录 -Flyway.configure() - .dataSource("jdbc:postgresql:///muse_local_test", username, password) - .locations("filesystem:sql/muse") - .baselineOnMigrate(true) - .baselineVersion("0") - .load() - .migrate() -``` - -结果: - -```text -PostgreSQL 17.10 -flyway_success=true -migrations_executed=11 -target_schema_version=11 -flyway_latest=11:extend account real api schema -flyway_versions=1,2,3,4,5,6,7,8,9,10,11 -v11_tables=12 -account_index_count=47 -``` - -结论:P1R-3 的 V11 迁移已在真实 PostgreSQL 实例的 `muse_local_test` 数据库通过 Flyway 迁移链验证。该验证覆盖 DDL/Flyway 可执行性,不等同于 New-API、FileService 或跨域端到端 runtime 验收。 - -### 未验证 runtime 风险 - -- PostgreSQL / Flyway 已在同实例 `muse_local_test` 数据库验证 V1-V11 迁移链;后续阶段若新增 DDL,应继续先在同实例 `_test` 数据库跑真实 Flyway,再允许阶段收口。 -- 未接真实 New-API runtime,binding / recheck / quota request / integration call 仍保持 `needs_verification`。 -- 未接真实 FileService,export task / download credential 只能证明边界和状态机,不能证明真实文件交付。 -- Market / AI / Knowledge owner 未闭合,purchase / license / publish / attribution 只能走 Account 投影或 unavailable 边界。 -- 端到端 auth、错误响应、SSE 与跨域 runtime 行为仍需后续阶段验收。 - -### 变更面记录 - -- OpenAPI:未修改 `docs/api-contracts/account/openapi.yaml`。 -- Scanner:未修改 `muse-cloud/scripts/**`。 -- SQL:新增 `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql`。 -- Java:新增 Account Controller / Service / DTO / VO / DAL / Mapper / Facade / Guard / AutoConfiguration 及 P1R gate tests;删除旧 Account 合同兜底 Controller。 -- Coverage:更新 `docs/superpowers/reports/p1r-api-coverage.json` 和 `docs/superpowers/reports/p1r-api-coverage.md`。 diff --git a/docs/memorys/2026-05-30-P1R4AI真实API规格计划.md b/docs/memorys/2026-05-30-P1R4AI真实API规格计划.md deleted file mode 100644 index 49b32963..00000000 --- a/docs/memorys/2026-05-30-P1R4AI真实API规格计划.md +++ /dev/null @@ -1,465 +0,0 @@ -# P1R4 AI 真实 API 规格计划 - -日期:2026-05-30 - -## 背景 - -本次进入 P1R-4 AI Real API spec + implementation plan + review gate 阶段,只完成 AI 域真实 API 的事实盘点、规格、执行计划和阶段留痕,不进入代码实现。 - -P1R-4 的核心不是继续增强 `MuseContractPersistenceService`,也不是把 `streamAiTask` 的占位 SSE 事件包装成真实结果,而是设计如何把 AI 41 个 operation 推进到 `dedicated / needs_verification`。 - -## 执行位置 - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -开始时已执行: - -```bash -git status --short --branch -git pull --ff-only origin dev/1.0.0 -``` - -结果: - -```text -## dev/1.0.0...origin/dev/1.0.0 -Already up to date. -HEAD 7f5553d -``` - -## 已验证事实 - -P1R 覆盖矩阵中 AI 域当前状态: - -```text -generic_persistence / incomplete 40 -sse_placeholder / incomplete 1 -``` - -同时保持: - -- completed operations = 0 -- Content 51 个 operation 为 `dedicated / needs_verification` -- Meta 16 个 operation 为 `dedicated / needs_verification` -- Account 33 个 operation 为 `dedicated / needs_verification` - -当前代码事实: - -- AI 合同入口仍是 `AdminMuseAiContractController` 和 `AppMuseAiContractController`。 -- 40 个非 SSE operation 仍通过 `MuseContractPersistenceService` 通用持久化处理 AI 路由,不是 P1R 完成证据。 -- `streamAiTask` 当前只发送一次固定摘要 `muse ai task stream ready` 的 `done` 事件后立即完成,被 scanner 识别为 `sse_placeholder / incomplete`。 -- `muse-module-ai-server` 已有 Yudao AI 基础服务、Spring AI model factory 和 provider tests,但没有持有 AI OpenAPI 41 个 operation 的 Muse dedicated controller / DTO / 状态机 / P1R 门禁。 - -当前 DDL 事实: - -- V4 已有 `muse_ai_generation`、`muse_ai_suggestion`、`muse_agent`、`muse_agent_version`、`muse_agent_slot_binding`、`muse_prompt`、`muse_prompt_version`、`muse_quality_policy`、`muse_quality_policy_version`、`muse_tool_grant`。 -- V7 已有 `muse_domain_operation_record`。 -- V8 已有 `muse_domain_workflow_task`。 -- 这些表不能直接证明 AI task runtime、New-API correlation、SSE event replay、job state、source status、API access log 和 business audit 的真实闭环。 - -P1R-3 数据库验收事实: - -```text -flyway_success=true -migrations_executed=11 -target_schema_version=11 -flyway_latest=11:extend account real api schema -v11_tables=12 -``` - -因此 P1R-4 如新增或修改 DDL,阶段收口前必须继续在同一 PostgreSQL 实例 `_test` 数据库跑真实 Flyway。 - -## 本阶段产物 - -- 规格:`docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md` -- 留痕:`docs/memorys/2026-05-30-P1R4AI真实API规格计划.md` - -## Review 结果 - -本阶段按要求使用 fresh subagent 做了两轮文档 review,review 子代理均使用 `gpt-5.5` + `xhigh`。 - -Spec compliance review 最终结果: - -```text -PASS -``` - -Quality / feasibility review 最终结果: - -```text -PASS -``` - -已按首轮 quality review 的阻塞意见补齐: - -- `muse-server` 当前只依赖 `muse-module-ai-contract-server` 的运行时装配风险,计划已要求实现阶段显式引入 `muse-module-ai-server` 或证明短期双依赖下合同兜底精确避让。 -- P1R-4 新增或修改 DDL 时的真实 PostgreSQL / Flyway `_test` 验收步骤,计划已补可复制命令、隔离 test DB、`filesystem:sql/muse` locations、V12 成功断言和失败处理。 -- New-API runtime 最小契约,计划已补 `RuntimeCommand`、`RuntimeResult`、`RuntimeFailure`、timeout 默认值、retry budget/backoff、错误分类、事务边界、correlation、失败状态映射和脱敏要求。 - -quality review 仍给出两条非阻塞建议: - -- 实现阶段建议在 Task 2 产出 V12 后立即提前跑一次真实 Flyway `_test`,最终 Task 11 再复核。 -- Task 10 / Task 11 的 Maven gate 建议显式确认目标测试类和非零测试数,避免 `-Dsurefire.failIfNoSpecifiedTests=false` 被误用。 - -## 关键设计结论 - -- P1R-4 目标状态只能是 `dedicated / needs_verification`,不能写成 `completed`。 -- AI owner 采用 `muse-module-ai`,新增 Muse 合同专用 Controller / Application Service / DTO / DAL / DDL,不复用通用持久化完成口径。 -- 推荐新增 `V12__extend_ai_real_api_schema.sql`,扩展 V4 AI 表并补齐 AI command、runtime call、task event、job、source event、source status projection、evaluation run、API access log projection 和 business audit event。 -- `createAiTask` 只接受任务意图和可验证引用;source snapshot 和 runtime permission envelope 必须由服务端生成。 -- New-API runtime 必须有 timeout、失败分类、重试预算、correlation、脱敏 request/response summary;不可用时 fail closed,不能 fake success。 -- `streamAiTask` 属于 P1R-4,但只能实现 AI task 级 SSE;统一 `/app-api/muse/events` 留给 P1R-7。 -- Tool Grant 只能消费 Security facade 批准结果;AI runtime 不得自授权或扩权。 -- Knowledge / RAGFlow 和 Market 主流程不属于 P1R-4;不可用时使用合同内 source status / action policy 表达阻断或重验。 -- API access log / business audit 必须脱敏;敏感详情读取本身必须写入审计。 -- OpenAPI 未声明字段不得添加到响应,未声明 response status 不得泄露到响应。 - -## 当前阶段边界 - -本阶段只完成规格、计划、review 和 memory 留痕,没有实现代码。 - -未修改: - -- `docs/api-contracts/ai/openapi.yaml` -- `docs/superpowers/reports/p1r-api-coverage.*` -- `muse-cloud/scripts/**` -- `muse-cloud/muse-module-ai/**` 实现代码 -- `muse-cloud/sql/muse/**` - -当前不应把任何 AI operation 标为 `completed`。 - -## 下一步 - -进入 P1R-4 实现前,应从 `docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md` Task 1 开始,按 fresh subagent + spec compliance review + quality / feasibility review 执行。 - -实现阶段完成口径: - -- AI 41 个 operation 退出 `generic_persistence` / `sse_placeholder`。 -- AI 41 个 operation 全部为 `dedicated / needs_verification`。 -- Content 51 个 operation 保持 `dedicated / needs_verification`。 -- Meta 16 个 operation 保持 `dedicated / needs_verification`。 -- Account 33 个 operation 保持 `dedicated / needs_verification`。 -- completed operations 仍为 0,除非后续有真实端到端验收证据并单独批准。 -- 不修改 OpenAPI 或 scanner 掩盖缺口。 -- 如新增 V12 DDL,阶段收口前必须在同一 PostgreSQL 实例 `_test` 数据库跑真实 Flyway。 - -## 2026-05-31 实现阶段收口更新 - -### 实现任务范围 - -本轮实现阶段继续在以下真实 worktree 执行: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -实现阶段按 `docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md` 执行 Task 1-12,并使用 fresh subagent 落地;每个 implementation task 后按计划执行两类 review: - -1. spec compliance review。 -2. quality / feasibility review。 - -Task 1-12 覆盖范围: - -1. Preflight 与覆盖基线冻结。 -2. AI 基础设施与 V12 DDL。 -3. Prompt / Quality Policy / Evaluation Run。 -4. Agent / Tool Grant / Agent Slot。 -5. AI Task / Runtime / Suggestion 创建基础。 -6. AI Task SSE Stream。 -7. Suggestion / Source Status / Source Event。 -8. Job Admin/App Query、Cancel、Retry。 -9. API Access Log / Business Audit。 -10. Retire AI 合同兜底与覆盖门禁。 -11. PostgreSQL / Flyway `_test` 迁移验收。 -12. 最终验证、报告对账与 memory。 - -这些 review 和门禁只证明当前仓库内 dedicated 入口、状态机边界、报告和测试门禁符合 P1R-4 规格计划;没有真实端到端验收前,不代表 AI runtime、New-API、Security、Knowledge、Market 或统一事件流已经完成。 - -### Coverage 最终状态 - -2026-05-31 Task 12 fresh 验证后的 coverage 对账: - -```text -AI 41 dedicated / needs_verification -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -Account 33 dedicated / needs_verification -completedOperations = 0 -``` - -`ssePlaceholderOperations = 1` 仍存在,但对应的是 P1R-7 `streamEvents`: - -```text -events streamEvents GET /app-api/muse/events P1R-7 End-to-End Acceptance incomplete -``` - -P1R-4 的 `streamAiTask` 已退出 `sse_placeholder`,但仍是 `needs_verification`,不能写成 completed。 - -### 运行态装配事实 - -`muse-server` 当前使用 `muse-module-ai-server`: - -```text -muse-cloud/muse-server/pom.xml:109 muse-module-ai-server -``` - -P1R-4 不再依赖运行时 `muse-module-ai-contract-server` 作为 41 个 AI 路由的兜底入口;旧合同 Controller 文件已从 contract server 删除,route ownership gate 证明 AI 路由由 dedicated Controller 持有。 - -### V12 PostgreSQL / Flyway `_test` 证据 - -2026-05-31 Task 12 已重新使用 `~/.config/muse-repo/infra.env` 中的 PostgreSQL 凭据,在同实例隔离库运行 Flyway IT: - -```text -Database: jdbc:postgresql://100.64.0.8:5433/muse_local_p1r4_test (PostgreSQL 17.10) -flyway_success=true -migrations_executed=12 -target_schema_version=12 -flyway_latest=12:extend ai real api schema -``` - -`P1rAiFlywayMigrationIT` 使用 `filesystem:sql/muse`,先清理隔离 `_test` 库的 `public` schema,再执行 V1-V12 全量迁移。关键表: - -```text -muse_ai_command -muse_ai_runtime_call -muse_ai_task_event -muse_ai_job -muse_ai_source_event -muse_ai_source_status_projection -muse_ai_evaluation_run -muse_api_access_log_projection -muse_business_audit_event -``` - -关键索引: - -```text -uk_muse_ai_job_active_retry_group -idx_muse_api_access_log_tenant_time -idx_muse_api_access_log_actor_time -idx_muse_business_audit_tenant_time -``` - -### Task 12 fresh 验证命令和结果 - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:通过,重新生成 `docs/superpowers/reports/p1r-api-coverage.json` 和 `docs/superpowers/reports/p1r-api-coverage.md`。 - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-4 AI Real API" or .domain == "ai")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '["content","meta","account"][] as $d | [$d, ([.operations[] | select(.domain==$d)] | group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring)))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.summary | [.completedOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -结果: - -```text -dedicated/needs_verification 41 -content dedicated/needs_verification:51 -meta dedicated/needs_verification:16 -account dedicated/needs_verification:33 -0 1 -``` - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAiMigrationSqlTest,P1rAiRouteOwnershipTest,P1rAiRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;`P1rApiCoverageReportTest`、`P1rContentRealApiGateTest`、`P1rMetaRealApiGateTest`、`P1rAccountRealApiGateTest`、`P1rAiMigrationSqlTest`、`P1rAiRouteOwnershipTest`、`P1rAiRealApiGateTest` 共 35 tests,0 failures,0 errors,0 skipped。 - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-ai/muse-module-ai-server -am \ - -Dtest=AdminMuseAuditControllerAnnotationTest,MuseApiAccessLogQueryServiceTest,MuseApiAccessLogProjectionMapperTest,MuseBusinessAuditQueryServiceTest,MuseAiAuditServiceTest,MuseAiTaskStreamServiceTest,MuseJobServiceTest,MuseAiRuntimeProjectionServiceTest,MuseAiJobMapperTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;9 个目标测试类共 66 tests,0 failures,0 errors,0 skipped。输出中存在 Mockito 动态 agent 预警;`MuseAiTaskStreamServiceTest` 覆盖失败路径时有预期 WARN,不是测试失败。 - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAiFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/muse_local_p1r4_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.password="$MUSE_POSTGRES_PASSWORD" \ - -Dp1r.flyway.locations="filesystem:sql/muse" \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;1 test,0 failures,0 errors,0 skipped;PostgreSQL 17.10,V1-V12 全量迁移成功。 - -### 未验证和 residual risk - -- 当前状态仍是 `needs_verification`,没有任何 operation 标记为 `completed`。 -- 尚未完成真实端到端 acceptance;coverage 与 Maven gate 不能替代真实用户流、真实 New-API runtime、Security owner、Knowledge / RAGFlow、Market owner 和生产式事件链路验收。 -- P1R-7 统一 `/app-api/muse/events` 的 `streamEvents` placeholder 仍然存在,明确不属于 P1R-4 范围。 -- 本轮 Task 12 已成功重跑 Flyway IT;如果后续环境缺少 `MUSE_POSTGRES_PASSWORD` 等凭据,只能引用本段 surefire / Flyway 输出作为既有证据,并重新运行非凭据 gates。 - -### 变更面记录 - -- OpenAPI:未修改 `docs/api-contracts/ai/openapi.yaml`。 -- Scanner:未修改 `muse-cloud/scripts/p1r-audit-api-coverage.py`。 -- Coverage:更新 `docs/superpowers/reports/p1r-api-coverage.json` 和 `docs/superpowers/reports/p1r-api-coverage.md`,不包含 completed 状态。 -- SQL:新增 `muse-cloud/sql/muse/V12__extend_ai_real_api_schema.sql`。 -- Java:`muse-server` 装配 `muse-module-ai-server`;删除 AI contract server 旧兜底 Controller;新增 AI dedicated Controller / Application Service / DTO / DAL / Mapper / Facade / Guard / SSE / audit / job / source / task 相关代码与 P1R gate tests。 - -## 2026-06-01 P1R-4 review blocker 修复与最终验证 - -本轮继续 P1R-4 实现阶段,针对 fresh spec / quality review 暴露的阻塞问题做了三轮修复和复审;最终两类 fresh review 均 PASS。 - -### 已验证事实 - -- `P1rAiRouteOwnershipTest` 已从纯静态扫描增强为 Spring `RequestMappingHandlerMapping` / `HandlerMethod` 运行态归属门禁,验证 AI 41 个 route 由 dedicated AI Controller 持有,不属于旧 `*MuseAiContractController`。 -- 新增 `muse-cloud/sql/muse/V13__add_ai_runtime_payload_store.sql`,为 `createAiTask` 的短生命周期 provider payload 提供数据库恢复路径;payload 使用 `EncryptTypeHandler`,不进入 `job.input_summary`、`runtime_call.request_summary`、audit summary 或 coverage。 -- `MuseAiRuntimePayloadStore` 从单机内存 TTL 改为 DB backed short TTL store;runtime 成功、不可重试失败、异常无 response、cancelled 终态都会按 `tenantId + job DB pk` 删除 runtime payload;仅 retryable failure 保留 payload 给下一轮 attempt。 -- `adminRetryJob` 对带 `userInstructionLength/hash` 的失败 AI task job,会在 active payload 存在时复制 payload 到 retry job;payload 缺失或过期时不创建必然失败的 retry job。 -- `MuseAiRuntimeClient.RuntimeCommand.toString()` 已覆盖 Java record 默认输出,只输出脱敏排障字段和 `hasProviderPayload`,不输出 `providerExecutionPrompt`、`inputSummary`、prompt 字段名或用户指令正文。 -- `cancelJob` 对 queued/running/streaming job 推进到 `cancelled` 后,同步删除 runtime payload;对应 admin/user cancel、linked task、tenant fallback 均有测试覆盖。 -- V13 cleanup SQL 覆盖 expired、consumed、soft-deleted 残留;mapper 测试已断言 `deleted = TRUE` 分支。 - -### fresh review gate - -最终 fresh spec compliance review:PASS。 - -最终 fresh quality / feasibility review:PASS。 - -非阻塞风险: - -- cancel running/streaming 只能保证本地 job/task 状态、payload 删除和 runtime 结果不覆盖 `cancelled`;无法中断已经发出的 provider HTTP 调用,仍可能产生外部调用成本。 -- `idx_muse_ai_runtime_payload_cleanup` 对 expired/consumed 有帮助;pure `deleted=true` 且未过期的行主要靠 DELETE 条件扫到,索引不是最优形态。 -- Maven 默认 Java 可能是 17;P1R-4 验证必须显式使用 Java 21。 - -### 最终验证命令和结果 - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:通过,重新生成 `docs/superpowers/reports/p1r-api-coverage.json` 和 `docs/superpowers/reports/p1r-api-coverage.md`。 - -coverage 对账: - -```text -AI 41 dedicated / needs_verification -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -Account 33 dedicated / needs_verification -completedOperations = 0 -ssePlaceholderOperations = 1 -``` - -唯一剩余 `sse_placeholder` 是 `events / streamEvents / P1R-7 End-to-End Acceptance / incomplete`,不属于 P1R-4。 - -AI module focused tests: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-module-ai/muse-module-ai-server -am \ - -Dtest=MuseAiRuntimeJobDispatcherTest,MuseAiJobMapperTest,MuseAiTaskServiceTest,MuseAiRuntimeClientTest,MuseAiRuntimeProjectionServiceTest,MuseAiTaskStreamServiceTest,MuseJobServiceTest,MuseAiAuditServiceTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;`114 tests, 0 failures, 0 errors, 0 skipped`。 - -P1R AI gate tests: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rAiRealApiGateTest,P1rAiRouteOwnershipTest,P1rAiMigrationSqlTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;`20 tests, 0 failures, 0 errors, 0 skipped`。 - -真实 PostgreSQL / Flyway `_test`: - -```bash -cd muse-cloud -set -a -. ~/.config/muse-repo/infra.env -set +a -P1R4_TEST_DB="${P1R4_TEST_DB:-muse_local_p1r4_test}" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAiFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R4_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.password="$MUSE_POSTGRES_PASSWORD" \ - -Dp1r.flyway.locations="filesystem:sql/muse" \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:通过;PostgreSQL 17.10,同实例隔离库 `muse_local_p1r4_test`,V1-V13 全量迁移成功。 - -```text -flyway_success=true -migrations_executed=13 -target_schema_version=13 -flyway_latest=13:add ai runtime payload store -v13_tables=muse_ai_command,muse_ai_runtime_call,muse_ai_task_event,muse_ai_job,muse_ai_source_event,muse_ai_source_status_projection,muse_ai_evaluation_run,muse_api_access_log_projection,muse_business_audit_event,muse_ai_runtime_payload -v13_indexes=uk_muse_ai_job_active_retry_group,idx_muse_api_access_log_tenant_time,idx_muse_api_access_log_actor_time,idx_muse_business_audit_tenant_time,uk_muse_ai_runtime_payload_job,idx_muse_ai_runtime_payload_active,idx_muse_ai_runtime_payload_cleanup -``` - -其他最终检查: - -```text -git diff --check PASS,无输出 -git diff -- docs/api-contracts/ai/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py PASS,无输出 -``` - -### New-API 探索事实和缺口 - -New-API 只读探索确认: - -- Base URL:`http://100.64.0.8:3000` -- Health:`GET /api/status` 可用,返回 200。 -- OpenAI-compatible endpoints:`GET /v1/models`、`POST /v1/chat/completions`。 -- Auth:`Authorization: Bearer `。 -- Muse internal sync:`POST /api/internal/muse/users/sync`,需要 `X-Muse-Service-Secret`。 -- `GET /v1/models` 使用 Muse token 返回 200,模型数 23。 -- `POST /v1/chat/completions` smoke 在 `qwen3.5-plus`、`doubao-seed-2.0-lite` 上出现超时;因此不能把真实 New-API 端到端验收写成已完成。 - -### 当前完成口径 - -P1R-4 本阶段 dedicated gate 已达成: - -- AI 41 个 operation 全部为 `dedicated / needs_verification`。 -- `streamAiTask` 已退出 `sse_placeholder`,改为 task 级 SSE dedicated 实现。 -- `completedOperations = 0`。 -- Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`。 -- 未修改 AI OpenAPI 合同,未修改 coverage scanner 掩盖缺口。 - -仍不能写成 completed: - -- 真实 New-API chat completions 端到端尚未跑通。 -- Security owner 仍是 facade / projection / unavailable 边界,未做完整外部 owner 验收。 -- Knowledge / RAGFlow、Market、统一 `/app-api/muse/events` 仍不属于 P1R-4,本轮未验收。 -- 真实用户流、生产式事件链路、provider 已发出请求的 cancel 中断能力仍是后续验收项。 diff --git a/docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md b/docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md deleted file mode 100644 index b4b4d511..00000000 --- a/docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md +++ /dev/null @@ -1,239 +0,0 @@ -# P1R5 Knowledge RAGFlow 规格计划 - -日期:2026-06-01 - -## 背景 - -本次进入 P1R-5 Knowledge / RAGFlow 的规格与计划阶段,只完成事实盘点、规格、执行计划和阶段留痕,不进入代码实现。 - -真实工作面: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -## 已执行同步 - -本轮开始时发现本地 `dev/1.0.0` ahead 1,最新提交为: - -```text -4f38bf0 feat(p1r): 实现 AI 真实 API dedicated gate -``` - -已执行: - -```bash -git push origin dev/1.0.0 -git pull --ff-only origin dev/1.0.0 -git status --short --branch -git log --oneline -3 -``` - -当前状态: - -```text -## dev/1.0.0...origin/dev/1.0.0 -4f38bf0 feat(p1r): 实现 AI 真实 API dedicated gate -7f5553d docs(p1r): 补记 Account 数据库迁移验收 -7c92a49 docs(p1r): 记录 Account 真实 API 收口 -``` - -上述是产出本阶段文档前的同步快照。规格、计划和本 memory 产出后,工作树会出现这三份 P1R-5 文档未跟踪;进入实现前必须重新检查状态,并确认只存在这些 review 文档或已完成提交。 - -## 已验证事实 - -P1R-5 preflight: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-5 Knowledge / RAGFlow Real API" or .domain == "knowledge")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出: - -```text -generic_persistence/incomplete 59 -``` - -`targetStage` 实际为: - -```text -P1R-5 Knowledge Real API -``` - -当前 Knowledge 实现事实: - -- `docs/api-contracts/knowledge/openapi.yaml` 存在,声明 59 个 operation。 -- `muse-module-knowledge-server` 当前只有 Admin/App 合同兜底 Controller 和 `package-info.java`。 -- 59 个 operation 均走 `MuseContractPersistenceService`,不能作为真实 API 完成证据。 -- V5 已有知识库、资料、资料版本、实体、关系、草稿和绑定初始表,但没有 RAGFlow 映射、处理任务、GraphRAG、绑定预检、发布快照、source projection、RAGFlow call log 和 draft decision 表。 -- `muse-module-ai` 下旧 `/ai/knowledge/**` 是 Yudao AI 管理入口和 Spring AI VectorStore 参考实现,不是 P1R-5 Knowledge owner。 - -RAGFlow 只读事实: - -- 本地 RAGFlow repo:`/Users/qingse/Sync/local-git/ragflow`。 -- RAGFlow HTTP API 需要 `Authorization: Bearer `;文档和日志不得写完整 key。 -- 公开集成面应使用 `/api/v1/**`,包括 dataset、document、parse/chunk、retrieval、GraphRAG、knowledge graph;UI 内部 `/v1/kb`、`/v1/document`、`/v1/chunk` 不能作为 Muse 后端合同。 -- 2026-06-01 已验证 `http://100.64.0.8:9380/v1/system/healthz` 返回 200,`db/doc_engine/redis/storage/status` 均为 `ok`。 -- 2026-06-01 已验证 `http://100.64.0.8/v1/system/healthz` 返回 200,`db/doc_engine/redis/storage/status` 均为 `ok`。 -- 2026-06-01 已验证 `http://100.64.0.11:9380/v1/system/healthz` 5 秒超时,不能作为当前默认地址。 -- RAGFlow parse 成功响应没有外部 task id;P1R-5 应用 Muse 内部 processing task/correlation 承接,再轮询 RAGFlow document `run/progress/progress_msg` 归一状态。 -- RAGFlow 上传只接收 multipart file;Knowledge 的 file、entryContent、linkUrl 必须先经材料化和扫描边界转成 fileRef,FileService/对象存储不可用时只能 blocked/needs_verification 或 failed。 -- GraphRAG 构图依赖 RAGFlow 默认 chat model 和 token 成本;未确认其经 New-API 且可归因前,P1R-5 只实现 `GRAPH_NOT_SUPPORTED` / `ATTRIBUTION_NOT_CONFIGURED` / needs_verification 边界,不写真实构图成功。 -- 当前仍未验证可用于 P1R-5 smoke 的 RAGFlow API key、dataset/document/retrieval/GraphRAG smoke,因此不能写 completed。 - -New-API 只读事实: - -- 本地 new-api repo:`/Users/qingse/Sync/local-git/new-api`。 -- New-API 提供 `/api/status`、`/v1/models`、`/v1/chat/completions` 和 Muse internal sync/revoke/status 接口。 -- 2026-06-01 已验证 `http://100.64.0.8:3000/api/status` 返回 200 且 `success=true`。 -- P1R-5 边界中 New-API 负责 LLM 调用;RAGFlow 负责知识引擎;Knowledge owner 负责 Muse 知识事实和状态机。 - -## 本阶段产物 - -- 规格:`docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md` -- 留痕:`docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md` - -## 当前阶段边界 - -本阶段只完成规格、计划、review gate 和 memory 留痕,不实现代码。 - -未修改: - -- `docs/api-contracts/knowledge/openapi.yaml` -- `docs/api-contracts/ai/openapi.yaml` -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `muse-cloud/muse-module-knowledge/**` 实现代码 -- `muse-cloud/sql/muse/**` - -当前不应把任何 Knowledge operation 标为 `completed`。 - -## Review gate 状态 - -待执行: - -1. spec compliance review。 -2. quality / feasibility review。 - -review 通过前不能进入 P1R-5 实现阶段。 - -## 下一步 - -完成两轮 fresh review 后,如通过,再按 `docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md` Task 1 开始实现。 - -实现阶段完成口径: - -- Knowledge 59 个 operation 退出 `generic_persistence`。 -- Knowledge 59 个 operation 全部为 `dedicated / needs_verification`。 -- AI 41、Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`。 -- completed operations 仍为 0,除非后续有真实端到端验收证据并单独批准。 -- 不修改 OpenAPI 或 scanner 掩盖缺口。 -- 如新增 V14 DDL,阶段收口前必须在同一 PostgreSQL 实例 `_test` 数据库跑真实 Flyway。 - -## 2026-06-02 实现 gate 留痕 - -本轮已推进到 P1R-5 Knowledge / RAGFlow dedicated gate 验证阶段,当前只允许表述为: - -- Knowledge 59 = `dedicated / needs_verification` -- `completedOperations = 0` -- `ssePlaceholderOperations = 1` -- 唯一剩余 `sse_placeholder` 仍为 P1R-7 `events / streamEvents` - -已验证命令: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -jq -r '[.operations[] | select(.domain == "knowledge")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '["ai","content","meta","account"][] as $d | [$d, ([.operations[] | select(.domain==$d)] | group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring)))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.summary | [.completedOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出: - -```text -dedicated/needs_verification 59 -ai dedicated/needs_verification:41 -content dedicated/needs_verification:51 -meta dedicated/needs_verification:16 -account dedicated/needs_verification:33 -0 1 -``` - -PostgreSQL/Flyway `_test` 迁移 gate 已验证: - -```bash -cd muse-cloud -set -a -. ~/.config/muse-repo/infra.env -set +a -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -P1R5_TEST_DB="${P1R5_TEST_DB:-muse_local_p1r5_test}" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server \ - -Dtest=P1rKnowledgeFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R5_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -验证结果: - -```text -P1rKnowledgeFlywayMigrationIT: Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 -migrations_executed=14 -successful_migration_count=14 -target_schema_version=14 -flyway_latest=14:extend knowledge ragflow real api schema -``` - -最终 Maven gate 已验证: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server \ - -Dtest=P1rApiCoverageReportTest,P1rKnowledgeRealApiGateTest,P1rKnowledgeRouteOwnershipTest,P1rKnowledgeMigrationSqlTest -``` - -验证结果: - -```text -P1rKnowledgeRouteOwnershipTest: 6 tests, 0 failures -P1rKnowledgeMigrationSqlTest: 8 tests, 0 failures -P1rKnowledgeRealApiGateTest: 8 tests, 0 failures -P1rApiCoverageReportTest: 5 tests, 0 failures -Total: 27 tests, 0 failures, 0 errors -``` - -Knowledge focused tests 已验证: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-knowledge/muse-module-knowledge-server -DfailIfNoTests=true -``` - -验证结果: - -```text -muse-module-knowledge-server: 194 tests, 0 failures, 0 errors -``` - -完整性检查: - -```bash -git diff --check -git diff -- docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/ai/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -输出均为空。当前仍不能表述为 P1R-5 completed,也不能表述任何 Knowledge operation completed;RAGFlow dataset/document/retrieval/GraphRAG 外部 smoke 仍属于 needs_verification,不得伪造成功。 diff --git a/docs/memorys/2026-06-02-P1R外部验收依赖留痕.md b/docs/memorys/2026-06-02-P1R外部验收依赖留痕.md deleted file mode 100644 index dde2eb50..00000000 --- a/docs/memorys/2026-06-02-P1R外部验收依赖留痕.md +++ /dev/null @@ -1,331 +0,0 @@ -# P1R 外部验收依赖留痕 - -## 结论 - -本次留痕的优先目标是保存 P1R-4 / P1R-5 外部验收过程依赖,避免后续会话只看到 `needs_verification` 状态,却丢失服务地址、凭据来源、模型选择、已跑通过的 smoke 和剩余缺口。 - -当前结论: - -- P1R-4 New-API:`MiniMax-M2.5` 已有真实 New-API `/v1/chat/completions` 成功证据,并且新增的 Muse AI runtime client live acceptance 已通过,可以作为申请 AI operations 进入 `completed` 的关键外部依赖证据之一。 -- P1R-4 仍未直接修改 coverage;是否推进 `completed` 还需要补齐 Muse API 层真实触发、Muse DB task/job/runtime_call/audit 落库和失败路径证据,并经用户批准。 -- P1R-5 RAGFlow:新增的 Muse Knowledge runtime client live acceptance 已通过,覆盖 health、create dataset、upload document、parse polling、list chunks、retrieval;可以作为申请 Knowledge operations 进入 `completed` 的关键外部依赖证据之一。 -- P1R-5 仍未直接修改 coverage;是否推进 `completed` 还需要补齐 Muse API 层真实触发、Muse DB dataset/document/task/runtime_call/audit 落库和失败路径证据,并经用户批准。 -- P1R-5 新增事实:用户后续提供的 `ragflow-*` API key 已通过 RAGFlow `/api/v1/datasets` 鉴权;早先复用的 New-API 系统管理令牌对 RAGFlow API 返回 `code=109 Authentication error`,不能作为 RAGFlow 验收凭据。 - -## 可执行依赖 - -P1R 外部验收 env 已记录在: - -```bash -muse-cloud/scripts/dev/p1r-external-acceptance.env -``` - -加载方式: - -```bash -set -a -. muse-cloud/scripts/dev/p1r-external-acceptance.env -set +a -``` - -该 env 文件包含: - -- `NEW_API_SYSTEM_MANAGEMENT_TOKEN`:New-API 系统管理令牌,用于管理接口或渠道配置核查。 -- `MUSE_AI_NEW_API_BASE_URL=http://100.64.0.8:3000`:New-API OpenAI-compatible 调用地址。 -- `MUSE_AI_NEW_API_TOKEN`:大模型调用令牌。 -- `MUSE_AI_NEW_API_DEFAULT_MODEL_KEY=MiniMax-M2.5`:本次已验收成功的模型。 -- `MUSE_AI_NEW_API_*_TIMEOUT_SECONDS`:与 P1R-4 runtime adapter 默认策略对齐的 smoke 超时配置。 -- `MUSE_KNOWLEDGE_RAGFLOW_BASE_URL=http://100.64.0.8`:RAGFlow Knowledge runtime 调用地址。 -- `MUSE_KNOWLEDGE_RAGFLOW_API_KEY`:用户提供且已通过 `/api/v1/datasets` 鉴权的 RAGFlow API key。 -- `MUSE_KNOWLEDGE_RAGFLOW_GRAPHRAG_ATTRIBUTION_READY=false`:GraphRAG attribution 与 Muse 侧引用链路尚未验收,不能用作 completed 证据。 - -说明:上述凭据按用户明确授权明文记录并允许提交;它们只适用于当前内网验收上下文。若仓库未来同步到更大范围,必须先轮换 New-API 令牌。 - -## 已验证事实 - -### New-API 部署状态 - -运行位置:Tabby SSH 到 `100.64.0.8`。 - -已确认: - -- `new-api` 容器运行中,端口 `3000`,Docker health 为 healthy。 -- `GET http://127.0.0.1:3000/api/status` 可达。 -- New-API DB 运行在 `infra-postgres` 容器内,外部宿主机端口为 `100.64.0.8:5433`,应用容器内 DSN 使用 `infra-postgres:5432/new-api`。 - -### New-API 数据库取证路径 - -远端 runtime 配置: - -- 文件:`/opt/new-api/.env.runtime` -- 配置项:`SQL_DSN` -- DSN 解析事实:`database=new-api`、`user=root`、Docker 内网 `host=infra-postgres`、`port=5432`、密码存在。 - -可用查询方式: - -```bash -eval "$(python3 - <<'PY' -from pathlib import Path -import urllib.parse, shlex -line = next(l for l in Path('/opt/new-api/.env.runtime').read_text(errors='replace').splitlines() if l.startswith('SQL_DSN=')) -v = line.split('=',1)[1].strip().strip('"').strip("'") -u = urllib.parse.urlparse(v) -print('PGUSER=' + shlex.quote(urllib.parse.unquote(u.username or ''))) -print('PGPASSWORD=' + shlex.quote(urllib.parse.unquote(u.password or ''))) -print('PGDATABASE=' + shlex.quote(u.path.lstrip('/'))) -PY -)" -docker exec -e PGPASSWORD="$PGPASSWORD" infra-postgres \ - psql -h 127.0.0.1 -U "$PGUSER" -d "$PGDATABASE" -qAtc 'select current_database(), current_user, now();' -``` - -已验证结果摘要: - -- DB 查询返回 `new-api | root | `。 -- token 表存在 `muse`、`muse-long-lived`、`rag` 等 token。 -- `muse-long-lived` token 指纹:`len=48 sha256=7b03ff4b23ea`。 - -### MiniMax-M2.5 chat smoke - -运行位置:Tabby SSH 到 `100.64.0.8`。 - -请求摘要: - -- endpoint:`http://127.0.0.1:3000/v1/chat/completions` -- token:`muse-long-lived`,仅记录指纹,不在日志摘要中展开。 -- model:`MiniMax-M2.5` -- prompt:`只输出 OK` -- `stream=false` -- `temperature=0` -- request id:`p1r4-minimax-m25-20260602141936` - -响应摘要: - -- HTTP 200 -- `choices=1` -- `finish=stop` -- `content_prefix='OK'` -- usage:`prompt_tokens=41`、`completion_tokens=87`、`total_tokens=128` -- 响应耗时约 `6.6s` - -New-API 日志摘要: - -- `record consume log` -- `model_name=MiniMax-M2.5` -- `token_name=muse-long-lived` -- `request_path=/v1/chat/completions` -- `POST /v1/chat/completions` HTTP 200 - -### P1R-4 Muse runtime client live acceptance - -运行位置:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud`。 - -命令: - -```bash -set -a -. scripts/dev/p1r-external-acceptance.env -set +a -MUSE_P1R_EXTERNAL_ACCEPTANCE=true JAVA_HOME=$(/usr/libexec/java_home -v 21) \ - mvn -pl muse-module-ai/muse-module-ai-server \ - -Dtest=P1rNewApiLiveAcceptanceIT#shouldCallNewApiThroughMuseRuntimeClientAndPrintRedactedEvidence test -``` - -已验证结果摘要: - -- `Tests run: 1, Failures: 0, Errors: 0, Skipped: 0` -- `BUILD SUCCESS` -- endpoint:`http://100.64.0.8:3000/v1/chat/completions` -- token:`len=51 sha256Prefix=0c1de5374f1a` -- model:`MiniMax-M2.5` -- `summaryNormalized=OK` -- `finishReason=stop` -- usage:`promptTokens=139`、`completionTokens=85`、`totalTokens=224` -- `providerRequestIdPresent=true` - -New-API 远端日志摘要: - -- `new-api` 容器为 `Up healthy`。 -- `POST /v1/chat/completions` HTTP 200。 -- `model_name=MiniMax-M2.5`。 -- `token_name=muse`。 -- `prompt_tokens=139`、`completion_tokens=85`。 -- `request_path=/v1/chat/completions`。 -- use time 约 `2s`,GIN relay 耗时约 `1.63s`。 - -### P1R coverage 状态 - -本次没有修改 coverage。 - -当前基线仍应保持: - -- AI 41:`dedicated / needs_verification` -- Knowledge 59:`dedicated / needs_verification` -- completed operations:`0` -- 唯一 `sse_placeholder`:P1R-7 `events / streamEvents / GET /app-api/muse/events` - -### RAGFlow API 鉴权与初始数据集状态 - -运行位置:本机 worktree,直连 `100.64.0.8`。 - -已验证: - -- `GET http://100.64.0.8/v1/system/healthz`:HTTP 200,`status/db/doc_engine/redis/storage = ok`。 -- `GET http://100.64.0.8:9380/v1/system/healthz`:HTTP 200,`status/db/doc_engine/redis/storage = ok`。 -- 使用 New-API 系统管理令牌请求 `GET /api/v1/datasets`:HTTP 200 业务返回 `code=109 Authentication error: API key is invalid!`。 -- 使用用户提供的 RAGFlow API key 请求 `GET /api/v1/datasets?page=1&page_size=10`:HTTP 200,业务返回 `code=0`。 -- 首次验收前 RAGFlow tenant 下 `dataset_count=0`、`total=0`,说明鉴权可用但没有可复用验收 dataset/document。 - -### P1R-5 Muse runtime client live acceptance - -运行位置:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud`。 - -命令: - -```bash -set -a -. scripts/dev/p1r-external-acceptance.env -set +a -MUSE_P1R_EXTERNAL_ACCEPTANCE=true JAVA_HOME=$(/usr/libexec/java_home -v 21) \ - mvn -pl muse-module-knowledge/muse-module-knowledge-server \ - -Dtest=P1rRagFlowLiveAcceptanceIT#shouldCallRagFlowThroughMuseRuntimeClientAndPrintRedactedEvidence test -``` - -已验证结果摘要: - -- `Tests run: 1, Failures: 0, Errors: 0, Skipped: 0` -- `BUILD SUCCESS` -- datasetName:`p1r-live-1780424506407` -- datasetId:`e8145fb65eaf11f199d5dd203c62e4a2` -- documentId:`e818cb1e5eaf11f199d5dd203c62e4a2` -- health:`SUCCEEDED` -- createDataset:`SUCCEEDED` -- uploadDocuments:`SUCCEEDED` -- startParseDocuments:`ACCEPTED` -- pollDocumentStatuses:`SUCCEEDED` -- document status:`museStatus=searchable`、`ragflowRun=DONE`、`progress=1` -- listChunks:`SUCCEEDED` -- retrieveChunks:`SUCCEEDED` -- `retrievalChunksCount=1` -- GraphRAG:`FAILED / ATTRIBUTION_NOT_CONFIGURED`,这是 attribution 未配置时的预期 fail-closed 行为,不作为 GraphRAG completed 证据。 - -RAGFlow 远端日志摘要: - -- `ragflow` 容器为 `Up healthy`。 -- 依赖容器 `infra-mysql`、`infra-redis`、`infra-minio`、`infra-ragflow-infinity`、`infra-nginx` 为 healthy。 -- 日志出现 `POST /api/v1/datasets` HTTP 200。 -- 日志出现 `POST /api/v1/datasets/{datasetId}/documents` HTTP 200。 -- 日志出现 `POST /api/v1/datasets/{datasetId}/chunks` HTTP 200。 -- 日志出现 `GET /api/v1/datasets/{datasetId}/documents` HTTP 200。 -- 日志出现 `GET /api/v1/datasets/{datasetId}/documents/{documentId}/chunks` HTTP 200。 -- 日志出现 `POST /api/v1/retrieval` HTTP 200。 -- parse 日志显示生成 1 个 chunk、完成 Embedding、Indexing done、Task done。 - -### RAGFlow create dataset 兼容性修复 - -已验证事实: - -- 真实 RAGFlow `POST /api/v1/datasets` 带 `config:{}` 时返回 `code=101 Extra inputs are not permitted`。 -- 真实 RAGFlow `POST /api/v1/datasets` 只发送 `name` 时返回 `code=0 data.id`。 -- Muse adapter 已调整为 createDataset 只发送 `name`;`updateDatasetConfig` 仍保留独立提交 config 的能力。 -- `pollDocumentStatuses` 已调整为使用真实支持的 `?id=`;多 document id 会在 Muse adapter 内 fail closed 为 `VALIDATION_ERROR`,避免生成 RAGFlow 不支持的批量查询。 - -## 推断 - -- New-API 服务、认证、模型路由、上游 LLM 调用、消费日志链路已经对 `MiniMax-M2.5` 跑通。 -- P1R-4 之前阻塞的“New-API chat/completions 未验收成功”已解除。 -- RAGFlow 服务、鉴权、dataset/document/chunk/retrieval 链路已经通过 Muse Knowledge runtime client 跑通。 -- 仅凭 runtime client live acceptance 还不足以证明 Muse 自身 AI 41 个 operation 和 Knowledge 59 个 operation 已完成端到端验收;还需要 Muse API 触发、任务状态、runtime call、job 和审计链路证据。 - -## 剩余缺口 - -P1R-4 仍需补齐: - -1. 通过 Muse dedicated AI API 触发一次真实 AI task,而不是只调用 runtime client。 -2. 验证 Muse DB 中 task/job/runtime_call/event/audit 记录形成成功态。 -3. 验证失败路径仍 fail closed,不因配置了真实 New-API 而伪造 success。 -4. 经用户批准后,才可以申请把对应 AI operations 从 `needs_verification` 推进到 `completed`。 - -P1R-5 仍需补齐: - -1. 通过 Muse dedicated Knowledge API 触发真实 dataset/document/retrieval 流程,而不是只调用 runtime client。 -2. 验证 Muse DB 中 dataset/document/task/runtime_call/audit 记录形成成功态。 -3. 验证失败路径仍 fail closed,不因配置了真实 RAGFlow 而伪造 success。 -4. GraphRAG 需要确认默认 chat model、New-API 归因和 token 预算后再测。 -5. 经用户批准后,才可以申请把对应 Knowledge operations 从 `needs_verification` 推进到 `completed`。 - -## 推荐下一步命令 - -加载 New-API 验收配置: - -```bash -set -a -. muse-cloud/scripts/dev/p1r-external-acceptance.env -set +a -``` - -直接重跑 New-API chat smoke: - -```bash -curl --noproxy '*' -sS -m 150 \ - -H "Authorization: Bearer ${MUSE_AI_NEW_API_TOKEN}" \ - -H 'Content-Type: application/json' \ - --data '{"model":"MiniMax-M2.5","messages":[{"role":"user","content":"只输出 OK"}],"stream":false,"temperature":0}' \ - "${MUSE_AI_NEW_API_BASE_URL}/v1/chat/completions" -``` - -Muse runtime adapter 方向应优先验证: - -```bash -cd muse-cloud -set -a -. scripts/dev/p1r-external-acceptance.env -set +a -MUSE_P1R_EXTERNAL_ACCEPTANCE=true JAVA_HOME=$(/usr/libexec/java_home -v 21) \ - mvn -pl muse-module-ai/muse-module-ai-server \ - -Dtest=P1rNewApiLiveAcceptanceIT#shouldCallNewApiThroughMuseRuntimeClientAndPrintRedactedEvidence test -MUSE_P1R_EXTERNAL_ACCEPTANCE=true JAVA_HOME=$(/usr/libexec/java_home -v 21) \ - mvn -pl muse-module-knowledge/muse-module-knowledge-server \ - -Dtest=P1rRagFlowLiveAcceptanceIT#shouldCallRagFlowThroughMuseRuntimeClientAndPrintRedactedEvidence test -``` - -注意:上面的 Maven 测试是 runtime client live acceptance,不等价于 Muse API 端到端 completed 证据。真正 completed 证据必须包含 Muse API 层触发、Muse 运行时对 New-API/RAGFlow 的真实调用、Muse 侧落库链路、审计记录和失败路径。 - -## 新增 live acceptance harness - -2026-06-02 新增两个默认跳过的 opt-in JUnit 验收入口,用于把当前 Muse runtime client 直接接到外部 New-API / RAGFlow,并输出脱敏 JSON 证据摘要: - -- P1R-4:`muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/P1rNewApiLiveAcceptanceIT.java` -- P1R-5:`muse-module-knowledge/muse-module-knowledge-server/src/test/java/cn/iocoder/muse/module/knowledge/application/muse/facade/P1rRagFlowLiveAcceptanceIT.java` - -默认未设置 `MUSE_P1R_EXTERNAL_ACCEPTANCE=true` 时只跳过,不失败: - -```bash -cd muse-cloud -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ - mvn -pl muse-module-ai/muse-module-ai-server -Dtest=P1rNewApiLiveAcceptanceIT test -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ - mvn -pl muse-module-knowledge/muse-module-knowledge-server -Dtest=P1rRagFlowLiveAcceptanceIT test -``` - -真实外部验收命令: - -```bash -cd muse-cloud -set -a -. scripts/dev/p1r-external-acceptance.env -set +a -export MUSE_P1R_EXTERNAL_ACCEPTANCE=true -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ - mvn -pl muse-module-ai/muse-module-ai-server -Dtest=P1rNewApiLiveAcceptanceIT test -JAVA_HOME=/Users/qingse/Library/Java/JavaVirtualMachines/corretto-21.0.7/Contents/Home \ - mvn -pl muse-module-knowledge/muse-module-knowledge-server -Dtest=P1rRagFlowLiveAcceptanceIT test -``` - -说明: - -- 当前 `muse-cloud` checkout 没有 `./mvnw`,本机验证使用 `mvn`;如果后续补回 Maven Wrapper,可把命令中的 `mvn` 替换为 `./mvnw`。 -- harness 输出只打印 endpoint、模型、dataset/document id、状态、耗时、usage、token/key 长度和 sha256 前 12 位,不打印完整 token/key。 -- P1R-5 RAGFlow smoke 会创建唯一 `p1r-live-时间戳` dataset 并保留,不做删除,便于外部验收取证。 -- GraphRAG 默认不作为 completed 证据;`MUSE_KNOWLEDGE_RAGFLOW_GRAPHRAG_ATTRIBUTION_READY=false` 时只验证 Muse adapter fail closed 为 `ATTRIBUTION_NOT_CONFIGURED`。真实 GraphRAG run/trace 需要额外设置 `MUSE_KNOWLEDGE_RAGFLOW_GRAPHRAG_ATTRIBUTION_READY=true` 和 `MUSE_KNOWLEDGE_RAGFLOW_GRAPHRAG_DATASET_ID`。 -- 该 harness 只提供外部依赖 live evidence,不等价于 coverage completed;coverage 仍需 Muse API 端到端调用、落库、任务状态、审计和失败路径证据后再单独申请更新。 diff --git a/docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md b/docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md deleted file mode 100644 index 51c20fc6..00000000 --- a/docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md +++ /dev/null @@ -1,178 +0,0 @@ -# P1R-4 / P1R-5 完成状态推进留痕 - -日期:2026-06-03 - -## 阶段 - -- P1R-4 AI / New-API Real API。 -- P1R-5 Knowledge / RAGFlow Real API。 - -## 结论 - -本次在用户明确批准后,只把 P1R-4 AI 与 P1R-5 Knowledge 的 coverage `completionStatus` 从 `needs_verification` 推进到 `completed`。 - -本次没有推进 P1R-1 Content、P1R-2 Meta、P1R-3 Account、P1R-6 Market 或 P1R-7 Events。 - -## 执行位置 - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -## 已验证事实 - -开始前已确认: - -```text -## dev/1.0.0...origin/dev/1.0.0 -Already up to date. -HEAD 48c750b test(p1r): 补齐 AI 与 Knowledge 外部端到端验收 -``` - -用户批准前,coverage 状态为: - -```text -AI 41 dedicated / needs_verification -Knowledge 59 dedicated / needs_verification -completedOperations = 0 -``` - -用户批准后,本次生成的 coverage 状态为: - -```text -completedOperations 100 -needsVerificationOperations 100 -incompleteOperations 33 -genericPersistenceOperations 32 -ssePlaceholderOperations 1 -``` - -按域统计: - -```text -account 33 dedicated / needs_verification -ai 41 dedicated / completed -content 51 dedicated / needs_verification -events 1 sse_placeholder / incomplete -knowledge 59 dedicated / completed -market 32 generic_persistence / incomplete -meta 16 dedicated / needs_verification -``` - -`completed` 只来自: - -```text -41 ai dedicated -59 knowledge dedicated -``` - -本次修改文件: - -```text -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/P1rAiRealApiGateTest.java -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/P1rKnowledgeRealApiGateTest.java -muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -本次未修改: - -```text -docs/api-contracts/ai/openapi.yaml -docs/api-contracts/knowledge/openapi.yaml -``` - -## 验证命令与结果 - -coverage 生成与校验: - -```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 -``` - -preflight jq 统计: - -```bash -jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -结果: - -```text -100 100 33 32 1 -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 sse_placeholder/incomplete:1 -knowledge 59 dedicated/completed:59 -market 32 generic_persistence/incomplete:32 -meta 16 dedicated/needs_verification:16 -``` - -Maven gate: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest test -``` - -结果: - -```text -P1rKnowledgeRealApiGateTest: Tests run: 8, Failures: 0, Errors: 0, Skipped: 0 -P1rAiRealApiGateTest: Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -P1rApiCoverageReportTest: Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 -Total: Tests run: 19, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -diff 检查: - -```bash -git diff --check -``` - -结果:无输出。 - -## 关键边界 - -- 本次 `completed` 推进依赖用户明确批准和前序 P1R-4 / P1R-5 外部端到端验收证据。 -- `dedicated gate PASS` 仍不等同于 `completed`;只有本次批准覆盖的 AI / Knowledge 被推进。 -- GraphRAG 当前仍只有 `ATTRIBUTION_NOT_CONFIGURED` fail-closed 边界证据,不作为 P1R-5 正向 completed 证据。 -- Market 仍停留在 P1R-6 `generic_persistence / incomplete`。 -- Events 仍停留在 P1R-7 `sse_placeholder / incomplete`。 - -## 执行协作规则 - -后续继续 P1R 主线时,implementation agent 的复用规则如下: - -- 同一实现任务内,复用最近一次负责该任务的 implementer,避免同一任务反复 fresh implementer 丢失上下文。 -- 只有进入新的实现 Task 时,才启用 fresh implementer。 -- 每个实现 Task 完成后的 review gate 不复用 reviewer,仍保持 fresh spec review 和 fresh quality / feasibility review。 -- review 结论只能证明对应 gate 的检查结果,不能替代真实运行态验收或 coverage `completed` 批准。 - -## 后续动作 - -如需正式收口本轮变更,建议提交前再次执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -cd muse-cloud && JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest test -git diff --check -``` diff --git a/docs/memorys/2026-06-03-P1R6Market规格计划.md b/docs/memorys/2026-06-03-P1R6Market规格计划.md deleted file mode 100644 index 820bb12b..00000000 --- a/docs/memorys/2026-06-03-P1R6Market规格计划.md +++ /dev/null @@ -1,221 +0,0 @@ -# P1R6 Market 规格计划 - -日期:2026-06-03 - -## 背景 - -本次进入 P1R-6 Market Real API 的规格、计划和 review gate 阶段,只完成事实盘点、规格、执行计划、review 修订和阶段留痕,不进入代码实现。 - -真实工作面: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -分支: - -```text -dev/1.0.0 -``` - -开始前已执行: - -```bash -git status --short --branch -git log --oneline -5 -git pull --ff-only origin dev/1.0.0 -``` - -结果: - -```text -## dev/1.0.0...origin/dev/1.0.0 -48c750b test(p1r): 补齐 AI 与 Knowledge 外部端到端验收 -Already up to date. -``` - -## 已验证事实 - -当前 coverage summary: - -```text -completedOperations 100 -needsVerificationOperations 100 -incompleteOperations 33 -genericPersistenceOperations 32 -ssePlaceholderOperations 1 -``` - -当前按域状态: - -```text -account 33 dedicated / needs_verification -ai 41 dedicated / completed -content 51 dedicated / needs_verification -events 1 sse_placeholder / incomplete -knowledge 59 dedicated / completed -market 32 generic_persistence / incomplete -meta 16 dedicated / needs_verification -``` - -Market 32 个 operation 当前全部为: - -```text -generic_persistence / incomplete -``` - -当前 Market 实现事实: - -- `docs/api-contracts/market/openapi.yaml` 声明 32 个 Market operation。 -- `muse-module-market-server` 当前合同入口仍是 `AdminMuseMarketContractController` 和 `AppMuseMarketContractController`。 -- 当前 Market 合同入口统一委托 `MuseContractPersistenceService`,不能作为真实 API 完成证据。 -- `muse-module-market-server` 当前只依赖 `muse-module-market-api`、security、mybatis。 -- `muse-server` 当前同时装配 `muse-module-market-server` 和 `muse-module-member-server`。 -- `muse-module-member-server` 已有 `MarketAccountProjectionFacade`、默认 fail-closed `UnavailableMarketAccountProjectionFacade`、`muse_account_record_projection` DO/Mapper 和 V11 DDL。 - -当前 DDL 事实: - -- V6 已有 Market 初始资产、版本、安装、发布申请、申诉和 handoff 表。 -- V8 已有 workflow task、favorite、purchase 表。 -- V11 已有 Account 查询读模型 `muse_account_record_projection`。 -- P1R-5 已使用 V14;P1R-6 如新增迁移,推荐使用 `V15__extend_market_real_api_schema.sql`。 - -## 本阶段产物 - -- 规格:`docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md` -- 计划:`docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md` -- 留痕:`docs/memorys/2026-06-03-P1R6Market规格计划.md` - -## Review 结果 - -本阶段按要求执行 fresh review gate。 - -Spec compliance review: - -```text -PASS_WITH_WARNINGS -``` - -已修复首轮阻塞点: - -- 计划开头错误写成只允许执行 `Task 1-12`,但正文实际包含 Task 13。已修为 `Task 1-13`。 - -Spec review 非阻塞提醒: - -- handoff token 业务状态里的 `completed` 不能和 coverage `completionStatus=completed` 混淆。计划已补充消歧说明。 - -Quality / feasibility review: - -```text -PASS_WITH_WARNINGS -``` - -已修复阻塞点: - -- Task 5 不再要求 Market 模块另建同名 `MarketAccountProjectionFacade` / `UnavailableMarketAccountProjectionFacade`。 -- 已明确 P1R-6 Account 投影采用最小可执行接线方案:`muse-module-market-server` 显式依赖 `muse-module-member-server`,复用 member 侧既有 `MarketAccountProjectionFacade`、`AccountRecordProjectionDO`、`AccountRecordProjectionMapper`。 -- 已明确 `muse_market_account_projection` 只表达 Market 侧 projection sync / outbox 状态,不是 Account 查询读模型。 -- 已明确 Account 查询读模型只能是 V11 已有 `muse_account_record_projection`。 -- 已要求实现阶段用 Maven reactor 或 dependency-tree 验证不形成 Maven 循环,并用 Spring context 测试证明真实 provider 替换默认 unavailable bean。 - -Quality review 非阻塞提醒: - -- 当前 worktree 仍有 P1R-4 / P1R-5 coverage/test/script 未提交 diff,P1R-6 spec/plan/memory 也是未跟踪文件。进入 P1R-6 实现前必须先提交或明确保留这些 diff,避免阶段混账。 - -## 关键设计结论 - -- P1R-6 目标状态只能是 `dedicated / needs_verification`,不能写成 `completed`。 -- Market owner 是 `muse-module-market`,负责市场来源侧事实、授权、安装、handoff、发布、治理和申诉。 -- Market 不直接写 AI / Knowledge / Content canonical facts。 -- 购买只产生授权,不等于安装;安装只进入账户可用列表,不等于绑定目标 owner。 -- Account 只消费 Market 写入的投影;Account 查询读模型为 `muse_account_record_projection`。 -- `needs_recheck` 不能作为 Market `SourceStatus` 值,只能表达为 action policy / recheck reasons。 -- P1R-7 统一 Events SSE 不属于 P1R-6。 -- 不修改 `docs/api-contracts/market/openapi.yaml`,不修改 scanner 掩盖缺口。 - -## 验证命令与结果 - -coverage audit: - -```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 -``` - -coverage 对账: - -```bash -jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain == "market") | [.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json | sort | uniq -c -``` - -结果: - -```text -100 100 33 32 1 -32 generic_persistence incomplete -``` - -文档结构检查: - -```bash -python3 - <<'PY' -import re, pathlib -plan=pathlib.Path('docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md').read_text() -spec=pathlib.Path('docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md').read_text() -print('plan_tasks', re.findall(r'^## Task (\d+):', plan, re.M)) -print('spec_operation_rows', len(re.findall(r'^\| `[^`]+` \| (GET|POST|DELETE|PUT|PATCH) \| `/', spec, re.M))) -print('plan_operation_lines', len(re.findall(r'^[A-Za-z].+ (GET|POST|DELETE|PUT|PATCH) /', plan, re.M))) -PY -``` - -结果: - -```text -plan_tasks ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'] -spec_operation_rows 32 -plan_operation_lines 32 -``` - -diff hygiene: - -```bash -git diff --check -``` - -结果:无输出。 - -OpenAPI diff 检查: - -```bash -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml -``` - -结果:无输出。 - -## 当前阶段边界 - -本阶段只完成 P1R-6 规格、计划、review 修订和 memory 留痕,没有实现代码。 - -当前不应把任何 Market operation 标为 `completed`。 - -## 下一步 - -进入 P1R-6 实现前必须先处理工作面: - -1. 提交或明确保留 P1R-4 / P1R-5 coverage completed 推进 diff。 -2. 提交或明确保留 P1R-6 spec / plan / memory 文档 diff。 -3. 再按 `docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md` Task 1-13 执行。 - -实现阶段执行规则: - -- 同一实现 Task 内复用最近 implementer。 -- 新 Task 才 fresh implementer。 -- 每个 Task 完成后仍必须 fresh spec review 和 fresh quality / feasibility review。 -- review 结论不能替代真实运行态验收或 coverage `completed` 批准。 diff --git a/docs/memorys/2026-06-04-P1R6Market真实API收口.md b/docs/memorys/2026-06-04-P1R6Market真实API收口.md deleted file mode 100644 index 39a8ba7a..00000000 --- a/docs/memorys/2026-06-04-P1R6Market真实API收口.md +++ /dev/null @@ -1,290 +0,0 @@ -# P1R6 Market 真实 API 收口 - -日期:2026-06-05 - -## 结论 - -P1R-6 Market Task 13 已完成最终验证、coverage 报告对账、真实 PostgreSQL / Flyway `_test` 迁移验收与 memory 留痕。 - -本次只把 Market 32 个 operation 收口到 `dedicated / needs_verification`,没有把 Market 标记为 `completed`。 - -P1R coverage 当前关键数字为: - -```text -completedOperations 100 -needsVerificationOperations 132 -incompleteOperations 1 -genericPersistenceOperations 0 -ssePlaceholderOperations 1 -``` - -统一 Events SSE 仍只有 P1R-7 `streamEvents`,状态为 `sse_placeholder / incomplete`;本次没有进入 P1R-7。 - -## 已验证事实 - -### 工作区与边界 - -本次验证在指定 worktree 执行: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支: - -```text -dev/1.0.0 -``` - -当前远端: - -```text -origin ssh://git@100.64.0.8:2222/admin/oh-my-muse.git -``` - -本次没有在 `/Users/qingse/Sync/local-git/oh-my-muse` 写文件。 - -本次没有清理、回退当前 dirty / untracked baseline。 - -### coverage 重新生成 - -执行命令: - -```bash -tmpvenv=$(mktemp -d /tmp/p1r6-py.XXXXXX) -/opt/homebrew/bin/python3.12 -m venv "$tmpvenv" -"$tmpvenv/bin/python" -m pip install --quiet PyYAML -"$tmpvenv/bin/python" muse-cloud/scripts/p1r-audit-api-coverage.py --check -code=$? -rm -rf "$tmpvenv" -echo scanner_exit=$code -exit $code -``` - -结果: - -```text -Generated docs/superpowers/reports/p1r-api-coverage.json -Generated docs/superpowers/reports/p1r-api-coverage.md -scanner_exit=0 -``` - -补充事实:本轮默认 `python3` 指向的 Framework Python 3.12 在导入标准库 `dataclasses -> inspect -> dis -> opcode` 阶段卡住,`timeout 60 python3 -u muse-cloud/scripts/p1r-audit-api-coverage.py --check` 返回 `scanner_exit=124`。使用 Homebrew Python 3.12 临时 venv 安装 PyYAML 后,同一 scanner 脚本通过;这不是 coverage 逻辑失败。 - -### coverage 对账 - -执行命令: - -```bash -jq -r '.operations | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -结果: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 sse_placeholder/incomplete:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -执行命令: - -```bash -jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -结果: - -```text -100 132 1 0 1 -``` - -执行命令: - -```bash -jq -r '.operations[] | select(.domain == "market") | [.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json | sort -``` - -结果:Market 32 个 operation 全部为 `dedicated / needs_verification`。 - -额外核验: - -```bash -jq -r '[.operations[] | select(.domain=="market" and (.completionStatus=="completed" or .implementationStatus!="dedicated" or .completionStatus!="needs_verification"))] | length' docs/superpowers/reports/p1r-api-coverage.json -jq -r '[.operations[] | select(.domain=="market")] | length' docs/superpowers/reports/p1r-api-coverage.json -``` - -结果: - -```text -0 -32 -``` - -### Maven gate - -执行命令: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rMarketRealApiGateTest,P1rMarketRouteOwnershipTest,P1rMarketMigrationSqlTest test -``` - -结果: - -```text -Tests run: 24, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -本次计划指定的 `-pl muse-server` 命令没有触发 reactor 依赖或无匹配测试模块失败,因此没有执行 fallback 复跑。 - -### Market focused tests - -先发现 Market 测试类: - -```bash -rg --files muse-cloud/muse-module-market/muse-module-market-server/src/test muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api | sort -``` - -发现 `muse-module-market-server/src/test` 下 21 个 Market 测试类,覆盖 service、controller 和 mapper。 - -执行聚合测试: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-market/muse-module-market-server -am -Dtest='*Market*Test' -Dsurefire.failIfNoSpecifiedTests=false test -``` - -结果: - -```text -Tests run: 191, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -Surefire XML 交叉汇总结果: - -```text -market_server_xml_classes 21 -market_server_xml_totals 191 0 0 0 -``` - -### Task 12 Flyway IT - -执行命令: - -```bash -cd muse-cloud -set -a -. ~/.config/muse-repo/infra.env -set +a -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -P1R6_TEST_DB="${P1R6_TEST_DB:-muse_local_p1r6_test}" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - timeout 180 mvn -o test -pl muse-server \ - -Dtest=P1rMarketFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R6_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -结果: - -```text -flyway_success=true -flyway_url=jdbc:postgresql://:5433/muse_local_p1r6_test -flyway_locations_requested=filesystem:sql/muse -flyway_locations_effective=filesystem:/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud/sql/muse -migrations_executed=15 -successful_migration_count=15 -target_schema_version=15 -flyway_latest=15:extend market real api schema -schema_version=15 -Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -Task 12 真实 Flyway IT 当前保持通过:`P1rMarketFlywayMigrationIT` 3 tests,验收到 V15,且 V1-V15 共 15 条 SQL migration 成功。 - -补充事实:不带 `-Dflyway.postgresql.transactional.lock=false` 的同一 Flyway IT 两次在 `PostgreSQLAdvisoryLockTemplate` / `flyway.migrate()` 路径卡住,必须终止 Maven/Surefire 进程;独立 `psql` 在临时 `_test` 探针库按 V1-V15 顺序执行全部 SQL 成功,证明 V15 SQL 本身可执行。加上 Flyway PostgreSQL 非事务 advisory lock 参数后,独立探针库和计划默认 `muse_local_p1r6_test` 均通过;探针库已清理。 - -### diff hygiene - -执行命令: - -```bash -git diff --check -``` - -结果:无输出。 - -执行命令: - -```bash -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -结果:无输出。 - -因此,本次没有修改 Market / AI / Knowledge OpenAPI。 - -本次也没有修改 `muse-cloud/scripts/p1r-audit-api-coverage.py` 来掩盖缺口。 - -### review gate 记录 - -P1R-6 Market 实现阶段本轮 fresh review gate 结论为: - -```text -Spec compliance review: PASS_WITH_WARNINGS -Quality / feasibility review: PASS_WITH_WARNINGS -``` - -reviewer: - -```text -spec compliance reviewer: 019e9576-b387-7671-8e4b-06573003b705 -quality / feasibility reviewer: 019e957c-b7d3-7170-bdbd-64e99176970e -``` - -这些 review 结论只证明 P1R-6 实现边界与质量可行性通过带警告评审,不能替代真实运行态 / 端到端验收,也不能作为把 Market coverage 推进到 `completed` 的依据。 - -## 推断 - -基于 coverage scanner、route ownership gate、Market focused tests、muse-server gate 与真实 Flyway IT 的组合证据,可以判断 P1R-6 Market 已具备 dedicated 入口、Market owner 路由归属、核心服务/controller/mapper 测试覆盖和 V15 数据库迁移验收基础。 - -由于本次没有做真实运行态 / 端到端用户流验收,不能推断 Market 已达到业务完成态。 - -## 假设 - -本次假设 `~/.config/muse-repo/infra.env` 中的 PostgreSQL 连接配置仍是项目允许用于 P1R `_test` Flyway 验收的本地/内网测试配置。 - -本次假设当前 dirty / untracked baseline 来自 Tasks 3-12 或用户已有工作,因此只在 Task 13 指定范围内新增 memory、重新生成 coverage 报告,不做清理或回退。 - -## 剩余 warnings - -- `recordFailed` 仍未被业务写路径使用,失败命令审计和重放语义仍弱;本轮 quality review 判定为 P2 warning,不阻塞 `dedicated / needs_verification`。 -- 并发首购覆盖是 Mockito 分支测试,不是真实 PostgreSQL 双事务竞争测试;若后续申请 Market `completed`,应补真实 DB 并发 / HTTP 幂等验收。 -- `P1rMarketRouteOwnershipTest` 仍是源码扫描 gate,只能证明当前源码静态匹配结果,不能替代运行态路由注册和真实 HTTP 调用验收。 -- `P1rApiCoverageReportTest` / coverage gate 读取生成后的 JSON 报告,能防止报告状态回退,但不能单独证明运行态 API 行为。 -- 当前环境默认 `python3` 导入标准库会卡住;后续复跑 scanner 建议显式使用可工作的 Homebrew Python 3.12 临时 venv,或先修复默认 Python 运行时。 -- 当前环境 Flyway PostgreSQL transactional advisory lock 会卡住;后续复跑 `P1rMarketFlywayMigrationIT` 建议带 `-Dflyway.postgresql.transactional.lock=false`。 -- `P1rMarketFlywayMigrationIT` 会通过 `System.setProperty` 把 `p1r.flyway.url` 和 `p1r.flyway.user` 改成脱敏值;这是为了减少 Surefire XML 泄露,但仍属于 JVM 全局属性副作用。 -- Surefire XML 可能包含本地绝对路径,例如 worktree 路径和 Flyway effective locations;外发测试报告前应清洗本地路径。 - -## 后续建议 - -如要把 Market 从 `needs_verification` 推进到 `completed`,需要用户单独批准,并补充真实运行态 / 端到端验收:HTTP 层真实请求、权限边界、幂等重放、失败路径、数据库落库、投影/outbox、审计日志和跨 owner 传播证据。 - -P1R-7 Events SSE 应另起任务,不应混入 P1R-6 Market Task 13。 - -## 本次改动文件 - -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` -- `docs/memorys/2026-06-04-P1R6Market真实API收口.md` diff --git a/docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md b/docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md deleted file mode 100644 index b3559d9d..00000000 --- a/docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md +++ /dev/null @@ -1,137 +0,0 @@ -# P1R7a Events SSE 审阅版留痕 - -## 结论 - -P1R-7a Events SSE 已完成只读 preflight、审阅版文档、执行版计划和 fresh review 双 PASS;当前可以在用户明确批准后从执行版 Task 1 开始实现。 - -P1R-7a 是总 P1R-7 End-to-End Acceptance 的前置子任务,不是全量 P1R-7 completed approval。阶段目标只允许把 `streamEvents` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`,不能标为 `completed`。 - -## 已验证事实 - -- 正确工作区:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`,远端:`origin/dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 输出 `Already up to date`。 -- 当前最新提交链路: - - `3deb0de test(p1r): 收口 Market 真实 API 门禁` - - `621d45a feat(p1r): 落地 Market 真实 API` - - `f02defd docs(p1r): 补齐 Market 真实 API 规格计划` -- coverage summary: - - `completedOperations = 100` - - `needsVerificationOperations = 132` - - `incompleteOperations = 1` - - `genericPersistenceOperations = 0` - - `ssePlaceholderOperations = 1` -- Events 当前只有 1 个 operation:`streamEvents GET /app-api/muse/events`,状态为 `sse_placeholder / incomplete`。 -- 当前 `AppMuseEventsController` 发送固定 `muse event stream ready` notification 后立即 `complete()`。 -- `docs/api-contracts/events/openapi.yaml` 只声明 `200 text/event-stream` 和 `401`,不声明 `400` 或版本错误响应。 -- `muse-studio/src/lib/sse.ts` 的全局事件流当前使用 `new EventSource(url)`,无法发送 `Authorization` 和 `X-API-Version` header。 - -## 本轮产物 - -- 审阅版文档:`docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md` -- 执行版计划:`docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md` -- 模块上下文:`docs/superpowers/.agent`,当前被 `.gitignore` 忽略,提交前如需纳入版本库必须单独处理。 - -审阅版关键决策: - -1. 命名为 `P1R-7a:Events SSE 前置子任务`,避免和总 P1R-7 End-to-End Acceptance 混淆。 -2. 新增独立 `muse-module-events-api/server` 作为 Events owner,不继续把统一事件流放在 Content 模块内。 -3. 新增 `muse_unified_event` 统一事件投影,推荐迁移版本 `V16__extend_events_sse_schema.sql`。 -4. source owner 通过 Events API / outbox 契约幂等发布事件;Events 不反向依赖 AI、Knowledge、Market、Account 或 Member server。 -5. `lastEventId` 推荐采用 `muse:`。 -6. 不修改 OpenAPI 时,非法 cursor / version 不支持在 SSE 连接内发送已声明 `error` event 后结束。 -7. 前端全局事件流从 `EventSource` 切换到 fetch 流式 SSE,以支持 app bearer auth 和 `X-API-Version`。 - -## Review 结果 - -### 审阅版第一轮 spec compliance review - -- 结果:FAIL。 -- blocker: - 1. P1R-7 阶段口径冲突。 - 2. 跨模块 projector 入口契约缺失。 - 3. 不改 OpenAPI 与连接前业务错误冲突。 - 4. 浏览器 EventSource 鉴权和 `X-API-Version` 链路未闭合。 - -### 审阅版修正后 fresh spec compliance re-review - -- 结果:PASS。 -- 复审结论:4 个 blocker 已收敛;剩余为执行版确认和落地风险。 - -### 审阅版 fresh quality / feasibility review - -- 结果:PASS with warnings。 -- warnings: - 1. 执行版必须明确 Maven 接线任务,并用 dependency tree 证明无循环。 - 2. `EventsPublishFacade.publish(...)` 必须固定重复发布、rejected、blocked 的返回语义。 - 3. fetch SSE 不能只替换构造函数,必须保留 `close()`、`on(event, handler)`、`lastEventId`、断线重连和事件名分发。 - 4. `notification.type` 合同很窄,首批 source mapping 必须严格限缩。 - 5. 失败路径要写成测试任务:非法 cursor、DB 异常、线程池拒绝、客户端断开、跨 tenant replay。 - -### 执行版第一轮 spec compliance review - -- 结果:FAIL。 -- blocker: - 1. source owner 发布策略没有闭合 retry / dead-letter / replay。 - 2. fetch SSE 没有保留断线重连语义。 - -### 执行版修正后 spec compliance review - -- 结果:PASS。 -- 复审结论:发布补偿链路和 fetch SSE 重连语义已收敛。 - -### 执行版第一轮 quality / feasibility review - -- 结果:FAIL。 -- blocker: - 1. Task 6 把 P1R-7a 扩大成 AI / Knowledge / Market / Member 四个 source owner 全量 outbox worker 改造。 -- 修正:Task 6 收窄为 source owner publish 兼容性盘点与可选最小接入;没有安全公开事实或可复用 outbox / job 的 owner 进入 P1R-7b / `needs_verification`。 - -### Task 6 修正后 spec compliance review - -- 结果:PASS。 -- warnings: - 1. 实现阶段必须证明 source owner 是否满足已有 outbox / job 条件。 - 2. 如果没有任何 source owner 可接入,最终报告必须明确 source owner propagation 进入 P1R-7b。 - -### Task 6 修正后 quality / feasibility review - -- 结果:FAIL。 -- blocker: - 1. V16 DDL 缺 `command_id` 与唯一约束,`source_revision` 可空导致 PostgreSQL 唯一约束不能可靠去重。 - 2. `sequence_no` 并发分配策略未写清,可能诱导 `max(sequence_no)+1`。 -- 修正:执行版补 `command_id VARCHAR NOT NULL`、`UNIQUE (tenant_id, command_id)`、`source_revision VARCHAR NOT NULL DEFAULT '__none__'`、`muse_unified_event_sequence_no_seq`、`sequence_no DEFAULT nextval(...)`,并禁止 `max(sequence_no)+1`。 - -### DDL 修正后 spec compliance review - -- 结果:FAIL。 -- blocker: - 1. unsupported `X-API-Version` 未明确用 SSE `error` event 收敛。 - 2. 空 `lastEventId` 首连不回放历史语义未闭合。 -- 修正:Task 5 补版本校验、unsupported version 发送 `error` event 后 complete、空 cursor 连接建立时冻结当前可见最大 `sequence_no`,并补对应 focused tests。 - -### 最终 fresh spec compliance review - -- 结果:PASS。 -- 结论:执行版计划符合审阅版 P1R-7a Events SSE 关键边界。 - -### 最终 fresh quality / feasibility review - -- 结果:PASS。 -- non-blocking warnings: - 1. Maven `-am + -Dtest` 可能遇到上游模块无匹配测试,执行时要避免掩盖拼写错误。 - 2. 新增 `events-server` 后,`mvn -pl muse-server -Dtest=... test` 可能依赖本地仓库构件,更稳妥是 `-pl muse-server -am` 或先 reactor install。 - 3. `payload_summary JSONB` 需要明确 MyBatis 映射策略。 - 4. `muse-studio` fetch SSE 实现前要确认 token 来源,不能硬编码或 query token。 - -## 后续边界 - -- 不修改以下受保护文件来掩盖缺口: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` -- 不把 Market 或 Events 标为 `completed`。 -- 不把 dedicated gate PASS 等同 completed。 -- 当前执行版计划已通过 fresh spec review + fresh quality review;但尚未实现、尚未运行实现验证、尚未推进 coverage。 -- 进入实现后仍必须按 `superpowers:subagent-driven-development` 逐 Task 使用 fresh implementer,并在每个 Task 后做 fresh spec review + fresh quality review。 diff --git a/docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md b/docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md deleted file mode 100644 index 879fd738..00000000 --- a/docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md +++ /dev/null @@ -1,265 +0,0 @@ -# P1R7a Events SSE 真实 API 收口 - -日期:2026-06-05 - -## 结论 - -P1R-7a Events SSE 本阶段只把唯一 Events operation `streamEvents GET /app-api/muse/events` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`。 - -P1R-7a 不是总 P1R-7 completed approval;Events 当前仍是 `needs_verification`,不得写成 `completed`。Market 32 个 operation 当前也仍是 `dedicated / needs_verification`,不得写成 `completed`。 - -本轮 Task 10 只做阶段收口文档留痕;不提交、不 push、不改代码、不改 coverage、不清理或回退 dirty baseline。 - -## 已验证事实 - -### 工作区与边界 - -- 正确工作区:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 本阶段不得在 `/Users/qingse/Sync/local-git/oh-my-muse` 写任何文件。 -- 本阶段不得修改以下受保护文件: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` -- 当前 git status dirty baseline 包括 P1R-7a 实现与文档文件;本阶段不清理、不回退。 - -### coverage scanner 当前结果 - -当前 coverage scanner 结果: - -```text -completedOperations 100 -needsVerificationOperations 133 -incompleteOperations 0 -genericPersistenceOperations 0 -ssePlaceholderOperations 0 -``` - -Events 当前唯一 operation: - -```text -events streamEvents GET /app-api/muse/events dedicated needs_verification P1R-7 End-to-End Acceptance -``` - -Market 当前 32 个 operation 仍为 `dedicated / needs_verification`。这只说明 Market 已退出 `generic_persistence / incomplete`,不能作为 Market `completed` 证据。 - -### Task 7 frontend 验证 - -`muse-studio` SSE 前端验证已通过: - -```text -pnpm test -- --run sse -12 files / 43 tests passed -sse.test.ts 12 passed -``` - -`pnpm build` 已成功,只有 Vite chunk size 大于 500k 的 warning。 - -质量 reviewer 额外验证: - -```text -pnpm exec vitest run src/lib/sse.test.ts -12 passed -tsc app/node noEmit pass -eslint pass -vite build to /tmp pass -``` - -### Task 8 scanner 与 P1R gate - -scanner 命令: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:exit 0,并更新 coverage JSON / Markdown。 - -P1R gate 命令: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false test -``` - -结果: - -```text -BUILD SUCCESS -14 tests run -0 failures -0 errors -0 skips -``` - -### Task 9 reactor build - -从 `muse-cloud` 执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install \ - -DskipTests \ - -Dspring-boot.repackage.skip=true \ - -pl muse-server -am -``` - -结果: - -```text -BUILD SUCCESS -47/47 reactor success -Finished at 2026-06-05T19:28:22+08:00 -``` - -### Task 9 dependency tree - -执行命令: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am dependency:tree \ - | rg 'muse-module-events|muse-module-(ai|knowledge|market|member|content)-server' -``` - -结果:exit 0。 - -关键证据: - -```text -muse-module-events-server 只显示 +- cn.iocoder.cloud:muse-module-events-api:jar:2026.04-SNAPSHOT:compile -muse-server tree 显示依赖 muse-module-events-server -未显示 events-server 依赖 ai/knowledge/market/member/content server -``` - -### Task 9 Flyway `_test` - -本次使用 `~/.config/muse-repo/infra.env` 连接 PostgreSQL `100.64.0.8:5433`,PostgreSQL 17.10。 - -本次按用户确认新建独立测试库: - -```text -muse_local_p1r7a_test -``` - -使用独立 `_test` 库的原因:`P1rEventsFlywayMigrationIT` 会 clean public schema。必须避免误清开发库或真实库,所以不能直接对开发库 / 真实库运行该 Flyway IT。 - -执行命令: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" timeout 240 mvn -o test \ - -pl muse-server \ - -Dtest=P1rEventsFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7A_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -结果: - -```text -BUILD SUCCESS -Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -flyway_success=true -flyway_url=jdbc:postgresql://:5433/muse_local_p1r7a_test -flyway_locations_requested=filesystem:sql/muse -flyway_locations_effective=filesystem:/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud/sql/muse -migrations_executed=16 -successful_migration_count=16 -target_schema_version=16 -flyway_latest=16:extend events sse schema -v16_table=muse_unified_event -v16_sequence=muse_unified_event_sequence_no_seq -v16_indexes=idx_muse_unified_event_owner_sequence -v16_constraints=uk_muse_unified_event_sequence,uk_muse_unified_event_command,uk_muse_unified_event_event,uk_muse_unified_event_source,chk_muse_unified_event_event_type,chk_muse_unified_event_publish_status -v16_triggers=trg_muse_unified_event_updated_at -``` - -### Source owner publish tests - -Task 6 已验证 AI / Knowledge / Market / Member(Account) 不同时满足 P1R-7a source-owner publish 最小接入条件。 - -因此 P1R-7a 未接入 source owner propagation,未运行 source owner publish tests。该范围应进入 P1R-7b / `needs_verification`,不能包装成 completed evidence。 - -### review PASS 序列 - -计划阶段经历多轮 FAIL 修正后最终 PASS。 - -Task 1-8 均完成 fresh spec + quality review gate。 - -关键 reviewer 与 Task 10 文档 review 序列: - -```text -Task 6 spec Mendel PASS / quality Ampere PASS -Task 7 spec Arendt PASS / quality Dirac PASS -Task 8 spec Curie PASS / quality Hooke PASS -Task 10 initial spec Pascal / 019e97c2-27c3-7c23-87da-29fe5daf1746 PASS -Task 10 initial quality Euler / 019e97c4-f952-7f02-b2f4-f0bd86323b80 FAIL -Task 10 review-fix Averroes / 019e97c9-2a62-7dc0-b1c0-e4edf564b852 DONE:只补 wrong checkout 流程 warning -Task 10 spec re-review James / 019e97cb-ac2d-7a03-a486-775ac3430a7c PASS -Task 10 quality re-review Ramanujan / 019e97ce-8c05-79f2-9d5a-eb79c8e84b93 PASS -Final overall reviewer Lovelace / 019e97d0-f253-79f3-9abe-e89b585b7bec FAIL:仅因本文档当时缺少 Task 10 双复审独立证据;技术状态无 coverage / protected-file blocker -``` - -Task 10 文档最终 gate 已有独立双复审 PASS:James PASS + Ramanujan PASS。本文档保留 Euler FAIL -> Averroes fix -> James/Ramanujan PASS 过程,避免掩盖曾失败的 review 历史。 - -## 推断 - -基于 coverage scanner、P1R gate、route ownership gate、frontend SSE focused tests、reactor build、dependency tree 和真实 PostgreSQL / Flyway `_test` 的组合证据,可以判断 P1R-7a Events SSE 已具备 dedicated 入口、无 server 反向依赖、前端 fetch SSE 基础验证和 V16 数据库迁移验收基础。 - -由于本轮没有完成 source owner propagation 接入,也没有做总 P1R-7 completed approval,不能推断 Events 或 P1R-7 已达到 completed。 - -## 假设 - -本阶段假设 `~/.config/muse-repo/infra.env` 中的 PostgreSQL 连接配置仍是项目允许用于 P1R `_test` Flyway 验收的本地 / 内网测试配置。 - -本阶段假设当前 dirty / untracked baseline 来自 P1R-7a 既有实现、验证产物或用户已有工作;Task 10 只新增阶段收口文档,不清理、不回退。 - -## 验证命令摘要 - -```bash -pnpm test -- --run sse -pnpm build -pnpm exec vitest run src/lib/sse.test.ts -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false test -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install \ - -DskipTests \ - -Dspring-boot.repackage.skip=true \ - -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am dependency:tree \ - | rg 'muse-module-events|muse-module-(ai|knowledge|market|member|content)-server' -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" timeout 240 mvn -o test \ - -pl muse-server \ - -Dtest=P1rEventsFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7A_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -## 未完成范围与 warning - -- P1R-7a 不是总 P1R-7 completed approval。 -- Events 当前仍是 `dedicated / needs_verification`,不得写成 `completed`。 -- Market 32 个 operation 当前仍是 `dedicated / needs_verification`,不得写成 `completed`。 -- P1R-7a 未接入 source owner propagation;AI / Knowledge / Market / Member(Account) source owner publish 进入 P1R-7b / `needs_verification`。 -- Source owner publish tests 未运行,不能作为本阶段 completed evidence。 -- `git diff --check` 当前仍报 `docs/superpowers/reports/p1r-api-coverage.md:252: new blank line at EOF.`。这是 scanner 生成 coverage Markdown 的已知 warning;本阶段不得手改 scanner 或 coverage 内容来掩盖。 -- 当前 dirty baseline 包括 P1R-7a 实现与文档文件;本阶段不清理、不回退。 -- 流程 warning:曾/当前需警惕 `/Users/qingse/Sync/local-git/oh-my-muse` 错误 checkout 写入;本轮只允许以 `/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` 为提交、review、清理依据;`/Users/qingse/Sync/local-git/oh-my-muse` 当前没有 P1R7a 同名收口文档残留,但存在其他未跟踪文件(如 `docs/superpowers/reports/p1r-api-coverage.json` / `.md` 和 P1R0 memory),不得在 Task 10 顺手清理非目标 checkout 残留,除非用户明确批准。 -- Final overall review 已由 Lovelace 标记 FAIL;原因仅是本文档当时缺少 Task 10 双复审证据。本次补证后仍需重跑 final overall review,当前不得伪造 final overall PASS。 - -## 下一步 - -1. 重跑 final overall review,确认本次 Task 10 双复审补证是否解除 Lovelace blocker。 -2. final overall review 通过后,再由用户决定是否需要提交 P1R-7a 阶段文档和实现 diff。 -3. 如后续要推进总 P1R-7 completed approval,必须另起范围,补 source owner propagation、真实端到端验收、失败路径、审计和 replay 证据,并经用户明确批准。 - -## 本次改动文件 - -- `docs/memorys/2026-06-05-P1R7aEventsSSE真实API收口.md` diff --git a/docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路.md b/docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路.md deleted file mode 100644 index 518aef4f..00000000 --- a/docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路.md +++ /dev/null @@ -1,218 +0,0 @@ -# P1R7b Source Owner Propagation 真实链路 - -日期:2026-06-06 - -## 结论 - -P1R-7b Task 7 fresh implementer 最终验证、阶段留痕与交接已执行,状态为 `DONE`。 - -本轮只把 AI source owner terminal event -> AI publish outbox -> AI worker -> EventsPublishApi -> `muse_unified_event` -> SSE 可见链路的 evidence 推进到 `needs_verification` 证据层;不代表 Events、P1R-7 或 Market completed approval。 - -本轮不提交、不 push、不清理或回退 dirty baseline;除本文档与 `docs/agent-specs/.agent` 外,不新增实现改动。 - -## 已验证事实 - -### 工作区与保护文件 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`,`git status --short --branch` 显示 `## dev/1.0.0...origin/dev/1.0.0`。 -- 初始与最终受保护文件定向 status/diff 均无输出: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### Reactor build - -从 `muse-cloud` 执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -``` - -结果:exit 0,`BUILD SUCCESS`,47/47 reactor modules success,Finished at `2026-06-06T15:42:09+08:00`。 - -### AI / Events focused tests - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-ai/muse-module-ai-server,muse-module-events/muse-module-events-server -am -Dtest=MuseAiEventPublishOutboxMapperTest,MuseAiEventPublishOutboxServiceTest,MuseAiEventPublishWorkerTest,MuseAiRuntimeProjectionServiceTest,MuseJobServiceTest,EventsPublishServiceTest,EventsStreamServiceTest -Dsurefire.failIfNoSpecifiedTests=false test -``` - -结果:exit 0。surefire XML 证明以下目标类实际运行且 test count > 0: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `MuseAiEventPublishOutboxMapperTest` | 5 | 0 | 0 | 0 | -| `MuseAiEventPublishOutboxServiceTest` | 11 | 0 | 0 | 0 | -| `MuseAiEventPublishWorkerTest` | 20 | 0 | 0 | 0 | -| `MuseAiRuntimeProjectionServiceTest` | 5 | 0 | 0 | 0 | -| `MuseJobServiceTest` | 25 | 0 | 0 | 0 | -| `EventsPublishServiceTest` | 17 | 0 | 0 | 0 | -| `EventsStreamServiceTest` | 9 | 0 | 0 | 0 | - -AI/Events focused tests 合计 87 个,failures/errors/skipped 均为 0。 - -### P1R mixed gates - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-server -am -Dtest=P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest -Dsurefire.failIfNoSpecifiedTests=false test -``` - -结果:exit 0。surefire XML 证明以下目标类实际运行: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `P1rAiEventsPublishMigrationSqlTest` | 4 | 0 | 0 | 0 | -| `P1rAiEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAiEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rApiCoverageReportTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRealApiGateTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rAiRealApiGateTest` | 6 | 0 | 0 | 0 | -| `P1rAiRouteOwnershipTest` | 7 | 0 | 0 | 0 | - -P1R mixed gates 合计 37 个,failures/errors/skipped 均为 0。 - -### Flyway `_test` - -使用 `~/.config/muse-repo/infra.env`,测试库名为 `muse_p1r7b_events_publish_test`,符合必须以 `_test` 结尾的保护规则。 - -执行命令包含: - -```bash --Dtest=P1rAiEventsPublishFlywayMigrationIT --Dflyway.postgresql.transactional.lock=false --Dp1r.flyway.locations=filesystem:sql/muse --Dp1r.flyway.url=jdbc:postgresql://:5433/muse_p1r7b_events_publish_test -``` - -结果:exit 0,`BUILD SUCCESS`,surefire XML 证明 `P1rAiEventsPublishFlywayMigrationIT` tests=4、failures=0、errors=0、skipped=0。 - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql://:5433/muse_p1r7b_events_publish_test -flyway_locations_requested=filesystem:sql/muse -flyway_locations_effective=filesystem:/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud/sql/muse -migrations_executed=17 -successful_migration_count=17 -target_schema_version=17 -flyway_latest=17:extend ai events publish outbox -v17_table=muse_ai_event_publish_outbox -v17_indexes=idx_muse_ai_event_publish_outbox_claim,idx_muse_ai_event_publish_outbox_owner_status -v17_constraints=uk_muse_ai_event_publish_outbox_command,uk_muse_ai_event_publish_outbox_source,chk_muse_ai_event_publish_outbox_event_type,chk_muse_ai_event_publish_outbox_status,chk_muse_ai_event_publish_outbox_attempt_count,chk_muse_ai_event_publish_outbox_max_attempt -v17_triggers=trg_muse_ai_event_publish_outbox_update_time -``` - -### Dependency tree gate - -执行 dependency tree 证明: - -- AI server 有 `cn.iocoder.cloud:muse-module-events-api:jar:2026.04-SNAPSHOT:compile`。 -- AI server 禁止依赖 `muse-module-events-server` 的显式探针输出 `AI_EVENTS_SERVER_ABSENT`。 -- Events server 禁止依赖 AI / Knowledge / Market / Member / Content server 的显式探针输出 `EVENTS_FORBIDDEN_SOURCE_OWNER_ABSENT`。 - -### Coverage scanner - -scanner 只在隔离副本 `/tmp/p1r7b-final-coverage-scan.AoKcDl` 运行,未在真实 worktree 运行会写报告的 scanner。 - -执行: - -```bash -python3 scripts/p1r-audit-api-coverage.py --check -``` - -结果:exit 0,并只在隔离副本中生成 coverage JSON/Markdown。 - -隔离副本 summary: - -```json -{ - "totalOperations": 233, - "completedOperations": 100, - "incompleteOperations": 0, - "needsVerificationOperations": 133, - "catchAllOperations": 0, - "genericPersistenceOperations": 0, - "ssePlaceholderOperations": 0, - "missingOperations": 0, - "blockedOperations": 0 -} -``` - -Events 状态: - -```text -events streamEvents GET /app-api/muse/events dedicated needs_verification P1R-7 End-to-End Acceptance -``` - -Market 状态: - -```text -dedicated/needs_verification 32 -``` - -回到真实 worktree 后,受保护文件 diff/status 仍为空。 - -### Dirty baseline 区分 - -P1R-7b 实现/测试 dirty baseline 包括: - -- `muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` -- `MuseAiRuntimeProjectionService.java` -- `MuseJobServiceImpl.java` -- `MuseAiProperties.java` -- `MuseAiRuntimeProjectionServiceTest.java` -- `MuseJobServiceTest.java` -- `MuseAiEventPublishOutboxService.java` -- `MuseAiEventPublishOutboxServiceImpl.java` -- `MuseAiEventPublishWorker.java` -- `MuseAiEventPublishOutboxDO.java` -- `MuseAiEventPublishOutboxMapper.java` -- `MuseAiEventPublishOutboxServiceTest.java` -- `MuseAiEventPublishWorkerTest.java` -- `MuseAiEventPublishOutboxMapperTest.java` -- `P1rAiEventsPublishDependencyTest.java` -- `P1rAiEventsPublishEndToEndTest.java` -- `P1rAiEventsPublishFlywayMigrationIT.java` -- `P1rAiEventsPublishMigrationSqlTest.java` -- `muse-cloud/sql/muse/V17__extend_ai_events_publish_outbox.sql` - -文档 dirty baseline / 本轮允许文档范围: - -- `docs/agent-specs/` 目录当前仍为 untracked 文档目录;本轮追加更新 `docs/agent-specs/.agent`。 -- 本轮新增本文档:`docs/memorys/2026-06-06-P1R7bSourceOwnerPropagation真实链路.md`。 - -## 推断 - -基于 reactor build、focused tests、P1R mixed gates、Flyway `_test`、dependency tree、隔离 coverage scanner 和受保护文件 diff 的组合证据,可以判断 P1R-7b AI source owner propagation 真实链路已具备进入 fresh review 的 needs_verification 证据。 - -由于本轮没有进行 fresh spec review + fresh quality review 双 PASS,也没有用户 completed approval,不能推断 Events、P1R-7 或 Market 已 completed。 - -## 假设 - -- `~/.config/muse-repo/infra.env` 仍是项目允许用于 P1R `_test` Flyway 验收的本地/内网测试配置。 -- 当前 P1R-7b 实现 dirty baseline 来自前置 fresh implementers;本轮 Task 7 不清理、不回退、不重新归因这些实现文件。 - -## 风险与未完成 - -- Maven 多次出现 nexus metadata handshake / connect timeout warning,但相关 build/test 命令最终 exit 0;该 warning 未阻塞本轮验证。 -- 当前 evidence 仍是 Task 7 implementer evidence,后续必须 fresh spec review + fresh quality review。未双 PASS 前不能最终收口、提交、push 或推进 completed。 -- Events 当前仍为 `dedicated / needs_verification`。 -- Market 32 operations 仍为 `dedicated / needs_verification`。 -- P1R-7 completed approval 必须另起单独审批,不能由本轮 Task 7 自动推出。 - -## 下一步 - -1. 派发 fresh Task 7 spec review,核对本文档、`.agent`、验证命令、surefire XML、dependency tree、coverage scanner 隔离路径和 protected diff 证据。 -2. 派发 fresh Task 7 quality / feasibility review,核对真实链路、失败路径、日志审计、依赖方向、测试覆盖和交接风险。 -3. 双 PASS 前不得最终收口、提交、push 或把 Events / P1R-7 / Market 标为 completed。 - diff --git a/docs/memorys/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation真实链路.md b/docs/memorys/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation真实链路.md deleted file mode 100644 index 3ca050af..00000000 --- a/docs/memorys/2026-06-06-P1R7cKnowledgeSourceOwnerPropagation真实链路.md +++ /dev/null @@ -1,209 +0,0 @@ -# P1R7c Knowledge Source Owner Propagation 真实链路 - -日期:2026-06-06 - -## 结论 - -P1R-7c Knowledge source owner propagation 主体实现、V18 SQL、focused tests、P1R mixed executable gates、V18 Flyway `_test`、reactor build、隔离 coverage scanner 与 P2 增强后 fresh review 已推进到 `needs_verification` 证据层。 - -当前仍不能写 `completed`。Rawls correctness/spec review 与 Boole data-integrity/testing review 已双 PASS,但该 PASS 只代表 Knowledge source owner terminal event -> Knowledge outbox -> worker -> EventsPublishApi -> `muse_unified_event` -> SSE 可见链路 evidence 推进到 `needs_verification`,不代表 Events / P1R-7 / Knowledge / Market completed。 - -本轮未修改 OpenAPI、coverage scanner 或真实 coverage 报告。 - -## 已验证事实 - -### 工作区与保护文件 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 受保护文件 diff 为空: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 本轮实现范围 - -- Knowledge server 新增直接依赖 `muse-module-events-api`。 -- 新增 Knowledge 本域 publish outbox: - - `MuseKnowledgeEventPublishOutboxDO` - - `MuseKnowledgeEventPublishOutboxMapper` - - `MuseKnowledgeEventPublishOutboxService` - - `MuseKnowledgeEventPublishOutboxServiceImpl` - - `MuseKnowledgeEventPublishWorker` - - `MuseKnowledgeEventsProperties` - - `MuseKnowledgeEventsConfiguration` -- `MuseKnowledgeSourceEventService` 在 canonical `triggerGlobalKBSourceEvent` 链路内创建 per target owner outbox,并将 `ownerFanoutSummary` 写入 projection task summary。 -- `MuseKnowledgeSourceBindingProjectionMapper` 新增按 `kbId + lastEventId` fan-out target 查询与 owner audit summary 查询。 -- 新增 V18 migration:`muse-cloud/sql/muse/V18__extend_knowledge_events_publish_outbox.sql`。 - -### Focused tests - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-module-knowledge/muse-module-knowledge-server -Dtest=MuseKnowledgeEventPublishOutboxServiceTest,MuseKnowledgeEventPublishWorkerTest,MuseKnowledgeSourceEventServiceTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,15 tests pass,failures/errors/skipped 均为 0。 - -覆盖点: - -- per distinct target owner 创建 outbox。 -- 不同 owner 的 `commandId/outboxId` 不相同。 -- `document_deleted` 不写 outbox。 -- invalid owner 只进入 audit summary,不进入 outbox。 -- worker disabled 不 claim。 -- accepted -> published。 -- rejected / blocked -> dead_letter。 -- API error -> retryable。 -- last attempt exception -> dead_letter。 -- terminal status update 必须携带本次 claim attempt ownership,避免过期 worker 覆盖新终态。 -- terminal status update 返回 0 时必须记录 stale claim 诊断日志,包含 tenantId、targetOwnerUserId、outboxId、sourceEventId、attempt、targetStatus、errorCode。 -- source event 集成写入 `ownerFanoutSummary`。 - -### P1R mixed executable gates - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest=P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,37 tests pass,failures/errors/skipped 均为 0。 - -覆盖点: - -- V18 SQL 静态 migration gate。 -- Knowledge server 依赖 Events API,不依赖 Events server。 -- Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。 -- Knowledge outbox -> worker -> EventsPublishServiceImpl -> `muse_unified_event` focused E2E。 -- target owner 可见,owner 0 / 非 target owner 不可见。 -- 同一 source event 不同 owner 不被合并。 -- 同一 source event + owner replay 回放同一 event identity。 -- stale claim terminal update 因 attempt 不匹配被忽略。 -- blocked publish 进入 outbox dead_letter 且不可见。 - -### Flyway IT 当前状态 - -新增: - -```text -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeEventsPublishFlywayMigrationIT.java -``` - -安全方法执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest='P1rKnowledgeEventsPublishFlywayMigrationIT#should_rejectNonTestDatabaseWhenQueryContainsSlashTestSuffix+should_rejectCredentialQueryParameters' -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,2 tests pass。 - -真实库准备: - -- 测试库 `muse_p1r7c_events_publish_test` 已确认/创建。 -- `psql` 可连接目标库,输出判断为 `psql_target_db_connect=true`。 - -真实 Flyway `_test`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest=P1rKnowledgeEventsPublishFlywayMigrationIT -Dflyway.postgresql.transactional.lock=false -Dp1r.flyway.locations=filesystem:sql/muse -Dp1r.flyway.url=jdbc:postgresql://:5433/muse_p1r7c_events_publish_test -Dp1r.flyway.user= -Djava.net.useSystemProxies=false -DsocksProxyHost= -DsocksProxyPort= -Dhttp.proxyHost= -Dhttp.proxyPort= -Dhttps.proxyHost= -Dhttps.proxyPort= -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,4 tests pass,failures/errors/skipped 均为 0。P2 修复后新增真实负向 insert 约束探针已纳入全量 Flyway IT。 - -关键输出: - -```text -flyway_success=true -migrations_executed=18 -successful_migration_count=18 -target_schema_version=18 -flyway_latest=18:extend knowledge events publish outbox -v18_table=muse_knowledge_event_publish_outbox -``` - -JDBC EOF 根因: - -```text -psql_target_db_connect=true -socksProxyHost_initial=127.0.0.1 -jdbc_default=false ... cause=java.io.EOFException:null -socksProxyHost_cleared=null -jdbc_no_proxy=true db=muse_p1r7c_events_publish_test -``` - -结论:V18 SQL 已通过真实 PostgreSQL / Flyway 验收;早前 EOF 是 Java 全局 SOCKS/HTTP/HTTPS 代理属性影响 PostgreSQL JDBC 连接链路,不是 V18 SQL 失败。 - -P2 增强: - -- `singleLiteralCheck` 已从 substring 判断改为精确单表达式匹配,避免未来误接受 `event_type='notification' OR ...`。 -- `P1rKnowledgeEventsPublishFlywayMigrationIT` 新增真实 PostgreSQL 负向 insert 探针,验证 event_type、notification_type、publish_status、target_owner_user_id check 会拒绝非法行。 -- 纯函数 RED/GREEN:`should_rejectBroadSingleLiteralConstraintExpression` 先失败,修复后 1/1 pass。 -- Worker RED/GREEN:`should_logWhenAcceptedTerminalUpdateIsSkippedByStaleClaim` 先失败,修复后 1/1 pass。 -- 一次并行 Maven 验证因两个命令同时触碰 Knowledge `target`,出现 testCompile 找不到已存在类;串行重跑 worker 测试通过,判定为并发构建污染,不作为代码缺陷。 - -### Reactor build - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -结果:exit 0,`BUILD SUCCESS`,62/62 modules success。 - -### Coverage scanner - -scanner 只在隔离副本运行。 - -最终干净隔离副本: - -```text -/tmp/p1r7c-final-coverage-scan.2HXo1Z -``` - -执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果: - -```text -coverage_exit=0 -``` - -真实 worktree 受保护 coverage JSON/Markdown diff 仍为空。 - -## 推断 - -基于 focused tests、P1R mixed executable gates、V18 Flyway `_test`、reactor build、隔离 coverage scanner、protected diff 以及 Rawls/Boole fresh review,可以判断 P1R-7c Knowledge source owner propagation 的应用层链路已具备收口到 `needs_verification` 的证据。 - -由于本轮证据只覆盖 Knowledge source owner propagation 第一批 canonical source status / projection summary 链路,不能推断 Events / P1R-7 / Knowledge / Market 已完成,也不能推进 completed。 - -## 假设 - -- `~/.config/muse-repo/infra.env` 是当前项目允许用于本地 P1R `_test` 数据库准备的配置来源。 -- 后续提交将按实现与测试/文档分组,避免把状态推进、coverage 口径和真实实现混成一笔。 - -## 风险与未完成 - -- P2 增强后 focused / mixed / Flyway / reactor / coverage / protected diff 已重跑,fresh review 已重新派发并双 PASS:Rawls correctness/spec PASS,Boole data-integrity/testing PASS。 -- fresh implementation review 历史:Sagan correctness PASS,Dalton testing evidence PASS,Halley data integrity FAIL。 -- Halley 的 P1 已验证并修复:outbox claim 后终态回写原先缺少本次 claim 所有权保护,过期 worker 可能覆盖新 claim 终态;已先写 RED 测试 `should_guardTerminalStatusUpdatesWithClaimAttemptOwnership`,再把 `markPublished/markRetryable/markDeadLetter` 补为 `publish_status='running' AND attempt_count=#{claimedAttemptCount}`,并由 worker 传入 claim 返回的 `attemptCount`。 -- 修复后 fresh review 第一轮:Kuhn correctness/spec PASS,Herschel data integrity PASS。Herschel 的两个 P2 已被纳入本轮增强,并已由 Rawls/Boole P2 后 fresh review 复核。 -- Boole 非阻塞风险:V18 `ON CONFLICT DO NOTHING` 可能静默丢弃非法 duplicate source/owner 行,当前由 focused replay gate 覆盖第一批行为;worker 默认关闭,启用前仍需要运行配置与运维监控确认。 -- 不得修改 OpenAPI、scanner、coverage report 来绕过缺口。 -- 不得把 coverage `needs_verification` 改成 `completed`。 - -## 下一步 - -1. 提交前复核 `git status --short --branch` 与 protected diff。 -2. 按逻辑分组准备提交:实现一组,测试/规格/留痕一组。 -3. 提交和 push 前仍不得推进 completed,不得修改 OpenAPI、scanner、coverage report。 diff --git a/docs/memorys/2026-06-07-P1R7dMarketSourceOwnerPropagation真实链路.md b/docs/memorys/2026-06-07-P1R7dMarketSourceOwnerPropagation真实链路.md deleted file mode 100644 index 8934b0d6..00000000 --- a/docs/memorys/2026-06-07-P1R7dMarketSourceOwnerPropagation真实链路.md +++ /dev/null @@ -1,289 +0,0 @@ -# P1R7d Market Source Owner Propagation 真实链路 - -日期:2026-06-07 - -## 结论 - -P1R-7d Market source owner propagation 主体实现、V19 SQL、focused tests、P1R mixed executable gates、V19 Flyway `_test`、dependency tree gate、reactor build、隔离 coverage scanner 与 protected diff gate 已形成进入修复后 fresh implementation review 的证据。 - -修复后 fresh implementation review 已双 PASS:Pascal spec/correctness review PASS,Mill quality/data-integrity/testing review PASS,均无 P0/P1/P2 blocker。 - -当前仍不能写 `completed`。本轮证据只代表 Market governance terminal fact -> Market publish outbox -> Market worker -> EventsPublishApi -> `muse_unified_event` -> `/app-api/muse/events` 对资产发布者可见的链路推进到 `needs_verification` 证据层,不代表 Events / P1R-7 / Market completed。 - -本轮未修改 OpenAPI、coverage scanner 或真实 coverage 报告。 - -## 已验证事实 - -### 工作区与保护文件 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- `git pull --ff-only origin dev/1.0.0` 返回 `Already up to date.`。 -- 受保护文件最终 `git diff --quiet -- ...` 返回 exit 0: - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 本轮实现范围 - -- Market server 新增直接依赖 `muse-module-events-api`。 -- 新增 Market 本域 publish outbox: - - `MuseMarketEventPublishOutboxDO` - - `MuseMarketEventPublishOutboxMapper` - - `MarketEventPublishOutboxService` - - `MarketEventPublishOutboxServiceImpl` - - `MarketEventPayloads` - - `MarketEventPublishWorker` - - `MuseMarketEventsProperties` - - `MuseMarketEventsConfiguration` -- `AdminMarketGovernanceServiceImpl.applyGovernanceAction` 在治理动作 `muse_market_governance_action` 落库后,同事务创建 Market Events publish outbox。 -- command replay 不补历史 outbox。 -- 新增 V19 migration:`muse-cloud/sql/muse/V19__extend_market_events_publish_outbox.sql`。 - -### Review 反馈与修复 - -- Pasteur fresh implementation spec review 返回 FAIL,两个有效问题已修复: - - V19 schema、DO、Mapper 补齐 `source_command_id`、`actor_user_id`、`preview_id`、`request_hash`。 - - `commandId/outboxId` 幂等 key 改为 `tenantId|actionId|ownerUserId|governance_action` 的 SHA-256 前 32 位。 -- Kant fresh quality/data-integrity review 返回 FAIL,两个有效问题已修复: - - Flyway IT `baseInsertSql()` 补齐四个 NOT NULL 字段,避免负向 insert 假绿。 - - payload validator 从“任意字符串 message”收紧为安全消息 allowlist。 -- Tesla fresh implementation spec review 返回 FAIL,两个有效问题已修复: - - `muse.market.events.publish-worker.max-attempt` 与 `claim-timeout-seconds` 从执行版配置合同落到 `MuseMarketEventsProperties`,outbox service 写入配置化 `maxAttempt`,worker claim 使用配置化超时时间。 - - payload `timestamp` 从 governance action `createTime` 派生,缺失时才回退当前时间,避免事件时间不可审计。 -- Volta fresh quality/data-integrity review 曾返回 PASS,但该 PASS 发生在 Tesla 修复前;由于后续修改了 outbox service、worker、properties 和测试,Volta PASS 已失效,不能计入最终 PASS gate。 -- 旧 Pasteur/Kant/Tesla/Volta 均不能计入最终 PASS gate。 -- 修复后 fresh implementation spec/correctness review(Pascal)PASS,无 P0/P1/P2 blocker;残余非阻塞风险:claim SQL 不在领取阶段过滤 `attempt_count < max_attempt`,但 worker 会在 claim 后判断耗尽并转 dead_letter;E2E 仍是 focused harness,不是真实 HTTP/SSE + 真实 DB 全链路;dependency 单测主要读 direct POM,后续最终 gate 应保留 dependency tree 原始输出。 -- 修复后 fresh implementation quality/data-integrity/testing review(Mill)PASS,无 P0/P1/P2 blocker;残余非阻塞风险:可后续把 `sourceCommandId / actorUserId / previewId / requestHash` 的 pre-insert 校验前移到 service;可后续把 `insertIgnore` 收窄到指定 conflict target 或在 insert=0 且 action/command 均查不到时记录 error;若未来审计要求精确同源时间,可统一 `EventsPublishReqDTO.emittedAt` 与 payload `timestamp`。 - -### 实现边界 - -- 第一切片只发布 `delist` / `recall` 的 `notification/governance_action`。 -- `recall` 第一切片只发布一条 `market_governance_action` notification;`muse_market_source_status_event` 只作为事实旁证和后续扩展点。 -- SSE 可见 owner 是资产发布者 `ownerUserId`,不是 admin actor,也不是 target owner。 -- payload exact allowlist: - - 顶层只允许 `type/message/resourceRef/timestamp`。 - - `resourceRef` 只允许 `resourceType/resourceId`。 - - `message` 只允许 `资产已下架` / `资产已召回`。 - - `payloadSummary.resourceRef.resourceId` 保持 Number/Long 语义。 - - `EventsPublishReqDTO.resourceId` 仍按 Events DTO 合同使用 String。 -- 禁止发布授权快照、handoff token、target owner、admin note、error stack、完整 actionSnapshot。 -- `request_hash` 只作为 outbox 幂等/审计字段保存,不进入 Events payload。 -- worker 默认配置关闭:`muse.market.events.publish-worker.enabled=false`。 -- 终态回写使用 `publish_status='running' AND attempt_count=#{claimedAttemptCount}` 防止过期 claim 覆盖新终态。 - -### Reactor build - -从 `muse-cloud` 执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -结果:exit 0,`BUILD SUCCESS`,62/62 modules success,Finished at `2026-06-07T20:36:27+08:00`。 - -### Market focused tests - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-module-market/muse-module-market-server -am -Dtest=AdminMarketGovernanceServiceTest,MarketEventPublishOutboxServiceTest,MarketEventPublishWorkerTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,28 tests pass,failures/errors/skipped 均为 0。 - -surefire XML 证明以下目标类实际运行且 test count > 0: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `AdminMarketGovernanceServiceTest` | 11 | 0 | 0 | 0 | -| `MarketEventPublishOutboxServiceTest` | 6 | 0 | 0 | 0 | -| `MarketEventPublishWorkerTest` | 11 | 0 | 0 | 0 | - -覆盖点: - -- `delist` / `recall` 成功治理动作会调用 Market Events outbox service。 -- command replay 不补历史 outbox。 -- invalid enum / invalid preview / CAS blocker 不创建 outbox。 -- outbox service 创建安全 payload,`resourceRef.resourceId` 是数值型。 -- duplicate command/action notification 返回既有 outbox,不重复创建。 -- unsupported action / invalid owner 不创建 outbox。 -- payload invalid fail-closed,抛出固定错误码 `MARKET_EVENTS_PAYLOAD_INVALID`,且不 insert outbox。 -- unsafe message 被 fail-closed,且不调用 EventsPublishApi。 -- outbox service 把配置化 `maxAttempt` 写入 outbox;默认值为 5。 -- payload timestamp 使用 governance action `createTime`,缺失时才回退当前时间。 -- Market outbox service 不依赖 `EventsPublishApi`。 -- worker disabled 不 claim。 -- worker claim 使用配置化 `claimTimeoutSeconds`;默认值为 60。 -- accepted -> published。 -- duplicate accepted 回放同一 event identity。 -- rejected / blocked -> dead_letter。 -- `CommonResult` error 非最后一次 -> retryable,最后一次 -> dead_letter。 -- API exception 非最后一次 -> retryable,最后一次 -> dead_letter。 -- invalid payload 在 worker 侧 fail-closed dead_letter。 -- terminal status update 必须携带 claim attempt ownership。 -- terminal update 返回 0 时记录 stale claim warning。 - -### P1R mixed executable gates - -一次旧类名命令只实际运行 29 个测试;该结果已丢弃,不计入最终 evidence。修正为真实存在的 14 个测试类后重新执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest=P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,56 tests pass,failures/errors/skipped 均为 0。 - -surefire XML 证明以下目标类实际运行且 test count > 0: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `P1rMarketEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rMarketEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rMarketEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishEndToEndTest` | 5 | 0 | 0 | 0 | -| `P1rAiEventsPublishMigrationSqlTest` | 4 | 0 | 0 | 0 | -| `P1rAiEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAiEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rApiCoverageReportTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRealApiGateTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rAiRealApiGateTest` | 6 | 0 | 0 | 0 | -| `P1rAiRouteOwnershipTest` | 7 | 0 | 0 | 0 | - -新增 Market gate 覆盖: - -- V19 SQL 静态 migration gate。 -- Market server 依赖 Events API,不依赖 Events server。 -- Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。 -- Market outbox -> worker -> EventsPublishServiceImpl -> `muse_unified_event` focused E2E。 -- asset publisher owner 可见,admin actor 不可见,非 owner 不可见。 -- `recall` 只产生一条 `governance_action` notification。 -- blocked publish 进入 outbox dead_letter 且 SSE 不可见。 -- stale claim terminal update 因 attempt 不匹配被忽略。 -- AI / Knowledge / Events 既有 P1R gates 不回退。 - -### Flyway `_test` - -执行命令包含: - -```bash --Dtest=P1rMarketEventsPublishFlywayMigrationIT --Dflyway.postgresql.transactional.lock=false --Dp1r.flyway.locations=filesystem:sql/muse --Dp1r.flyway.url=jdbc:postgresql://:/muse_p1r7d_events_publish_test --Djava.net.useSystemProxies=false --DsocksProxyHost= --Dhttp.proxyHost= --Dhttps.proxyHost= -``` - -结果:exit 0,`P1rMarketEventsPublishFlywayMigrationIT` tests=4、failures=0、errors=0、skipped=0。 - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql:///muse_p1r7d_events_publish_test -flyway_locations_requested=filesystem:sql/muse -flyway_locations_effective=filesystem:/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud/sql/muse -migrations_executed=19 -successful_migration_count=19 -target_schema_version=19 -flyway_latest=19:extend market events publish outbox -schema_version=19 -v19_table=muse_market_event_publish_outbox -v19_indexes=idx_muse_market_event_publish_outbox_claim,idx_muse_market_event_publish_outbox_owner_status,idx_muse_market_event_publish_outbox_action -v19_constraints=uk_muse_market_event_publish_outbox_id,uk_muse_market_event_publish_outbox_command,uk_muse_market_event_publish_outbox_action_notification,chk_muse_market_event_publish_outbox_event_type,chk_muse_market_event_publish_outbox_notification_type,chk_muse_market_event_publish_outbox_action_type,chk_muse_market_event_publish_outbox_status,chk_muse_market_event_publish_outbox_owner,chk_muse_market_event_publish_outbox_attempt_count,chk_muse_market_event_publish_outbox_max_attempt -v19_triggers=trg_muse_market_event_publish_outbox_update_time -``` - -Flyway IT 覆盖: - -- 只允许 `_test` 数据库。 -- JDBC URL 不允许携带 credential query parameters。 -- V19 table / indexes / constraints / trigger 存在。 -- V19 required columns 包含 `source_command_id`、`actor_user_id`、`preview_id`、`request_hash`,且为 NOT NULL。 -- 非法 `event_type` / `notification_type` / `action_type` / `publish_status` / `owner_user_id <= 0` 的真实 insert 被 PostgreSQL 拒绝。 - -### Dependency tree gate - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o dependency:tree -pl muse-module-market/muse-module-market-server -Dincludes=cn.iocoder.cloud:muse-module-events-api,cn.iocoder.cloud:muse-module-events-server -DoutputType=text -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o dependency:tree -pl muse-module-events/muse-module-events-server -Dincludes=cn.iocoder.cloud:muse-module-ai-server,cn.iocoder.cloud:muse-module-knowledge-server,cn.iocoder.cloud:muse-module-market-server,cn.iocoder.cloud:muse-module-member-server,cn.iocoder.cloud:muse-module-content-server -DoutputType=text -``` - -结果: - -- Market server dependency tree 明确包含 `cn.iocoder.cloud:muse-module-events-api:jar:2026.04-SNAPSHOT:compile`。 -- Market server dependency tree 未命中 `muse-module-events-server`。 -- Events server dependency tree 对 AI / Knowledge / Market / Member / Content server 过滤无输出。 - -### Coverage scanner - -scanner 只在隔离副本运行,未在真实 worktree 写 coverage 报告。 - -隔离副本: - -```text -/tmp/p1r7d-final-coverage-scan.nhBVhM -``` - -执行: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:exit 0,并只在隔离副本中生成 coverage JSON/Markdown。 - -隔离副本 summary: - -```text -completedOperations=100 -needsVerificationOperations=133 -incompleteOperations=0 -genericPersistenceOperations=0 -ssePlaceholderOperations=0 -events.streamEvents=dedicated/needs_verification -marketOperations=32 -marketStatuses=dedicated/needs_verification -``` - -真实 worktree 受保护 coverage JSON/Markdown diff 仍为空。 - -### 静态检查 - -执行: - -```bash -git diff --check -``` - -结果:exit 0,无 whitespace error。 - -## 推断 - -基于 focused tests、P1R mixed executable gates、V19 Flyway `_test`、dependency tree、reactor build、隔离 coverage scanner、protected diff 和 `git diff --check` 的组合证据,可以判断 P1R-7d Market source owner propagation 应用层链路已具备进入 fresh implementation review 的 `needs_verification` 证据。 - -由于 Pascal/Mill 已 fresh 双 PASS,可以推断 P1R-7d Market source owner propagation implementation evidence 已收口到 `needs_verification`。该推断不代表 Events / P1R-7 / Market completed,也不代表可以自动提交或 push。 - -## 假设 - -- 当前 `_test` PostgreSQL 环境和 `~/.config/muse-repo/infra.env` 仍代表本地 Flyway 验证环境。 -- Events 既有 `EventsPublishApi` 与 SSE visible query 合同不在 P1R-7d 中修改。 -- P1R-7d 不要求补 Market OpenAPI coverage 状态到 `completed`。 - -## 下一步 - -1. 按逻辑分组准备提交,但提交和 push 仍需用户明确批准。 -2. 提交前再次确认 protected diff、`git diff --check` 和 `git status --short --branch`。 -3. 双 PASS 后仍只允许收口到 `needs_verification`,不得自动推进 completed。 diff --git a/docs/memorys/2026-06-07-P1R7eAccountSourceOwnerPropagation真实链路.md b/docs/memorys/2026-06-07-P1R7eAccountSourceOwnerPropagation真实链路.md deleted file mode 100644 index 233442c7..00000000 --- a/docs/memorys/2026-06-07-P1R7eAccountSourceOwnerPropagation真实链路.md +++ /dev/null @@ -1,302 +0,0 @@ -# P1R7e Account Source Owner Propagation 真实链路 - -日期:2026-06-07 - -## 结论 - -P1R-7e Account/Member source owner propagation 主体实现、V20 SQL、Member focused tests、P1R Account Events publish gate tests、dependency tree gate 与 protected diff gate 已形成 `needs_verification` evidence。 - -当前仍不能写 `completed`。本轮证据只代表 Account quota adjustment terminal fact -> Account publish outbox -> Account worker -> EventsPublishApi -> `muse_unified_event` -> `/app-api/muse/events` 对 `accountUserId` 可见的链路推进到证据层,不代表 Events / P1R-7 / Account completed。 - -本轮未修改 OpenAPI、coverage scanner 或真实 coverage 报告。 - -## 已验证事实 - -### 工作区与保护文件 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 最新 migration 前置检查:当前 V19 后新增 V20,未发现既有 V20+。 -- 受保护文件最终 staged / unstaged diff 均为 exit 0: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 本轮实现范围 - -- Member server 新增直接依赖 `muse-module-events-api`。 -- 新增 Account 本域 publish outbox: - - `AccountEventPublishOutboxDO` - - `AccountEventPublishOutboxMapper` - - `AccountEventPublishOutboxService` - - `AccountEventPublishOutboxServiceImpl` - - `AccountEventPayloads` - - `AccountEventPublishWorker` - - `MuseAccountEventsProperties` - - `MuseAccountEventsConfiguration` -- `AccountQuotaServiceImpl.adminCreateQuotaAdjustment` 在每条 `muse_member_entitlement_audit_log(change_type=quota_adjustment)` insert 后,同事务创建 Account Events publish outbox。 -- command replay 不补历史 outbox。 -- 新增 V20 migration:`muse-cloud/sql/muse/V20__extend_account_events_publish_outbox.sql`。 - -## 合同边界 - -- 每条 entitlement audit log 对应一条 outbox,不做 command 聚合,不做历史 backfill。 -- Events source tuple: - - `sourceOwner=account` - - `sourceType=account_quota_adjustment` - - `sourceId=auditLogId` - - `sourceRevision=__none__` - - `eventType=notification` -- notification type 固定为 `quota_alert`。 -- `sourceCommandId` 优先使用原始 `reqVO.getCommandId()`;历史 fallback 才从 `idempotencyKey.lastIndexOf(':')` 解析。 -- payload exact allowlist: - - 顶层只允许 `type/message/resourceRef/timestamp`。 - - `resourceRef` 只允许 `resourceType/resourceId`。 - - `message` 固定为 `额度已调整`。 - - `resourceRef.resourceId` 保持 Number/Long 语义,值为 entitlement audit log id。 - - `EventsPublishReqDTO.resourceId` 仍按 Events DTO 合同使用 String。 -- 禁止发布 requestHash、reason 原文、operatorUserId、before/after snapshot、correlationId、external detail、error stack、credential。 -- timestamp 优先使用 audit log `createTime`,缺失时回退 outbox `createTime`。 -- worker 默认配置关闭:`muse.account.events.publish-worker.enabled=false`。 -- 终态回写使用 `publish_status='running' AND attempt_count=#{claimedAttemptCount}` 防止过期 claim 覆盖新终态;终态更新 0 行会记录 stale claim 诊断日志。 - -## 验证证据 - -### Member focused tests - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" mvn -pl muse-module-member/muse-module-member-server -Dtest=AccountEventPublishOutboxServiceTest,AccountEventPublishWorkerTest,AccountQuotaServiceTest -Dsurefire.failIfNoSpecifiedTests=false test -``` - -结果:exit 0,36 tests pass,failures/errors/skipped 均为 0。 - -Surefire XML: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `AccountQuotaServiceTest` | 17 | 0 | 0 | 0 | -| `AccountEventPublishOutboxServiceTest` | 8 | 0 | 0 | 0 | -| `AccountEventPublishWorkerTest` | 11 | 0 | 0 | 0 | - -覆盖点: - -- quota adjustment 首次成功路径为每条 entitlement audit log 创建 outbox。 -- command replay 不创建 outbox。 -- 多 resourceType 产生多条不同 outbox。 -- invalid owner / missing audit log id / 非 quota_adjustment 不创建 outbox。 -- payload exact allowlist 与敏感字段负断言。 -- `resourceRef.resourceId` 是数值型 audit log id。 -- configured `maxAttempt` 生效。 -- timestamp 使用 audit log `createTime`,缺失时回退 outbox `createTime`。 -- worker disabled 不 claim。 -- accepted / duplicate accepted -> published。 -- rejected / blocked -> dead_letter。 -- CommonResult error / exception -> retryable 或 retry exhausted dead_letter。 -- invalid owner / payload invalid -> dead_letter 且不调用 EventsPublishApi。 -- terminal update 具备 claim attempt ownership guard。 -- stale terminal update 记录诊断日志。 - -### P1R mixed executable gates - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,73 tests pass,failures/errors/skipped 均为 0。 - -Surefire XML: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `P1rAccountEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rAccountEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAccountEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rAccountRealApiGateTest` | 4 | 0 | 0 | 0 | -| `P1rAccountRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rMarketEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rMarketEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rMarketEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishEndToEndTest` | 5 | 0 | 0 | 0 | -| `P1rAiEventsPublishMigrationSqlTest` | 4 | 0 | 0 | 0 | -| `P1rAiEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAiEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rApiCoverageReportTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRealApiGateTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rAiRealApiGateTest` | 6 | 0 | 0 | 0 | -| `P1rAiRouteOwnershipTest` | 7 | 0 | 0 | 0 | - -覆盖点: - -- Member server 直接依赖 `muse-module-events-api`,不依赖 `muse-module-events-server`。 -- Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。 -- V20 SQL 静态断言覆盖字段、唯一约束、check 约束、索引、trigger、BaseDO/TenantBaseDO 审计列、敏感列和外键禁用。 -- focused E2E 证明 Account worker 调用真实 `EventsPublishServiceImpl` 后,`quota_alert` 只对 `accountUserId` 可见,admin operator 不可见,非 owner 不可见。 -- 多条 quota alert 对同一 owner 可见。 -- rejected / dead_letter 后 SSE visible query 为空。 -- stale claim terminal update attempt mismatch 不覆盖当前 running outbox。 -- 同时回归 Account 真实 API gate、Account route ownership gate、AI/Knowledge/Market/Events 既有 P1R gates 与 coverage report gate。 - -### Full reactor build - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -结果:exit 0,日志保留在 `/tmp/p1r7e-full-reactor-20260608022955.log`;机器统计 reactor summary 为 `62` 个 `SUCCESS`,包含 `BUILD SUCCESS`,Finished at `2026-06-08T02:30:46+08:00`。 - -### Surefire XML 计数核对 - -clean build 后已重新运行 focused tests、P1R mixed executable gates 与 Flyway `_test`,以保留最终 surefire XML。 - -结果:Account focused + P1R mixed 目标 XML 合计 `109` tests,failures=0、errors=0、skipped=0;Flyway `_test` XML 单独保留 4/4,failures=0、errors=0、skipped=0。 - -### V20 Flyway `_test` - -准备: - -```bash -source ~/.config/muse-repo/infra.env -psql "host=$MUSE_POSTGRES_HOST port=$MUSE_POSTGRES_PORT user=$MUSE_POSTGRES_USERNAME dbname=postgres" -c 'CREATE DATABASE muse_p1r7e_account_events_publish_test' -``` - -已验证 `muse_p1r7e_account_events_publish_test` 存在。 - -执行: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/muse_p1r7e_account_events_publish_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -Dhttp.proxyHost= \ - -Dhttp.proxyPort= \ - -Dhttps.proxyHost= \ - -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,`P1rAccountEventsPublishFlywayMigrationIT` 4 tests pass,failures/errors/skipped 均为 0。 - -补证据:clean build 后已重新运行该 Flyway IT,`muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountEventsPublishFlywayMigrationIT.xml` 已保留,机器校验为 tests=4、failures=0、errors=0、skipped=0。 - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql:///muse_p1r7e_account_events_publish_test -migrations_executed=20 -successful_migration_count=20 -target_schema_version=20 -flyway_latest=20:extend account events publish outbox -v20_table=muse_account_event_publish_outbox -v20_indexes=idx_muse_account_event_publish_outbox_claim,idx_muse_account_event_publish_outbox_owner_status,idx_muse_account_event_publish_outbox_audit_log -v20_triggers=trg_muse_account_event_publish_outbox_update_time -``` - -Flyway IT 覆盖: - -- 只允许 `_test` 数据库。 -- 拒绝 JDBC URL 携带 credential query。 -- V1-V20 clean migrate。 -- V20 表、索引、约束、trigger 存在。 -- `event_type`、`notification_type`、`publish_status`、`account_user_id`、`audit_log_id/resource_ref_id` 非法插入会被数据库拒绝。 - -### Dependency tree gate - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-member/muse-module-member-server -DskipTests dependency:tree > /tmp/p1r7e-member-server-dependency-tree.txt -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-events/muse-module-events-server -DskipTests dependency:tree > /tmp/p1r7e-events-server-dependency-tree.txt -``` - -结果: - -- Member server tree 命中 `muse-module-events-api`。 -- Member server tree 未命中 `muse-module-events-server`。 -- Events server tree 对 AI / Knowledge / Market / Member / Content server 无命中输出。 -- 命令输出 `dependency_gate=pass`。 - -### Isolated coverage scanner - -执行范围:只在 `/tmp/p1r7e-coverage-scan.Ozo9Xm` 隔离副本运行 scanner,真实 worktree 的 `docs/superpowers/reports/*` 未被修改。 - -执行: - -```bash -tmpdir=$(mktemp -d /tmp/p1r7e-coverage-scan.XXXXXX) -rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/ -cd "$tmpdir" -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:exit 0。 - -summary: - -```json -{ - "totalOperations": 233, - "completedOperations": 100, - "needsVerificationOperations": 133, - "incompleteOperations": 0, - "genericPersistenceOperations": 0, - "ssePlaceholderOperations": 0 -} -``` - -边界核对: - -- Events `streamEvents=dedicated / needs_verification`。 -- Account 33 operations 均为 `dedicated / needs_verification`。 -- 未推进 completed。 - -### Diff gates - -执行: - -```bash -git diff --check -git diff --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -git diff --cached --quiet -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -结果: - -- `git diff --check` exit 0。 -- protected unstaged diff exit 0。 -- protected staged diff exit 0。 -- protected 文件 diff name-only 为空。 - -## 风险与后续 - -- 早前 implementer 用 `127.0.0.1:5432/muse_p1r7e_events_publish_test` 运行 Flyway IT 失败,原因是本地 PostgreSQL 服务不可达;该旧失败已被 controller 使用 `~/.config/muse-repo/infra.env` 和 `muse_p1r7e_account_events_publish_test` 真实 `_test` 库的 4/4 PASS 覆盖。 -- 本轮没有修改 OpenAPI、coverage scanner 或真实 coverage report,也没有推进 completed。 -- focused E2E 是内存 mapper + 真实 `EventsPublishServiceImpl`,不是 HTTP/SSE + 真实 DB 全链路;controller 后续可补真实 controller/SSE 验证。 -- 最终 fresh implementation review 已双 PASS:Banach correctness/spec PASS、Raman quality/data-integrity/testing PASS,均无 P0/P1/P2/P3 blocker。 -- 双 PASS 只代表 P1R-7e Account quota adjustment source owner propagation evidence 可以进入提交整理,不代表 Events/P1R-7/Account completed。 -- 当前未 push;提交整理仍不得推进 completed。 diff --git a/docs/memorys/2026-06-08-P1R7fContentSourceOwnerPropagation真实链路.md b/docs/memorys/2026-06-08-P1R7fContentSourceOwnerPropagation真实链路.md deleted file mode 100644 index 5ae5d9c7..00000000 --- a/docs/memorys/2026-06-08-P1R7fContentSourceOwnerPropagation真实链路.md +++ /dev/null @@ -1,328 +0,0 @@ -# P1R7f Content Source Owner Propagation 真实链路 - -日期:2026-06-08 - -## 结论 - -P1R-7f Content source owner propagation 主体实现、V21 SQL、Content focused tests、P1R mixed executable gates、V21 Flyway `_test`、dependency tree gate、full reactor build、隔离 coverage scanner 与 protected diff gate 已形成 `needs_verification` evidence。 - -当前仍不能写 `completed`。本轮证据只代表 Content `saveBlock` 首次成功 -> `muse_content_block_source_attribution(source_status=active)` -> Content publish outbox -> Content worker -> EventsPublishApi -> `muse_unified_event` -> `/app-api/muse/events` 对 owner 可见的链路推进到证据层,不代表 Events / P1R-7 / Content completed。 - -本轮未修改 OpenAPI、coverage scanner 或真实 coverage 报告。 - -## 已验证事实 - -### 工作区与保护文件 - -- 正确 worktree:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 分支:`dev/1.0.0`。 -- 起点 HEAD:`68cee5d test(p1r): 收口 Account 事件传播真实链路门禁`。 -- 本轮执行过 `git pull --ff-only origin dev/1.0.0`,结果为 `Already up to date.`。 -- 受保护文件最终 staged / unstaged diff 均为空: - - `docs/api-contracts/account/openapi.yaml` - - `docs/api-contracts/market/openapi.yaml` - - `docs/api-contracts/ai/openapi.yaml` - - `docs/api-contracts/knowledge/openapi.yaml` - - `docs/api-contracts/events/openapi.yaml` - - `muse-cloud/scripts/p1r-audit-api-coverage.py` - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -### 本轮实现范围 - -- Content server 新增直接依赖 `muse-module-events-api`。 -- 新增 Content 本域 publish outbox: - - `ContentEventPublishOutboxDO` - - `ContentEventPublishOutboxMapper` - - `ContentEventPublishOutboxService` - - `ContentEventPublishOutboxServiceImpl` - - `ContentEventPayloads` - - `ContentEventPublishWorker` - - `MuseContentEventsProperties` - - `MuseContentEventsConfiguration` -- `ContentAppServiceImpl.saveBlock` 在首次成功写入 `muse_content_block_source_attribution(source_status=active)` 后,同事务创建 Content Events publish outbox。 -- command replay 与 revision conflict 不创建 outbox。 -- 新增 V21 migration:`muse-cloud/sql/muse/V21__extend_content_events_publish_outbox.sql`。 - -## 合同边界 - -- 第一切片只覆盖 `ContentAppServiceImpl.saveBlock` 首次成功路径。 -- 不覆盖 `create_block`、`delete_block`、`splitBlock`、`mergeBlocks`、`mergeBlockSuggestion`。 -- 不覆盖 import、parse、export、planning、meta projection、admin risk action。 -- Events source tuple: - - `sourceOwner=content` - - `sourceType=content_block_source_attribution` - - `sourceId=blockId` - - `sourceRevision=blockRevision` - - `eventType=notification` -- notification type 固定为 `source_status_change`。 -- 幂等 key: - - `commandId=content_evt:` - - `outboxId=content_out:` -- outbox 唯一约束覆盖 `(tenant_id, block_id, block_revision, notification_type)`。 -- payload exact allowlist: - - 顶层只允许 `type/message/resourceRef/timestamp`。 - - `resourceRef` 只允许 `resourceType/resourceId`。 - - `message` 固定为 `内容来源状态已更新`。 - - `resourceRef.resourceId` 保持 Number/Long 语义,值为 block id。 - - `EventsPublishReqDTO.resourceId` 仍按 Events DTO 合同使用 String。 -- 禁止发布正文、sourceSnapshot 原文、sourceObjectId、authorizationSnapshotId、lineagePayload、licenseRestrictionSnapshot、auditReason、requestHash、resultSnapshot、错误堆栈。 -- worker 默认配置关闭:`muse.content.events.publish-worker.enabled=false`。 -- worker 只发布 `source_status=active`,非 active / invalid owner / invalid payload 均 fail-closed。 -- 终态回写使用 `publish_status='running' AND attempt_count=#{claimedAttemptCount}` 防止过期 claim 覆盖新终态;终态更新 0 行会记录 stale claim 诊断日志。 - -## 验证证据 - -### Full reactor build - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -``` - -结果:exit 0,62/62 reactor modules success,包含 `BUILD SUCCESS`。 - -### Content focused tests - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-module-content/muse-module-content-server -am \ - -Dtest=ContentAppServiceTest,ContentEventPublishOutboxServiceTest,ContentEventPublishWorkerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,29 tests pass,failures/errors/skipped 均为 0。 - -Surefire XML: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `ContentAppServiceTest` | 13 | 0 | 0 | 0 | -| `ContentEventPublishOutboxServiceTest` | 6 | 0 | 0 | 0 | -| `ContentEventPublishWorkerTest` | 10 | 0 | 0 | 0 | - -覆盖点: - -- `saveBlock` 首次成功路径在 source attribution insert 后创建 outbox。 -- command replay 不创建 outbox。 -- revision conflict 不更新 block、不写 source attribution、不创建 outbox。 -- outbox payload exact allowlist 与敏感字段负断言。 -- configured `maxAttempt` 与 `claimTimeoutSeconds` 生效。 -- timestamp 优先使用 source attribution `createTime`,缺失时回退 outbox `createTime`。 -- worker disabled 不 claim。 -- accepted / duplicate accepted -> published。 -- rejected / blocked -> dead_letter。 -- CommonResult error / exception -> retryable 或 retry exhausted dead_letter。 -- invalid payload / invalid owner / 非 active source status -> dead_letter 且不调用 EventsPublishApi。 -- terminal update 具备 claim attempt ownership guard。 -- stale terminal update 记录诊断日志。 - -### P1R mixed executable gates - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test \ - -pl muse-server -am \ - -Dtest=P1rContentEventsPublishMigrationSqlTest,P1rContentEventsPublishDependencyTest,P1rContentEventsPublishEndToEndTest,P1rContentRealApiGateTest,P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest,P1rMarketEventsPublishMigrationSqlTest,P1rMarketEventsPublishDependencyTest,P1rMarketEventsPublishEndToEndTest,P1rKnowledgeEventsPublishMigrationSqlTest,P1rKnowledgeEventsPublishDependencyTest,P1rKnowledgeEventsPublishEndToEndTest,P1rAiEventsPublishMigrationSqlTest,P1rAiEventsPublishDependencyTest,P1rAiEventsPublishEndToEndTest,P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,88 tests pass,failures/errors/skipped 均为 0。 - -Surefire XML: - -| 测试类 | tests | failures | errors | skipped | -|---|---:|---:|---:|---:| -| `P1rContentEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rContentEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rContentEventsPublishEndToEndTest` | 5 | 0 | 0 | 0 | -| `P1rContentRealApiGateTest` | 5 | 0 | 0 | 0 | -| `P1rAccountEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rAccountEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAccountEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rAccountRealApiGateTest` | 4 | 0 | 0 | 0 | -| `P1rAccountRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rMarketEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rMarketEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rMarketEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishMigrationSqlTest` | 3 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rKnowledgeEventsPublishEndToEndTest` | 5 | 0 | 0 | 0 | -| `P1rAiEventsPublishMigrationSqlTest` | 4 | 0 | 0 | 0 | -| `P1rAiEventsPublishDependencyTest` | 2 | 0 | 0 | 0 | -| `P1rAiEventsPublishEndToEndTest` | 4 | 0 | 0 | 0 | -| `P1rApiCoverageReportTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRealApiGateTest` | 5 | 0 | 0 | 0 | -| `P1rEventsRouteOwnershipTest` | 4 | 0 | 0 | 0 | -| `P1rAiRealApiGateTest` | 6 | 0 | 0 | 0 | -| `P1rAiRouteOwnershipTest` | 7 | 0 | 0 | 0 | - -覆盖点: - -- Content server 直接依赖 `muse-module-events-api`,不依赖 `muse-module-events-server`。 -- Events server 不反向依赖 AI / Knowledge / Market / Member / Content server。 -- V21 SQL 静态断言覆盖字段、唯一约束、check 约束、索引、trigger、BaseDO/TenantBaseDO 审计列、敏感列和外键禁用。 -- focused E2E 使用真实 `EventsPublishServiceImpl`,只 mock / in-memory `UnifiedEventMapper`,避免 bare fake EventsPublishApi 假绿。 -- Content notification 只对 owner 可见,非 owner 不可见。 -- duplicate same block revision 复用同一 eventId/sequenceNo,different revision 产生不同 notification。 -- rejected -> dead_letter 且 SSE visible query 为空。 -- non-active source status -> dead_letter 且不调用 Events。 -- stale claim terminal update attempt mismatch 不覆盖当前 running outbox。 -- 同时回归 Account/Market/Knowledge/AI/Events 既有 P1R gates 与 coverage report gate。 - -### Surefire XML 计数核对 - -clean build 后已重新运行 focused tests 与 P1R mixed executable gates,以保留最终 surefire XML。 - -结果:Content focused + P1R mixed 目标 XML 合计 `117` tests,failures=0、errors=0、skipped=0。 - -### V21 Flyway `_test` - -准备: - -```bash -source ~/.config/muse-repo/infra.env -createdb -h "$MUSE_POSTGRES_HOST" -p "$MUSE_POSTGRES_PORT" -U "$MUSE_POSTGRES_USERNAME" muse_p1r7f_content_events_publish_test -``` - -执行: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -JAVA_HOME=$(/usr/libexec/java_home -v 21) 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/muse_p1r7f_content_events_publish_test" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Djava.net.useSystemProxies=false \ - -DsocksProxyHost= \ - -DsocksProxyPort= \ - -Dhttp.proxyHost= \ - -Dhttp.proxyPort= \ - -Dhttps.proxyHost= \ - -Dhttps.proxyPort= \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:exit 0,`P1rContentEventsPublishFlywayMigrationIT` 4 tests pass,failures/errors/skipped 均为 0。 - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql:///muse_p1r7f_content_events_publish_test -migrations_executed=21 -successful_migration_count=21 -target_schema_version=21 -flyway_latest=21:extend content events publish outbox -v21_table=muse_content_event_publish_outbox -v21_indexes=idx_muse_content_event_publish_outbox_claim,idx_muse_content_event_publish_outbox_owner_status,idx_muse_content_event_publish_outbox_block_revision -v21_triggers=trg_muse_content_event_publish_outbox_update_time -``` - -Flyway IT 覆盖: - -- 只允许 `_test` 数据库。 -- 拒绝 JDBC URL 携带 credential query。 -- V1-V21 clean migrate。 -- V21 表、索引、约束、trigger 存在。 -- `event_type='chunk'`、`notification_type!='source_status_change'`、`source_status='deleted'`、非法 `publish_status`、非法 owner/block/resource/attempt 会被数据库拒绝。 - -调试留痕: - -- 首次 Flyway `_test` 失败于测试 helper 对 PostgreSQL 单值 check constraint 的判定过窄,真实约束为 `CHECK (((event_type)::text = 'notification'::text))`,生产 SQL 未改,修正的是测试判定函数。 -- 第二次运行出现长等待,线程栈显示新建 JDBC 连接或 Flyway advisory lock 阶段等待;最终通过复用已建立 connection 做非法 insert 验收并在每次失败后 rollback,避免连接层不稳定干扰 schema 断言。 - -### Dependency tree gate - -执行: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-content/muse-module-content-server -DskipTests dependency:tree > /tmp/p1r7f-content-server-dependency-tree.txt -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o -pl muse-module-events/muse-module-events-server -DskipTests dependency:tree > /tmp/p1r7f-events-server-dependency-tree.txt -rg "muse-module-events-api" /tmp/p1r7f-content-server-dependency-tree.txt -! rg "muse-module-events-server" /tmp/p1r7f-content-server-dependency-tree.txt -! rg "muse-module-(ai|knowledge|market|member|content)-server" /tmp/p1r7f-events-server-dependency-tree.txt -``` - -结果: - -- Content server tree 命中 `muse-module-events-api`。 -- Content server tree 未命中 `muse-module-events-server`。 -- Events server tree 未命中 AI / Knowledge / Market / Member / Content server。 - -### Coverage scanner 隔离副本 - -首次尝试在临时 venv 安装 PyYAML 失败,原因为 pip TLS 证书校验失败,不计入 scanner 失败。随后确认系统 Python 已有 PyYAML 6.0.3,复用同一 `/tmp` 隔离副本执行 scanner。 - -执行: - -```bash -tmpdir=$(mktemp -d /tmp/p1r7f-coverage-scan.XXXXXX) -rsync -a --delete --exclude .git --exclude 'muse-cloud/**/target' ./ "$tmpdir"/ -cd "$tmpdir" -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果:exit 0,仅在 `/tmp/p1r7f-coverage-scan.N1DHvZ` 生成报告。 - -summary: - -```text -completedOperations=100 -needsVerificationOperations=133 -incompleteOperations=0 -genericPersistenceOperations=0 -ssePlaceholderOperations=0 -``` - -### Protected diff 与 whitespace - -执行: - -```bash -git diff --check -git diff --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -git diff --cached --name-only -- docs/api-contracts/account/openapi.yaml docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/p1r-api-coverage.json docs/superpowers/reports/p1r-api-coverage.md -``` - -结果: - -- `git diff --check` exit 0。 -- protected unstaged diff 输出为空。 -- protected staged diff 输出为空。 - -## 推断 - -- 当前实现与 P1R-7f 执行版第一切片一致:只把 Content `saveBlock` 产生的 active source attribution terminal fact 推送到 Events notification 链路,不试图把 Content 全部写命令一次性纳入 Events。 -- V21 DB check、worker fail-closed、SQL gate、Flyway `_test` 与 E2E 共同降低了把 Content save 误发布为 `chunk` 或把 deleted source status 发布给 SSE 的风险。 - -## 假设 - -- 本轮 focused E2E 使用 in-memory / mocked mapper 验证 `EventsPublishServiceImpl` 语义,仍不是完整生产运行时。 -- 后续如果要推进 Content / Events / P1R-7 completed,仍需要单独 approval gate,不能复用本文件作为 completed 依据。 - -## 后续动作 - -## Fresh implementation review - -fresh implementation 双 review 已通过: - -- Hubble correctness/spec review:PASS,无 P0/P1/P2/P3 blocker。 -- Dalton quality/data-integrity/testing review:PASS,无 P0/P1/P2/P3 blocker。 - -Hubble 复核了正确 worktree、protected diff、scope、`saveBlock` 接入点、outbox/worker source tuple、V21 防 `chunk/deleted`、真实 `EventsPublishServiceImpl` E2E、依赖边界与 `git diff --check`。 - -Dalton 复核了 V21 migration 的 NOT NULL / unique / check / index / trigger、无 FK/敏感列、事务 outbox、幂等复用、payload exact allowlist、worker 状态机、focused/P1R/Flyway 测试质量与 Surefire XML 防空跑,读取目标 XML 得到 121 tests 且 failures/errors/skipped 均为 0。 - -## 后续动作 - -- 可按逻辑分组准备提交。 -- 提交、push 或推进 completed 仍需用户明确批准。 -- 本轮双 PASS 仍不代表 Events / P1R-7 / Content completed。 diff --git a/docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md b/docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md deleted file mode 100644 index 047b9571..00000000 --- a/docs/memorys/2026-06-09-P1R7CompletedApproval状态推进.md +++ /dev/null @@ -1,203 +0,0 @@ -# P1R-7 Completed Approval 状态推进 - -## 结论 - -本次在用户明确批准后,只把 P1R-7 Events End-to-End Acceptance 的唯一 operation: - -```text -events:streamEvents / GET /app-api/muse/events -``` - -从 `dedicated / needs_verification` 推进为 `dedicated / completed`。 - -本次没有把整个 `events` domain 加入 completed 白名单,没有推进 Market / Account / Content / Meta completed,也不代表总 P1R completed。 - -## 用户批准点 - -用户已明确批准三项: - -1. 只审批 `events:streamEvents / GET /app-api/muse/events`。 -2. 采用 operation-level approval,不采用 `events` domain-level 白名单。 -3. 允许在真实 worktree 修改 scanner、coverage gate test 与 coverage report,把 completed summary 从 `100/133` 推进到 `101/132`。 - -实现中发现既有 P1R mixed gate 还包含旧 coverage 口径断言。用户于 2026-06-10 回复“允许”,批准把以下 4 个旧 RealApi gate 纳入本轮测试口径修正: - -- `P1rEventsRealApiGateTest.java` -- `P1rAiRealApiGateTest.java` -- `P1rKnowledgeRealApiGateTest.java` -- `P1rMarketRealApiGateTest.java` - -该扩展只允许同步测试断言到 `events:streamEvents` operation-level completed,不允许修改业务实现或 OpenAPI。 - -## 状态变化 - -推进前 coverage summary: - -```text -233 100 133 0 0 0 -``` - -推进后 coverage summary: - -```text -233 101 132 0 0 0 -``` - -推进后 domain 状态: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/completed:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 16 dedicated/needs_verification:16 -``` - -唯一新增 completed: - -```text -events streamEvents GET /app-api/muse/events dedicated completed P1R-7 End-to-End Acceptance -``` - -## 修改文件 - -scanner: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` - -测试: - -- `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/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` - -report: - -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -文档: - -- `docs/agent-specs/2026-06-09-P1R7CompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-09-P1R7CompletedApproval执行版.md` -- `docs/agent-specs/.agent` - -## 关键实现口径 - -scanner 保留历史 domain-level approval: - -```python -APPROVED_COMPLETED_DOMAINS = {"ai", "knowledge"} -``` - -并新增 operation-level approval: - -```python -APPROVED_COMPLETED_OPERATIONS = {"events:streamEvents"} -``` - -`approved_completed_operation` 仍要求 completed operation 必须是 `dedicated`,并且只能来自已批准 domain 或已批准 operation key。 - -## 验证证据 - -TDD RED: - -```text -P1rApiCoverageReportTest tests=6 failures=1 -失败原因:expected completed=101 but was=100 -``` - -隔离 scanner 预验证: - -```text -/tmp/p1r7-completed-approval-impl-scan.QMrhrf -233 101 132 0 0 0 -events streamEvents GET /app-api/muse/events dedicated completed P1R-7 End-to-End Acceptance -``` - -真实 scanner: - -```text -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -233 101 132 0 0 0 -``` - -局部 gate: - -```text -P1rApiCoverageReportTest tests=6 failures=0 errors=0 skipped=0 -``` - -旧 RealApi gate 同步后局部 gate: - -```text -P1rMarketRealApiGateTest tests=6 failures=0 errors=0 skipped=0 -P1rKnowledgeRealApiGateTest tests=8 failures=0 errors=0 skipped=0 -P1rAiRealApiGateTest tests=7 failures=0 errors=0 skipped=0 -P1rApiCoverageReportTest tests=6 failures=0 errors=0 skipped=0 -P1rEventsRealApiGateTest tests=5 failures=0 errors=0 skipped=0 -``` - -P1R focused gates: - -```text -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o test -pl muse-server -am -Dtest=... -Dsurefire.failIfNoSpecifiedTests=false -Tests run: 118, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -XML 防空跑计数: - -```text -p1r_completed_approval_gate_tests=118 -``` - -最终 diff gate: - -```text -git diff --check -OpenAPI staged / unstaged diff 为空 -allowed_diff_gate=PASS -``` - -Fresh implementation review: - -```text -Linnaeus spec/correctness review PASS -Hypatia quality/data-integrity/testing review PASS -``` - -review 结论边界: - -- PASS 只代表 `events:streamEvents` operation-level completed approval implementation 可收口。 -- PASS 不代表总 P1R completed。 -- PASS 不代表 Market / Account / Content / Meta completed。 - -## 已验证事实 - -- `events:streamEvents` 是本次唯一新增 completed operation。 -- Market 32 个 operation 仍为 `dedicated / needs_verification`。 -- Account 33 个 operation 仍为 `dedicated / needs_verification`。 -- Content 51 个 operation 仍为 `dedicated / needs_verification`。 -- Meta 16 个 operation 仍为 `dedicated / needs_verification`。 -- 7 个 OpenAPI 合同没有 diff。 -- 本次没有修改业务实现代码。 - -## 推断 - -基于 P1R-7a 到 P1R-7f 已收口的 source owner propagation evidence、operation-level approval scanner、coverage gate、P1R focused gates 与 protected diff,可以把 P1R-7 Events completed approval 作为状态推进完成候选,进入 fresh implementation review。 - -## 假设 - -提交和 push 仍需用户明确指令。 - -## 后续约束 - -- 不得把整个 `events` domain 加入 completed 白名单。 -- 不得把 Market / Account / Content / Meta 推进 completed。 -- 不得把总 P1R completed 写成已完成。 -- fresh implementation 双 review PASS 前不得提交或 push。 diff --git a/docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md b/docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md deleted file mode 100644 index e09dec65..00000000 --- a/docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md +++ /dev/null @@ -1,183 +0,0 @@ -# P1R Meta Remaining 5 状态推进 - -日期:2026-06-10 - -## 结论 - -本轮只把 Meta 剩余 5 个 operation 从 `dedicated / needs_verification` 推进为 `dedicated / completed`,继续使用 operation-level approval,没有把 `meta` 加入 domain-level completed allowlist。 - -审批对象: - -- `meta:listProtectionNodes` -- `meta:getProtectionNode` -- `meta:listFunctionChains` -- `meta:previewFunctionChainImpact` -- `meta:activateFunctionChainVersion` - -推进后 coverage summary: - -```text -total=233 -completed=117 -needsVerification=116 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -Meta 当前为 16 个 operation 全部 `dedicated / completed`。Account 33、Market 32、Content 51 仍保持 `dedicated / needs_verification`,总 P1R 仍未 completed。 - -## 修改范围 - -允许范围内修改: - -- `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/P1rMetaRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaFlywayMigrationIT.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` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-10-P1RMetaRemaining5CompletedApproval执行版.md` -- `docs/memorys/2026-06-10-P1RMetaRemaining5状态推进.md` - -未修改: - -- `docs/api-contracts/**` -- Meta 业务实现 -- Account / Market / Content 业务实现 - -## 关键实现 - -`p1r-audit-api-coverage.py` 仅在 `APPROVED_COMPLETED_OPERATIONS` 追加 5 个 Meta operation key: - -```text -meta:listProtectionNodes -meta:getProtectionNode -meta:listFunctionChains -meta:previewFunctionChainImpact -meta:activateFunctionChainVersion -``` - -`APPROVED_COMPLETED_DOMAINS` 仍只有: - -```text -ai -knowledge -``` - -新增 `P1rMetaFlywayMigrationIT`,用真实 PostgreSQL `_test` 库验证 V1 到 V10 Flyway: - -- 目标版本:`10` -- 当前描述:`extend meta real api schema` -- 成功 SQL migration 数:`10` -- 关键表:`muse_meta_function_chain_version`、`muse_meta_function_chain_slot`、`muse_meta_function_chain_node`、`muse_meta_protection_node`、`muse_meta_function_chain` -- 关键约束:FunctionChain version unique、active partial unique、slot/node unique、ProtectionNode unique -- 关键 trigger:ProtectionNode、FunctionChain、FunctionChain version/slot/node update trigger -- 失败路径:重复 `tenant_id/chain_id/version_no`、重复 active version、缺少 `chain_id` 均由 PostgreSQL 拒绝 - -## 验证证据 - -TDD RED: - -```text -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -RED 失败点符合预期: - -- `P1rApiCoverageReportTest` 期望 completed `117`,旧 report 仍为 `112` -- `P1rMetaRealApiGateTest` 期望 Meta 16 completed,旧 report 仍是 11 completed / 5 needs_verification - -Scanner: - -```text -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 117 116 0 0 0 -``` - -P1R mixed gates: - -```text -P1rApiCoverageReportTest -P1rMetaRealApiGateTest -P1rEventsRealApiGateTest -P1rAiRealApiGateTest -P1rKnowledgeRealApiGateTest -P1rMarketRealApiGateTest -P1rContentRealApiGateTest -P1rAccountRealApiGateTest -``` - -结果:45/45 pass。 - -Meta focused gates: - -```text -ProtectionNodeServiceTest -FunctionChainServiceTest -AdminProtectionNodeControllerTest -AdminFunctionChainControllerTest -FunctionChainGuardTest -MetaProtectionNodeGuardTest -P1rMetaMigrationSqlTest -P1rMetaFlywayMigrationIT -``` - -XML 计数确认: - -```text -ProtectionNodeServiceTest tests=3 -FunctionChainServiceTest tests=12 -AdminProtectionNodeControllerTest tests=7 -AdminFunctionChainControllerTest tests=7 -FunctionChainGuardTest tests=4 -MetaProtectionNodeGuardTest tests=5 -P1rMetaMigrationSqlTest tests=3 -P1rMetaFlywayMigrationIT tests=4 -``` - -真实 Flyway `_test`: - -```text -test_db=muse_p1r2_meta_remaining5_test -flyway_success=true -migrations_executed=10 -successful_migration_count=10 -target_schema_version=10 -flyway_latest=10:extend meta real api schema -``` - -XML 防空跑覆盖 16 个目标测试类,全部 failures/errors/skipped 为 0。 - -Diff gates: - -- `git diff --check`:通过 -- `docs/api-contracts` staged / unstaged protected diff:空 -- allowed-diff gate:只允许本轮批准范围内文件 - -## 边界 - -本轮不代表: - -- 总 P1R completed -- Account completed -- Market completed -- Content completed -- 未来 Meta 新 operation 自动 completed - -后续如果推进 Account / Market / Content,仍必须分别进入审阅版、执行版、fresh review、用户批准和 TDD/验证流程。 diff --git a/docs/memorys/2026-06-10-P1RMeta状态推进.md b/docs/memorys/2026-06-10-P1RMeta状态推进.md deleted file mode 100644 index d931f692..00000000 --- a/docs/memorys/2026-06-10-P1RMeta状态推进.md +++ /dev/null @@ -1,143 +0,0 @@ -# P1R Meta 状态推进 - -日期:2026-06-10 - -## 结论 - -本次只把 P1R-2 Meta 中 11 个 MetaSchema 管理 operation 从 `dedicated / needs_verification` 推进为 `dedicated / completed`。 - -本次没有把 Meta 整域加入 completed 白名单,没有推进 FunctionChain / ProtectionNode,也没有推进 Account / Market / Content 或总 P1R completed。 - -## 状态变化 - -推进前 coverage summary: - -```text -233 101 132 0 0 0 -``` - -推进后 coverage summary: - -```text -233 112 121 0 0 0 -``` - -推进后 domain 状态: - -```text -account 33 dedicated/needs_verification:33 -ai 41 dedicated/completed:41 -content 51 dedicated/needs_verification:51 -events 1 dedicated/completed:1 -knowledge 59 dedicated/completed:59 -market 32 dedicated/needs_verification:32 -meta 11 dedicated/completed:11 -meta 5 dedicated/needs_verification:5 -``` - -## 新增 completed 范围 - -本次新增 completed 仅限: - -```text -meta:listMetaSchemas -meta:getMetaSchema -meta:getMetaSchemaVersion -meta:saveMetaSchemaDraft -meta:validateMetaSchemaDraft -meta:previewMetaSchemaDraftImpact -meta:publishMetaSchemaDraft -meta:activateMetaSchemaVersion -meta:rollbackMetaSchemaVersion -meta:deprecateMetaSchemaVersion -meta:setMetaSchemaGrayRules -``` - -## 保留 needs_verification 范围 - -以下 5 个 Meta operation 仍保留 `dedicated / needs_verification`: - -```text -meta:listProtectionNodes -meta:getProtectionNode -meta:listFunctionChains -meta:previewFunctionChainImpact -meta:activateFunctionChainVersion -``` - -其中 `FunctionChain` 与 `ProtectionNode` 不纳入本轮 approval;后续如要推进,必须单独补 completed evidence 和用户批准。 - -## 修改文件 - -scanner: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` - -测试: - -- `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/P1rMetaRealApiGateTest.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` - -report: - -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -文档: - -- `docs/memorys/2026-06-10-P1RMeta状态推进.md` -- `docs/agent-specs/.agent` - -## 验证证据 - -TDD RED: - -```text -P1rApiCoverageReportTest + P1rMetaRealApiGateTest -Tests run: 10, Failures: 3 -失败原因:旧 report 仍为 completed=101,MetaSchema operation 仍为 needs_verification。 -``` - -真实 scanner: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -结果: - -```text -233 112 121 0 0 0 -meta dedicated/completed:11 -meta dedicated/needs_verification:5 -``` - -focused gates: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rMetaRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果: - -```text -Tests run: 36, Failures: 0, Errors: 0, Skipped: 0 -BUILD SUCCESS -``` - -## 边界 - -- completed 仍要求 `implementationStatus=dedicated`。 -- scanner 仍只允许已批准 domain 或已批准 operation 进入 completed。 -- 本次没有修改任何 OpenAPI。 -- 本次没有修改 Meta 业务实现代码。 -- 本次没有把 `meta` 加入 domain-level completed 白名单。 -- 后续不得把本轮 11 个 operation approval 扩写成 Meta 16/16 completed。 diff --git a/docs/memorys/2026-06-11-P1RAccount状态推进.md b/docs/memorys/2026-06-11-P1RAccount状态推进.md deleted file mode 100644 index 472484f1..00000000 --- a/docs/memorys/2026-06-11-P1RAccount状态推进.md +++ /dev/null @@ -1,253 +0,0 @@ -# P1R Account 状态推进 - -日期:2026-06-11 - -## 结论 - -本轮只推进 Account 第一批 10 个 operation-level completed approval,没有把 `account` 加入 domain-level completed allowlist。 - -coverage summary 从: - -```text -233 117 116 0 0 0 -``` - -推进为: - -```text -233 127 106 0 0 0 -``` - -Account 状态从 33 个 `dedicated / needs_verification` 推进为: - -```text -completed=10 -needsVerification=23 -``` - -本轮不代表 Account 33/33 completed,不代表 Market / Content / 总 P1R completed。 - -## 已批准 completed 的 Account operation - -```text -account:getCurrentUser -account:getProfile -account:updateProfile -account:adminListAccountUsers -account:adminGetUserEntitlements -account:getAppEntitlements -account:adminGetBalanceSnapshots -account:getAppBalanceSnapshots -account:adminCreateQuotaAdjustment -account:adminListQuotaAdjustments -``` - -## 继续 needs_verification 的 Account operation - -```text -account:adminCreateCallAttributionJob -account:adminGetCallAttributionJob -account:adminGetIntegrationCallByCorrelation -account:adminListNewApiBindings -account:adminListPurchaseRecords -account:adminListUsageRecords -account:adminCreateNewApiBinding -account:adminCreateQuotaRequest -account:appDownloadExport -account:appCreateExportTask -account:appGetExportTask -account:appGetIntegrationCallByCorrelation -account:appListLicenses -account:getAppNewApiBinding -account:appRecheckNewApiBinding -account:appListPublishRecords -account:appListPurchases -account:appCreateQuotaRequest -account:appGetQuotaRequest -account:appListSecurityEvents -account:appGetSecurityEvent -account:appAcknowledgeSecurityEvent -account:getAppUsage -``` - -主要原因: - -- New-API runtime、binding、recheck、quota request、integration correlation 仍缺真实 runtime completed evidence。 -- FileService / export download 仍缺真实文件交付、下载字节和对象存储闭环 evidence。 -- call attribution external source 仍缺真实外部来源 completed evidence。 -- Market purchase / license / publish projection 仍缺跨 owner 上游与 Account projection 一致性 evidence。 -- security event 生产路径仍缺 completed-grade source fact。 -- `getAppUsage` 与 `adminListUsageRecords` 虽有 dedicated 实现和 focused tests,但仍依赖 `muse_account_integration_call` / attribution source 的上游事实质量;本轮未纳入第一批。 - -## 修改范围 - -本轮修改: - -- `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/P1rAccountRealApiGateTest.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` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-11-P1RAccountCompletedApproval执行版.md` -- `docs/memorys/2026-06-11-P1RAccount状态推进.md` - -未修改: - -- 7 个 OpenAPI。 -- Account 业务实现。 -- 数据库迁移。 -- Account domain-level completed allowlist。 - -## TDD evidence - -RED: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:37 tests run,10 failures,BUILD FAILURE。 - -失败点来自旧 report 仍为 `117/116`、Account 仍为 `0 completed / 33 needs_verification`,符合预期。 - -GREEN 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 127 106 0 0 0 -Account Counter({'needs_verification': 23, 'completed': 10}) -``` - -## Verification evidence - -### Account focused tests - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-member/muse-module-member-server,muse-server -am \ - -Dtest=AccountProfileServiceTest,AccountEntitlementServiceTest,AccountQuotaServiceTest,AccountAuditServiceTest,AccountCommandServiceTest,AccountOwnerGuardTest,AccountApiVersionGuardTest,AccountQuotaGuardTest,AdminAccountUserControllerTest,AppAccountProfileControllerTest,AppAccountEntitlementControllerTest,P1rAccountEventsPublishMigrationSqlTest,P1rAccountEventsPublishDependencyTest,P1rAccountEventsPublishEndToEndTest,P1rAccountRealApiGateTest,P1rApiCoverageReportTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果: - -- `muse-module-member-server`: 114/114 pass。 -- `muse-server`: 19/19 pass。 -- BUILD SUCCESS。 - -### P1R mixed gates - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:46/46 pass,BUILD SUCCESS。 - -### Flyway `_test` - -测试库: - -```text -muse_p1r_account_completed_approval_test -``` - -命令: - -```bash -source ~/.config/muse-repo/infra.env -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -export P1R_ACCOUNT_TEST_DB=muse_p1r_account_completed_approval_test - -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountEventsPublishFlywayMigrationIT \ - -Dflyway.postgresql.transactional.lock=false \ - -Dp1r.flyway.locations=filesystem:sql/muse \ - -Dp1r.flyway.url="jdbc:postgresql://$MUSE_POSTGRES_HOST:$MUSE_POSTGRES_PORT/$P1R_ACCOUNT_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 -``` - -结果:4/4 pass,BUILD SUCCESS。 - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql:///muse_p1r_account_completed_approval_test -migrations_executed=20 -successful_migration_count=20 -target_schema_version=20 -flyway_latest=20:extend account events publish outbox -v20_table=muse_account_event_publish_outbox -``` - -### Account outbox / worker enhanced evidence - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ -mvn -o test -pl muse-module-member/muse-module-member-server -am \ - -Dtest=AccountEventPublishOutboxServiceTest,AccountEventPublishWorkerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -结果:19/19 pass,BUILD SUCCESS。 - -### XML 防空跑 - -读取 25 个目标 Surefire XML: - -- Account focused XML:Profile、Entitlement、Quota、Audit、Command、Guard、Controller、outbox、worker。 -- P1R XML:Account events SQL / dependency / E2E / Flyway、Coverage、Account、Events、AI、Knowledge、Market、Content、Meta gates。 - -结果: - -```text -XML_TOTAL_OK tests=192 -failures=0 -errors=0 -skipped=0 -``` - -## 当前边界 - -- Account 不是整域 completed。 -- Market 32 个 operation 继续 `dedicated / needs_verification`。 -- Content 51 个 operation 继续 `dedicated / needs_verification`。 -- Meta 16 个 operation 继续 `dedicated / completed`。 -- AI 41、Knowledge 59、Events 1 保持已批准 completed。 -- 未修改 OpenAPI 或 Account 业务实现。 - -## 下一步 - -必须进行 fresh implementation spec/correctness review + fresh implementation quality/data-integrity/testing review。 - -双 PASS 前不得提交、push 或宣称总 P1R / Account 33/33 / Market / Content completed。 diff --git a/docs/memorys/2026-06-11-P1RMarket状态推进.md b/docs/memorys/2026-06-11-P1RMarket状态推进.md deleted file mode 100644 index e18b613e..00000000 --- a/docs/memorys/2026-06-11-P1RMarket状态推进.md +++ /dev/null @@ -1,262 +0,0 @@ -# P1R Market 状态推进 - -日期:2026-06-11 - -## 结论 - -本轮只推进 Market 第一批 4 个 operation-level completed approval: - -- `market:getMarketplaceAsset` -- `market:listMarketplaceCategories` -- `market:favoriteAsset` -- `market:unfavoriteAsset` - -Market 仍不是整域 completed。`listMarketplaceAssets`、`listMarketplaceRecommendations` 以及 purchase/install/handoff/governance/publish/review/appeal/projection 相关 28 个 Market operation 继续 `dedicated/needs_verification`。 - -本轮未修改 Market 业务实现,未修改数据库迁移,未把 `market` 加入 domain-level completed allowlist。 - -## 工作区与基线 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -基线 HEAD: - -```text -f18116a test(p1r): 收口 Account 第一批 completed approval 门禁 -``` - -实施前 coverage summary: - -```text -233 127 106 0 0 0 -``` - -实施后 coverage summary: - -```text -233 131 102 0 0 0 -``` - -实施后 Market 状态: - -```text -completed=4 -needs_verification=28 -``` - -## 允许变更与实际变更 - -OpenAPI 只修改: - -- `docs/api-contracts/market/openapi.yaml` - -变更内容只是在: - -- `POST /app-api/muse/marketplace/assets/{assetId}/favorite` -- `DELETE /app-api/muse/marketplace/assets/{assetId}/favorite` - -增加 required `X-Command-Id` header。原因是 Java Controller / Service 已强制校验 command id,而原 OpenAPI 没有声明,coverage report 因此误显示 `requiresCommandId=false`。 - -scanner 只修改: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` - -实际只追加 4 个 operation-level completed key,没有把 `market` 加入 `APPROVED_COMPLETED_DOMAINS`。 - -coverage report 已重新生成: - -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` - -## TDD 证据 - -RED 阶段先修改 gate 期望值和 Market operation 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现: - -- 失败点包括 expected completed=131 but was 127。 -- 失败点包括 Market expected 4 completed but was 0。 -- 该失败证明 gate 会阻止未获 scanner/report 支撑的 completed 推进。 - -GREEN 阶段完成 OpenAPI commandId 合同修正、scanner operation-level allowlist、report 重新生成和 focused evidence tests 后,目标验证通过。 - -## MockMvc HTTP 入口 + DB 证据 - -新增: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketDiscoveryFavoriteCompletedApprovalIT.java` - -验证方式: - -- 使用 Spring Mock web context + `MockMvc`。 -- 使用真实 PostgreSQL `_test` 数据库。 -- 使用 Flyway clean/migrate V1-V15。 -- 密码只从环境变量读取,禁止通过 JVM system property 传入。 - -说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。 - -验证数据库: - -```text -muse_p1r_market_discovery_favorite_completed_approval_test -``` - -通过结果: - -```text -P1rMarketDiscoveryFavoriteCompletedApprovalIT: tests=9, failures=0, errors=0, skipped=0 -``` - -覆盖内容: - -- 资产详情 happy path。 -- missing asset / invisible asset 不泄露详情、不写 command。 -- 分类聚合包含 fallback slot 与 unavailable exposure summary。 -- favorite 缺少 `X-Command-Id` 不写 DB。 -- favorite 写 active favorite 与 command,重复 command 回放不重复写。 -- unfavorite 写 inactive,不删除审计事实,重复 command 回放不重复写。 -- missing/invisible target 对 favorite/unfavorite 不污染 command/favorite fact。 - -## Flyway 证据 - -增强: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketFlywayMigrationIT.java` - -验证数据库: - -```text -muse_p1r_market_completed_approval_test -``` - -通过结果: - -```text -P1rMarketFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0 -``` - -验证内容: - -- V1-V15 clean migrate。 -- `muse_market_favorite` 表存在。 -- favorite user/asset/command 相关索引存在。 -- `uk_muse_market_favorite_user_asset` 唯一约束存在。 -- `trg_muse_market_favorite_updated_at` trigger 存在。 -- 同 tenant/user/asset duplicate insert 被 PostgreSQL 唯一约束拒绝,SQLState 为 `23505`。 -- 同 tenant/command_id duplicate insert 被 PostgreSQL partial unique index 拒绝,SQLState 为 `23505`。 -- favorite status 更新为 `inactive` 时,`trg_muse_market_favorite_updated_at` 会覆盖旧 `update_time`。 - -额外组合验证: - -```text -P1rMarketDiscoveryFavoriteCompletedApprovalIT -> P1rMarketFlywayMigrationIT: tests=13, failures=0, errors=0, skipped=0 -P1rMarketFlywayMigrationIT -> P1rMarketDiscoveryFavoriteCompletedApprovalIT: tests=13, failures=0, errors=0, skipped=0 -``` - -该组合验证使用真实 PostgreSQL `_test` 库: - -```text -muse_p1r_market_combined_completed_approval_test -``` - -组合验证用于证明新增 Discovery IT 与增强 Flyway IT 对 `p1r.flyway.url/user` 的脱敏不会污染同一 Surefire JVM 内其他测试类。曾复现一次失败:Discovery IT 先运行后,Flyway IT 读到脱敏 system property,报缺少原始 `p1r.flyway.url`。修复方式是在 Discovery IT 与 Flyway IT 中都保存原始 `p1r.flyway.url/user`,并在 `@AfterAll` 恢复;随后补充 `-Dsurefire.runOrder=reversealphabetical`,日志确认先跑 Flyway IT、再跑 Discovery IT,合计 13/13 pass。 - -注意:Surefire XML 的 `` 会记录原始 `p1r.flyway.url` host 与 `p1r.flyway.user`,未发现 password 泄露。raw XML 只能作为本地验证产物,不应直接外发;如需外发测试报告,应先清洗 host/user 或改造为不通过 JVM system property 传递 URL/user。 - -review 处理: - -- correctness reviewer 指出的 system property 污染为有效问题,已修复并用双向 combined IT 13/13 pass 关闭。 -- data/testing reviewer 提到“真正 HTTP”若理解为 socket 级 live server则当前证据不足。核对执行版后,本轮要求是 WebApplicationContext/MockMvc + real mapper + `_test` DB,不是 live socket server;本文档已把措辞收窄为 MockMvc HTTP 入口,避免过度声明。 -- data/testing reviewer 提到 favorite `status` 没有 DB-level CHECK、`asset_id/user_id` 没有 FK。该问题属于后续 schema hardening;本轮执行版明确禁止改迁移,只能在现有 V8/V15 约束上补强 unique/index/trigger 断言,不把它扩成新增迁移。 -- 最终 fresh implementation review 已双 PASS:Raman spec/correctness review PASS、Hubble data-integrity/testing review PASS,均无 P0/P1/P2/P3 阻塞项。两名 reviewer 均确认 `market` 未进入 domain-level allowlist、只推进 4 个 `market:*` operation、Market 仍为 4 completed / 28 needs_verification、`listMarketplaceAssets` 与 `listMarketplaceRecommendations` 继续 needs_verification、OpenAPI diff 仅限 favorite/unfavorite required `X-Command-Id`、Market main/java 与 SQL diff 为空、13 个目标 XML 合计 85 tests 且 failures/errors/skipped 均为 0。非阻塞注意事项仍是 raw Surefire XML 暴露 host/user 但不含 password,外发前需清洗。 - -## focused gates - -Market focused verification 通过: - -```text -AppMuseMarketplaceAssetControllerTest: tests=11, failures=0, errors=0, skipped=0 -MarketAssetQueryServiceTest: tests=6, failures=0, errors=0, skipped=0 -MarketFavoriteServiceTest: tests=7, failures=0, errors=0, skipped=0 -MuseMarketAssetMapperTest: tests=2, failures=0, errors=0, skipped=0 -P1rMarketMigrationSqlTest: tests=9, failures=0, errors=0, skipped=0 -P1rMarketRouteOwnershipTest: tests=4, failures=0, errors=0, skipped=0 -P1rMarketRealApiGateTest: tests=6, failures=0, errors=0, skipped=0 -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -``` - -跨域 P1R focused gates 通过: - -```text -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -P1rMarketRealApiGateTest: tests=6, failures=0, errors=0, skipped=0 -P1rEventsRealApiGateTest: tests=6, failures=0, errors=0, skipped=0 -P1rAiRealApiGateTest: tests=7, failures=0, errors=0, skipped=0 -P1rKnowledgeRealApiGateTest: tests=8, failures=0, errors=0, skipped=0 -``` - -合计: - -```text -P1R mixed gates: 33/33 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 131 102 0 0 0 -``` - -Market operation 状态确认: - -```text -getMarketplaceAsset completed false -listMarketplaceCategories completed false -favoriteAsset completed true -unfavoriteAsset completed true -listMarketplaceAssets needs_verification false -listMarketplaceRecommendations needs_verification false -``` - -diff check: - -```text -git diff --check: pass -``` - -OpenAPI diff: - -```text -docs/api-contracts/market/openapi.yaml -``` - -仅包含 favorite/unfavorite 两个 operation 的 required `X-Command-Id` header。 - -## 后续边界 - -本轮完成后仍不得宣称: - -- Market 32/32 completed。 -- Content completed。 -- Account remaining 23 completed。 -- 总 P1R completed。 - -后续如果继续 Market completed approval,推荐单独处理: - -1. `listMarketplaceAssets`:先补 pageNo/pageSize 1..100 边界与防全量查询证据。 -2. `listMarketplaceRecommendations`:先解决 OpenAPI 个性化推荐语义与 fallback 排序实现的合同不一致。 -3. publish/review/governance 13 个 operation:另写执行版冻结范围、Events outbox 边界和 fresh runtime evidence。 -4. purchase/install/handoff/governanceImpact:必须先补目标 owner 消费闭环与 Account 读侧 E2E。 -5. appeal/projection:不能把申诉审计、治理 outbox 和 Account projection 混成一个 completed 闭环。 diff --git a/docs/memorys/2026-06-12-P1RContent状态推进.md b/docs/memorys/2026-06-12-P1RContent状态推进.md deleted file mode 100644 index 14daef48..00000000 --- a/docs/memorys/2026-06-12-P1RContent状态推进.md +++ /dev/null @@ -1,378 +0,0 @@ -# P1R Content 状态推进 - -日期:2026-06-12 - -## 结论 - -本轮只推进 Content 第一批 8 个 operation-level completed approval: - -- `content:listWorks` -- `content:getWork` -- `content:listChapters` -- `content:getChapter` -- `content:listBlocks` -- `content:getBlock` -- `content:saveBlock` -- `content:getBlockSourceAttribution` - -Content 仍不是整域 completed。其余 43 个 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 -0032774 test(p1r): 收口 Market 第一批 completed approval 门禁 -``` - -实施前 coverage summary: - -```text -233 131 102 0 0 0 -``` - -实施后 coverage summary: - -```text -233 139 94 0 0 0 -``` - -实施后 Content 状态: - -```text -completed=8 -needs_verification=43 -``` - -## 继续 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:getPlanning -content:listPlanningCandidates -content:createPlanningCandidate -content:getPlanningCandidate -content:confirmPlanningCandidate -content:discardPlanningCandidate -content:createStyleCheck -content:getStyleCheckResult -content:savePlanningItem -``` - -主要原因: - -- 创建、更新、删除、重排、拆分、合并等写命令仍存在 OpenAPI / coverage `requiresCommandId` 与服务端 VO 或实现口径不一致风险,本轮不夹带合同修正。 -- 导入、导出、解析、下载仍缺 FileService、任务产物和真实下载字节闭环 evidence。 -- Meta projection、AI planning candidate、style check、suggestion 等能力仍依赖 Meta / AI / Knowledge 外部 owner closure 和运行时证据。 -- admin read / risk action 需要单独 admin guard、审计和跨 owner 边界 evidence。 -- planning section 虽属 Content 本域,但有 schemaVersion、projectionVersion、CAS、command replay 等独立合同,后续应单独切片。 - -## 修改范围 - -本轮修改: - -- `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/P1rContentEventsPublishFlywayMigrationIT.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentCoreCompletedApprovalIT.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` - -未修改: - -- 7 个 OpenAPI。 -- Content 业务实现。 -- SQL migration。 -- Content domain-level completed allowlist。 - -## TDD evidence - -RED 阶段先修改 P1R gate 期望值和 Content 8 个 operation 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现: - -- 失败点来自旧 report 仍为 `completed=131 / needsVerification=102`。 -- 失败点来自 Content 8 个 operation 仍为 `needs_verification`。 -- 该失败证明 gate 会阻止未获 scanner/report 支撑的 completed 推进。 - -GREEN 阶段完成 scanner operation-level allowlist、report 重新生成、Content HTTP+DB `_test` evidence 和 focused gates 后,目标验证通过。 - -## MockMvc HTTP 入口 + DB 证据 - -新增: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentCoreCompletedApprovalIT.java` - -验证方式: - -- 使用 Spring Mock web context + `MockMvc`。 -- 使用真实 mapper / service。 -- 使用真实 PostgreSQL `_test` 数据库。 -- 使用 Flyway clean/migrate V1-V21。 -- 密码只从环境变量读取,禁止通过 JVM system property 传入。 - -说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。 - -验证数据库: - -```text -muse_p1r_content_core_completed_approval_test -``` - -通过结果: - -```text -P1rContentCoreCompletedApprovalIT: tests=12, failures=0, errors=0, skipped=0 -``` - -review 修复: - -- fresh testing review(Hooke)指出 chapter/block missing 负路径缺显式 HTTP 断言。 -- 已补 `missing work chapter list`、`missing chapter detail`、`missing chapter block list`、`missing block detail`。 -- 已补上述查询负路径的 Content outbox no-write 断言。 -- 修复后已重跑 `P1rContentCoreCompletedApprovalIT`,结果仍为 tests=12、failures=0、errors=0、skipped=0,XML mtime 在本次运行窗口内。 -- fresh implementation review 第一轮中,Bohr correctness review 与 Euclid testing/data-integrity review 均 FAIL,指出 `saveBlock` 缺 `expectedRevision`、缺 `sourceSnapshot` 两个执行版 mandatory 负路径。 -- 已补 `p1r-content-missing-expected-revision` 与 `p1r-content-missing-source-snapshot` 两个 HTTP+DB no-write 断言,并复用 `assertSnapshotUnchanged` 覆盖 block/source/outbox/command 不写入。 -- 修复后已重跑 `P1rContentCoreCompletedApprovalIT`,结果仍为 tests=12、failures=0、errors=0、skipped=0,XML mtime 在本次运行窗口内。 - -覆盖内容: - -- `listWorks` 分页、状态过滤、owner 隔离、tenant 隔离、空页。 -- `getWork` happy、missing、cross owner、cross tenant。 -- `listChapters` 的 happy、empty、missing work、cross owner、cross tenant。 -- `getChapter` 的 happy、missing chapter、path mismatch、cross owner、cross tenant。 -- `listBlocks` 的 happy、empty、missing chapter、path mismatch、cross owner、cross tenant。 -- `getBlock` 的 happy、missing block、path mismatch、cross owner、cross tenant。 -- `saveBlock` 写入 block revision、source attribution、Content outbox、command log。 -- `saveBlock` command replay 不重复写。 -- `saveBlock` 缺 `commandId`、缺 `expectedRevision`、缺 `sourceSnapshot`、revision conflict、command conflict、path mismatch、cross owner、cross tenant 均断言 no-write 或事务回滚后行数不变。 -- `getBlockSourceAttribution` active/current、empty、path mismatch、cross owner、cross tenant,错误路径不写 DB。 - -## Flyway V21 证据 - -增强: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentEventsPublishFlywayMigrationIT.java` - -验证数据库: - -```text -muse_p1r_content_core_completed_approval_test -``` - -通过结果: - -```text -P1rContentEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0 -``` - -关键输出: - -```text -flyway_success=true -flyway_url=jdbc:postgresql:///muse_p1r_content_core_completed_approval_test -flyway_locations_effective=filesystem:/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud/sql/muse -migrations_executed=21 -successful_migration_count=21 -target_schema_version=21 -flyway_latest=21:extend content events publish outbox -v21_table=muse_content_event_publish_outbox -v21_triggers=trg_muse_content_event_publish_outbox_update_time -``` - -验证内容: - -- V1-V21 clean migrate。 -- V21 Content event publish outbox 表存在。 -- claim、owner/status、block/revision 相关索引存在。 -- event type、notification type、source status、publish status、owner、block、block revision、resource ref、attempt count、max attempt 等约束存在。 -- update time trigger 存在。 -- `attempt_count=-1` 被 PostgreSQL check 拒绝。 -- `max_attempt=0` 被 PostgreSQL check 拒绝。 - -## system property 组合验证 - -`P1rContentCoreCompletedApprovalIT` 与 `P1rContentEventsPublishFlywayMigrationIT` 都会保存并在 `@AfterAll` 恢复 `p1r.flyway.url/user`,避免脱敏后的 system property 污染同一 Surefire JVM 后续测试。 - -组合验证使用真实 PostgreSQL `_test` 库: - -```text -muse_p1r_content_core_completed_approval_test -``` - -通过结果: - -```text -P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT: tests=16, failures=0, errors=0, skipped=0 -P1rContentCoreCompletedApprovalIT,P1rContentEventsPublishFlywayMigrationIT with -Dsurefire.runOrder=reversealphabetical: tests=16, failures=0, errors=0, skipped=0 -``` - -Hooke 修复后已重跑上述两组组合验证,结果仍为 16/16 pass,两个目标 XML 均为本次运行窗口内的新产物。 - -Bohr / Euclid P1 修复后已重跑普通组合验证,结果仍为 16/16 pass,两个目标 XML 均为本次运行窗口内的新产物。 - -注意:两次组合验证的实际 Surefire 执行顺序均为 `P1rContentEventsPublishFlywayMigrationIT` 先、`P1rContentCoreCompletedApprovalIT` 后。`-Dsurefire.runOrder=reversealphabetical` 未改变实际顺序,因此本轮只能证明 Flyway IT 脱敏/恢复不会污染后续 Core IT,不能写成已证明 Core IT -> Flyway IT 反向顺序。 - -## focused gates - -Content focused verification 通过,并已做 XML 防空跑: - -```text -ContentAppServiceTest: tests=13, failures=0, errors=0, skipped=0 -ContentSourceServiceTest: tests=23, failures=0, errors=0, skipped=0 -ContentEventPublishOutboxServiceTest: tests=6, failures=0, errors=0, skipped=0 -ContentEventPublishWorkerTest: tests=10, failures=0, errors=0, skipped=0 -AppContentControllerTest: tests=32, failures=0, errors=0, skipped=0 -AppContentSourceControllerTest: tests=5, failures=0, errors=0, skipped=0 -P1rContentEventsPublishMigrationSqlTest: tests=3, failures=0, errors=0, skipped=0 -P1rContentEventsPublishDependencyTest: tests=2, failures=0, errors=0, skipped=0 -P1rContentEventsPublishEndToEndTest: tests=5, failures=0, errors=0, skipped=0 -P1rContentRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -``` - -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 139 94 0 0 0 -``` - -Content operation 状态确认: - -```text -completed=8 -needs_verification=43 -``` - -completed 清单: - -```text -listWorks -getWork -listChapters -getChapter -listBlocks -getBlock -saveBlock -getBlockSourceAttribution -``` - -代表性非目标 operation 仍为 `needs_verification`: - -```text -adminRiskAction -createWork -updateWork -mergeBlockSuggestion -createChapter -exportWork -getPlanning -``` - -diff check: - -```text -git diff --check: pass -``` - -protected OpenAPI diff: - -```text -empty -``` - -allowed diff 检查结果: - -```text -仅包含执行版允许的 docs/agent-specs、docs/memorys、coverage report、scanner、P1R gate test、Content Core IT 与 Content Flyway IT 文件。 -``` - -## 当前边界 - -- fresh implementation re-review 已双 PASS:Mendel correctness review PASS,确认 Bohr/Euclid P1 已关闭,8 个 Content completed operation、scanner operation-level allowlist、coverage summary、protected diff 与 XML 证据均自洽;Anscombe testing/data-integrity review PASS,无 findings、无 testing gaps,残余风险仅为 raw Surefire XML 可能包含本地 DB host/user,未发现 password 泄露。 -- 本轮只代表 Content 第一批 8 个 operation-level completed approval。 -- 不代表 Content 51/51 completed。 -- 不代表 Account remaining completed。 -- 不代表 Market remaining completed。 -- 不代表总 P1R completed。 -- raw Surefire XML 可能包含 DB host/user,未发现 password 泄露;外发前需清洗。 -- 当前已具备提交条件;提交或 push 仍需用户明确指令。 diff --git a/docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md b/docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md deleted file mode 100644 index 5b4f3fad..00000000 --- a/docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md +++ /dev/null @@ -1,274 +0,0 @@ -# P1R Account Security Events 状态推进 - -日期:2026-06-13 - -## 结论 - -本轮只推进 Account 用户安全事件只读 2 个 operation-level completed approval: - -- `account:appListSecurityEvents` -- `account:appGetSecurityEvent` - -Account 仍不是整域 completed。`account:appAcknowledgeSecurityEvent` 继续保持 `dedicated/needs_verification/requiresCommandId=true`。 - -本轮未修改 OpenAPI,未修改 Account 业务实现,未修改 SQL migration,未把 `account` 加入 domain-level completed allowlist。 - -## 工作区与基线 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -基线 HEAD: - -```text -304f665 test(p1r): 收口 Content Admin RiskAction completed approval 门禁 -``` - -实施前 coverage summary: - -```text -233 145 88 0 0 0 -``` - -实施后 coverage summary: - -```text -233 147 86 0 0 0 -``` - -实施后 Account 状态: - -```text -total=33 -completed=12 -needs_verification=21 -incomplete=0 -genericPersistence=0 -ssePlaceholder=0 -``` - -目标 operation 状态: - -```text -appListSecurityEvents dedicated completed false GET /app-api/muse/account/security-events -appGetSecurityEvent dedicated completed false GET /app-api/muse/account/security-events/{eventId} -``` - -必须保持 needs_verification: - -```text -appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge -``` - -## 修改范围 - -本轮修改: - -- `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/P1rAccountRealApiGateTest.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/P1rAccountSecurityEventsCompletedApprovalIT.java` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RAccountSecurityEventsCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RAccountSecurityEvents状态推进.md` - -未修改: - -- 7 个 OpenAPI。 -- Account 业务实现。 -- SQL migration。 -- Account domain-level completed allowlist。 -- 既有 Account focused tests。 - -## TDD evidence - -RED 阶段先修改 P1R gate 期望值和 2 个 Security Events read operation completed 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现: - -```text -Tests run: 49, Failures: 10, Errors: 0, Skipped: 0 -BUILD FAILURE -``` - -失败点来自旧 report 仍为 `completed=145 / needsVerification=88`,Account 仍为 `10 completed / 23 needs_verification`,以及 `appListSecurityEvents` / `appGetSecurityEvent` 仍为 `needs_verification`;没有编译错误或无关测试失败。 - -GREEN 阶段只把两个 `account:*` key 加入 scanner operation-level allowlist,重新生成 report,并完成 HTTP+DB `_test` evidence 和 focused gates。 - -## MockMvc HTTP 入口 + DB 证据 - -新增: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountSecurityEventsCompletedApprovalIT.java` - -验证方式: - -- 使用 Spring Mock web context + `MockMvc`。 -- 通过 `/app-api/muse/account/security-events` 与 `/app-api/muse/account/security-events/{eventId}` HTTP 入口进入 `AppAccountSecurityController`。 -- 使用真实 `AccountSecurityServiceImpl`、mapper、tenant SQL interceptor 和 PostgreSQL `_test` 数据库。 -- Flyway clean/migrate V1-V21。 -- 在隔离 `_test` 库创建 test-local `member_user` fixture,满足 `requireUser()` 的真实 `MemberUserMapper.selectById` 前置;该 fixture 不计入 P1R production schema evidence。 -- 密码只从环境变量读取,拒绝 JVM password system property 和 JDBC credential query。 -- `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user`,避免污染同一 Surefire JVM 后续 Flyway 测试。 - -单类验证数据库: - -```text -muse_p1r_account_security_events_completed_approval_test -``` - -通过结果: - -```text -P1rAccountSecurityEventsCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0 -``` - -覆盖内容: - -- 拒绝非 `_test` 数据库、JVM password system property 和 JDBC credential query。 -- V1-V21 schema gate,覆盖 `muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_event_publish_outbox` 和 test-local `member_user`。 -- `idx_muse_member_security_user`、`trg_muse_member_security_updated_at`、`uk_muse_account_security_event_ack_command`、ack user/event 索引和 updated_at trigger。 -- list 支持 severity、分页、owner/tenant 隔离,并合并当前用户最新 ack。 -- detail 支持 eventId + owner 查询,返回脱敏 IP、脱敏 userAgent、脱敏 description / suggestedActions。 -- 请求参数不能覆盖当前登录 owner。 -- missing / invalid API version、invalid severity、missing user、missing event、非数字 eventId、跨 owner、跨 tenant 都不泄露目标事实。 -- read happy/error 都使用 `row_to_json(t)::text` 对 `member_user`、`muse_member_security_event`、`muse_account_security_event_ack`、`muse_account_command`、`muse_account_audit`、`muse_account_event_publish_outbox` 做全表快照 no-write。 - -## 组合验证 - -Account focused Maven 命令通过,muse-server 部分: - -```text -P1rAccountSecurityEventsCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0 -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -focused muse-server total: tests=19, failures=0, errors=0, skipped=0 -``` - -member-server focused XML: - -```text -AccountSecurityServiceTest: tests=8, failures=0, errors=0, skipped=0 -AppAccountSecurityControllerTest: tests=6, failures=0, errors=0, skipped=0 -AccountSecurityEventAckMapperTest: tests=4, failures=0, errors=0, skipped=0 -MemberSecurityEventMapperTest: tests=2, failures=0, errors=0, skipped=0 -AccountConvertTest: tests=9, failures=0, errors=0, skipped=0 -focused member-server total: tests=29, failures=0, errors=0, skipped=0 -``` - -Account V20 Flyway 迁移门禁使用真实 PostgreSQL `_test` 库: - -```text -muse_p1r_account_security_events_flyway_test -``` - -通过结果: - -```text -P1rAccountEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0 -flyway_success=true -migrations_executed=20 -successful_migration_count=20 -target_schema_version=20 -flyway_latest=20:extend account events publish outbox -``` - -P1R mixed gates: - -```text -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -P1rAccountRealApiGateTest: 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 -P1rContentRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rMetaRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -mixed total: tests=49, failures=0, errors=0, skipped=0 -``` - -fresh implementation testing review 后处理: - -```text -Banach testing/data-integrity review: PASS -P2 residual: 新增 IT 未直接覆盖 X-API-Version: 2 -处理结果: 已补 invalid API version HTTP+DB no-write 断言 -复验: P1rAccountSecurityEventsCompletedApprovalIT 8/8 pass -复验: Account focused muse-server 19/19 pass -复验: P1R mixed gates 49/49 pass -复验: scanner/report 233/147/86 -复验: XML 防空跑与 protected diff 通过 -``` - -## scanner / report 验证 - -命令: - -```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 147 86 0 0 0 -``` - -Account 聚合: - -```text -33 12 21 0 0 0 -``` - -目标 operation 明细: - -```text -account appListSecurityEvents dedicated completed false GET /app-api/muse/account/security-events -account appGetSecurityEvent dedicated completed false GET /app-api/muse/account/security-events/{eventId} -account appAcknowledgeSecurityEvent dedicated needs_verification true POST /app-api/muse/account/security-events/{eventId}/acknowledge -``` - -## protected diff - -通过: - -```bash -git diff --check -``` - -通过: - -```bash -git diff --quiet -- docs/api-contracts muse-cloud/muse-module-member/muse-module-member-server/src/main/java muse-cloud/sql/muse -git diff --cached --quiet -- docs/api-contracts muse-cloud/muse-module-member/muse-module-member-server/src/main/java muse-cloud/sql/muse -``` - -结论: - -```text -protected_diff_empty=true -``` - -## 后续边界 - -本轮完成候选只代表 2 个 Account Security Events read operation-level completed approval。 - -仍不代表: - -- Account 33/33 completed。 -- `account:appAcknowledgeSecurityEvent` completed。 -- Account remaining 21 completed。 -- Market remaining completed。 -- Content remaining completed。 -- 总 P1R completed。 - -提交或 push 前仍需要 fresh implementation spec/correctness review + fresh implementation quality/data-integrity/testing review 双 PASS。 diff --git a/docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md b/docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md deleted file mode 100644 index fa6edf4d..00000000 --- a/docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md +++ /dev/null @@ -1,307 +0,0 @@ -# P1R Content Admin Read 状态推进 - -日期:2026-06-13 - -## 结论 - -本轮只推进 Content Admin Read 3 个 operation-level completed approval: - -- `content:adminListWorks` -- `content:adminGetWork` -- `content:adminListChapters` - -Content 仍不是整域 completed。其余 38 个 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 -2692af3 test(p1r): 收口 Content Planning completed approval 门禁 -``` - -实施前 coverage summary: - -```text -233 141 92 0 0 0 -``` - -实施后 coverage summary: - -```text -233 144 89 0 0 0 -``` - -实施后 Content 状态: - -```text -completed=13 -needs_verification=38 -``` - -目标 operation 状态: - -```text -adminListWorks dedicated completed false -adminGetWork dedicated completed false -adminListChapters dedicated completed false -``` - -明确保留的 admin 非目标 operation: - -```text -adminListExportTasks dedicated needs_verification false -adminListImportTasks dedicated needs_verification false -adminRiskAction dedicated needs_verification true -``` - -## 继续 needs_verification 的 Content operation - -```text -content:adminListExportTasks -content:adminListImportTasks -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/P1rContentAdminReadCompletedApprovalIT.java` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RContentAdminReadCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RContentAdminRead状态推进.md` - -未修改: - -- 7 个 OpenAPI。 -- Content 业务实现。 -- SQL migration。 -- Content domain-level completed allowlist。 - -## TDD evidence - -RED 阶段先修改 P1R gate 期望值和 Content Admin Read 3 个 operation 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现: - -- `P1rApiCoverageReportTest` 失败点来自旧 report 仍为 `completed=141 / needsVerification=92`。 -- `P1rContentRealApiGateTest` 失败点来自 `adminListWorks`、`adminGetWork`、`adminListChapters` 仍为 `needs_verification`。 - -GREEN 阶段完成 scanner operation-level allowlist、report 重新生成、Admin Read HTTP+DB `_test` evidence 和 focused gates 后,目标验证通过。 - -## MockMvc HTTP 入口 + DB 证据 - -新增: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminReadCompletedApprovalIT.java` - -验证方式: - -- 使用 Spring Mock web context + `MockMvc`。 -- 使用真实 mapper / service。 -- 使用真实 PostgreSQL `_test` 数据库。 -- 使用 Flyway clean/migrate V1-V21。 -- 启用 method security,并用 test-local `ss` bean 验证 `muse:content:query` 权限门禁。 -- 注册真实 tenant SQL interceptor,证明 admin read 的 tenant 边界来自后端 SQL 层。 -- 密码只从环境变量读取,禁止通过 JVM system property 或 JDBC query 传入。 - -说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。 - -单类验证数据库: - -```text -muse_p1r_content_admin_read_completed_approval_test -``` - -通过结果: - -```text -P1rContentAdminReadCompletedApprovalIT: tests=5, failures=0, errors=0, skipped=0 -``` - -覆盖内容: - -- 拒绝 JVM password system property、JDBC credential query 和非 `_test` 数据库。 -- `adminListWorks` 覆盖 status、keyword、riskFlag=true、治理摘要、tenant 隔离、正文不泄露和 no-write。 -- `adminListWorks` 覆盖治理动作为空时 `riskFlag=true` 返回空页。 -- `adminGetWork` 覆盖作品详情、章节摘要、exception summary、governance history、same-tenant different owner admin 可读、missing/cross tenant、正文不泄露和 no-write。 -- `adminListChapters` 覆盖章节排序、blockCount、wordCount、governance target scope、missing/cross tenant、正文不泄露和 no-write。 -- `X-API-Version` 不支持时返回 `CONTENT_API_VERSION_UNSUPPORTED`,不泄露正文且不写入。 -- 缺 `muse:content:query` 权限时由 method security 返回 403 CommonResult,确认 `ss` bean 被调用,不泄露正文且不写入。 -- no-write 不是只看行数:测试用 `row_to_json(t)::text` 对 `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_governance_action`、`muse_content_command_log`、`muse_content_event_publish_outbox` 做请求前后全表快照比对,能发现新增、删除和 UPDATE。 -- riskFlag 空结果场景在测试自身删除治理事实后重新采样快照,避免测试 DML 遮蔽前置 admin read 的写污染。 - -## 组合验证 - -组合验证使用真实 PostgreSQL `_test` 库: - -```text -muse_p1r_content_admin_read_combined_test -``` - -通过结果: - -```text -P1rContentAdminReadCompletedApprovalIT: tests=5, failures=0, errors=0, skipped=0 -P1rContentPlanningCompletedApprovalIT: tests=11, failures=0, errors=0, skipped=0 -P1rContentCoreCompletedApprovalIT: tests=12, failures=0, errors=0, skipped=0 -P1rContentEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0 -combined: tests=32, failures=0, errors=0, skipped=0 -``` - -## fresh review 修复 - -Lorentz correctness review: - -```text -PASS -findings=0 -``` - -Hegel testing/data-integrity review: - -```text -FAIL -P1: no-write 证据会被测试自身 deleteGovernanceActions() 掩盖。 -P2: no-write 只比对行数,不能证明读路径没有 UPDATE。 -``` - -修复: - -- 每个 admin read 请求前后执行 `ContentFactSnapshot` 全表快照比对。 -- riskFlag 空结果场景在测试自身 DML 后重新采样 no-write baseline。 -- 删除旧的行数式 `assertNoAdminReadWrites` 断言。 - -修复后验证: - -```text -P1rContentAdminReadCompletedApprovalIT: tests=5, failures=0, errors=0, skipped=0 -P1rContentAdminReadCompletedApprovalIT + Planning + Core + Flyway combined: tests=32, failures=0, errors=0, skipped=0 -``` - -fresh re-review: - -```text -Peirce correctness/spec re-review: PASS, findings=0 -Carver testing/data-integrity re-review: PASS, findings=0 -``` - -Carver P3 residual: - -```text -focused ContentAdminServiceTest/AdminContentControllerTest XML 时间早于最后一次 Admin Read no-write 修复源码时间。 -``` - -处理结果: - -- 这两个 focused 测试源码本轮未改,但为消除 XML 新鲜度残余,已重新运行 focused Content tests。 -- fresh focused 结果为 `ContentAdminServiceTest 13/13`、`AdminContentControllerTest 8/8`,合计 `21/21 pass`。 - -## focused Content tests - -执行版 focused tests 已单独运行,并确认 XML 为本轮 fresh 产物: - -```text -ContentAdminServiceTest: tests=13, failures=0, errors=0, skipped=0 -AdminContentControllerTest: tests=8, failures=0, errors=0, skipped=0 -focused total=21 -``` - -## P1R mixed gates - -P1R mixed gates fresh 运行: - -```text -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -P1rContentRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rEventsRealApiGateTest: tests=7, failures=0, errors=0, skipped=0 -P1rAiRealApiGateTest: tests=7, failures=0, errors=0, skipped=0 -P1rKnowledgeRealApiGateTest: tests=8, failures=0, errors=0, skipped=0 -P1rMarketRealApiGateTest: tests=6, failures=0, errors=0, skipped=0 -P1rAccountRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rMetaRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -mixed total=49 -``` - -## 最终边界检查 - -已运行: - -```text -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -git diff --check -git -c core.quotePath=false diff -- docs/api-contracts -git -c core.quotePath=false diff -- muse-cloud/muse-module-content/muse-module-content-server/src/main/java muse-cloud/sql/muse -``` - -结果: - -- scanner `--check` 通过并重生成 report。 -- coverage summary 为 `233/144/89/0/0/0`。 -- `git diff --check` 无输出。 -- OpenAPI protected diff 为空。 -- Content main/java diff 为空。 -- SQL migration diff 为空。 - -## 注意事项 - -- 当前状态只代表 3 个 Admin Read operation-level completed approval 的 implementation 已完成本轮本地验证。 -- fresh implementation review 双 PASS 前,不得提交、push 或宣称本轮收口。 -- 不得把本轮结果外推为 Content 51/51 completed。 -- `content:adminRiskAction` 是治理写命令,仍需单独 completed approval;不能用 admin read 的 riskFlag 聚合证据替代。 -- admin import/export task 仍需单独证明任务状态、产物和 work 关联;不能夹入本轮。 -- raw Surefire XML 可能包含本地 classpath、user.dir、测试 DB URL 等本机信息,外发前需要清洗。 diff --git a/docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md b/docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md deleted file mode 100644 index 28b268ad..00000000 --- a/docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md +++ /dev/null @@ -1,261 +0,0 @@ -# P1R Content Admin RiskAction 状态推进 - -日期:2026-06-13 - -## 结论 - -本轮只推进 Content 管理端治理写命令 1 个 operation-level completed approval: - -- `content:adminRiskAction` - -Content 仍不是整域 completed。其余 37 个 Content operation 继续 `dedicated/needs_verification`。 - -本轮未修改 OpenAPI,未修改 Content 业务实现,未修改 SQL migration,未把 `content` 加入 domain-level completed allowlist。 - -## 工作区与基线 - -工作区: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -基线 HEAD: - -```text -b0b4b2c test(p1r): 收口 Content Admin Read completed approval 门禁 -``` - -实施前 coverage summary: - -```text -233 144 89 0 0 0 -``` - -实施后 coverage summary: - -```text -233 145 88 0 0 0 -``` - -实施后 Content 状态: - -```text -completed=14 -needs_verification=37 -``` - -目标 operation 状态: - -```text -adminRiskAction dedicated completed true -``` - -代表性非目标 operation 仍保持: - -```text -adminListExportTasks dedicated needs_verification false -adminListImportTasks dedicated needs_verification false -createWork dedicated needs_verification false -exportWork dedicated needs_verification false -createStyleCheck dedicated needs_verification true -getStyleCheckResult dedicated needs_verification false -``` - -## 修改范围 - -本轮修改: - -- `muse-cloud/scripts/p1r-audit-api-coverage.py` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/superpowers/reports/p1r-api-coverage.md` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java` -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminRiskActionCompletedApprovalIT.java` -- `docs/agent-specs/.agent` -- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval审阅版.md` -- `docs/agent-specs/2026-06-13-P1RContentAdminRiskActionCompletedApproval执行版.md` -- `docs/memorys/2026-06-13-P1RContentAdminRiskAction状态推进.md` - -未修改: - -- 7 个 OpenAPI。 -- Content 业务实现。 -- SQL migration。 -- Content domain-level completed allowlist。 - -## TDD evidence - -RED 阶段先修改 P1R gate 期望值和 `adminRiskAction` completed 断言,在旧 scanner/report 下运行 focused P1R gates,预期失败已出现: - -```text -Tests run: 49, Failures: 12, Errors: 0, Skipped: 0 -BUILD FAILURE -``` - -失败点来自旧 report 仍为 `completed=144 / needsVerification=89`,以及 `adminRiskAction` 仍为 `needs_verification`;没有编译错误或无关测试失败。 - -GREEN 阶段只把 `content:adminRiskAction` 加入 scanner operation-level allowlist,重新生成 report,并完成 HTTP+DB `_test` evidence 和 focused gates。 - -## MockMvc HTTP 入口 + DB 证据 - -新增: - -- `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentAdminRiskActionCompletedApprovalIT.java` - -验证方式: - -- 使用 Spring Mock web context + `MockMvc`。 -- 通过 `/admin-api/muse/content/works/{workId}/risk-actions` HTTP 入口进入 `AdminContentController`。 -- 使用真实 `ContentAdminServiceImpl`、`ContentCommandServiceImpl`、`ContentAuditServiceImpl`、mapper 和 PostgreSQL `_test` 数据库。 -- 启用 method security,并用 test-local `ss` bean 验证 `muse:content:risk-action` 权限门禁。 -- 注册真实 tenant SQL interceptor。 -- Flyway clean/migrate V1-V21。 -- 密码只从环境变量读取,拒绝 JVM password system property 和 JDBC credential query。 -- `@AfterAll` 恢复 `p1r.flyway.url` / `p1r.flyway.user`,避免污染同一 Surefire JVM 后续 Flyway 测试。 - -说明:这里的 HTTP 入口证据是执行版要求的 WebApplicationContext + MockMvc 请求路径证据,不是启动真实 socket 端口的 live servlet container 证据。 - -单类验证数据库: - -```text -muse_p1r_content_admin_risk_action_completed_approval_test -``` - -通过结果: - -```text -P1rContentAdminRiskActionCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0 -``` - -覆盖内容: - -- 拒绝非 `_test` 数据库、JVM password system property 和 JDBC credential query。 -- V1-V21 schema gate,覆盖 `muse_content_governance_action` 的 JSONB `target_ids`、unique command 约束、work 索引和 updated_at trigger。 -- duplicate `(tenant_id, command_id)` 返回 SQLState `23505`。 -- `WorkMapper.selectByIdForUpdate` 源码断言包含 `FOR UPDATE`。 -- API version、RBAC 和 Bean Validation 拒绝路径均 no-write。 -- work scope happy path 写 governance action、command/audit envelope,不写 Content Events outbox。 -- chapter / block scope happy path 只允许 target 属于 URL work。 -- `notify_owner` 返回 `pending_notification`,其它支持 action 返回 `executed`。 -- command replay 返回首次结果,不重复写 governance action、command/audit 或 outbox。 -- command conflict、revision conflict、unsupported action、unsupported targetScope、空 targetIds、null targetIds、missing/cross-work/cross-tenant target、block->chapter->work 链路不匹配和 cross-tenant work 都整体回滚。 -- command row 断言 `tenant_id`、`command_id`、`command_type=adminRiskAction`、`owner_user_id`、`target_type=work`、`target_id`、`request_hash`。 -- governance action row 断言 `tenant_id`、`work_id`、`operator_user_id`、`command_id`、`audit_log_id`、`action`、`reason`、`target_scope`、`target_ids`、`expected_version`、`status`。 -- command/audit snapshot 解析断言 `operationId=adminRiskAction`、`side=admin`、`targetType=work`、`targetId`、`status=succeeded`、`reason`、`requestSummary` 和 `result`。 -- no-write / rollback 使用 `row_to_json(t)::text` 对 `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_governance_action`、`muse_content_command_log`、`muse_content_event_publish_outbox` 做全表快照比对。 - -## 组合验证 - -组合验证使用真实 PostgreSQL `_test` 库: - -```text -muse_p1r_content_admin_risk_action_combined_test -``` - -通过结果: - -```text -P1rContentAdminRiskActionCompletedApprovalIT: tests=8, failures=0, errors=0, skipped=0 -P1rContentAdminReadCompletedApprovalIT: tests=5, failures=0, errors=0, skipped=0 -P1rContentPlanningCompletedApprovalIT: tests=11, failures=0, errors=0, skipped=0 -P1rContentCoreCompletedApprovalIT: tests=12, failures=0, errors=0, skipped=0 -P1rContentEventsPublishFlywayMigrationIT: tests=4, failures=0, errors=0, skipped=0 -combined: tests=40, failures=0, errors=0, skipped=0 -``` - -## focused Content tests - -执行版 focused tests 已单独运行,并确认 XML 为本轮 fresh 产物: - -```text -ContentAdminServiceTest: tests=13, failures=0, errors=0, skipped=0 -AdminContentControllerTest: tests=8, failures=0, errors=0, skipped=0 -focused total: tests=21, failures=0, errors=0, skipped=0 -``` - -## P1R mixed gates - -P1R mixed gates 已 fresh 运行: - -```text -P1rApiCoverageReportTest: tests=6, failures=0, errors=0, skipped=0 -P1rContentRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rEventsRealApiGateTest: tests=7, failures=0, errors=0, skipped=0 -P1rAiRealApiGateTest: tests=7, failures=0, errors=0, skipped=0 -P1rKnowledgeRealApiGateTest: tests=8, failures=0, errors=0, skipped=0 -P1rMarketRealApiGateTest: tests=6, failures=0, errors=0, skipped=0 -P1rAccountRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -P1rMetaRealApiGateTest: tests=5, failures=0, errors=0, skipped=0 -mixed total: tests=49, failures=0, errors=0, skipped=0 -``` - -## scanner / report - -执行命令: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -通过后确认: - -```text -233 145 88 0 0 0 -51 14 37 0 0 0 -adminRiskAction dedicated completed true -``` - -## protected diff - -以下保护面 diff 为空: - -```text -docs/api-contracts -muse-cloud/muse-module-content/muse-module-content-server/src/main/java -muse-cloud/sql/muse -``` - -`git diff --check` 无输出。 - -## fresh implementation review - -Lagrange data-integrity/testing review: - -```text -PASS -findings=none -``` - -Lagrange 已只读验证: - -- `P1rContentAdminRiskActionCompletedApprovalIT` 使用 MockMvc HTTP + WebApplicationContext + 真实 PostgreSQL `_test`。 -- 真实 controller/service/mapper、method security、tenant interceptor、Flyway V1-V21、command/governance 行级事实、command/audit snapshot、no-write / rollback 和 XML evidence 均闭合。 -- coverage 为 `233/145/88/0/0/0`,Content 为 `14 completed / 37 needs_verification`,非目标 Content operation 仍保持 `needs_verification`。 -- protected diff 与 `git diff --check` 均无问题。 - -Gauss scope review: - -```text -PASS -findings=none -``` - -Gauss 已只读验证: - -- diff 只在 scanner/report、P1R gate tests、新 IT、`docs/agent-specs`、`docs/memorys` 范围内。 -- protected diff 为空。 -- scanner 只追加 `content:adminRiskAction` operation-level key,`APPROVED_COMPLETED_DOMAINS` 仍为 `{"ai", "knowledge"}`。 -- summary 为 `233/145/88/0/0/0`,Content 为 `14 completed / 37 needs_verification`。 -- `adminRiskAction` 为 `dedicated/completed/requiresCommandId=true`,抽样非目标 Content operation 仍为 `dedicated/needs_verification`。 -- 文档没有宣称 Content 51/51、Account remaining、Market remaining 或总 P1R completed。 - -## 后续路线 - -`content:adminRiskAction` 之后,Content 仍剩余 37 个 `dedicated/needs_verification` operation。后续应继续按 operation-level approval 切片推进,优先只选择证据闭合、外部依赖少、可用 HTTP+DB `_test` 证明的 operation;不要把 Content 51/51、Account remaining、Market remaining 或总 P1R completed 混入本轮结论。 diff --git a/docs/memorys/2026-06-13-P1RContentPlanning状态推进.md b/docs/memorys/2026-06-13-P1RContentPlanning状态推进.md deleted file mode 100644 index 3d2a84f2..00000000 --- a/docs/memorys/2026-06-13-P1RContentPlanning状态推进.md +++ /dev/null @@ -1,369 +0,0 @@ -# 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 仍需用户明确指令。 diff --git a/docs/memorys/README.md b/docs/memorys/README.md new file mode 100644 index 00000000..2302f745 --- /dev/null +++ b/docs/memorys/README.md @@ -0,0 +1,5 @@ +# docs/memorys/(已归档清理) + +早期任务过程快照(收口 / 状态推进 / 规格计划)已于 **2026-06-14 清理**——它们是 churn,实质已沉淀在代码、[现状基线](../agent-specs/2026-06-13-项目目标与模块现状基线.md) 与 [ADR](../../design-docs/架构-03-关键决策与原则(ADR).md);唯一操作干货已蒸馏入 [`.agents/knowledge/external-deps-and-gotchas.md`](../../.agents/knowledge/external-deps-and-gotchas.md);完整历史见 git。 + +**进度不再写这里**:统一进 [`docs/mvp/进度总账.md`](../mvp/进度总账.md) + 各模块 `.agent`(规约见 [AGENTS.md](../../AGENTS.md) §5/§6、[ai-development-protocol](../../.agents/workflows/ai-development-protocol.md))。 diff --git a/docs/mvp/进度总账.md b/docs/mvp/进度总账.md new file mode 100644 index 00000000..28239219 --- /dev/null +++ b/docs/mvp/进度总账.md @@ -0,0 +1,70 @@ +# Muse 进度总账(单一进度源) + +> **本文件是项目进度的唯一 SSOT**。进度更新只进**本文件 + 各模块 `.agent`**;**不再新增“状态推进 / 收口 / completedApproval”过程文档**(过程文档 churn 是失控根因之一,见 [对抗复盘](../agent-specs/2026-06-13-目标达成对抗复盘.md))。 +> 模块**真实现状**的权威基线见 [现状基线 spec](../agent-specs/2026-06-13-项目目标与模块现状基线.md);本文件只记“进度”,不重复其证据细节。 + +--- + +## 一、Agent 开发基建(本轮交付,机械门禁优先) + +| 砖 | 内容 | 状态 | 机械证据 / 落点 | +|---|---|---|---| +| P0 | CI 真跑测试(JDK21、去 `-Dmaven.test.skip`)+ 覆盖台账去硬编码 + P0 冻结令 | ✅ | `.github/workflows/maven.yml`、`P1rApiCoverageReportTest`、[脊柱规则](../../.agents/rules/verification-and-anti-false-green.md) | +| ① | BC 边界 ArchUnit 门(AI 不得直连他域 DAL) | ✅ 已验证绿 | `BcBoundaryArchTest`(Tests run 1/0F);[bc-boundaries](../../.agents/rules/bc-boundaries.md) | +| ② | 契约先行门(Flyway 迁移卫生 + OpenAPI 结构;原地 SSOT) | ✅ 已验证绿 | `ContractFirstGateTest`(Tests run 2/0F);[contract-first](../../.agents/rules/contract-first.md) | +| ③ | knowledge 蒸馏(定位架构 / 现状基线指针 / 决策) | ✅ | [`.agents/knowledge/`](../../.agents/knowledge/) | +| ④ | skills(黄金旅程“完成”定义 / 新增 BC 模块) | ✅ | [`.agents/skills/`](../../.agents/skills/) | +| ⑤ | workflow(AI 开发协议元流程) | ✅ | [`.agents/workflows/ai-development-protocol.md`](../../.agents/workflows/ai-development-protocol.md) | +| ⑥ | 进度总账(本文件)+ per-module `.agent` | ✅ | 本文件 + `muse-cloud/muse-module-*/.agent` | + +**后续基建 TODO(非阻塞)**:openapi-diff 破坏性变更 CI(就绪片段见 contract-first §四)、`docs/dev-baseline/global/*` 收敛进 `.agents/rules/`(engineering-conventions、security-and-reliability)、BC 边界门扩到其它方向。 + +--- + +## 二、产品 / 模块进度(指针,勿在此重复细节) + +- 各模块目标 / 边界 / out-of-scope / 现状 / TODO → 见对应 **`muse-cloud/muse-module-*/.agent`**。 +- 真实完成度与逐项证据 → 见 [现状基线 spec §四/§五](../agent-specs/2026-06-13-项目目标与模块现状基线.md)。 +- 接口完成台账(门禁批准口径,≠端到端可用)→ `P1rApiCoverageReportTest` + `docs/superpowers/reports/p1r-api-coverage.json`。 + +| BC 模块 | 只读评估 | `.agent` | +|---|---|---| +| AI 编排 (ai) | 72% | [.agent](../../muse-cloud/muse-module-ai/.agent) | +| 作品/编辑器 (content) | 82% | [.agent](../../muse-cloud/muse-module-content/.agent) | +| 知识库 (knowledge) | 72% | [.agent](../../muse-cloud/muse-module-knowledge/.agent) | +| 市场 (market) | 82% | [.agent](../../muse-cloud/muse-module-market/.agent) | +| 元治理/MetaSchema (meta) | 82% | [.agent](../../muse-cloud/muse-module-meta/.agent) | +| 事件/SSE (events) | 88% | [.agent](../../muse-cloud/muse-module-events/.agent) | +| 账户/个人中心 (account→member) | 68% | [.agent](../../muse-cloud/muse-module-member/.agent) | + +> 整体只读评估 ≈ 76%:后端实现高、内部验证门禁中、前端用户端低、跨 BC 集成与机械门禁低(机械门禁项本轮已补 BC + 契约门)。 + +--- + +## 三、最大共性风险(跨模块,来自基线) +1. **前端 muse-studio 严重滞后**:AI 候选闭环断链、知识库/市场生产侧 UI 缺失、个人中心约 21% 面跑 MSW——“先审后入”在用户可见层当前不可用。 +2. **跨 BC facade 多为 `Unavailable` 占位** → 运行期 `*_UNAVAILABLE`。 +3. **gateway 路由未接任何 Muse BC + 死路由**:若误以网关为真实入口部署,Muse BC 全部 404。 + +--- + +## 四、历史交付时间线(过程文档已清理,留此精简记录) + +> 2026-06-14 共清理约 97 份过程 churn(`docs/memorys` 34、`agent-specs` 审阅/执行版+迁移review 34、`design-docs/临时` 2、`design-docs/memorys` 2、`superpowers/plans` 14 + `specs` 11;**保留 `superpowers/reports/coverage`**——被门禁引用)。唯一操作干货蒸馏入 [`.agents/knowledge/external-deps-and-gotchas.md`](../../.agents/knowledge/external-deps-and-gotchas.md);均 git 可恢复,完整历史在 git。 + +``` +2026-05-24 muse-studio 脚手架+基础设施(Vite/React/TS6.0/MSW/IndexedDB) +2026-05-24 muse-studio 写作台+AI 协作(Tiptap + Coarse-to-Fine Diff + SSE 采纳) +2026-05-25 muse-cloud 后端 P1(yudao-cloud fork + 业务模块骨架) +2026-05-25 muse-admin 管理端(Vben fork:MetaSchema/治理/审计) +2026-05-26 P1R-0 基线门禁(OpenAPI 233 operation 覆盖矩阵) +2026-05-27→28 P1R-1/2 Content / Meta 真实 API +2026-05-31 P1R-4 AI 真实 API + task SSE 退占位(V12/V13) +2026-06-02 P1R-4/5 外部验收:New-API(MiniMax-M2.5)+ RAGFlow live 跑通 +2026-06-05 P1R-6 Market 真实 API;P1R-7a Events SSE +2026-06-06→08 P1R-7b~7f Source Owner Propagation 五切片(V17–V21 outbox) +2026-06-09→13 Events/Meta/Account/Market/Content 分批 completed approval(coverage→147/233) +2026-06-13 现状基线 + 对抗复盘(诊断"假绿")+ P0 止血(CI 真跑测试/JDK21/去硬编码) +2026-06-14 Agent 开发基建六砖(BC 门 + 契约门 + .agents 中枢 + 单一总账)+ 历史文档清理 +2026-06-14 P1 harness 验证:消除 BC 违例 ContentMuseWorkOwnerFacade(content-api 端口 + AI 改消费)→ ArchUnit 豁免删除、门禁收紧(反向红 31 例 / 正向绿;13+7 单测) +``` diff --git a/docs/superpowers/plans/2026-05-24-Muse四仓执行计划.md b/docs/superpowers/plans/2026-05-24-Muse四仓执行计划.md deleted file mode 100644 index d8a62562..00000000 --- a/docs/superpowers/plans/2026-05-24-Muse四仓执行计划.md +++ /dev/null @@ -1,2791 +0,0 @@ -# Muse 四仓开发 — 主执行计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**目标:** 从零搭建 Muse 四仓(muse-cloud / muse-studio / muse-admin),实现 6 个业务模块全量 API + 双前端,完成集成切换和质量加固。 - -**架构:** API-first + 三仓并行。Phase 0 在本仓产出 OpenAPI 3.0 契约文件,Phase 1 三仓并行开发(后端逐步产出真实 API,前端先 Mock 独立开发),Phase 2 按模块逐个切换真实 API 并完成质量加固。 - -**技术栈:** Java 21 + Spring Boot 3 + PostgreSQL 16 + Yudao Cloud(后端)| React + Vite + TypeScript + TanStack Query + Tiptap(用户端)| Vue 3 + Vben Admin + TypeScript(管理端) - ---- - -## 子计划索引 - -本计划覆盖 18 周、4 个代码仓库。按子系统拆分为 5 个子计划: - -| # | 子计划 | 仓库 | 周期 | 状态 | -|---|--------|------|------|------| -| P0 | [API 契约冻结](#phase-0-api-契约冻结) | muse-design-docs | Week 1-2 | 就绪 | -| P1 | [muse-cloud 后端搭建](docs/superpowers/plans/2026-05-24-P1-muse-cloud.md)(待创建) | muse-cloud | Week 3-16 | P0 完成后 | -| P2 | [muse-studio 用户端搭建](docs/superpowers/plans/2026-05-24-P2-muse-studio.md)(待创建) | muse-studio | Week 3-16 | P0 完成后 | -| P3 | [muse-admin 管理端搭建](docs/superpowers/plans/2026-05-24-P3-muse-admin.md)(待创建) | muse-admin | Week 3-16 | P0 完成后 | -| P4 | [集成切换 + 质量加固](docs/superpowers/plans/2026-05-24-P4-集成切换.md)(待创建) | 四仓联合 | Week 9-18 | P1-P3 部分完成后 | - -**依赖关系:** - -```mermaid -graph TD - P0[P0: API 契约冻结] --> P1[P1: muse-cloud 后端] - P0 --> P2[P2: muse-studio 用户端] - P0 --> P3[P3: muse-admin 管理端] - P1 --> P4[P4: 集成切换] - P2 --> P4 - P3 --> P4 -``` - ---- - -## 前置条件 - -- [x] Spec: `docs/superpowers/specs/2026-05-24-Muse四仓开发计划-design.md` -- [x] 设计文档 SSOT: `design-docs/` 下 30+ 份正式文档 -- [x] AI 开发管理基线: `docs/dev-baseline/` 下各仓规范 -- [x] muse-cloud fork 完成(Yudao Cloud) -- [x] muse-admin fork 完成(Vben Admin) -- [ ] muse-studio 需从零创建 - ---- - -## Phase 0: API 契约冻结 - -**仓库:** muse-design-docs(本仓) -**周期:** Week 1-2 -**输入:** `design-docs/后端-05-统一API契约-v1.md`(767 行,146+ 接口) -**输出:** 6 个模块的 OpenAPI 3.0 YAML + TypeScript 类型包 + Java DTO 骨架 - -### Task 0.0: 准备工作 - -**文件:** -- 创建: `docs/api-contracts/openapi-base.yaml` -- 创建: `docs/api-contracts/content/openapi.yaml` -- 创建: `docs/api-contracts/ai/openapi.yaml` -- 创建: `docs/api-contracts/knowledge/openapi.yaml` -- 创建: `docs/api-contracts/market/openapi.yaml` -- 创建: `docs/api-contracts/account/openapi.yaml` -- 创建: `docs/api-contracts/meta/openapi.yaml` -- 创建: `docs/api-contracts/generated/typescript/.gitkeep` -- 创建: `docs/api-contracts/generated/java/.gitkeep` - -- [ ] **Step 1: 创建目录结构** - -```bash -mkdir -p docs/api-contracts/{content,ai,knowledge,market,account,meta,generated/typescript,generated/java} -``` - -- [ ] **Step 2: 提交空目录结构(含 .gitkeep)** - -```bash -touch docs/api-contracts/generated/typescript/.gitkeep -touch docs/api-contracts/generated/java/.gitkeep -git add docs/api-contracts/ -git commit -m "feat(api): 初始化 API 契约目录结构" -``` - -### Task 0.1: 编写 openapi-base.yaml(全局配置) - -**文件:** -- 创建: `docs/api-contracts/openapi-base.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` 全局约定章节 -- 参考: `design-docs/架构-03-关键决策与原则(ADR).md` ADR-010 (API 版本) - -- [ ] **Step 1: 从设计文档提取全局配置信息** - -阅读 `后端-05` 中的: -- 认证方式(Bearer Token / OAuth2) -- 分页格式(pageNo + pageSize,CommonResult 包裹) -- 错误格式(system-module-category-sequence 四段式错误码) -- API 版本策略(X-API-Version Header) -- 通用响应格式(code + message + data) - -- [ ] **Step 2: 编写 openapi-base.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse API - description: | - Muse AI 驱动长篇创作平台统一 API 契约。 - 所有接口通过 X-API-Version Header 进行版本控制。 - version: 1.0.0 - contact: - name: Muse Team - -servers: - - url: http://localhost:48080 - description: 本地开发环境 - -security: - - bearerAuth: [] - -components: - securitySchemes: - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: | - 用户端使用 app-api token,管理端使用 admin-api token。 - 两个端点的认证域独立,token 不互通。 - - parameters: - XApiVersion: - name: X-API-Version - in: header - required: false - schema: - type: string - default: "2026-05-01" - description: API 版本号,格式 YYYY-MM-DD - - pageNo: - name: pageNo - in: query - required: false - schema: - type: integer - minimum: 1 - default: 1 - description: 页码,从 1 开始 - - pageSize: - name: pageSize - in: query - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - description: 每页条数,上限 100 - - schemas: - CommonResult: - type: object - required: [code, message] - properties: - code: - type: integer - description: 业务状态码,0 表示成功 - example: 0 - message: - type: string - description: 提示信息 - example: "操作成功" - data: - description: 响应数据,类型视具体接口而定 - - PaginatedResult: - type: object - required: [total, pageNo, pageSize, list] - properties: - total: - type: integer - description: 总记录数 - example: 150 - pageNo: - type: integer - description: 当前页码 - example: 1 - pageSize: - type: integer - description: 每页条数 - example: 20 - list: - type: array - description: 当前页数据列表 - - ErrorResponse: - type: object - required: [code, message] - properties: - code: - type: string - description: | - 错误码格式: {system}-{module}-{category}-{sequence} - 示例: MUSE-CONTENT-001-0001 - example: "MUSE-CONTENT-001-0001" - message: - type: string - description: 人类可读的错误描述 - detail: - type: string - description: 详细错误信息(仅开发环境返回) - - # 通用时间戳字段 - TimestampMixin: - type: object - properties: - createdAt: - type: string - format: date-time - description: 创建时间 - updatedAt: - type: string - format: date-time - description: 最后更新时间 - - responses: - BadRequest: - description: 请求参数有误 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - Unauthorized: - description: 未认证或 token 过期 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - Forbidden: - description: 无权限 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - NotFound: - description: 资源不存在 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - Conflict: - description: 资源冲突 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - InternalError: - description: 服务器内部错误 - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - -tags: - - name: Content - description: 作品、章节、Block、导出 - - name: AI - description: AI 生成、候选管理、智能体、质量门控 - - name: Knowledge - description: 知识实体、关系、草稿、确认 - - name: Market - description: 资产发布、安装绑定、审核 - - name: Account - description: 权益、配额、用量、安全事件 - - name: Meta - description: MetaSchema 字段定义、版本、scope -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/openapi-base.yaml -git commit -m "feat(api): 添加 OpenAPI 全局基础配置(认证/分页/错误格式/版本)" -``` - -### Task 0.2: 编写 Content 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/content/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` Content 模块章节 - -- [ ] **Step 1: 从 API 契约文档提取 Content 模块接口列表** - -Content 模块包含: -- 作品(Work) CRUD -- 章节(Chapter) CRUD + 排序 -- Block CRUD + 分割/合并 -- 导出(TXT/EPUB/DOCX) - -- [ ] **Step 2: 编写 content/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse Content API - version: 1.0.0 - -paths: - # ========== 作品(Work) ========== - /app-api/content/works: - get: - tags: [Content] - summary: 获取作品列表 - operationId: listWorks - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - - name: status - in: query - schema: - type: string - enum: [draft, active, archived] - description: 作品状态过滤 - responses: - '200': - description: 作品分页列表 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/PaginatedResult' - - type: object - properties: - list: - type: array - items: - $ref: '#/components/schemas/WorkSummary' - post: - tags: [Content] - summary: 创建新作品 - operationId: createWork - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [title] - properties: - title: - type: string - minLength: 1 - maxLength: 200 - description: - type: string - maxLength: 2000 - genre: - type: string - coverImageUrl: - type: string - format: uri - responses: - '201': - description: 作品创建成功 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Work' - - /app-api/content/works/{workId}: - get: - tags: [Content] - summary: 获取作品详情 - operationId: getWork - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 作品详情 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Work' - put: - tags: [Content] - summary: 更新作品 - operationId: updateWork - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - title: - type: string - maxLength: 200 - description: - type: string - maxLength: 2000 - genre: - type: string - coverImageUrl: - type: string - format: uri - status: - type: string - enum: [draft, active, archived] - responses: - '200': - description: 更新成功 - content: - application/json: - schema: - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - delete: - tags: [Content] - summary: 删除作品 - operationId: deleteWork - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功 - '404': - $ref: '../openapi-base.yaml#/components/responses/NotFound' - - # ========== 章节(Chapter) ========== - /app-api/content/works/{workId}/chapters: - get: - tags: [Content] - summary: 获取作品章节列表 - operationId: listChapters - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 章节列表(按 sortOrder 排序) - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Chapter' - post: - tags: [Content] - summary: 创建章节 - operationId: createChapter - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [title] - properties: - title: - type: string - minLength: 1 - maxLength: 500 - sortOrder: - type: integer - description: 插入位置,默认追加到末尾 - responses: - '201': - description: 创建成功 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Chapter' - - /app-api/content/chapters/{chapterId}: - get: - tags: [Content] - summary: 获取章节详情 - operationId: getChapter - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 章节详情(含 Block 列表) - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/ChapterDetail' - put: - tags: [Content] - summary: 更新章节 - operationId: updateChapter - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - title: - type: string - maxLength: 500 - sortOrder: - type: integer - status: - type: string - enum: [draft, completed] - responses: - '200': - description: 更新成功 - delete: - tags: [Content] - summary: 删除章节 - operationId: deleteChapter - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功 - - /app-api/content/chapters/{chapterId}/reorder: - put: - tags: [Content] - summary: 调整章节排序 - operationId: reorderChapters - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [newSortOrder] - properties: - newSortOrder: - type: integer - minimum: 0 - responses: - '200': - description: 排序更新成功 - - # ========== Block ========== - /app-api/content/chapters/{chapterId}/blocks: - get: - tags: [Content] - summary: 获取章节 Block 列表 - operationId: listBlocks - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: Block 列表 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Block' - post: - tags: [Content] - summary: 创建 Block - operationId: createBlock - parameters: - - name: chapterId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [content, blockType] - properties: - content: - type: string - description: Block 正文内容(JSON 格式的 ProseMirror 文档) - blockType: - type: string - enum: [scene, section, note] - description: Block 类型 - sortOrder: - type: integer - title: - type: string - maxLength: 500 - responses: - '201': - description: 创建成功 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Block' - - /app-api/content/blocks/{blockId}: - get: - tags: [Content] - summary: 获取 Block 详情 - operationId: getBlock - parameters: - - name: blockId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: Block 详情 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Block' - put: - tags: [Content] - summary: 保存 Block 正文 - operationId: saveBlock - parameters: - - name: blockId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [content, sourceVersion] - properties: - content: - type: string - description: Block 正文(ProseMirror JSON) - sourceVersion: - type: integer - description: 客户端持有的版本号,用于乐观锁冲突检测 - responses: - '200': - description: 保存成功 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: object - properties: - newVersion: - type: integer - description: 保存后的新版本号 - '409': - description: 版本冲突,需客户端处理 - content: - application/json: - schema: - $ref: '../openapi-base.yaml#/components/schemas/ErrorResponse' - delete: - tags: [Content] - summary: 删除 Block - operationId: deleteBlock - parameters: - - name: blockId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功 - - /app-api/content/blocks/{blockId}/split: - post: - tags: [Content] - summary: 分割 Block - operationId: splitBlock - parameters: - - name: blockId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [splitPosition] - properties: - splitPosition: - type: integer - description: 分割位置(字符偏移量) - responses: - '200': - description: 分割成功,返回两个新 Block - - /app-api/content/blocks/{blockId}/merge: - post: - tags: [Content] - summary: 合并 Block - operationId: mergeBlocks - parameters: - - name: blockId - in: path - required: true - schema: - type: string - format: uuid - description: 与下一个 Block 合并 - responses: - '200': - description: 合并成功 - - # ========== 导出 ========== - /app-api/content/works/{workId}/export: - post: - tags: [Content] - summary: 导出作品 - operationId: exportWork - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [format] - properties: - format: - type: string - enum: [txt, epub, docx] - includeChapters: - type: array - items: - type: string - format: uuid - description: 指定导出章节,空数组表示导出全部 - responses: - '200': - description: 导出任务已创建,返回任务 ID 供轮询 - '202': - description: 导出文件二进制流(小文件直接返回) - -components: - schemas: - Work: - type: object - required: [id, title, status, createdAt, updatedAt] - properties: - id: - type: string - format: uuid - title: - type: string - description: - type: string - genre: - type: string - coverImageUrl: - type: string - format: uri - status: - type: string - enum: [draft, active, archived] - wordCount: - type: integer - description: 总字数 - chapterCount: - type: integer - description: 章节数 - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - - WorkSummary: - type: object - required: [id, title, status, updatedAt] - properties: - id: - type: string - format: uuid - title: - type: string - genre: - type: string - status: - type: string - enum: [draft, active, archived] - wordCount: - type: integer - chapterCount: - type: integer - updatedAt: - type: string - format: date-time - - Chapter: - type: object - required: [id, workId, title, sortOrder, status, createdAt, updatedAt] - properties: - id: - type: string - format: uuid - workId: - type: string - format: uuid - title: - type: string - sortOrder: - type: integer - status: - type: string - enum: [draft, completed] - wordCount: - type: integer - blockCount: - type: integer - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - - ChapterDetail: - allOf: - - $ref: '#/components/schemas/Chapter' - - type: object - properties: - blocks: - type: array - items: - $ref: '#/components/schemas/Block' - - Block: - type: object - required: [id, chapterId, blockType, content, sortOrder, version, createdAt, updatedAt] - properties: - id: - type: string - format: uuid - chapterId: - type: string - format: uuid - blockType: - type: string - enum: [scene, section, note] - title: - type: string - content: - type: string - description: ProseMirror JSON 格式的正文内容 - sortOrder: - type: integer - version: - type: integer - description: 乐观锁版本号,每次保存 +1 - wordCount: - type: integer - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/content/openapi.yaml -git commit -m "feat(api): 添加 Content 模块 OpenAPI 契约(作品/章节/Block/导出)" -``` - -### Task 0.3: 编写 AI 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/ai/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` AI 模块章节 -- 参考: `design-docs/专题-03-AI编排上下文与质量评测实现规范.md` - -- [ ] **Step 1: 提取 AI 模块接口** - -AI 模块包含: -- 生成请求(提交生成任务) -- 候选(Candidate)管理(列表、详情、接受/拒绝) -- SSE 流式传输(AI stream 独立端点) -- 智能体(Agent)管理(创建、配置、槽位绑定) -- 质量门控配置 - -- [ ] **Step 2: 编写 ai/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse AI API - version: 1.0.0 - -paths: - # ========== AI 生成 ========== - /app-api/ai/generations: - post: - tags: [AI] - summary: 提交 AI 生成请求 - operationId: requestGeneration - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [blockId, agentId] - properties: - blockId: - type: string - format: uuid - description: 目标 Block - agentId: - type: string - format: uuid - description: 使用的智能体 - prompt: - type: string - description: 用户附加的提示词 - contextBlocks: - type: array - items: - type: string - format: uuid - description: 上下文 Block ID 列表 - responses: - '202': - description: 生成任务已提交 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: object - properties: - generationId: - type: string - format: uuid - - /app-api/ai/generations/{generationId}: - get: - tags: [AI] - summary: 获取生成任务状态 - operationId: getGenerationStatus - parameters: - - name: generationId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 生成状态 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/Generation' - - # ========== SSE: AI Stream(独立连接) ========== - /app-api/ai/generations/{generationId}/stream: - get: - tags: [AI] - summary: AI 生成 SSE 流 - operationId: streamGeneration - parameters: - - name: generationId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: SSE 事件流 - content: - text/event-stream: - schema: - type: string - description: | - 事件类型: - - chunk: { "content": "文本片段", "sequenceNo": 1 } - - quality_check: { "dimension": "fluency", "score": 0.92, "passed": true } - - done: { "generationId": "uuid", "candidateId": "uuid" } - - error: { "code": "...", "message": "..." } - - # ========== 候选(Candidate)管理 ========== - /app-api/ai/candidates: - get: - tags: [AI] - summary: 获取候选列表 - operationId: listCandidates - parameters: - - name: blockId - in: query - required: true - schema: - type: string - format: uuid - - name: status - in: query - schema: - type: string - enum: [pending, accepted, rejected] - responses: - '200': - description: 候选列表 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Candidate' - - /app-api/ai/candidates/{candidateId}: - get: - tags: [AI] - summary: 获取候选详情 - operationId: getCandidate - parameters: - - name: candidateId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 候选详情(含生成内容 diff) - - /app-api/ai/candidates/{candidateId}/accept: - post: - tags: [AI] - summary: 接受候选 - operationId: acceptCandidate - parameters: - - name: candidateId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - type: object - properties: - modifications: - type: string - description: 用户在接受前对候选内容的手动修改 - responses: - '200': - description: 候选已接受,Block 正文更新 - '409': - description: 源版本已变更,需重新对比 - $ref: '../openapi-base.yaml#/components/responses/Conflict' - - /app-api/ai/candidates/{candidateId}/reject: - post: - tags: [AI] - summary: 拒绝候选 - operationId: rejectCandidate - parameters: - - name: candidateId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - type: object - properties: - reason: - type: string - description: 拒绝原因(可选,用于改进生成质量) - responses: - '200': - description: 已拒绝 - - # ========== 智能体(Agent) ========== - /app-api/ai/agents: - get: - tags: [AI] - summary: 获取智能体列表 - operationId: listAgents - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - - name: type - in: query - schema: - type: string - enum: [config, custom] - description: 智能体类型 - responses: - '200': - description: 智能体分页列表 - post: - tags: [AI] - summary: 创建自定义智能体 - operationId: createAgent - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [name] - properties: - name: - type: string - maxLength: 100 - description: - type: string - maxLength: 1000 - promptTemplate: - type: string - description: 系统提示词模板 - slotBindings: - type: object - description: 槽位绑定配置(知识库/MetaSchema/模型参数) - responses: - '201': - description: 创建成功 - - /app-api/ai/agents/{agentId}: - get: - tags: [AI] - summary: 获取智能体详情 - operationId: getAgent - parameters: - - name: agentId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 智能体详情 - put: - tags: [AI] - summary: 更新智能体配置 - operationId: updateAgent - parameters: - - name: agentId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: - type: string - promptTemplate: - type: string - slotBindings: - type: object - status: - type: string - enum: [active, archived] - responses: - '200': - description: 更新成功 - delete: - tags: [AI] - summary: 删除自定义智能体 - operationId: deleteAgent - parameters: - - name: agentId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功 - - # ========== 管理端: Prompt 模板管理 ========== - /admin-api/ai/prompt-templates: - get: - tags: [AI] - summary: 获取 Prompt 模板列表(管理端) - operationId: adminListPromptTemplates - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - responses: - '200': - description: 模板分页列表 - post: - tags: [AI] - summary: 创建 Prompt 模板(管理端) - operationId: adminCreatePromptTemplate - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [name, template] - properties: - name: - type: string - template: - type: string - variables: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - enum: [string, number, boolean] - required: - type: boolean - defaultValue: - type: string - responses: - '201': - description: 创建成功 - - /admin-api/ai/prompt-templates/{templateId}: - put: - tags: [AI] - summary: 更新 Prompt 模板(管理端) - operationId: adminUpdatePromptTemplate - parameters: - - name: templateId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 更新成功 - delete: - tags: [AI] - summary: 删除 Prompt 模板(管理端) - operationId: adminDeletePromptTemplate - parameters: - - name: templateId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功 - - # ========== 管理端: 质量门控配置 ========== - /admin-api/ai/quality-gates: - get: - tags: [AI] - summary: 获取质量门控维度列表 - operationId: adminListQualityGates - responses: - '200': - description: 质量门控维度列表 - post: - tags: [AI] - summary: 创建质量门控维度 - operationId: adminCreateQualityGate - responses: - '201': - description: 创建成功 - - /admin-api/ai/protection-nodes: - get: - tags: [AI] - summary: 获取保护节点列表 - operationId: adminListProtectionNodes - responses: - '200': - description: 保护节点列表 - post: - tags: [AI] - summary: 注册保护节点 - operationId: adminCreateProtectionNode - responses: - '201': - description: 注册成功 - -components: - schemas: - Generation: - type: object - required: [id, blockId, agentId, status, createdAt] - properties: - id: - type: string - format: uuid - blockId: - type: string - format: uuid - agentId: - type: string - format: uuid - status: - type: string - enum: [pending, streaming, completed, failed] - candidateId: - type: string - format: uuid - description: 生成完成后关联的候选 ID - errorMessage: - type: string - createdAt: - type: string - format: date-time - - Candidate: - type: object - required: [id, blockId, generationId, status, createdAt] - properties: - id: - type: string - format: uuid - blockId: - type: string - format: uuid - generationId: - type: string - format: uuid - agentId: - type: string - format: uuid - agentName: - type: string - status: - type: string - enum: [pending, accepted, rejected] - sourceVersion: - type: integer - description: 生成时的源 Block 版本号 - diffSummary: - type: string - description: 变更摘要 - qualityScores: - type: object - description: 各质量维度的评分 - createdAt: - type: string - format: date-time -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/ai/openapi.yaml -git commit -m "feat(api): 添加 AI 模块 OpenAPI 契约(生成/候选/SSE/智能体/管理端配置)" -``` - -### Task 0.4: 编写 Knowledge 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/knowledge/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` Knowledge 模块章节 - -- [ ] **Step 1: 提取 Knowledge 模块接口** - -Knowledge 模块包含: -- 知识实体(Entity) CRUD -- 知识关系(Relation) CRUD -- 知识草稿(Draft)管理 -- 自动确认 + 手动确认 -- 实体/关系可视化数据 -- 知识来源管理 - -- [ ] **Step 2: 编写 knowledge/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse Knowledge API - version: 1.0.0 - -paths: - # ========== 知识实体(Entity) ========== - /app-api/knowledge/works/{workId}/entities: - get: - tags: [Knowledge] - summary: 获取作品知识实体列表 - operationId: listEntities - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - - name: status - in: query - schema: - type: string - enum: [confirmed, draft, conflicted] - - name: type - in: query - schema: - type: string - enum: [character, location, event, item, concept, note] - responses: - '200': - description: 实体列表 - - /app-api/knowledge/entities/{entityId}: - get: - tags: [Knowledge] - summary: 获取知识实体详情 - operationId: getEntity - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 实体详情(含关联关系和来源引用) - put: - tags: [Knowledge] - summary: 更新知识实体 - operationId: updateEntity - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: - type: string - attributes: - type: object - description: 自定义属性键值对 - responses: - '200': - description: 更新成功 - - # ========== 知识确认 ========== - /app-api/knowledge/entities/{entityId}/confirm: - post: - tags: [Knowledge] - summary: 手动确认知识实体 - operationId: confirmEntity - description: | - 知识实体的默认确认规则: - - 无冲突 + 非外部来源 + 置信度 > 阈值 → 自动确认 - - 有冲突 或 外部来源 或 低置信度 → 需手动确认 - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - type: object - properties: - overrides: - type: object - description: 手动修正的属性值 - responses: - '200': - description: 确认成功 - - /app-api/knowledge/entities/{entityId}/reject: - post: - tags: [Knowledge] - summary: 拒绝知识实体草稿 - operationId: rejectEntity - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - type: object - properties: - reason: - type: string - responses: - '200': - description: 已拒绝 - - # ========== 知识关系(Relation) ========== - /app-api/knowledge/entities/{entityId}/relations: - get: - tags: [Knowledge] - summary: 获取实体关联关系 - operationId: listRelations - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 关系列表 - post: - tags: [Knowledge] - summary: 创建知识关系 - operationId: createRelation - parameters: - - name: entityId - in: path - required: true - schema: - type: string - format: uuid - description: 源实体 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [targetEntityId, relationType] - properties: - targetEntityId: - type: string - format: uuid - relationType: - type: string - example: "knows" - description: - type: string - responses: - '201': - description: 创建成功 - - # ========== 知识图谱可视化 ========== - /app-api/knowledge/works/{workId}/graph: - get: - tags: [Knowledge] - summary: 获取知识图谱数据 - operationId: getKnowledgeGraph - description: 返回节点和边的 JSON,用于前端可视化渲染 - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - - name: depth - in: query - schema: - type: integer - minimum: 1 - maximum: 3 - default: 2 - description: 图谱展开深度 - responses: - '200': - description: 图谱数据(nodes + edges) - - # ========== 知识草稿(Draft) ========== - /app-api/knowledge/works/{workId}/drafts: - get: - tags: [Knowledge] - summary: 获取待处理知识草稿列表 - operationId: listDrafts - parameters: - - name: workId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 草稿列表 -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/knowledge/openapi.yaml -git commit -m "feat(api): 添加 Knowledge 模块 OpenAPI 契约(实体/关系/草稿/确认/图谱)" -``` - -### Task 0.5: 编写 Market 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/market/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` Market 模块章节 - -- [ ] **Step 1: 提取 Market 模块接口** - -Market 模块包含: -- 资产(Asset)浏览、搜索、安装 -- 资产发布(用户端提交 + 管理端审核) -- 绑定管理 -- 管理端审核(上架/驳回/下架/召回) - -- [ ] **Step 2: 编写 market/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse Market API - version: 1.0.0 - -paths: - # ========== 用户端: 市场浏览 ========== - /app-api/market/assets: - get: - tags: [Market] - summary: 浏览市场资产 - operationId: listAssets - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - - name: type - in: query - schema: - type: string - enum: [agent, prompt_template, knowledge_source, metaschema] - - name: keyword - in: query - schema: - type: string - description: 搜索关键词 - - name: sortBy - in: query - schema: - type: string - enum: [popular, newest, rating] - default: popular - responses: - '200': - description: 资产分页列表 - - /app-api/market/assets/{assetId}: - get: - tags: [Market] - summary: 获取资产详情 - operationId: getAsset - parameters: - - name: assetId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 资产详情 - - # ========== 用户端: 安装与绑定 ========== - /app-api/market/assets/{assetId}/install: - post: - tags: [Market] - summary: 安装资产到我的工作区 - operationId: installAsset - parameters: - - name: assetId - in: path - required: true - schema: - type: string - format: uuid - responses: - '201': - description: 安装成功 - - /app-api/market/installations: - get: - tags: [Market] - summary: 获取我的已安装资产 - operationId: listInstallations - responses: - '200': - description: 已安装资产列表 - - /app-api/market/installations/{installationId}/bindings: - post: - tags: [Market] - summary: 绑定已安装资产到作品 - operationId: bindInstallation - parameters: - - name: installationId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [workId] - properties: - workId: - type: string - format: uuid - slotConfig: - type: object - description: 槽位配置 - responses: - '201': - description: 绑定成功 - get: - tags: [Market] - summary: 获取资产的作品绑定列表 - operationId: listBindings - parameters: - - name: installationId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 绑定列表 - - # ========== 用户端: 资产发布 ========== - /app-api/market/publish: - post: - tags: [Market] - summary: 发布资产到市场 - operationId: publishAsset - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [name, type, sourceId] - properties: - name: - type: string - maxLength: 200 - description: - type: string - maxLength: 5000 - type: - type: string - enum: [agent, prompt_template, knowledge_source, metaschema] - sourceId: - type: string - format: uuid - description: 来源对象 ID - tags: - type: array - items: - type: string - coverImageUrl: - type: string - format: uri - responses: - '201': - description: 发布申请已提交,待审核 - - # ========== 管理端: 资产审核 ========== - /admin-api/market/reviews: - get: - tags: [Market] - summary: 获取待审核资产列表 - operationId: adminListReviews - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - - name: status - in: query - schema: - type: string - enum: [pending, approved, rejected] - responses: - '200': - description: 审核列表 - - /admin-api/market/reviews/{reviewId}/approve: - post: - tags: [Market] - summary: 审核通过 - operationId: adminApproveAsset - parameters: - - name: reviewId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - content: - application/json: - schema: - type: object - properties: - note: - type: string - responses: - '200': - description: 已上架 - - /admin-api/market/reviews/{reviewId}/reject: - post: - tags: [Market] - summary: 审核驳回 - operationId: adminRejectAsset - parameters: - - name: reviewId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [reason] - properties: - reason: - type: string - responses: - '200': - description: 已驳回 - - /admin-api/market/assets/{assetId}/delist: - post: - tags: [Market] - summary: 下架资产 - operationId: adminDelistAsset - parameters: - - name: assetId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [reason] - properties: - reason: - type: string - responses: - '200': - description: 已下架 - - /admin-api/market/assets/{assetId}/recall: - post: - tags: [Market] - summary: 召回已安装资产 - operationId: adminRecallAsset - parameters: - - name: assetId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [reason] - properties: - reason: - type: string - responses: - '200': - description: 已触发召回 -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/market/openapi.yaml -git commit -m "feat(api): 添加 Market 模块 OpenAPI 契约(浏览/安装/绑定/审核)" -``` - -### Task 0.6: 编写 Account 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/account/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` Account 模块章节 - -- [ ] **Step 1: 提取 Account 模块接口** - -Account 模块包含: -- 权益(Entitlement)查询 -- 配额(Quota)查询 + 消费 -- Token 用量统计 -- 安全事件记录 - -- [ ] **Step 2: 编写 account/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse Account API - version: 1.0.0 - -paths: - # ========== 权益(Entitlement) ========== - /app-api/account/entitlements: - get: - tags: [Account] - summary: 获取当前用户权益列表 - operationId: listEntitlements - responses: - '200': - description: 权益列表 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Entitlement' - - # ========== 配额(Quota) ========== - /app-api/account/quotas: - get: - tags: [Account] - summary: 获取当前用户配额 - operationId: listQuotas - responses: - '200': - description: 配额列表 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Quota' - - # ========== 用量(Usage) ========== - /app-api/account/usage/summary: - get: - tags: [Account] - summary: 获取用量摘要 - operationId: getUsageSummary - parameters: - - name: period - in: query - schema: - type: string - enum: [today, week, month, billing_cycle] - default: month - responses: - '200': - description: 用量摘要 - content: - application/json: - schema: - allOf: - - $ref: '../openapi-base.yaml#/components/schemas/CommonResult' - - type: object - properties: - data: - $ref: '#/components/schemas/UsageSummary' - - /app-api/account/usage/tokens: - get: - tags: [Account] - summary: 获取 Token 用量明细 - operationId: getTokenUsage - parameters: - - name: startDate - in: query - schema: - type: string - format: date - - name: endDate - in: query - schema: - type: string - format: date - responses: - '200': - description: Token 用量明细 - - # ========== 安全事件 ========== - /app-api/account/security-events: - get: - tags: [Account] - summary: 获取安全事件列表 - operationId: listSecurityEvents - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - responses: - '200': - description: 安全事件分页列表 - - # ========== 管理端 ========== - /admin-api/account/entitlements/{userId}: - get: - tags: [Account] - summary: 查看用户权益(管理端) - operationId: adminGetUserEntitlements - parameters: - - name: userId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 用户权益详情 - put: - tags: [Account] - summary: 修改用户权益(管理端) - operationId: adminUpdateUserEntitlements - parameters: - - name: userId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [entitlements] - properties: - entitlements: - type: array - items: - type: object - properties: - resourceType: - type: string - limit: - type: integer - expiresAt: - type: string - format: date-time - reason: - type: string - description: 变更原因(写入审计日志) - responses: - '200': - description: 更新成功 - -components: - schemas: - Entitlement: - type: object - required: [id, resourceType, limit, used, expiresAt] - properties: - id: - type: string - format: uuid - resourceType: - type: string - description: 权益资源类型 - limit: - type: integer - description: 额度上限(-1 表示无限制) - used: - type: integer - description: 已使用量 - expiresAt: - type: string - format: date-time - - Quota: - type: object - required: [id, resourceType, total, remaining, resetAt] - properties: - id: - type: string - format: uuid - resourceType: - type: string - total: - type: integer - remaining: - type: integer - resetAt: - type: string - format: date-time - description: 配额重置时间 - - UsageSummary: - type: object - properties: - totalTokens: - type: integer - totalGenerations: - type: integer - byModel: - type: object - description: 按模型的用量分布 - byDate: - type: array - items: - type: object - properties: - date: - type: string - format: date - tokens: - type: integer - generations: - type: integer -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/account/openapi.yaml -git commit -m "feat(api): 添加 Account 模块 OpenAPI 契约(权益/配额/用量/安全事件)" -``` - -### Task 0.7: 编写 Meta 模块 OpenAPI - -**文件:** -- 创建: `docs/api-contracts/meta/openapi.yaml` -- 参考: `design-docs/后端-05-统一API契约-v1.md` Meta 模块章节 - -- [ ] **Step 1: 提取 Meta 模块接口** - -Meta 模块包含 MetaSchema 的完整管理: -- 字段定义 CRUD -- 版本管理 -- Scope 配置(全局/租户/用户/作品) - -- [ ] **Step 2: 编写 meta/openapi.yaml** - -```yaml -openapi: 3.0.3 -info: - title: Muse MetaSchema API - version: 1.0.0 - -paths: - # ========== 字段定义 ========== - /admin-api/meta/schemas: - get: - tags: [Meta] - summary: 获取 MetaSchema 列表 - operationId: adminListSchemas - parameters: - - $ref: '../openapi-base.yaml#/components/parameters/pageNo' - - $ref: '../openapi-base.yaml#/components/parameters/pageSize' - - name: scope - in: query - schema: - type: string - enum: [global, tenant, user, work] - responses: - '200': - description: MetaSchema 分页列表 - post: - tags: [Meta] - summary: 创建 MetaSchema 定义 - operationId: adminCreateSchema - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [name, fieldType, scope] - properties: - name: - type: string - description: 字段名(英文标识) - displayName: - type: string - description: 显示名称 - fieldType: - type: string - enum: [string, text, number, boolean, date, enum, relation, json] - scope: - type: string - enum: [global, tenant, user, work] - description: 作用域 - defaultValue: - description: 默认值 - required: - type: boolean - default: false - enumValues: - type: array - items: - type: string - description: fieldType=enum 时的可选值 - validationRules: - type: object - description: 校验规则(min/max/pattern 等) - responses: - '201': - description: 创建成功 - - /admin-api/meta/schemas/{schemaId}: - get: - tags: [Meta] - summary: 获取 MetaSchema 详情 - operationId: adminGetSchema - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: MetaSchema 详情(含版本历史) - put: - tags: [Meta] - summary: 更新 MetaSchema 定义 - operationId: adminUpdateSchema - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - displayName: - type: string - defaultValue: - description: 默认值 - required: - type: boolean - enumValues: - type: array - items: - type: string - validationRules: - type: object - responses: - '200': - description: 更新成功(自动创建新版本) - delete: - tags: [Meta] - summary: 删除 MetaSchema 定义 - operationId: adminDeleteSchema - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 删除成功(软删除,已有数据不受影响) - '409': - description: 有数据引用时不允许删除 - $ref: '../openapi-base.yaml#/components/responses/Conflict' - - # ========== 版本管理 ========== - /admin-api/meta/schemas/{schemaId}/versions: - get: - tags: [Meta] - summary: 获取 MetaSchema 版本历史 - operationId: adminListSchemaVersions - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: 版本历史列表 - - /admin-api/meta/schemas/{schemaId}/versions/{version}: - get: - tags: [Meta] - summary: 获取指定版本详情 - operationId: adminGetSchemaVersion - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - - name: version - in: path - required: true - schema: - type: integer - responses: - '200': - description: 版本详情 - - /admin-api/meta/schemas/{schemaId}/rollback: - post: - tags: [Meta] - summary: 回滚到指定版本 - operationId: adminRollbackSchema - parameters: - - name: schemaId - in: path - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [targetVersion] - properties: - targetVersion: - type: integer - reason: - type: string - responses: - '200': - description: 回滚成功,创建新版本指向目标版本配置 -``` - -- [ ] **Step 3: 提交** - -```bash -git add docs/api-contracts/meta/openapi.yaml -git commit -m "feat(api): 添加 Meta 模块 OpenAPI 契约(MetaSchema CRUD/版本/回滚)" -``` - -### Task 0.8: 契约完整性校验 - -- [ ] **Step 1: 接口数量验证** - -统计各模块接口数,与 `后端-05` 设计文档对照: - -```bash -echo "=== Content ===" && grep -c "operationId:" docs/api-contracts/content/openapi.yaml -echo "=== AI ===" && grep -c "operationId:" docs/api-contracts/ai/openapi.yaml -echo "=== Knowledge ===" && grep -c "operationId:" docs/api-contracts/knowledge/openapi.yaml -echo "=== Market ===" && grep -c "operationId:" docs/api-contracts/market/openapi.yaml -echo "=== Account ===" && grep -c "operationId:" docs/api-contracts/account/openapi.yaml -echo "=== Meta ===" && grep -c "operationId:" docs/api-contracts/meta/openapi.yaml -echo "=== Total ===" && grep -r "operationId:" docs/api-contracts/*/openapi.yaml docs/api-contracts/openapi-base.yaml 2>/dev/null | wc -l -``` - -- [ ] **Step 2: YAML 语法校验** - -```bash -# 使用 Python 校验所有 YAML 文件 -python3 -c " -import yaml, sys, glob -errors = [] -for f in glob.glob('docs/api-contracts/**/*.yaml', recursive=True): - try: - with open(f) as fh: - yaml.safe_load(fh) - print(f'OK: {f}') - except Exception as e: - errors.append(f'{f}: {e}') - print(f'FAIL: {f}: {e}') -if errors: - print(f'\n{len(errors)} file(s) failed') - sys.exit(1) -else: - print('\nAll files valid') -" -``` - -- [ ] **Step 3: 交叉引用检查** - -确认各模块 YAML 中的 `$ref` 路径正确指向 `openapi-base.yaml`,且被引用的 schema/response/parameter 确实存在。 - -- [ ] **Step 4: 与设计文档一致性复查** - -逐模块对比 `后端-05-统一API契约-v1.md`: -- 接口路径是否一致 -- HTTP 方法是否一致 -- 路径参数名是否一致 -- Request/Response body 字段是否覆盖 - -- [ ] **Step 5: 提交** - -```bash -git add docs/api-contracts/ -git commit -m "feat(api): 完成 6 个模块 OpenAPI 契约文件 + 完整性校验通过" -``` - -### Task 0.9: 生成 TypeScript 类型包 - -**文件:** -- 创建: `docs/api-contracts/generated/typescript/` 下的类型文件 -- 工具: `openapi-typescript` + `openapi-fetch` - -- [ ] **Step 1: 安装代码生成工具** - -```bash -# 在仓库根目录安装(或使用 npx) -npm init -y --prefix /tmp/muse-api-gen -cd /tmp/muse-api-gen -npm install openapi-typescript @hey-api/openapi-ts -``` - -- [ ] **Step 2: 合并所有模块 YAML 为单一入口** - -```bash -# 创建合并入口文件 -cat > /tmp/muse-api-gen/openapi-merged.yaml << 'YAMLEND' -openapi: 3.0.3 -info: - title: Muse API (Merged) - version: 1.0.0 -paths: {} -components: - schemas: {} -YAMLEND - -# 使用 yq 或手动合并各模块 paths -# 此处用简单方式:为每个模块分别生成类型,再合并 index.ts -``` - -- [ ] **Step 3: 为每个模块生成 TypeScript 类型** - -```bash -cd /tmp/muse-api-gen -for module in content ai knowledge market account meta; do - npx openapi-typescript \ - "/Users/qingse/Sync/local-git/oh-my-muse/docs/api-contracts/${module}/openapi.yaml" \ - --output "/Users/qingse/Sync/local-git/oh-my-muse/docs/api-contracts/generated/typescript/${module}.ts" -done -``` - -- [ ] **Step 4: 生成共享组件类型** - -```bash -npx openapi-typescript \ - "/Users/qingse/Sync/local-git/oh-my-muse/docs/api-contracts/openapi-base.yaml" \ - --output "/Users/qingse/Sync/local-git/oh-my-muse/docs/api-contracts/generated/typescript/base.ts" -``` - -- [ ] **Step 5: 编写 barrel 导出文件** - -```typescript -// docs/api-contracts/generated/typescript/index.ts -// Muse API TypeScript 类型包入口 -// 自动生成于 OpenAPI 3.0 契约文件,请勿手动编辑 - -export * from './base'; -export * from './content'; -export * from './ai'; -export * from './knowledge'; -export * from './market'; -export * from './account'; -export * from './meta'; - -// 重新导出通用工具类型 -export type { - CommonResult, - PaginatedResult, - ErrorResponse, - TimestampMixin, -} from './base'; -``` - -- [ ] **Step 6: 提交** - -```bash -git add docs/api-contracts/generated/typescript/ -git commit -m "feat(api): 从 OpenAPI 生成 TypeScript 类型包(6 模块 + base)" -``` - -### Task 0.10: 生成 Java DTO 骨架 - -**文件:** -- 创建: `docs/api-contracts/generated/java/` 下的 DTO 类 -- 工具: `openapi-generator-cli` - -- [ ] **Step 1: 准备 OpenAPI Generator 配置** - -```bash -# 创建生成器配置 -cat > /tmp/muse-api-gen/openapi-generator-config.yaml << 'EOF' -# OpenAPI Generator 配置 for Java DTO -generatorName: spring -library: spring-boot -inputSpec: /tmp/muse-api-gen/openapi-merged.yaml -outputDir: /Users/qingse/Sync/local-git/oh-my-muse/docs/api-contracts/generated/java -apiPackage: com.muse.api -modelPackage: com.muse.dto -generateApis: false -generateModels: true -generateApiTests: false -generateModelTests: false -generateApiDocumentation: false -generateModelDocumentation: false -skipValidateSpec: true -additionalProperties: - java21: true - useSpringBoot3: true - useJakartaEe: true - dateLibrary: java8 - serializableModel: true - openApiNullable: false -EOF -``` - -- [ ] **Step 2: 执行代码生成** - -```bash -npx @openapitools/openapi-generator-cli generate \ - -c /tmp/muse-api-gen/openapi-generator-config.yaml -``` - -- [ ] **Step 3: 清理不需要的生成文件** - -```bash -# 只保留 model/DTO 类,删除 API 接口和测试文件 -rm -rf docs/api-contracts/generated/java/api/ -rm -rf docs/api-contracts/generated/java/test/ -rm -f docs/api-contracts/generated/java/pom.xml -rm -f docs/api-contracts/generated/java/README.md -``` - -- [ ] **Step 4: 提交** - -```bash -git add docs/api-contracts/generated/java/ -git commit -m "feat(api): 从 OpenAPI 生成 Java DTO 骨架(6 模块)" -``` - ---- - -## Phase 0 完成标准 - -- [ ] 6 个模块的 openapi.yaml 全部通过 YAML 语法校验 -- [ ] 接口总数 ≥ 146(与设计文档对齐) -- [ ] 交叉引用 $ref 路径全部有效 -- [ ] TypeScript 类型包生成无报错 -- [ ] Java DTO 骨架生成无报错 -- [ ] 所有提交已推送到 gitea - ---- - -## Phase 1-2 子计划概览 - -以下子计划在 Phase 0 完成后分别在各仓创建和展开: - -### P1: muse-cloud 后端搭建(Week 3-16) - -| Step | 内容 | 周期 | 关键输出 | -|------|------|------|----------| -| 1.1 | Yudao Cloud 基础调整(包名、Docker Compose、.idea) | Week 3 | 可启动的开发环境 | -| 1.2 | 数据库 Migration(全量 DDL + Flyway) | Week 3-4 | 建表 SQL 已执行 | -| 1.3 | 6 个业务模块骨架 | Week 5-8 | pom.xml + 包结构 + API 接口定义 | -| 1.4 | API 实现 Phase 1.1: content + account | Week 9-10 | 作品/章节/Block/权益/配额 API | -| 1.5 | API 实现 Phase 1.2: meta | Week 11 | MetaSchema CRUD API | -| 1.6 | API 实现 Phase 1.3: ai + knowledge | Week 12-14 | AI 生成/候选/智能体 + 知识实体/关系 | -| 1.7 | API 实现 Phase 1.4: market | Week 15-16 | 市场浏览/发布/审核 API | - -### P2: muse-studio 用户端搭建(Week 3-16) - -| Step | 内容 | 周期 | 关键输出 | -|------|------|------|----------| -| 2.1 | 工程脚手架(Vite + React + TS + Tailwind + 路由) | Week 3-4 | 可启动的空 SPA | -| 2.2 | 核心基础设施(MSW + SSE + IndexedDB + Zustand) | Week 5-8 | Mock API 全覆盖 | -| 2.3 | 写作台(编辑器 + AI 生成 + 候选面板) | Week 9-12 | 核心创作流程 | -| 2.4 | 我的作品 + 工作台(列表/章节/Block) | Week 13-14 | 作品管理 | -| 2.5 | 知识库 + 智能体 + 市场 + 个人中心 | Week 15-16 | 辅助功能 | - -### P3: muse-admin 管理端搭建(Week 3-16) - -| Step | 内容 | 周期 | 关键输出 | -|------|------|------|----------| -| 3.1 | 工程脚手架(Vben Admin + 目录组织 + Mock) | Week 3-4 | 可启动的管理端 | -| 3.2 | MetaSchema 管理页面 | Week 5-8 | 字段定义/版本/回滚 | -| 3.3 | 系统治理页面 | Week 9-12 | 用户/角色/审计 | -| 3.4 | AI 配置 + 市场治理 + 全局知识 | Week 13-16 | 管理端完整 | - -### P4: 集成切换 + 质量加固(Week 9-18) - -| Step | 内容 | 周期 | -|------|------|------| -| 4.1 | content+account 集成切换 | Week 9-10 | -| 4.2 | meta 集成切换 | Week 11 | -| 4.3 | ai+knowledge 集成切换 | Week 12-14 | -| 4.4 | market 集成切换 | Week 15-16 | -| 4.5 | 全量质量加固(测试/CI/安全) | Week 17-18 | - ---- - -## 下一步 - -Phase 0 执行完成后: -1. 输出 6 个模块的 OpenAPI YAML + TypeScript 类型 + Java DTO -2. 创建 P1-P4 子计划(在各仓分别展开为详细任务) -3. 三仓并行启动 Phase 1 \ No newline at end of file diff --git a/docs/superpowers/plans/2026-05-24-P1-muse-cloud.md b/docs/superpowers/plans/2026-05-24-P1-muse-cloud.md deleted file mode 100644 index c3df484d..00000000 --- a/docs/superpowers/plans/2026-05-24-P1-muse-cloud.md +++ /dev/null @@ -1,1695 +0,0 @@ -# P1: muse-cloud 后端搭建 — 执行计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. - -**目标:** 在 Yudao Cloud fork 基础上搭建 6 个 Muse 业务模块,实现全量 API(229 接口),提供可调用的后端服务。 - -**架构:** Yudao Cloud 微服务 + PostgreSQL 16 + Flyway 迁移。6 个业务模块按依赖顺序实现:content+account → meta → ai+knowledge → market。Java 21 + Spring Boot 3 + JUnit 5 + Testcontainers。 - -**技术栈:** Java 21, Spring Boot 3, PostgreSQL 16, Flyway, Maven, Docker Compose, JUnit 5, Testcontainers, ArchUnit - ---- - -## Step 1: 基础环境搭建 - -### Task 1.1: 确认 Yudao Cloud Fork 状态 - -**仓库路径:** `muse-cloud/` - -- [ ] **Step 1: 检查当前模块结构** - -```bash -ls muse-cloud/muse-module-*/pom.xml -``` - -预期输出: -``` -muse-cloud/muse-module-ai/muse-module-ai-api/pom.xml -muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml -muse-cloud/muse-module-system/muse-module-system-api/pom.xml -muse-cloud/muse-module-system/muse-module-system-server/pom.xml -muse-cloud/muse-module-infra/.../pom.xml -muse-cloud/muse-module-bpm/.../pom.xml -muse-cloud/muse-module-member/.../pom.xml -muse-cloud/muse-module-mp/.../pom.xml -muse-cloud/muse-module-pay/.../pom.xml -muse-cloud/muse-module-report/.../pom.xml -``` - -- [ ] **Step 2: 确认包名是否已改为 com.muse** - -```bash -find muse-cloud -name "*.java" -path "*/com/muse/*" | head -10 -``` - -若仍为 `cn.iocoder.yudao`,需全局替换。 - -- [ ] **Step 3: 确认 pom.xml 中 Java 版本** - -```bash -grep -r "java.version\|maven.compiler" muse-cloud/pom.xml | head -5 -``` - -确保为 Java 21。 - -- [ ] **Step 4: 验证可编译** - -```bash -cd muse-cloud && mvn compile -q 2>&1 | tail -5 -``` - -### Task 1.2: Docker Compose 开发环境 - -**文件:** -- 创建: `muse-cloud/docker-compose.yml` - -- [ ] **Step 1: 编写 docker-compose.yml** - -```yaml -version: '3.8' -services: - postgres: - image: postgres:16-alpine - environment: - POSTGRES_USER: muse - POSTGRES_PASSWORD: muse_dev - POSTGRES_DB: muse - ports: - - "5432:5432" - volumes: - - pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U muse"] - interval: 5s - timeout: 5s - retries: 5 - - redis: - image: redis:8-alpine - ports: - - "6379:6379" - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 5s - retries: 5 - -volumes: - pgdata: -``` - -- [ ] **Step 2: 启动并验证** - -```bash -cd muse-cloud && docker compose up -d -docker compose ps -``` - -预期输出: postgres 和 redis 均为 `running` 状态。 - -- [ ] **Step 3: 提交** - -```bash -git add docker-compose.yml -git commit -m "feat(infra): 添加 Docker Compose 开发环境(PostgreSQL 16 + Redis 8)" -``` - -### Task 1.3: 调整 Yudao 配置适配 PostgreSQL - -**文件:** -- 修改: `muse-cloud/muse-server/src/main/resources/application-local.yaml` - -- [ ] **Step 1: 确认 PostgreSQL 数据源配置** - -```bash -grep -A10 "datasource" muse-cloud/muse-server/src/main/resources/application-local.yaml -``` - -确保 `url` 为 `jdbc:postgresql://localhost:5432/muse`,driver 为 `org.postgresql.Driver`。 - -- [ ] **Step 2: 提交** - -```bash -git add muse-cloud/muse-server/src/main/resources/application-local.yaml -git commit -m "chore(config): 确认 PostgreSQL 数据源配置" -``` - -### Task 1.4: 配置 .idea 团队共享 - -**文件:** -- 创建: `muse-cloud/.idea/` 下多个配置文件 - -- [ ] **Step 1: 创建 .idea/.gitignore** - -```gitignore -# 团队共享 IDE 配置 -# 个人配置不入库 -/shelf/ -/workspace.xml -/httpRequests/ -/dataSources/ -/dataSources.local.xml -``` - -- [ ] **Step 2: 创建代码风格配置** - -```bash -mkdir -p muse-cloud/.idea/codeStyles -cat > muse-cloud/.idea/codeStyles/Project.xml << 'XML' - - - - - - - -XML -``` - -- [ ] **Step 3: 提交** - -```bash -git add muse-cloud/.idea/ -git commit -m "chore(ide): 添加团队共享 .idea 配置" -``` - ---- - -## Step 2: 数据库 Migration - -### Task 2.1: 编写全量 DDL - -**文件:** -- 创建: `muse-cloud/sql/muse/V1__init_content_schema.sql` -- 创建: `muse-cloud/sql/muse/V2__init_account_schema.sql` -- 创建: `muse-cloud/sql/muse/V3__init_meta_schema.sql` -- 创建: `muse-cloud/sql/muse/V4__init_ai_schema.sql` -- 创建: `muse-cloud/sql/muse/V5__init_knowledge_schema.sql` -- 创建: `muse-cloud/sql/muse/V6__init_market_schema.sql` -- 参考: `design-docs/后端-04-统一数据库Schema-v1.md` -- 参考: `design-docs/后端-04a-完整建表SQL.sql` - -- [ ] **Step 1: 创建 Migration 目录** - -```bash -mkdir -p muse-cloud/sql/muse -``` - -- [ ] **Step 1b: 创建 updated_at 触发器函数(PostgreSQL 通用)** - -所有业务表通过触发器自动更新 `update_time`,无需 `ON UPDATE CURRENT_TIMESTAMP`。 - -```sql --- 触发器函数:自动更新 update_time 列 -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.update_time = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; -``` - -- [ ] **Step 2: 编写 Content 模块 DDL (V1)** - -从 `design-docs/后端-04a-完整建表SQL.sql` 提取 Content 相关表(Work, Chapter, Block, BlockSourceAttribution 等),调整表名为 Yudao 约定(加 `muse_content_` 前缀),添加必备审计字段。 - -```sql --- V1__init_content_schema.sql --- Muse Content 模块初始 Schema(PostgreSQL 16) --- 对应设计文档: design-docs/后端-04-统一数据库Schema-v1.md - --- ============================================================ --- 作品表 --- ============================================================ -CREATE TABLE muse_content_work ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - owner_user_id BIGINT NOT NULL, - title VARCHAR(200) NOT NULL, - genre VARCHAR(50), - summary TEXT, - status VARCHAR(20) NOT NULL DEFAULT 'writing', - work_schema_id BIGINT, - import_status VARCHAR(20), - parse_status VARCHAR(20), - word_count INT NOT NULL DEFAULT 0, - chapter_count INT NOT NULL DEFAULT 0, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0 -); - -CREATE INDEX idx_muse_content_work_owner ON muse_content_work(owner_user_id, update_time); -CREATE INDEX idx_muse_content_work_status ON muse_content_work(status); - -CREATE TRIGGER trg_muse_content_work_updated_at - BEFORE UPDATE ON muse_content_work - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- ============================================================ --- 章节表 --- ============================================================ -CREATE TABLE muse_content_chapter ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - title VARCHAR(200) NOT NULL, - order_no INT NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'draft', - goal_snapshot JSON, - outline_snapshot JSON, - parse_review_status VARCHAR(20), - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0, - CONSTRAINT uk_muse_content_chapter_work_order UNIQUE (work_id, order_no) -); - -CREATE INDEX idx_muse_content_chapter_work ON muse_content_chapter(work_id); - -CREATE TRIGGER trg_muse_content_chapter_updated_at - BEFORE UPDATE ON muse_content_chapter - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- ============================================================ --- Block 正文块表 --- ============================================================ -CREATE TABLE muse_content_block ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - chapter_id BIGINT NOT NULL, - order_no INT NOT NULL, - content_doc JSON, - revision INT NOT NULL DEFAULT 1, - word_count INT NOT NULL DEFAULT 0, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0, - CONSTRAINT uk_muse_content_block_chapter_order UNIQUE (chapter_id, order_no) -); - -CREATE INDEX idx_muse_content_block_work ON muse_content_block(work_id); -CREATE INDEX idx_muse_content_block_chapter ON muse_content_block(chapter_id); - -CREATE TRIGGER trg_muse_content_block_updated_at - BEFORE UPDATE ON muse_content_block - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- ============================================================ --- Block 来源归因表 --- ============================================================ -CREATE TABLE muse_content_block_source_attribution ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - block_id BIGINT NOT NULL, - revision INT NOT NULL, - source_type VARCHAR(50) NOT NULL, - source_object_id VARCHAR(100), - source_version INT, - lineage_payload JSON, - authorization_snapshot_id BIGINT, - source_status VARCHAR(30), - license_restriction_snapshot JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_content_block_src_revision UNIQUE (block_id, revision) -); - -CREATE INDEX idx_muse_content_block_src_block ON muse_content_block_source_attribution(block_id); - -CREATE TRIGGER trg_muse_content_block_src_updated_at - BEFORE UPDATE ON muse_content_block_source_attribution - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -``` - -- [ ] **Step 3: 编写 Account 模块 DDL (V2)** - -```sql --- V2__init_account_schema.sql --- Muse Account 模块初始 Schema(PostgreSQL 16) --- 物理承载: muse-module-member, 表前缀 muse_member_ - --- 用户权益表(可变表 + 审计日志) -CREATE TABLE muse_member_entitlement ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - account_user_id BIGINT NOT NULL, - plan_type VARCHAR(50) NOT NULL DEFAULT 'free', - plan_started_at TIMESTAMP, - plan_expires_at TIMESTAMP, - ai_call_quota INT NOT NULL DEFAULT 0, - ai_call_used INT NOT NULL DEFAULT 0, - storage_quota_mb BIGINT NOT NULL DEFAULT 0, - storage_used_mb BIGINT NOT NULL DEFAULT 0, - kb_count_quota INT NOT NULL DEFAULT 0, - work_count_quota INT NOT NULL DEFAULT 0, - export_quota INT NOT NULL DEFAULT 0, - features_json JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0, - CONSTRAINT uk_muse_member_entitlement_user UNIQUE (account_user_id) -); - -CREATE INDEX idx_muse_member_entitlement_user ON muse_member_entitlement(account_user_id); - -CREATE TRIGGER trg_muse_member_entitlement_updated_at - BEFORE UPDATE ON muse_member_entitlement - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 权益审计日志表(append-only) -CREATE TABLE muse_member_entitlement_audit_log ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - account_user_id BIGINT NOT NULL, - change_type VARCHAR(30) NOT NULL, - source_owner VARCHAR(50), - source_id BIGINT, - idempotency_key VARCHAR(200) NOT NULL, - before_value_snapshot JSON, - delta_value_snapshot JSON, - after_value_snapshot JSON, - reason_code VARCHAR(50), - reason_message VARCHAR(500), - operator_user_id BIGINT, - audit_event_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_member_ent_audit_idem UNIQUE (idempotency_key) -); - -CREATE INDEX idx_muse_member_ent_audit_user ON muse_member_entitlement_audit_log(account_user_id, create_time); - --- 用户配额表 -CREATE TABLE muse_member_quota ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - account_user_id BIGINT NOT NULL, - quota_type VARCHAR(50) NOT NULL, - total_amount BIGINT NOT NULL DEFAULT 0, - used_amount BIGINT NOT NULL DEFAULT 0, - reset_cycle VARCHAR(20), - last_reset_at TIMESTAMP, - next_reset_at TIMESTAMP, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0, - CONSTRAINT uk_muse_member_quota_user_type UNIQUE (account_user_id, quota_type) -); - -CREATE INDEX idx_muse_member_quota_user ON muse_member_quota(account_user_id); - -CREATE TRIGGER trg_muse_member_quota_updated_at - BEFORE UPDATE ON muse_member_quota - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 用量记录表 -CREATE TABLE muse_member_usage_record ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - account_user_id BIGINT NOT NULL, - usage_type VARCHAR(50) NOT NULL, - work_id BIGINT, - task_id BIGINT, - token_input INT DEFAULT 0, - token_output INT DEFAULT 0, - cost_reference VARCHAR(100), - gateway_request_id VARCHAR(200), - correlation_id VARCHAR(200), - status VARCHAR(20) NOT NULL DEFAULT 'completed', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0 -); - -CREATE INDEX idx_muse_member_usage_user_time ON muse_member_usage_record(account_user_id, create_time); -CREATE INDEX idx_muse_member_usage_correlation ON muse_member_usage_record(correlation_id); - -CREATE TRIGGER trg_muse_member_usage_record_updated_at - BEFORE UPDATE ON muse_member_usage_record - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -``` - -- [ ] **Step 4: 编写 Meta 模块 DDL (V3)** - -```sql --- V3__init_meta_schema.sql --- MetaSchema 模块初始 Schema(PostgreSQL 16) - -CREATE TABLE muse_meta_schema ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - schema_key VARCHAR(100) NOT NULL, - domain VARCHAR(20) NOT NULL, - scope VARCHAR(20) NOT NULL, - target_type VARCHAR(50), - display_name VARCHAR(200) NOT NULL, - active_version_id BIGINT, - effective_scope JSON, - projection_version INT NOT NULL DEFAULT 1, - status VARCHAR(20) NOT NULL DEFAULT 'active', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_meta_schema_key UNIQUE (domain, scope, target_type, schema_key) -); - -CREATE TRIGGER trg_muse_meta_schema_updated_at - BEFORE UPDATE ON muse_meta_schema - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - -CREATE TABLE muse_meta_schema_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - schema_id BIGINT NOT NULL, - schema_key VARCHAR(100) NOT NULL, - version_no INT NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'draft', - active_flag BOOLEAN NOT NULL DEFAULT FALSE, - effective_scope JSON, - field_contract_snapshot JSON, - impact_preview_snapshot JSON, - rollback_from_version_id BIGINT, - rollback_to_version_id BIGINT, - published_by VARCHAR(64), - published_at TIMESTAMP, - activated_by VARCHAR(64), - activated_at TIMESTAMP, - change_note VARCHAR(500), - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_meta_schema_version_key UNIQUE (schema_key, version_no) -); - -CREATE INDEX idx_muse_meta_schema_ver_sid ON muse_meta_schema_version(schema_id); - -CREATE TRIGGER trg_muse_meta_schema_version_updated_at - BEFORE UPDATE ON muse_meta_schema_version - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - -CREATE TABLE muse_meta_field ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - schema_version_id BIGINT NOT NULL, - field_key VARCHAR(100) NOT NULL, - display_name VARCHAR(200) NOT NULL, - field_type VARCHAR(20) NOT NULL, - is_required BOOLEAN NOT NULL DEFAULT FALSE, - default_value JSON, - enum_values JSON, - validation_rules JSON, - sort_order INT NOT NULL DEFAULT 0, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX idx_muse_meta_field_ver ON muse_meta_field(schema_version_id); - -CREATE TABLE muse_meta_visibility_policy ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - schema_version_id BIGINT NOT NULL, - ui_visible BOOLEAN NOT NULL DEFAULT TRUE, - ai_context BOOLEAN NOT NULL DEFAULT FALSE, - user_editable BOOLEAN NOT NULL DEFAULT TRUE, - user_searchable BOOLEAN NOT NULL DEFAULT FALSE, - exportable BOOLEAN NOT NULL DEFAULT FALSE, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX idx_muse_meta_vis_policy_ver ON muse_meta_visibility_policy(schema_version_id); -``` - -- [ ] **Step 5: 编写 AI 模块 DDL (V4)** - -```sql --- V4__init_ai_schema.sql --- Muse AI 模块初始 Schema(PostgreSQL 16) - --- AI 生成任务表 -CREATE TABLE muse_ai_generation ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT, - chapter_id BIGINT, - block_id BIGINT, - agent_id BIGINT, - agent_version_id BIGINT, - task_type VARCHAR(50) NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'queued', - idempotency_key VARCHAR(200) NOT NULL, - input_snapshot JSON, - output_summary JSON, - candidate_id BIGINT, - error_code VARCHAR(50), - error_message VARCHAR(500), - retry_count INT NOT NULL DEFAULT 0, - started_at TIMESTAMP, - finished_at TIMESTAMP, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_ai_generation_idem UNIQUE (idempotency_key) -); - -CREATE INDEX idx_muse_ai_gen_work_status ON muse_ai_generation(work_id, status, create_time); -CREATE INDEX idx_muse_ai_gen_block ON muse_ai_generation(block_id); - -CREATE TRIGGER trg_muse_ai_generation_updated_at - BEFORE UPDATE ON muse_ai_generation - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- AI 候选/Suggestion 表 -CREATE TABLE muse_ai_suggestion ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - chapter_id BIGINT, - block_id BIGINT, - generation_id BIGINT NOT NULL, - candidate_type VARCHAR(50) NOT NULL DEFAULT 'suggestion', - content_snapshot JSON, - agent_id BIGINT, - agent_name VARCHAR(200), - agent_version VARCHAR(50), - status VARCHAR(20) NOT NULL DEFAULT 'pending', - source_revision INT, - source_hash VARCHAR(100), - diff_summary JSON, - quality_scores JSON, - quality_result_id BIGINT, - source_snapshot_id BIGINT, - authorization_snapshot_id BIGINT, - source_status VARCHAR(30), - source_action_policy VARCHAR(30), - decision_archive_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_ai_suggestion_work_status ON muse_ai_suggestion(work_id, status, create_time); -CREATE INDEX idx_muse_ai_suggestion_gen ON muse_ai_suggestion(generation_id); - -CREATE TRIGGER trg_muse_ai_suggestion_updated_at - BEFORE UPDATE ON muse_ai_suggestion - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 智能体定义表 -CREATE TABLE muse_agent ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - agent_key VARCHAR(100) NOT NULL, - name VARCHAR(200) NOT NULL, - description VARCHAR(500), - agent_type VARCHAR(20) NOT NULL DEFAULT 'system', - owner_user_id BIGINT, - prompt_key VARCHAR(100), - status VARCHAR(20) NOT NULL DEFAULT 'active', - current_version_id BIGINT, - slot_bindings JSON, - category VARCHAR(50), - tags JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_agent_key UNIQUE (agent_key) -); - -CREATE INDEX idx_muse_agent_type_status ON muse_agent(agent_type, status); - -CREATE TRIGGER trg_muse_agent_updated_at - BEFORE UPDATE ON muse_agent - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 智能体版本历史表 -CREATE TABLE muse_agent_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - agent_id BIGINT NOT NULL, - version VARCHAR(50) NOT NULL, - config JSON, - change_note VARCHAR(500), - status VARCHAR(20) NOT NULL DEFAULT 'draft', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_agent_version_agent_ver UNIQUE (agent_id, version) -); - -CREATE INDEX idx_muse_agent_version_agent ON muse_agent_version(agent_id); - --- 作品级槽位绑定表 -CREATE TABLE muse_agent_slot_binding ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - slot_key VARCHAR(100) NOT NULL, - agent_id BIGINT NOT NULL, - agent_version VARCHAR(50) NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'active', - revision INT NOT NULL DEFAULT 1, - authorization_snapshot_id BIGINT, - source_snapshot_id BIGINT, - fallback_agent_id BIGINT, - binding_source VARCHAR(50), - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_agent_slot_binding_work ON muse_agent_slot_binding(work_id, slot_key, status); - -CREATE TRIGGER trg_muse_agent_slot_binding_updated_at - BEFORE UPDATE ON muse_agent_slot_binding - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- Prompt 模板表 -CREATE TABLE muse_prompt ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - prompt_key VARCHAR(100) NOT NULL, - name VARCHAR(200) NOT NULL, - description VARCHAR(500), - content TEXT, - active_version VARCHAR(50), - status VARCHAR(20) NOT NULL DEFAULT 'active', - category VARCHAR(50), - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_prompt_key UNIQUE (prompt_key) -); - -CREATE TRIGGER trg_muse_prompt_updated_at - BEFORE UPDATE ON muse_prompt - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- Prompt 版本历史表 -CREATE TABLE muse_prompt_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - prompt_id BIGINT NOT NULL, - prompt_key VARCHAR(100) NOT NULL, - version VARCHAR(50) NOT NULL, - content TEXT, - variables JSON, - change_note VARCHAR(500), - status VARCHAR(20) NOT NULL DEFAULT 'draft', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_prompt_version_key_ver UNIQUE (prompt_key, version) -); - -CREATE INDEX idx_muse_prompt_version_prompt ON muse_prompt_version(prompt_id); - --- 质量策略表 -CREATE TABLE muse_quality_policy ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - policy_key VARCHAR(100) NOT NULL, - name VARCHAR(200) NOT NULL, - description VARCHAR(500), - active_version VARCHAR(50), - task_types JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_quality_policy_key UNIQUE (policy_key) -); - -CREATE TRIGGER trg_muse_quality_policy_updated_at - BEFORE UPDATE ON muse_quality_policy - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 质量策略版本表 -CREATE TABLE muse_quality_policy_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - policy_id BIGINT NOT NULL, - policy_key VARCHAR(100) NOT NULL, - version_no INT NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'draft', - active_flag BOOLEAN NOT NULL DEFAULT FALSE, - effective_scope JSON, - metric_contract_snapshot JSON, - threshold_snapshot JSON, - rewrite_policy_snapshot JSON, - rollback_from_version_id BIGINT, - rollback_to_version_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_quality_policy_ver UNIQUE (policy_key, version_no) -); - -CREATE INDEX idx_muse_quality_policy_ver_pid ON muse_quality_policy_version(policy_id); - --- 工具授权表 -CREATE TABLE muse_tool_grant ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - grant_key VARCHAR(100) NOT NULL, - agent_id BIGINT NOT NULL, - tool_name VARCHAR(100) NOT NULL, - action VARCHAR(100) NOT NULL, - purpose VARCHAR(200), - authority_owner VARCHAR(50) NOT NULL DEFAULT 'governance', - scope JSON, - budget JSON, - input_source_policy JSON, - output_target_policy JSON, - egress_policy JSON, - outbound_policy JSON, - audit_requirement JSON, - approval_status VARCHAR(20) NOT NULL DEFAULT 'pending', - approved_by VARCHAR(64), - status VARCHAR(20) NOT NULL DEFAULT 'active', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_tool_grant_key UNIQUE (grant_key) -); - -CREATE INDEX idx_muse_tool_grant_agent ON muse_tool_grant(agent_id); - -CREATE TRIGGER trg_muse_tool_grant_updated_at - BEFORE UPDATE ON muse_tool_grant - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -``` - -- [ ] **Step 6: 编写 Knowledge 模块 DDL (V5)** - -```sql --- V5__init_knowledge_schema.sql --- Muse Knowledge 模块初始 Schema(PostgreSQL 16) - --- 知识库根表 -CREATE TABLE muse_knowledge_base ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - name VARCHAR(200) NOT NULL, - description VARCHAR(500), - kb_type VARCHAR(20) NOT NULL DEFAULT 'user', - owner_user_id BIGINT, - source_market_asset_id BIGINT, - status VARCHAR(20) NOT NULL DEFAULT 'active', - active_version INT NOT NULL DEFAULT 1, - visibility_policy JSON, - license_snapshot_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - tenant_id BIGINT NOT NULL DEFAULT 0 -); - -CREATE INDEX idx_muse_kb_owner_status ON muse_knowledge_base(owner_user_id, status); -CREATE INDEX idx_muse_kb_type ON muse_knowledge_base(kb_type); - -CREATE TRIGGER trg_muse_knowledge_base_updated_at - BEFORE UPDATE ON muse_knowledge_base - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 知识库资料表 -CREATE TABLE muse_knowledge_document ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - kb_id BIGINT NOT NULL, - title VARCHAR(200) NOT NULL, - file_name VARCHAR(500), - file_size BIGINT, - mime_type VARCHAR(100), - file_hash VARCHAR(100), - storage_ref VARCHAR(500), - scan_status VARCHAR(20) NOT NULL DEFAULT 'pending', - parse_status VARCHAR(20), - index_status VARCHAR(20), - retention_policy JSON, - author VARCHAR(200), - source_url VARCHAR(1000), - tags JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_kb_doc_kb ON muse_knowledge_document(kb_id, status); -CREATE INDEX idx_muse_kb_doc_hash ON muse_knowledge_document(file_hash); - -CREATE TRIGGER trg_muse_knowledge_document_updated_at - BEFORE UPDATE ON muse_knowledge_document - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 资料版本表 -CREATE TABLE muse_knowledge_document_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - document_id BIGINT NOT NULL, - version INT NOT NULL, - file_hash VARCHAR(100) NOT NULL, - storage_ref VARCHAR(500), - content_hash VARCHAR(100), - source_snapshot_id BIGINT, - scan_status VARCHAR(20) NOT NULL DEFAULT 'pending', - parse_status VARCHAR(20) NOT NULL DEFAULT 'pending', - processing_status VARCHAR(20) NOT NULL DEFAULT 'pending', - previous_version_id BIGINT, - change_summary JSON, - change_reason VARCHAR(500), - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_kb_doc_ver UNIQUE (document_id, version) -); - -CREATE INDEX idx_muse_kb_doc_ver_doc ON muse_knowledge_document_version(document_id); - --- 知识实体表(Local KB Canonical) -CREATE TABLE muse_knowledge_entity ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - kb_id BIGINT, - entity_type VARCHAR(50) NOT NULL, - normalized_name VARCHAR(200) NOT NULL, - scope VARCHAR(50) NOT NULL DEFAULT 'global', - description TEXT, - attributes JSON, - source_snapshot_id BIGINT, - authorization_snapshot_id BIGINT, - source_status VARCHAR(30) NOT NULL DEFAULT 'active', - source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed', - source_event_version INT, - lineage_payload JSON, - status VARCHAR(20) NOT NULL DEFAULT 'active', - confidence DOUBLE PRECISION DEFAULT 1.0, - source_type VARCHAR(50), - source_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_knowledge_entity UNIQUE (work_id, entity_type, normalized_name, scope) -); - -CREATE INDEX idx_muse_knowledge_entity_work_type ON muse_knowledge_entity(work_id, entity_type); -CREATE INDEX idx_muse_knowledge_entity_kb ON muse_knowledge_entity(kb_id); - -CREATE TRIGGER trg_muse_knowledge_entity_updated_at - BEFORE UPDATE ON muse_knowledge_entity - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 知识关系表 -CREATE TABLE muse_knowledge_relation ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - source_entity_id BIGINT NOT NULL, - target_entity_id BIGINT NOT NULL, - relation_type VARCHAR(50) NOT NULL, - description VARCHAR(500), - attributes JSON, - source_status VARCHAR(30) NOT NULL DEFAULT 'active', - source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_knowledge_rel_work ON muse_knowledge_relation(work_id); -CREATE INDEX idx_muse_knowledge_rel_src ON muse_knowledge_relation(source_entity_id); -CREATE INDEX idx_muse_knowledge_rel_tgt ON muse_knowledge_relation(target_entity_id); - -CREATE TRIGGER trg_muse_knowledge_relation_updated_at - BEFORE UPDATE ON muse_knowledge_relation - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 知识草稿表 -CREATE TABLE muse_knowledge_draft ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - entity_id BIGINT, - draft_type VARCHAR(30) NOT NULL DEFAULT 'entity', - target_object_id BIGINT, - proposed_changes JSON, - draft_payload JSON, - current_canonical_snapshot JSON, - source_snapshot_id BIGINT, - authorization_snapshot_id BIGINT, - risk_marker_snapshot JSON, - source_status VARCHAR(30), - source_action_policy VARCHAR(30), - status VARCHAR(20) NOT NULL DEFAULT 'pending', - confidence DOUBLE PRECISION, - source_type VARCHAR(50), - source_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_knowledge_draft_work_status ON muse_knowledge_draft(work_id, status, create_time); -CREATE INDEX idx_muse_knowledge_draft_entity ON muse_knowledge_draft(entity_id); - -CREATE TRIGGER trg_muse_knowledge_draft_updated_at - BEFORE UPDATE ON muse_knowledge_draft - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 作品-知识库绑定表 -CREATE TABLE muse_knowledge_binding ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - work_id BIGINT NOT NULL, - kb_id BIGINT NOT NULL, - binding_type VARCHAR(50) NOT NULL DEFAULT 'read', - binding_scope VARCHAR(50) NOT NULL DEFAULT 'read', - binding_status VARCHAR(30) NOT NULL DEFAULT 'active', - source_snapshot_id BIGINT, - authorization_snapshot_id BIGINT, - source_version INT, - target_version INT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - CONSTRAINT uk_muse_knowledge_binding_work_kb UNIQUE (work_id, kb_id) -); - -CREATE INDEX idx_muse_knowledge_binding_work ON muse_knowledge_binding(work_id); -CREATE INDEX idx_muse_knowledge_binding_kb ON muse_knowledge_binding(kb_id); - -CREATE TRIGGER trg_muse_knowledge_binding_updated_at - BEFORE UPDATE ON muse_knowledge_binding - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -``` - -- [ ] **Step 7: 编写 Market 模块 DDL (V6)** - -```sql --- V6__init_market_schema.sql --- Muse Market 模块初始 Schema(PostgreSQL 16) - --- 市场资产表 -CREATE TABLE muse_market_asset ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - name VARCHAR(200) NOT NULL, - description VARCHAR(1000), - asset_type VARCHAR(50) NOT NULL, - category VARCHAR(50), - source_id BIGINT, - publisher_id BIGINT NOT NULL, - listing_status VARCHAR(20) NOT NULL DEFAULT 'draft', - license_type VARCHAR(50) NOT NULL DEFAULT 'standard', - status VARCHAR(20) NOT NULL DEFAULT 'draft', - current_version_id BIGINT, - rating DOUBLE PRECISION DEFAULT 0, - install_count BIGINT NOT NULL DEFAULT 0, - tags JSON, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_market_asset_type_status ON muse_market_asset(asset_type, listing_status); -CREATE INDEX idx_muse_market_asset_publisher ON muse_market_asset(publisher_id); - -CREATE TRIGGER trg_muse_market_asset_updated_at - BEFORE UPDATE ON muse_market_asset - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 资产版本表 -CREATE TABLE muse_market_asset_version ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - asset_id BIGINT NOT NULL, - version VARCHAR(50) NOT NULL, - change_note VARCHAR(500), - version_snapshot JSON, - publish_check_snapshot_id BIGINT, - status VARCHAR(20) NOT NULL DEFAULT 'draft', - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_market_asset_ver UNIQUE (asset_id, version) -); - -CREATE INDEX idx_muse_market_asset_ver_asset ON muse_market_asset_version(asset_id); - --- 用户安装表 -CREATE TABLE muse_market_installation ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - asset_id BIGINT NOT NULL, - asset_version_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'installed', - installed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - uninstalled_at TIMESTAMP, - authorization_snapshot_id BIGINT, - source_snapshot_id BIGINT, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_market_install UNIQUE (user_id, asset_id, asset_version_id) -); - -CREATE INDEX idx_muse_market_install_user ON muse_market_installation(user_id); -CREATE INDEX idx_muse_market_install_asset ON muse_market_installation(asset_id); - --- 发布请求表 -CREATE TABLE muse_market_publish_request ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - asset_id BIGINT NOT NULL, - asset_version_id BIGINT, - publisher_id BIGINT NOT NULL, - draft_version INT NOT NULL DEFAULT 1, - status VARCHAR(20) NOT NULL DEFAULT 'pending_review', - review_note VARCHAR(1000), - reviewer_id BIGINT, - reviewed_at TIMESTAMP, - submitted_at TIMESTAMP, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_market_pub_req_asset ON muse_market_publish_request(asset_id, status); -CREATE INDEX idx_muse_market_pub_req_reviewer ON muse_market_publish_request(reviewer_id); - -CREATE TRIGGER trg_muse_market_publish_request_updated_at - BEFORE UPDATE ON muse_market_publish_request - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- 申诉表 -CREATE TABLE muse_market_appeal ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - asset_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - appeal_type VARCHAR(50) NOT NULL, - reason TEXT, - status VARCHAR(20) NOT NULL DEFAULT 'pending', - resolution VARCHAR(1000), - resolver_id BIGINT, - resolved_at TIMESTAMP, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updater VARCHAR(64) NOT NULL DEFAULT '', - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN NOT NULL DEFAULT FALSE -); - -CREATE INDEX idx_muse_market_appeal_asset ON muse_market_appeal(asset_id, status); -CREATE INDEX idx_muse_market_appeal_user ON muse_market_appeal(user_id); - -CREATE TRIGGER trg_muse_market_appeal_updated_at - BEFORE UPDATE ON muse_market_appeal - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- Handoff 会话表 -CREATE TABLE muse_market_handoff ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - token VARCHAR(200) NOT NULL, - source_asset_id BIGINT NOT NULL, - asset_version_id BIGINT, - target_owner_hint VARCHAR(100), - target_endpoint VARCHAR(500), - handoff_token_hash VARCHAR(200) NOT NULL, - license_summary JSON, - governance_summary JSON, - authorization_snapshot_id BIGINT, - source_snapshot_id BIGINT, - status VARCHAR(20) NOT NULL DEFAULT 'active', - consumed_at TIMESTAMP, - canceled_at TIMESTAMP, - jump_audit_id BIGINT, - expires_at TIMESTAMP NOT NULL, - creator VARCHAR(64) NOT NULL DEFAULT '', - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT uk_muse_market_handoff_token UNIQUE (token) -); - -CREATE INDEX idx_muse_market_handoff_asset ON muse_market_handoff(source_asset_id); - -CREATE TRIGGER trg_muse_market_handoff_updated_at - BEFORE UPDATE ON muse_market_handoff - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -``` - -- [ ] **Step zero: 更新 Flyway migration 顺序引用** - -确保 `update_updated_at_column()` 函数在第一个 migration 文件中创建,后续所有 migration 文件可直接引用。触发器创建语句包含在每个表的 DDL 文件中。 - -- [ ] **Step 8: 配置 Flyway** - -修改 `muse-cloud/muse-server/src/main/resources/application.yaml`,确保 Flyway 启用并指向 `sql/muse` 目录。 - -- [ ] **Step 9: 运行 Migration 并验证** - -```bash -cd muse-cloud && mvn flyway:migrate -pl muse-server -``` - -验证: 所有表创建成功,索引正确。 - -- [ ] **Step 10: 提交** - -```bash -git add muse-cloud/sql/ -git commit -m "feat(db): 添加 6 个模块全量 DDL + Flyway 迁移配置" -``` - ---- - -## Step 3: 业务模块骨架 - -### Task 3.1: 创建 Content 模块 - -**文件:** -- 创建: `muse-cloud/muse-module-content/pom.xml` -- 创建: `muse-cloud/muse-module-content/muse-module-content-api/pom.xml` -- 创建: `muse-cloud/muse-module-content/muse-module-content-server/pom.xml` -- 创建: API 接口包 `com.muse.module.content.api` -- 创建: Server 包结构 `com.muse.module.content.controller/service/dal` - -- [ ] **Step 1: 创建父 pom.xml** - -```xml - - - - 4.0.0 - - com.muse - muse - ${revision} - - muse-module-content - pom - Muse Module Content - 作品、章节、Block、导出模块 - - muse-module-content-api - muse-module-content-server - - -``` - -- [ ] **Step 2: 创建 api 子模块 pom.xml** - -```xml - - - - 4.0.0 - - com.muse - muse-module-content - ${revision} - - muse-module-content-api - jar - Muse Module Content API - Content 模块 API 接口定义(Feign 接口 + DTO) - - - com.muse - muse-common - - - -``` - -- [ ] **Step 3: 创建 server 子模块 pom.xml** - -```xml - - - - 4.0.0 - - com.muse - muse-module-content - ${revision} - - muse-module-content-server - jar - Muse Module Content Server - Content 模块服务实现 - - - com.muse - muse-module-content-api - - - com.muse - muse-module-system-api - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - org.testcontainers - postgresql - test - - - -``` - -- [ ] **Step 4: 创建 API 接口类 (Feign)** - -```java -// muse-module-content/muse-module-content-api/src/main/java/com/muse/module/content/api/ContentApi.java -package com.muse.module.content.api; - -import com.muse.module.content.api.dto.*; -import com.muse.common.result.CommonResult; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -/** - * Content 模块 Feign API 接口 - * 对应 OpenAPI: content/openapi.yaml - */ -@FeignClient(name = "muse-module-content") -public interface ContentApi { - - // ===== Work ===== - @GetMapping("/app-api/muse/works") - CommonResult> listWorks( - @RequestParam("pageNo") Integer pageNo, - @RequestParam("pageSize") Integer pageSize, - @RequestParam(value = "status", required = false) String status - ); - - @PostMapping("/app-api/muse/works") - CommonResult createWork(@RequestBody CreateWorkRequest request); - - @GetMapping("/app-api/muse/works/{workId}") - CommonResult getWork(@PathVariable("workId") Long workId); - - @PutMapping("/app-api/muse/works/{workId}") - CommonResult updateWork(@PathVariable("workId") Long workId, @RequestBody UpdateWorkRequest request); - - @DeleteMapping("/app-api/muse/works/{workId}") - CommonResult deleteWork(@PathVariable("workId") Long workId); - - // ===== Chapter ===== - @GetMapping("/app-api/muse/works/{workId}/chapters") - CommonResult> listChapters(@PathVariable("workId") Long workId); - - @PostMapping("/app-api/muse/works/{workId}/chapters") - CommonResult createChapter(@PathVariable("workId") Long workId, @RequestBody CreateChapterRequest request); - - @GetMapping("/app-api/muse/chapters/{chapterId}") - CommonResult getChapter(@PathVariable("chapterId") Long chapterId); - - @PutMapping("/app-api/muse/chapters/{chapterId}") - CommonResult updateChapter(@PathVariable("chapterId") Long chapterId, @RequestBody UpdateChapterRequest request); - - @DeleteMapping("/app-api/muse/chapters/{chapterId}") - CommonResult deleteChapter(@PathVariable("chapterId") Long chapterId); - - // ===== Block ===== - @GetMapping("/app-api/muse/chapters/{chapterId}/blocks") - CommonResult> listBlocks(@PathVariable("chapterId") Long chapterId); - - @PostMapping("/app-api/muse/chapters/{chapterId}/blocks") - CommonResult createBlock(@PathVariable("chapterId") Long chapterId, @RequestBody CreateBlockRequest request); - - @PutMapping("/app-api/muse/blocks/{blockId}") - CommonResult saveBlock(@PathVariable("blockId") Long blockId, @RequestBody SaveBlockRequest request); - - @DeleteMapping("/app-api/muse/blocks/{blockId}") - CommonResult deleteBlock(@PathVariable("blockId") Long blockId); -} -``` - -- [ ] **Step 5: 创建 DTO 类** - -从 `docs/api-contracts/generated/java/com/muse/dto/` 复制 Content 相关 DTO 到 `muse-module-content-api/src/main/java/com/muse/module/content/api/dto/`。 - -- [ ] **Step 6: 创建 Server 包结构** - -```bash -mkdir -p muse-cloud/muse-module-content/muse-module-content-server/src/main/java/com/muse/module/content/{controller,service,dal/{mysql,convert}} -mkdir -p muse-cloud/muse-module-content/muse-module-content-server/src/test/java/com/muse/module/content -``` - -- [ ] **Step 7: 在父 pom.xml 注册模块** - -```bash -# 在 muse-cloud/pom.xml 的 中添加 -# muse-module-content -``` - -- [ ] **Step 8: 编译验证** - -```bash -cd muse-cloud && mvn compile -pl muse-module-content -am -``` - -- [ ] **Step 9: 提交** - -```bash -git add muse-cloud/muse-module-content/ muse-cloud/pom.xml -git commit -m "feat(content): 创建 Content 模块骨架(api+server)" -``` - -### Task 3.2-3.6: 创建其余 5 个业务模块骨架 - -按相同模式创建 Account, Meta, AI, Knowledge, Market 五个模块。每个模块: -1. 创建 api/server 子模块 pom.xml -2. 创建 Feign API 接口类(对齐 OpenAPI 定义) -3. 从 `docs/api-contracts/generated/java/com/muse/dto/` 复制 DTO -4. 创建 server 包结构(controller/service/dal) -5. 在父 pom.xml 注册 -6. 编译验证 - -每个模块单独提交。 - ---- - -## Step 4: API 实现(按依赖顺序) - -### Task 4.1: Content API 实现 - -**依赖:** system(用户认证) - -- [ ] **Step 1: 编写 WorkService** - -```java -// muse-module-content-server/src/main/java/com/muse/module/content/service/WorkService.java -package com.muse.module.content.service; - -import com.muse.module.content.api.dto.*; -import org.springframework.data.domain.Page; - -/** - * 作品服务接口 - */ -public interface WorkService { - Page listWorks(Long userId, Integer pageNo, Integer pageSize, String status); - WorkDTO createWork(Long userId, CreateWorkRequest request); - WorkDTO getWork(Long workId); - void updateWork(Long workId, UpdateWorkRequest request); - void deleteWork(Long workId); -} -``` - -- [ ] **Step 2: 编写 WorkService 实现** - -```java -// muse-module-content-server/src/main/java/com/muse/module/content/service/impl/WorkServiceImpl.java -package com.muse.module.content.service.impl; - -import com.muse.module.content.dal.mysql.WorkMapper; -import com.muse.module.content.dal.dataobject.WorkDO; -import com.muse.module.content.service.WorkService; -import com.muse.module.content.api.dto.*; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -public class WorkServiceImpl implements WorkService { - - private final WorkMapper workMapper; - - public WorkServiceImpl(WorkMapper workMapper) { - this.workMapper = workMapper; - } - - @Override - public Page listWorks(Long userId, Integer pageNo, Integer pageSize, String status) { - // 分页查询作品列表 - } - - @Override - @Transactional - public WorkDTO createWork(Long userId, CreateWorkRequest request) { - WorkDO work = new WorkDO(); - work.setTitle(request.getTitle()); - work.setDescription(request.getDescription()); - work.setGenre(request.getGenre()); - work.setCreator(String.valueOf(userId)); - workMapper.insert(work); - return convertToDTO(work); - } - - // ... 其余方法 -} -``` - -- [ ] **Step 3: 编写 WorkMapper (MyBatis)** - -```java -// muse-module-content-server/src/main/java/com/muse/module/content/dal/mysql/WorkMapper.java -package com.muse.module.content.dal.mysql; - -import com.muse.framework.mybatis.core.mapper.BaseMapperX; -import com.muse.module.content.dal.dataobject.WorkDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface WorkMapper extends BaseMapperX { -} -``` - -- [ ] **Step 4: 编写 WorkController** - -```java -// muse-module-content-server/src/main/java/com/muse/module/content/controller/WorkController.java -package com.muse.module.content.controller; - -import com.muse.module.content.service.WorkService; -import com.muse.module.content.api.dto.*; -import com.muse.common.result.CommonResult; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/app-api/muse/works") -public class WorkController { - - private final WorkService workService; - - public WorkController(WorkService workService) { - this.workService = workService; - } - - @GetMapping - public CommonResult> listWorks( - @RequestParam(defaultValue = "1") Integer pageNo, - @RequestParam(defaultValue = "20") Integer pageSize, - @RequestParam(required = false) String status - ) { - Long userId = SecurityUtils.getLoginUserId(); - return CommonResult.success(workService.listWorks(userId, pageNo, pageSize, status)); - } - - @PostMapping - public CommonResult createWork(@RequestBody CreateWorkRequest request) { - Long userId = SecurityUtils.getLoginUserId(); - return CommonResult.success(workService.createWork(userId, request)); - } - - // ... 其余端点 -} -``` - -- [ ] **Step 5: 编写集成测试** - -```java -// muse-module-content-server/src/test/java/com/muse/module/content/WorkControllerTest.java -package com.muse.module.content; - -import com.muse.module.content.api.dto.*; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import static org.junit.jupiter.api.Assertions.*; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@Testcontainers -class WorkControllerTest { - - @Container - static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine") - .withDatabaseName("muse_test") - .withUsername("test") - .withPassword("test"); - - @Autowired - private TestRestTemplate restTemplate; - - @Test - void shouldCreateAndGetWork() { - // 创建作品 - CreateWorkRequest request = new CreateWorkRequest(); - request.setTitle("测试作品"); - - ResponseEntity createResp = restTemplate.postForEntity( - "/app-api/muse/works", request, CommonResult.class - ); - assertEquals(HttpStatus.OK, createResp.getStatusCode()); - assertNotNull(createResp.getBody().getData()); - } -} -``` - -- [ ] **Step 6: 运行测试** - -```bash -cd muse-cloud && mvn test -pl muse-module-content/muse-module-content-server -``` - -预期: 集成测试通过。 - -- [ ] **Step 7: 提交** - -```bash -git add muse-cloud/muse-module-content/ -git commit -m "feat(content): Content API 实现(Work CRUD + 集成测试)" -``` - -### Task 4.2: Chapter/Block API 实现 - -按相同 TDD 模式实现章节和 Block CRUD API: -1. 编写测试(验证 CRUD + 乐观锁冲突 + 排序逻辑) -2. 编写 Service + Controller + Mapper -3. 测试通过 -4. 提交 - -### Task 4.3: Block 保存(乐观锁 + 版本管理) - -核心:`PUT /app-api/muse/blocks/{blockId}` 实现乐观锁更新。 - -```java -@Transactional -public SaveBlockResultDTO saveBlock(Long userId, Long blockId, SaveBlockRequest request) { - // 1. 查询当前 Block - BlockDO block = blockMapper.selectById(blockId); - if (block == null) throw new ResourceNotFoundException("Block not found"); - - // 2. 乐观锁校验:expectedRevision 必须匹配 - if (!request.getExpectedRevision().equals(block.getRevision())) { - throw new RevisionConflictException(block.getRevision()); - } - - // 3. 更新 Block 内容和版本号 - block.setContent(request.getContent()); - block.setRevision(block.getRevision() + 1); - block.setUpdater(String.valueOf(userId)); - blockMapper.updateById(block); - - // 4. 写入来源归因 - BlockSourceAttributionDO attribution = new BlockSourceAttributionDO(); - attribution.setBlockId(blockId); - attribution.setRevision(block.getRevision()); - attribution.setSourceType(request.getSourceType()); - sourceAttributionMapper.insert(attribution); - - // 5. 返回新版本号 - SaveBlockResultDTO result = new SaveBlockResultDTO(); - result.setNewRevision(block.getRevision()); - return result; -} -``` - -### Task 4.4-4.11: 其余 Content 接口实现 - -按 OpenAPI 顺序逐步实现: -4.4: Source Attribution 接口 -4.5: Meta Projections 接口 -4.6: Planning 接口(规划查询/保存/候选创建/确认/丢弃) -4.7: Style Check 接口 -4.8: Import 接口(上传/解析/确认/批量确认) -4.9: Export 接口(创建任务/查询/下载) -4.10: Admin Content 接口(列表/详情/风险治理) -4.11: apply-suggestion 接口(接受 AI 候选后写入 Canonical) - -每个子任务:TDD → 实现 → 测试通过 → 提交。 - -### Task 4.12: Account API 实现 - -**依赖:** content 模块不相关,可独立开始。 - -实现权益查询、配额查询、用量摘要、安全事件、个人中心等接口。 -每个子任务按 TDD 模式:测试 → Service → Controller → Mapper → 验证 → 提交。 - -### Task 4.13: Meta API 实现 - -**依赖:** content + account 已稳定。 - -实现 MetaSchema CRUD、版本管理、草稿/校验/影响预览/发布/激活/回滚/废弃/灰度、保护节点查询、功能链路管理。 - -### Task 4.14: AI API 实现 - -**依赖:** content + meta 已稳定。 - -实现 AI 任务提交、SSE 流式输出、候选管理(列表/详情/接受/拒绝)、智能体管理(创建/版本/试用)、槽位绑定、Admin Prompt/Agent/质量策略管理、来源状态查询/重验。 - -### Task 4.15: Knowledge API 实现 - -**依赖:** content + meta 已稳定。 - -实现知识实体/关系、自动确认逻辑、全局KB管理、用户KB管理、文档上传/解析、发布流程、知识草稿确认/忽略、知识绑定/解绑。 - -### Task 4.16: Market API 实现 - -**依赖:** account + ai + knowledge 已稳定。 - -实现市场资产浏览/推荐、收藏、购买/安装、Handoff 创建/消费、发布草稿/检查/提交、管理端审核/下架/召回、申诉处理。 - ---- - -## 完成标准 - -- [ ] 6 个模块全部 API 自测通过 -- [ ] Domain 层单元测试覆盖率 ≥90%(JUnit 5 + Mockito) -- [ ] Application 层集成测试覆盖率 ≥80%(Testcontainers + PostgreSQL) -- [ ] ArchUnit 规则通过(AI grant/runtime 包隔离检查) -- [ ] Checkstyle / SpotBugs lint 通过 -- [ ] Maven build 成功(含所有测试) \ No newline at end of file diff --git a/docs/superpowers/plans/2026-05-24-P2-muse-studio.md b/docs/superpowers/plans/2026-05-24-P2-muse-studio.md deleted file mode 100644 index 1270a2f7..00000000 --- a/docs/superpowers/plans/2026-05-24-P2-muse-studio.md +++ /dev/null @@ -1,1128 +0,0 @@ -# P2: muse-studio 用户端搭建 — 执行计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. - -**目标:** 从零搭建 muse-studio React SPA,实现写作台、作品管理、知识库、智能体、市场、个人中心 6 个功能域。 - -**架构:** Vite + React + TypeScript SPA,TanStack Query 数据层,Zustand 状态管理,Tiptap/ProseMirror 编辑器,MSW Mock API,SSE 双通道实时通信,IndexedDB 安全网。 - -**技术栈:** React 18, TypeScript 5, Vite 6, TanStack Query 5, Zustand 4, React Hook Form 7 + Zod 3, Tiptap 2, Tailwind CSS 3, MSW 2, Vitest + Testing Library + Playwright - ---- - -## 执行状态(2026-05-25) - -**当前提交范围:** `muse-studio/` 用户端 SPA 已完成 6 个功能域的可交互实现:作品工作区、写作台、知识库工作台、智能体工作台、创作市场、个人中心。 - -**已完成基础设施:** - -- Vite + React 18 + TypeScript 工程骨架。 -- React Router 应用路由与侧边栏布局。 -- TanStack Query 数据层、Zustand 编辑器状态、统一 API Client。 -- MSW Mock API,覆盖 content、ai、knowledge、market、account。 -- Tiptap 编辑器、IndexedDB 草稿安全网、SSE AI 流式生成、候选 diff 与采纳流程。 - -**已完成业务域:** - -- Feature 1 写作台:编辑器、AI 生成面板、候选对比与采纳。 -- Feature 2 我的作品 + 工作台:作品列表、创建、删除、章节大纲、新建章节、删除章节、切章重载。 -- Feature 3 知识库工作台:按当前产品规格演进为知识库列表、资料管理、上传、手写条目、外链导入、解析状态轮询、删除/卸载。 -- Feature 4 智能体工作台:智能体列表、创建、试用、作品开放槽位预检与绑定。 -- Feature 5 市场 + 个人中心:市场搜索筛选、授权获取、安装;账户资料、权益配额、用量摘要。 - -**验证记录:** - -- `pnpm exec tsc -b --pretty false` 通过。 -- `pnpm exec vitest run` 通过:7 个测试文件、23 个测试。 -- `pnpm build` 通过;仍有 Vite chunk > 500KB 提示,后续可做路由级 code splitting。 -- `pnpm lint` 退出码 0;仅 `public/mockServiceWorker.js` 生成文件有 unused eslint-disable warning。 -- 已用 Playwright + 本机 Chrome 冒烟 `/agents`、`/market`、`/account`,页面非空且无明显加载失败或重叠。 - -**未完成或后续增强:** - -- 尚未补 Playwright E2E 自动化用例。 -- 尚未按完成标准统计组件/Hook 覆盖率百分比。 -- `public/mockServiceWorker.js` lint warning 建议通过生成文件 ignore 处理,不手改生成文件。 - -## Step 1: 工程脚手架 - -### Task 1.1: 初始化 Vite + React + TS 项目 - -**仓库路径:** `muse-studio/`(新建) - -- [ ] **Step 1: 创建项目** - -```bash -pnpm create vite@latest muse-studio --template react-ts -cd muse-studio -``` - -- [ ] **Step 2: 安装核心依赖** - -```bash -pnpm add react@18 react-dom@18 react-router-dom@6 -pnpm add @tanstack/react-query@5 -pnpm add zustand@4 -pnpm add react-hook-form@7 zod@3 @hookform/resolvers@3 -pnpm add @tiptap/react @tiptap/pm @tiptap/starter-kit @tiptap/extension-placeholder -pnpm add -D tailwindcss@3 postcss autoprefixer -pnpm add -D @types/react @types/react-dom -pnpm add -D vitest@2 @testing-library/react @testing-library/jest-dom jsdom -pnpm add -D eslint@9 prettier @typescript-eslint/parser -pnpm add -D msw@2 -pnpm add -D playwright@1 @playwright/test -pnpm add -D start-server-and-test -``` - -- [ ] **Step 3: 配置 Vite** - -```typescript -// muse-studio/vite.config.ts -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import path from 'path' - -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, 'src'), - }, - }, - server: { - port: 5173, - proxy: { - '/app-api': { - target: 'http://localhost:48080', - changeOrigin: true, - }, - }, - }, - test: { - globals: true, - environment: 'jsdom', - setupFiles: ['./src/test/setup.ts'], - css: true, - }, -}) -``` - -- [ ] **Step 4: 创建目录结构** - -```bash -mkdir -p muse-studio/src/{app/routes,components/{ui,layout,feedback},features/{editor,agent,knowledge,market,account},hooks,stores,lib,types,api/{hooks,mocks/handlers},test} -``` - -- [ ] **Step 5: 配置 Tailwind** - -```bash -npx tailwindcss init -p -``` - -```javascript -// muse-studio/tailwind.config.js -export default { - content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], - theme: { extend: {} }, - plugins: [], -} -``` - -- [ ] **Step 6: 提交** - -```bash -git init && git add -A && git commit -m "feat(init): 初始化 Vite + React + TS 项目脚手架" -``` - -### Task 1.2: 路由框架 + 布局 - -**文件:** -- 创建: `src/app/routes/index.tsx` -- 创建: `src/components/layout/AppLayout.tsx` -- 创建: `src/pages/{Workspace,Editor,Knowledge,Agent,Market,Account}Page.tsx` - -- [ ] **Step 1: 编写路由配置** - -```tsx -// src/app/routes/index.tsx -import { createBrowserRouter } from 'react-router-dom'; -import AppLayout from '@/components/layout/AppLayout'; -import WorkspacePage from '@/pages/WorkspacePage'; -import EditorPage from '@/pages/EditorPage'; -import KnowledgePage from '@/pages/KnowledgePage'; -import AgentPage from '@/pages/AgentPage'; -import MarketPage from '@/pages/MarketPage'; -import AccountPage from '@/pages/AccountPage'; - -export const router = createBrowserRouter([ - { - path: '/', - element: , - children: [ - { index: true, element: }, - { path: 'works/:workId', element: }, - { path: 'works/:workId/editor/:chapterId', element: }, - { path: 'knowledge', element: }, - { path: 'knowledge/:workId', element: }, - { path: 'agents', element: }, - { path: 'market', element: }, - { path: 'account', element: }, - ], - }, -]); -``` - -- [ ] **Step 2: 编写布局组件** - -```tsx -// src/components/layout/AppLayout.tsx -import { Outlet } from 'react-router-dom'; -import Sidebar from './Sidebar'; - -export default function AppLayout() { - return ( -
- -
- -
-
- ); -} -``` - -- [ ] **Step 3: 实现侧边栏导航** - -```tsx -// src/components/layout/Sidebar.tsx -import { NavLink } from 'react-router-dom'; - -const navItems = [ - { to: '/', label: '我的作品', icon: '📝' }, - { to: '/knowledge', label: '知识库', icon: '📚' }, - { to: '/agents', label: '智能体', icon: '🤖' }, - { to: '/market', label: '市场', icon: '🏪' }, - { to: '/account', label: '个人中心', icon: '👤' }, -]; - -export default function Sidebar() { - return ( - - ); -} -``` - -- [ ] **Step 4: 创建占位页面** - -```tsx -// src/pages/WorkspacePage.tsx -export default function WorkspacePage() { - return

我的作品

; -} -``` - -- [ ] **Step 5: Main.tsx 集成路由** - -```tsx -// src/main.tsx -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { RouterProvider } from 'react-router-dom'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { router } from './app/routes'; -import './index.css'; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 30_000, - retry: 1, - }, - }, -}); - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - -); -``` - -- [ ] **Step 6: 验证启动** - -```bash -cd muse-studio && pnpm dev -``` - -浏览 `http://localhost:5173`,验证路由导航正常。 - -- [ ] **Step 7: 提交** - -```bash -git add -A && git commit -m "feat(router): 添加路由框架 + 布局组件 + 6 个功能域占位" -``` - ---- - -## Step 2: 核心基础设施 - -### Task 2.1: API 客户端 + 类型集成 - -**文件:** -- 创建: `src/api/client.ts` -- 创建: `src/types/openapi.ts`(从 OpenAPI 生成物导入) - -- [ ] **Step 1: 创建 API 客户端** - -```typescript -// src/api/client.ts -import { Base } from '@/types/openapi'; - -const BASE_URL = '/app-api/muse'; - -class ApiError extends Error { - constructor(public code: string, message: string, public status: number) { - super(message); - } -} - -export async function request( - path: string, - options: RequestInit = {} -): Promise { - const response = await fetch(`${BASE_URL}${path}`, { - headers: { - 'Content-Type': 'application/json', - 'X-API-Version': '1', - ...options.headers, - }, - ...options, - }); - - const body: Base.CommonResult = await response.json(); - - if (body.code !== 0) { - throw new ApiError(String(body.code), body.msg, response.status); - } - - return body.data as T; -} - -export const api = { - get: (path: string) => request(path), - post: (path: string, data?: unknown) => - request(path, { - method: 'POST', - body: data ? JSON.stringify(data) : undefined, - }), - put: (path: string, data?: unknown) => - request(path, { - method: 'PUT', - body: data ? JSON.stringify(data) : undefined, - }), - delete: (path: string) => request(path, { method: 'DELETE' }), -}; -``` - -- [ ] **Step 2: 复制类型包** - -```bash -cp docs/api-contracts/generated/typescript/*.ts muse-studio/src/types/ -``` - -- [ ] **Step 3: 编写类型入口** - -```typescript -// src/types/openapi.ts -export type * as Base from './base'; -export type * as Content from './content'; -export type * as AI from './ai'; -export type * as Knowledge from './knowledge'; -export type * as Market from './market'; -export type * as Account from './account'; -export type * as Meta from './meta'; -``` - -- [ ] **Step 4: 提交** - -```bash -git add -A && git commit -m "feat(api): 集成 API 客户端 + OpenAPI 类型包" -``` - -### Task 2.2: MSW Mock API 配置 - -**文件:** -- 创建: `src/api/mocks/handlers/` 下各模块 handler - -- [ ] **Step 1: 安装 MSW** - -```bash -pnpm add -D msw@2 -npx msw init public/ --save -``` - -- [ ] **Step 2: 编写 Content Mock Handlers** - -```typescript -// src/test/mocks/handlers/content.ts -import { http, HttpResponse } from 'msw'; - -export const contentHandlers = [ - // GET /app-api/muse/works — 作品列表 - http.get('/app-api/muse/works', ({ request }) => { - const url = new URL(request.url); - const pageNo = parseInt(url.searchParams.get('pageNo') || '1'); - return HttpResponse.json({ - code: 0, - msg: 'success', - data: { - total: 3, - pageNo, - pageSize: 20, - list: [ - { - id: '1', - title: '星海迷途', - status: 'active', - genre: '科幻', - wordCount: 52300, - chapterCount: 12, - updatedAt: '2026-05-20T10:30:00Z', - }, - { - id: '2', - title: '长安旧事', - status: 'draft', - genre: '历史', - wordCount: 12800, - chapterCount: 3, - updatedAt: '2026-05-24T08:00:00Z', - }, - { - id: '3', - title: '深渊笔记', - status: 'active', - genre: '悬疑', - wordCount: 89500, - chapterCount: 24, - updatedAt: '2026-05-23T18:00:00Z', - }, - ], - }, - }); - }), - - // POST /app-api/muse/works — 创建作品 - http.post('/app-api/muse/works', async ({ request }) => { - const body = await request.json(); - const work = { - id: crypto.randomUUID(), - title: (body as any).title || '未命名作品', - description: (body as any).description || '', - status: 'draft', - genre: (body as any).genre || '', - wordCount: 0, - chapterCount: 0, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }; - return HttpResponse.json({ code: 0, msg: 'success', data: work }, { status: 201 }); - }), - - // GET /app-api/muse/works/:workId — 作品详情 - http.get('/app-api/muse/works/:workId', ({ params }) => { - return HttpResponse.json({ - code: 0, msg: 'success', - data: { - id: params.workId, - title: '星海迷途', - description: '人类在星际探索中...', - status: 'active', - genre: '科幻', - wordCount: 52300, - chapterCount: 12, - createdAt: '2026-05-01T00:00:00Z', - updatedAt: '2026-05-20T10:30:00Z', - }, - }); - }), -]; - -// src/api/mocks/handlers/ai.ts -import { http, HttpResponse } from 'msw'; - -export const aiHandlers = [ - // Mock AI SSE 流式生成接口 - http.get('/app-api/muse/ai/stream', () => { - const encoder = new TextEncoder(); - const stream = new ReadableStream({ - async start(controller) { - const chunks = ['宇宙', '深处', '闪烁着', '未知的', '微光,', '那是', '文明的', '遗迹。']; - for (const chunk of chunks) { - await new Promise((resolve) => setTimeout(resolve, 100)); // 模拟 Token 渲染间隔 - controller.enqueue( - encoder.encode(`data: ${JSON.stringify({ type: 'chunk', content: chunk, sequenceNo: chunks.indexOf(chunk) })}\n\n`) - ); - } - controller.enqueue( - encoder.encode(`data: ${JSON.stringify({ type: 'done', generationId: 'g-mock-123', candidateId: 'c-mock-456' })}\n\n`) - ); - controller.close(); - }, - }); - - return new HttpResponse(stream, { - headers: { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', - }, - }); - }), -]; -``` - -- [ ] **Step 3: 配置 MSW 入口** - -```typescript -// src/api/mocks/browser.ts -import { setupWorker } from 'msw/browser'; -import { contentHandlers } from './handlers/content'; -import { aiHandlers } from './handlers/ai'; -import { knowledgeHandlers } from './handlers/knowledge'; -import { marketHandlers } from './handlers/market'; -import { accountHandlers } from './handlers/account'; - -export const worker = setupWorker( - ...contentHandlers, - ...aiHandlers, - ...knowledgeHandlers, - ...marketHandlers, - ...accountHandlers -); -``` - -- [ ] **Step 4: 在开发环境启用 MSW** - -```tsx -// src/main.tsx 开头追加 -if (import.meta.env.DEV) { - const { worker } = await import('./api/mocks/browser'); - await worker.start({ onUnhandledRequest: 'bypass' }); -} -``` - -- [ ] **Step 5: 提交** - -```bash -git add -A && git commit -m "feat(mock): 配置 MSW Mock API(Content handlers)" -``` - -### Task 2.3: SSE 双通道客户端 - -**文件:** -- 创建: `src/lib/sse.ts` - -```typescript -// src/lib/sse.ts - -/** - * Muse SSE 双通道设计: - * 1. AI stream: 独立连接,用于 AI 生成实时流 - * 2. Event stream: 长连接,用于事件通知,支持 lastEventId 续传 - */ - -export type SSEEventHandler = { - onChunk?: (data: { content: string; sequenceNo: number }) => void; - onQualityCheck?: (data: { dimension: string; score: number; passed: boolean }) => void; - onDone?: (data: { generationId: string; candidateId: string }) => void; - onError?: (data: { code: string; message: string }) => void; -}; - -export function connectAIStream( - url: string, - handlers: SSEEventHandler -): AbortController { - const controller = new AbortController(); - - fetch(url, { - headers: { - 'Accept': 'text/event-stream', - 'X-API-Version': '1', - }, - signal: controller.signal, - }).then(async (response) => { - const reader = response.body!.getReader(); - const decoder = new TextDecoder(); - let buffer = ''; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split('\n'); - buffer = lines.pop() || ''; - - for (const line of lines) { - if (line.startsWith('data: ')) { - const data = JSON.parse(line.slice(6)); - switch (data.type) { - case 'chunk': handlers.onChunk?.(data); break; - case 'quality_check': handlers.onQualityCheck?.(data); break; - case 'done': handlers.onDone?.(data); break; - case 'error': handlers.onError?.(data); break; - } - } - } - } - }).catch((err) => { - if (err.name !== 'AbortError') { - handlers.onError?.({ code: 'SSE_ERROR', message: err.message }); - } - }); - - return controller; -} - -export function connectEventStream( - lastEventId?: string -): { close: () => void; on: (event: string, handler: (data: unknown) => void) => void } { - const eventSource = new EventSource( - `/app-api/muse/events${lastEventId ? `?lastEventId=${lastEventId}` : ''}` - ); - - return { - close: () => eventSource.close(), - on: (event, handler) => { - eventSource.addEventListener(event, (e) => { - handler(JSON.parse(e.data)); - }); - }, - }; -} -``` - -- [ ] **Step: 提交** - -```bash -git add -A && git commit -m "feat(sse): 实现 SSE 双通道客户端(AI stream + Event stream)" -``` - -### Task 2.4: IndexedDB 持久化层 - -**文件:** -- 创建: `src/lib/indexed-db.ts` - -```typescript -// src/lib/indexed-db.ts -const DB_NAME = 'muse-studio'; -const BLOCK_STORE = 'block-drafts'; -const DB_VERSION = 1; - -function openDB(): Promise { - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); - request.onupgradeneeded = () => { - const db = request.result; - if (!db.objectStoreNames.contains(BLOCK_STORE)) { - db.createObjectStore(BLOCK_STORE, { keyPath: 'blockId' }); - } - }; - request.onsuccess = () => resolve(request.result); - request.onerror = () => reject(request.error); - }); -} - -export async function saveBlockDraft(blockId: string, content: Record): Promise { - const db = await openDB(); - const tx = db.transaction(BLOCK_STORE, 'readwrite'); - tx.objectStore(BLOCK_STORE).put({ blockId, content, savedAt: Date.now() }); -} - -export async function getBlockDraft(blockId: string): Promise | null> { - const db = await openDB(); - const tx = db.transaction(BLOCK_STORE, 'readonly'); - return new Promise((resolve, reject) => { - const req = tx.objectStore(BLOCK_STORE).get(blockId); - req.onsuccess = () => resolve(req.result?.content ?? null); - req.onerror = () => reject(req.error); - }); -} - -export async function removeBlockDraft(blockId: string): Promise { - const db = await openDB(); - const tx = db.transaction(BLOCK_STORE, 'readwrite'); - tx.objectStore(BLOCK_STORE).delete(blockId); -} -``` - -- [ ] **Step: 提交** - -```bash -git add -A && git commit -m "feat(storage): 实现 IndexedDB 持久化层(Block draft 安全网)" -``` - -### Task 2.5: Zustand 状态管理 - -**文件:** -- 创建: `src/stores/editorStore.ts` -- 创建: `src/stores/uiStore.ts` - -```typescript -// src/stores/editorStore.ts -import { create } from 'zustand'; - -interface EditorState { - activeBlockId: string | null; - isDirty: boolean; - lastSavedAt: Date | null; - setActiveBlock: (blockId: string | null) => void; - markDirty: () => void; - markSaved: () => void; -} - -export const useEditorStore = create((set) => ({ - activeBlockId: null, - isDirty: false, - lastSavedAt: null, - setActiveBlock: (blockId) => set({ activeBlockId: blockId, isDirty: false }), - markDirty: () => set({ isDirty: true }), - markSaved: () => set({ isDirty: false, lastSavedAt: new Date() }), -})); -``` - -```typescript -// src/stores/uiStore.ts -import { create } from 'zustand'; - -interface UIState { - sidebarCollapsed: boolean; - candidatePanelOpen: boolean; - toggleSidebar: () => void; - toggleCandidatePanel: () => void; -} - -export const useUIStore = create((set) => ({ - sidebarCollapsed: false, - candidatePanelOpen: false, - toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })), - toggleCandidatePanel: () => set((s) => ({ candidatePanelOpen: !s.candidatePanelOpen })), -})); -``` - -- [ ] **Step: 提交** - -```bash -git add -A && git commit -m "feat(state): 配置 Zustand store(编辑器状态 + UI 状态)" -``` - ---- - -## Step 3: 功能域开发(按优先级) - -### Feature 1: 写作台(核心路径) - -**优先级 1 — 编辑器集成 + AI 生成 + 候选面板** - -- [ ] **Task 3.1: Tiptap 编辑器集成** - -文件: `src/features/editor/components/MuseEditor.tsx` - -```tsx -import { useEditor, EditorContent } from '@tiptap/react'; -import StarterKit from '@tiptap/starter-kit'; -import Placeholder from '@tiptap/extension-placeholder'; -import { useEffect, useCallback } from 'react'; -import { useEditorStore } from '@/stores/editorStore'; -import { saveBlockDraft, getBlockDraft, removeBlockDraft } from '@/lib/indexed-db'; -import { debounce } from '@/lib/debounce'; -import { api } from '@/api/client'; - -interface MuseEditorProps { - blockId: string; - initialContent?: unknown; - revision: number; -} - -export default function MuseEditor({ blockId, initialContent, revision }: MuseEditorProps) { - const { markDirty, markSaved } = useEditorStore(); - - const editor = useEditor({ - extensions: [ - StarterKit, - Placeholder.configure({ placeholder: '开始创作...' }), - ], - content: initialContent as any, - onUpdate: ({ editor }) => { - markDirty(); - // 自动保存到 IndexedDB(debounce 2s) - debouncedSave(blockId, editor.getJSON()); - }, - autofocus: 'end', - }); - - // 自动保存到后端 + IndexedDB 安全网(debounce 2s) - const debouncedSave = useCallback( - debounce(async (blockId: string, content: unknown) => { - // 1. 先保存到 IndexedDB(即时,安全网) - await saveBlockDraft(blockId, content as Record); - // 2. 再调用 API(乐观锁) - try { - const result = await api.put<{ newRevision: number }>( - `/app-api/muse/blocks/${blockId}`, - { content: JSON.stringify(content), expectedRevision: revision } - ); - await removeBlockDraft(blockId); - markSaved(); - } catch (err) { - // API 失败:IndexedDB 已保存,下次恢复 - } - }, 2000), - [revision] - ); - - // 恢复 IndexedDB 草稿 - useEffect(() => { - getBlockDraft(blockId).then((draft) => { - if (draft !== null && draft !== undefined && editor) { - editor.commands.setContent(draft as any); - } - }); - }, [blockId, editor]); - - return ( -
- -
- ); -} -``` - -**关键共享模块:** `src/lib/debounce.ts` - -```typescript -// src/lib/debounce.ts — 通用去抖工具函数 -export function debounce any>(fn: T, ms: number): T { - let timer: ReturnType; - return ((...args: any[]) => { - clearTimeout(timer); - timer = setTimeout(() => fn(...args), ms); - }) as T; -} -``` - -- [ ] **Task 3.2: AI 生成面板** — 调用 AI SSE stream,展示实时流式输出 - -- [ ] **Task 3.3: 候选面板** — 展示候选列表,支持接受/拒绝,显示 diff - -- [ ] **Task 3.4: 编程式提交** - -```bash -git add -A && git commit -m "feat(editor): Tiptap 编辑器集成 + IndexedDB 安全网 + 自动保存" -``` - -### Feature 2: 我的作品 + 工作台 - -**涉及的 API 端点:** `GET /works`、`POST /works`、`GET /works/:workId`、`DELETE /works/:workId`、`GET /works/:workId/chapters`、`POST /works/:workId/chapters`、`PATCH /works/:workId/chapters/:chapterId`、`DELETE /works/:workId/chapters/:chapterId` - -**文件:** -- 创建: `src/features/editor/components/WorkListPage.tsx` -- 创建: `src/features/editor/components/CreateWorkModal.tsx` -- 创建: `src/features/editor/components/ChapterPanel.tsx` -- 创建: `src/features/editor/hooks/useWorks.ts` - -**关键组件与 Props:** - -| 组件 | Props | 说明 | -|------|-------|------| -| WorkListPage | — | 作品列表页,使用 TanStack Query 拉取列表,展示卡片网格 | -| CreateWorkModal | `open: boolean; onClose: () => void; onCreated: (work: WorkVO) => void` | 创建作品弹窗,React Hook Form + Zod 校验 | -| ChapterPanel | `workId: string; chapters: ChapterVO[]; activeChapterId?: string; onSelect: (chapterId: string) => void; onDelete: (chapterId: string) => void` | 章节管理侧面板,支持拖拽排序 | - -**代码示例 — `useWorks` Hook:** - -```typescript -// src/features/editor/hooks/useWorks.ts -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { api } from '@/api/client'; -import type { WorkVO, WorkCreateDTO } from '@/types/openapi'; - -export function useWorks(pageNo = 1, pageSize = 20) { - return useQuery({ - queryKey: ['works', pageNo, pageSize], - queryFn: () => api.get<{ total: number; list: WorkVO[] }>(`/works?pageNo=${pageNo}&pageSize=${pageSize}`), - }); -} - -export function useCreateWork() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (dto: WorkCreateDTO) => api.post('/works', dto), - onSuccess: () => { qc.invalidateQueries({ queryKey: ['works'] }); }, - }); -} - -export function useDeleteWork() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (workId: string) => api.delete(`/works/${workId}`), - onSuccess: () => { qc.invalidateQueries({ queryKey: ['works'] }); }, - }); -} -``` - -**测试场景:** -1. 列表页加载展示 3 个 Mock 作品卡片 -2. 点击「创建作品」按钮,弹窗打开,填写表单,提交后列表刷新 -3. 删除按钮点击后弹出确认框,确认后列表移除该条目 -4. 点击作品卡片进入章节面板,展示章节列表 - -- [ ] **Task: 提交** - -```bash -git add -A && git commit -m "feat(workspace): 作品列表/创建/删除 + 章节管理面板" -``` - ---- - -### Feature 3: 知识库工作台 - -**涉及的 API 端点:** `GET /knowledge/:workId/entities`、`POST /knowledge/:workId/entities`、`PATCH /knowledge/:workId/entities/:entityId`、`GET /knowledge/:workId/drafts`、`POST /knowledge/:workId/drafts/:draftId/confirm`、`POST /knowledge/:workId/drafts/:draftId/reject`、`GET /knowledge/:workId/graph` - -**文件:** -- 创建: `src/features/knowledge/components/EntityList.tsx` -- 创建: `src/features/knowledge/components/DraftPanel.tsx` -- 创建: `src/features/knowledge/components/GraphStub.tsx` -- 创建: `src/features/knowledge/hooks/useKnowledge.ts` - -**关键组件与 Props:** - -| 组件 | Props | 说明 | -|------|-------|------| -| EntityList | `workId: string` | 知识实体列表,支持按类型筛选、搜索关键字 | -| DraftPanel | `workId: string; drafts: KnowledgeDraftVO[]; onConfirm: (draftId: string) => void; onReject: (draftId: string, reason: string) => void` | 知识草稿确认/拒绝面板,拒绝需填写原因 | -| GraphStub | `workId: string; width?: number; height?: number` | 知识图谱可视化占位,后续接入 RAGFlow GraphRAG API | - -**代码示例 — `useKnowledgeDrafts` Hook:** - -```typescript -// src/features/knowledge/hooks/useKnowledge.ts -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { api } from '@/api/client'; -import type { KnowledgeDraftVO, KnowledgeEntityVO } from '@/types/openapi'; - -export function useKnowledgeEntities(workId: string) { - return useQuery({ - queryKey: ['knowledge', workId, 'entities'], - queryFn: () => api.get(`/knowledge/${workId}/entities`), - enabled: !!workId, - }); -} - -export function useKnowledgeDrafts(workId: string) { - return useQuery({ - queryKey: ['knowledge', workId, 'drafts'], - queryFn: () => api.get(`/knowledge/${workId}/drafts`), - enabled: !!workId, - }); -} - -export function useConfirmDraft() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ workId, draftId }: { workId: string; draftId: string }) => - api.post(`/knowledge/${workId}/drafts/${draftId}/confirm`), - onSuccess: (_, vars) => { - qc.invalidateQueries({ queryKey: ['knowledge', vars.workId] }); - }, - }); -} - -export function useRejectDraft() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ workId, draftId, reason }: { workId: string; draftId: string; reason: string }) => - api.post(`/knowledge/${workId}/drafts/${draftId}/reject`, { reason }), - onSuccess: (_, vars) => { - qc.invalidateQueries({ queryKey: ['knowledge', vars.workId] }); - }, - }); -} -``` - -**测试场景:** -1. 实体列表加载展示,按类型(人物/地点/事件)筛选 -2. 草稿面板展示待确认列表,点击「确认」更新状态 -3. 点击「拒绝」,弹出拒绝原因输入框,提交后刷新 -4. 知识图谱占位区域正确渲染,显示 "Graph visualization coming soon" 占位文本 - -- [ ] **Task: 提交** - -```bash -git add -A && git commit -m "feat(knowledge): 知识实体列表 + 草稿确认/拒绝面板 + 图谱占位" -``` - ---- - -### Feature 4: 智能体工作台 - -**涉及的 API 端点:** `GET /agents`、`POST /agents`、`GET /agents/:agentId`、`PATCH /agents/:agentId`、`DELETE /agents/:agentId`、`GET /agents/:agentId/slots`、`PATCH /agents/:agentId/slots` - -**文件:** -- 创建: `src/features/agent/components/AgentList.tsx` -- 创建: `src/features/agent/components/AgentCreateForm.tsx` -- 创建: `src/features/agent/components/SlotBindingPanel.tsx` -- 创建: `src/features/agent/hooks/useAgents.ts` - -**关键组件与 Props:** - -| 组件 | Props | 说明 | -|------|-------|------| -| AgentList | `onSelect: (agentId: string) => void` | 智能体列表卡片,展示名称、模型、状态 | -| AgentCreateForm | `onCreated: (agent: AgentVO) => void; onCancel: () => void` | 创建智能体表单:名称、system prompt、模型选择、温度等 | -| SlotBindingPanel | `agentId: string; slots: AgentSlotVO[]; onBind: (slotId: string, toolId: string) => void` | 槽位-工具绑定面板,支持搜索可用工具 | - -**代码示例 — `AgentList` 组件:** - -```tsx -// src/features/agent/components/AgentList.tsx -import { useAgents } from '../hooks/useAgents'; - -interface AgentListProps { - onSelect: (agentId: string) => void; -} - -export default function AgentList({ onSelect }: AgentListProps) { - const { data, isLoading, error } = useAgents(); - - if (isLoading) return
加载中...
; - if (error) return
加载失败
; - - return ( -
- {data?.list.map((agent) => ( -
onSelect(agent.id)} - > -

{agent.name}

-

{agent.model}

- - {agent.status === 'active' ? '运行中' : '已停用'} - -
- ))} -
- ); -} -``` - -**测试场景:** -1. 智能体列表加载展示,每个卡片显示名称、模型、状态标签 -2. 点击「创建智能体」打开表单,填写 system prompt、选择模型,提交后列表刷新 -3. 点击卡片进入槽位绑定面板,为智能体绑定/解绑工具槽位 -4. 删除智能体,确认后列表中移除 - -- [ ] **Task: 提交** - -```bash -git add -A && git commit -m "feat(agent): 智能体列表 + 创建表单 + 槽位绑定面板" -``` - ---- - -### Feature 5: 市场 + 个人中心 - -**涉及的 API 端点:** `GET /market/assets`、`GET /market/assets/:assetId`、`POST /market/assets/:assetId/install`、`GET /account/profile`、`PATCH /account/profile`、`GET /account/usage-stats`、`GET /account/entitlements` - -**文件:** -- 创建: `src/features/market/components/MarketBrowse.tsx` -- 创建: `src/features/market/hooks/useMarket.ts` -- 创建: `src/features/account/components/PersonalCenter.tsx` -- 创建: `src/features/account/components/UsageStats.tsx` -- 创建: `src/features/account/hooks/useAccount.ts` - -**关键组件与 Props:** - -| 组件 | Props | 说明 | -|------|-------|------| -| MarketBrowse | `onInstall: (assetId: string) => void` | 市场资源浏览页,支持按类型/标签筛选,展示资产卡片网格 | -| PersonalCenter | — | 个人中心:头像、昵称、配额详情、使用统计概览 | -| UsageStats | `stats: UsageStatsVO` | 用量统计面板:Token 消耗、存储用量、API 调用次数 | - -**代码示例 — `UsageStats` 组件:** - -```tsx -// src/features/account/components/UsageStats.tsx -import type { UsageStatsVO } from '@/types/openapi'; - -interface UsageStatsProps { - stats: UsageStatsVO; -} - -export default function UsageStats({ stats }: UsageStatsProps) { - const items: { label: string; value: string; used: number; total: number }[] = [ - { label: 'Token 消耗', value: `${(stats.tokenUsed / 1000).toFixed(1)}K`, used: stats.tokenUsed, total: stats.tokenQuota }, - { label: '存储用量', value: `${(stats.storageUsed / 1024 / 1024).toFixed(0)} MB`, used: stats.storageUsed, total: stats.storageQuota }, - { label: 'API 调用', value: `${stats.apiCalls}`, used: stats.apiCalls, total: stats.apiQuota }, - ]; - - return ( -
- {items.map((item) => ( -
-
- {item.label} - {item.value} / {item.total > 0 ? item.total : '无限制'} -
-
-
0 ? `${Math.min((item.used / item.total) * 100, 100)}%` : '0%' }} - /> -
-
- ))} -
- ); -} -``` - -**测试场景:** -1. 市场页面加载展示资产卡片,筛选器切换有效 -2. 点击「安装」按钮,调用安装 API,按钮变为「已安装」 -3. 个人中心显示用户信息、配额用量进度条 -4. 用量统计各指标数据正确渲染,百分比进度条与实际数据成比例 - -- [ ] **Task: 提交** - -```bash -git add -A && git commit -m "feat(market+account): 市场资源浏览 + 个人中心 + 用量统计" -``` - ---- - -## 完成标准 - -- [ ] 6 个功能域页面可交互 -- [ ] 组件单元测试覆盖率 ≥75%(Vitest + Testing Library) -- [ ] Hook 测试覆盖率 ≥80% -- [ ] E2E 测试覆盖核心写作流程(Playwright) -- [ ] ESLint + Prettier + TypeScript 类型检查通过 -- [ ] Vite build 成功 diff --git a/docs/superpowers/plans/2026-05-24-P3-muse-admin.md b/docs/superpowers/plans/2026-05-24-P3-muse-admin.md deleted file mode 100644 index f7411dcc..00000000 --- a/docs/superpowers/plans/2026-05-24-P3-muse-admin.md +++ /dev/null @@ -1,436 +0,0 @@ -# P3: muse-admin 管理端搭建 — 执行计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. - -**目标:** 在 Vben Admin fork 基础上搭建 muse-admin,实现 MetaSchema 管理、系统治理、AI 配置、市场治理、全局知识管理、Account/New-API、任务监控、日志审计功能域。 - -**架构:** Vue 3 + Vben Admin + TypeScript,Composition API + SFC script setup,defHttp API 集成,Vben 内置表格/表单组件复用。 - -**技术栈:** Vue 3.5, TypeScript 5, Vben Admin 5, Pinia, Vitest + Vue Test Utils + Playwright - -注: muse-admin 已 fork 完成,存在 `apps/web-antd` 和 `packages/` 结构。 - -**合同校准(2026-05-24):** - -- 以 `design-docs/产品-02B-管理员控制台功能规格.md`、`docs/dev-baseline/muse-admin/CLAUDE.md` 和 `docs/api-contracts/**/openapi.yaml` 为实现依据;本文示例代码如与上述合同冲突,以上述合同为准。 -- Muse 业务页面目录使用 `apps/web-antd/src/views/muse/**`,不是根级 `src/views/{governance,ai,...}`。 -- Account/New-API 页面只实现治理和对账所需的脱敏摘要、配额调整、网关绑定、余额查询、调用日志归属;用户侧账户完整体验仍由 `产品-02G` 承接。 -- 日志审计必须包含接口调用日志和业务审计日志两个 surface;对应契约为 `/admin-api/muse/audit/api-logs`、`/admin-api/muse/audit/api-logs/{logId}`、`/admin-api/muse/audit/business-events`、`/admin-api/muse/audit/business-events/{eventId}`。 - ---- - -## Step 1: 工程调整 - -### Task 1.1: 业务目录结构建立 - -**仓库路径:** `muse-admin/` - -- [ ] **Step 1: 创建业务模块目录** - -```bash -mkdir -p muse-admin/apps/web-antd/src/views/muse/{governance,ai,knowledge,market,account,newapi,jobs,audit} -mkdir -p muse-admin/apps/web-antd/src/api/muse/{governance,ai,knowledge,market,account,newapi,jobs,audit} -``` - -- [ ] **Step 2: 检查现有结构** - -```bash -ls muse-admin/apps/web-antd/src/views/ -ls muse-admin/packages/@core/ -``` - -- [ ] **Step 3: 提交** - -```bash -git add muse-admin/apps/web-antd/src/views/muse/ muse-admin/apps/web-antd/src/api/muse/ -git commit -m "feat(struct): 建立管理端业务模块目录" -``` - -### Task 1.2: API 类型集成 - -- [ ] **Step 1: 复制 OpenAPI 生成的类型** - -```bash -cp docs/api-contracts/generated/typescript/*.ts muse-admin/apps/web-antd/src/api/types/ -``` - -- [ ] **Step 2: 编写 API 客户端** - -```typescript -// muse-admin/apps/web-antd/src/api/muse/client.ts -import { requestClient } from '#/api/request'; - -const BASE = '/admin-api/muse'; - -export const museAdminApi = { - get: (url: string, params?: Record) => - requestClient.get(`${BASE}${url}`, { params }), - - post: (url: string, data?: unknown) => - requestClient.post(`${BASE}${url}`, data), - - put: (url: string, data?: unknown) => - requestClient.put(`${BASE}${url}`, data), - - delete: (url: string) => - requestClient.delete(`${BASE}${url}`), -}; -``` - -- [ ] **Step 3: 提交** - -```bash -git add -A && git commit -m "feat(api): 集成管理端 API 客户端 + OpenAPI 类型包" -``` - ---- - -## Step 2: 管理页面开发(按优先级) - -### Task 2.1: MetaSchema 管理 — 列表页 - -**文件:** -- 创建: `apps/web-antd/src/views/governance/MetaSchemaList.vue` -- 创建: `apps/web-antd/src/api/muse/governance/meta-schema.ts` - -- [ ] **Step 1: 编写 API service** - -```typescript -// apps/web-antd/src/api/muse/governance/meta-schema.ts -import { museAdminApi } from '../client'; - -export interface MetaSchemaSummary { - schemaKey: string; - displayName: string; - fieldType: string; - scope: string; - activeVersion: number; - status: string; -} - -export function listMetaSchemas(params: { - pageNo: number; - pageSize: number; - scope?: string; -}) { - return museAdminApi.get<{ - total: number; - list: MetaSchemaSummary[]; - }>('/governance/meta-schemas', params); -} - -export function getMetaSchema(schemaKey: string) { - return museAdminApi.get(`/governance/meta-schemas/${schemaKey}`); -} - -export function createMetaSchemaDraft(schemaKey: string, data: any) { - return museAdminApi.post(`/governance/meta-schemas/${schemaKey}/drafts`, data); -} - -export function publishMetaSchemaDraft(schemaKey: string, draftVersion: number, data: any) { - return museAdminApi.post( - `/governance/meta-schemas/${schemaKey}/drafts/${draftVersion}/publish`, - data - ); -} -``` - -- [ ] **Step 2: 编写列表页** - -```vue - - - - -``` - -- [ ] **Step 3: 编写测试** - -```typescript -// apps/web-antd/src/views/governance/__tests__/MetaSchemaList.spec.ts -import { describe, it, expect, vi } from 'vitest'; -import { mount } from '@vue/test-utils'; -import MetaSchemaList from '../MetaSchemaList.vue'; - -vi.mock('#/api/muse/governance/meta-schema', () => ({ - listMetaSchemas: vi.fn().mockResolvedValue({ - total: 2, - list: [ - { schemaKey: 'character_name', displayName: '角色名', fieldType: 'string', scope: 'work', activeVersion: 3, status: 'active' }, - { schemaKey: 'world_setting', displayName: '世界设定', fieldType: 'text', scope: 'global', activeVersion: 1, status: 'active' }, - ], - }), -})); - -describe('MetaSchemaList', () => { - it('should render table', () => { - const wrapper = mount(MetaSchemaList); - expect(wrapper.find('.p-4').exists()).toBe(true); - }); -}); -``` - -- [ ] **Step 4: 提交** - -```bash -git add -A && git commit -m "feat(governance): 实现 MetaSchema 列表页(Vben Table + API 集成)" -``` - -### Task 2.2: MetaSchema 管理 — 详情/草稿编辑页 - -实现 MetaSchema 详情页,包含: -- 版本历史时间线 -- 当前 active 版本字段展示 -- 草稿编辑 + 校验 + 影响预览 + 发布流程 -- 灰度规则配置 - -使用 Vben 内置表单组件 + 动态表单渲染。 - -### Task 2.3-2.5: 其余管理页面 - -按优先级依次实现: - -**Task 2.3: 系统治理** — 用户管理(复用 Yudao system 模块)、角色权限配置、审计日志查看 - -**Task 2.4: AI 配置** — Prompt 模板管理、质量门控维度配置、保护节点注册管理、Tool Grant 配置 - -**Task 2.5: 市场治理 + 全局知识管理** — 资产审核(上架/驳回)、下架/召回、申诉处理、全局知识库维护、知识来源管理 - -每个页面遵循相同模式:API service → 组件实现 → 测试 → 提交。 - ---- - -### Task 2.6: Account 管理页面 - -**文件:** -- 创建: `apps/web-antd/src/api/muse/account/index.ts` - -**页面清单:** - -| 页面 | 路由 | 说明 | -|------|------|------| -| 用户权益列表 | `/account/entitlements` | 表格:用户 + 权益类型 + 上限 + 已用 + 过期时间 | -| 配额调整表单 | `/account/quota-adjustments` | 弹窗:用户 + 资源类型 + 调整量 + 原因 + commandId | -| New-API 绑定状态 | `/account/new-api-bindings` | 表格:用户 + 绑定状态 + 同步状态 | -| 购买/使用记录 | `/account/records` | 只读脱敏治理摘要;不实现 `产品-02G` 的用户侧完整账户体验 | - -**API endpoints:** - -``` -GET /admin-api/muse/account/users # 用户列表 -GET /admin-api/muse/account/users/{userId}/entitlements # 用户权益 -POST /admin-api/muse/account/users/{userId}/quota-adjustments # 配额调整 -GET /admin-api/muse/account/new-api-bindings # New-API 绑定状态 -GET /admin-api/muse/account/usage-records # 脱敏用量摘要 -GET /admin-api/muse/account/purchase-records # 脱敏购买摘要 -``` - -**代码示例 — API service:** - -```typescript -// apps/web-antd/src/api/muse/account/index.ts -import { museAdminApi } from '../client'; - -export interface UserEntitlement { - userId: string; - entitlementType: string; - limit: number; - used: number; - expiry: string; -} - -export interface QuotaAdjustment { - userId: string; - resourceType: string; - adjustmentAmount: number; - reason: string; - commandId: string; -} - -export function getAccountUsers(params: { pageNo: number; pageSize: number }) { - return museAdminApi.get<{ total: number; list: any[] }>('/account/users', params); -} - -export function getUserEntitlements(userId: string) { - return museAdminApi.get(`/account/users/${userId}/entitlements`); -} - -export function adjustQuota(userId: string, data: QuotaAdjustment) { - return museAdminApi.post(`/account/users/${userId}/quota-adjustments`, data); -} - -export function getNewApiBindings(params: { pageNo: number; pageSize: number }) { - return museAdminApi.get<{ total: number; list: any[] }>('/account/new-api-bindings', params); -} -``` - -### Task 2.7: 任务监控页面 - -**文件:** -- 创建: `apps/web-antd/src/api/muse/jobs/index.ts` - -**页面清单:** - -| 页面 | 路由 | 说明 | -|------|------|------| -| 任务列表 | `/jobs` | 表格:job ID + 类型 + 状态 + 创建时间 + 重试次数 | -| 任务详情 | `/jobs/:jobId` | 状态 + 失败原因 + 重试/取消按钮 | -| 源事件列表 | `/jobs/source-events` | 表格:事件类型 + 来源 + 状态 + 影响摘要 | - -**API endpoints:** - -``` -GET /admin-api/muse/jobs # 任务列表 -GET /admin-api/muse/jobs/{jobId} # 任务详情 -POST /admin-api/muse/jobs/{jobId}/retry # 重试任务 -GET /admin-api/muse/source-events # 源事件列表 -``` - -**代码示例 — API service:** - -```typescript -// apps/web-antd/src/api/muse/jobs/index.ts -import { museAdminApi } from '../client'; - -export interface JobSummary { - jobId: string; - jobType: string; - status: string; - createdAt: string; - retryCount: number; -} - -export interface JobDetail extends JobSummary { - failureReason?: string; - payload?: Record; - result?: Record; -} - -export interface SourceEvent { - eventType: string; - source: string; - status: string; - impactSummary: string; - createdAt: string; -} - -export function listJobs(params: { pageNo: number; pageSize: number; status?: string }) { - return museAdminApi.get<{ total: number; list: JobSummary[] }>('/jobs', params); -} - -export function getJob(jobId: string) { - return museAdminApi.get(`/jobs/${jobId}`); -} - -export function retryJob(jobId: string) { - return museAdminApi.post(`/jobs/${jobId}/retry`); -} - -export function listSourceEvents(params: { pageNo: number; pageSize: number }) { - return museAdminApi.get<{ total: number; list: SourceEvent[] }>('/source-events', params); -} -``` - -### Task 2.8: 审计日志页面 - -**文件:** -- 创建: `apps/web-antd/src/api/muse/audit/index.ts` - -**页面清单:** - -| 页面 | 路由 | 说明 | -|------|------|------| -| 业务审计事件列表 | `/audit/business-events` | 表格:时间戳 + 操作者 + 操作 + 目标 + 结果 | -| 审计详情 | `/audit/business-events/:eventId` | 完整 before/after 快照对比 | -| 接口调用日志 | `/audit/api-logs` | 表格:请求 ID + 调用人 + 路径摘要 + 状态码 + 耗时 | -| 接口调用详情 | `/audit/api-logs/:logId` | 只展示脱敏请求/响应摘要,不展示 token、secret、完整 header 或私有正文 | - -**API endpoints:** - -``` -GET /admin-api/muse/audit/api-logs # 接口调用日志列表 -GET /admin-api/muse/audit/api-logs/{logId} # 接口调用日志详情 -GET /admin-api/muse/audit/business-events # 业务审计事件列表 -GET /admin-api/muse/audit/business-events/{eventId} # 业务审计详情 -``` - -**代码示例 — API service:** - -```typescript -// apps/web-antd/src/api/muse/audit/index.ts -import { museAdminApi } from '../client'; - -export interface BusinessAuditEvent { - eventId: string; - timestamp: string; - actor: string; - action: string; - target: string; - result: string; -} - -export interface BusinessAuditDetail extends BusinessAuditEvent { - beforeSnapshot?: Record; - afterSnapshot?: Record; -} - -export function listBusinessAuditEvents(params: { pageNo: number; pageSize: number; actor?: string; action?: string }) { - return museAdminApi.get<{ total: number; list: BusinessAuditEvent[] }>('/audit/business-events', params); -} - -export function getBusinessAuditDetail(eventId: string) { - return museAdminApi.get(`/audit/business-events/${eventId}`); -} -``` - ---- - -## Step 3: Mock API 配置 - -- [ ] **Step 1: 配置 MSW 或 Vben 内置 Mock** - -为每个 `/admin-api/muse/**` 接口提供 mock 数据,使管理端可独立开发。 - ---- - -## 完成标准 - -- [ ] 5 个功能域管理页面可交互 -- [ ] 组件单元测试覆盖率 ≥70%(Vitest + Vue Test Utils) -- [ ] E2E 测试覆盖核心管理流程(Playwright) -- [ ] ESLint + Prettier + TypeScript 类型检查通过 -- [ ] Vite build 成功 diff --git a/docs/superpowers/plans/2026-05-24-P4-集成切换.md b/docs/superpowers/plans/2026-05-24-P4-集成切换.md deleted file mode 100644 index 6727586e..00000000 --- a/docs/superpowers/plans/2026-05-24-P4-集成切换.md +++ /dev/null @@ -1,295 +0,0 @@ -# P4: 集成切换 + 质量加固 — 执行计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. - -**目标:** 按模块将前端从 Mock API 切换到真实后端 API,完成端到端联调,修复契约缺陷,实现全量质量加固(测试/CI/安全)。 - -**架构:** 按依赖顺序逐个模块切换:content+account → meta → ai+knowledge → market。每模块切换后更新 OpenAPI 契约。最后进行全量质量加固。 - -**涉及仓库:** muse-cloud, muse-studio, muse-admin, muse-design-docs - ---- - -## Phase 1: 集成切换 - -### 时间线总览 - -| 阶段 | 内容 | 周次 | -|------|------|------| -| Switch S1 | content + account 集成切换 | Week 11-12 | -| Switch S2 | meta 集成切换 | Week 13 | -| Switch S3 | ai + knowledge 集成切换 | Week 14-15 | -| Switch S4 | market 集成切换 | Week 16-17 | -| Quality G1-G4 | 质量加固 | Week 18-19 | - -### 各模块 API 数量(OpenAPI SSOT) - -| 模块 | App 接口 | Admin 接口 | 总计 | -|------|----------|-----------|------| -| Content | ~35 | ~15 | ~50 | -| AI | ~25 | ~15 | ~40 | -| Knowledge | ~34 | ~23 | ~57 | -| Market | ~21 | ~11 | ~32 | -| Account | ~20 | ~13 | ~33 | -| Meta | 0 | ~16 | ~16 | -| **总计** | **~135** | **~93** | **~228** | - -### Switch S1: content + account 集成切换 - -**前置条件检查:** -- P1 content+account API 全部集成测试通过 → 可开始 S1 - -**依赖:** muse-cloud content + account API 自测通过 - -- [ ] **Step 1: 后端契约测试通过** - -```bash -cd muse-cloud && mvn test -pl muse-module-content,muse-module-account -``` - -验证: Content (50 接口) + Account (33 接口) 全部集成测试通过。 - -- [ ] **Step 2: 移除前端 Content MSW handlers** - -```typescript -// muse-studio/src/test/mocks/browser.ts -// 注释掉 contentHandlers,保留其他模块的 Mock -export const worker = setupWorker( - // ...contentHandlers, // ← 切换真实 API - ...aiHandlers, - ...knowledgeHandlers, - ...marketHandlers, - ...accountHandlers -); -``` - -- [ ] **Step 3: 前端联调验证** - -```bash -cd muse-studio && pnpm dev -``` - -测试流程: -1. 作品列表加载正常 -2. 创建新作品 → 自动跳转 -3. 章节列表 + 创建章节 -4. Block 编辑器加载 → 自动保存 → 验证 IndexedDB 安全网 - -- [ ] **Step 4: 修正接口不一致** - -联调中发现的契约缺陷: -1. 记录具体差异(字段名/类型/状态码不一致) -2. 决定修后端还是修 OpenAPI -3. 更新 OpenAPI 契约文件 -4. 重新生成 TS 类型包 -5. 前端更新类型引用 - -- [ ] **Step 5: 冒烟测试(写作流程)** - -Playwright E2E: -```typescript -// muse-studio/e2e/writing-flow.spec.ts -import { test, expect } from '@playwright/test'; - -test('完整写作流程', async ({ page }) => { - await page.goto('http://localhost:5173'); - - // 1. 查看作品列表 - await expect(page.locator('h1')).toContainText('我的作品'); - - // 2. 创建新作品 - await page.click('button:has-text("新建作品")'); - await page.fill('input[name="title"]', 'E2E 测试作品'); - await page.click('button:has-text("创建")'); - - // 3. 进入编辑器 - await expect(page.locator('.ProseMirror')).toBeVisible(); - - // 4. 输入文本 - await page.locator('.ProseMirror').fill('第一段测试文字'); - - // 5. 等待自动保存 - await page.waitForTimeout(3000); - await expect(page.locator('[data-testid="save-indicator"]')).toContainText('已保存'); -}); -``` - -- [ ] **Step 6: Account API 切换(同时)** - -类似流程:移除 account MSW handlers → 联调验证个人中心/用量/配额 → 修正不一致 → 更新契约 - -- [ ] **Step 7: 提交契约更新** - -```bash -git add docs/api-contracts/ -git commit -m "fix(api): content+account 联调后契约修正" -``` - -- [ ] **Step 8: 更新 OpenAPI 契约(如有不一致)** - -```bash -# 编辑对应的 openapi.yaml 修正 -git add docs/api-contracts/ -git commit -m "fix(api): content+account 联调后契约修正" -# 重新生成 TypeScript 类型 -npx openapi-typescript docs/api-contracts/content/openapi.yaml --output docs/api-contracts/generated/typescript/content.ts -npx openapi-typescript docs/api-contracts/account/openapi.yaml --output docs/api-contracts/generated/typescript/account.ts -``` - -### Switch S2: meta API 集成切换 - -**前置条件检查:** -- P1 meta API 全部集成测试通过 → 可开始 S2 - -**依赖:** muse-cloud meta API 自测通过 - -1. MetaSchema 管理页面切换到真实 API -2. 验证 CRUD + 草稿/发布/版本管理流程 -3. 修正不一致,更新 OpenAPI - -- [ ] **更新 OpenAPI 契约(如有不一致)** - -```bash -# 编辑对应的 openapi.yaml 修正 -git add docs/api-contracts/ -git commit -m "fix(api): meta 联调后契约修正" -# 重新生成 TypeScript 类型 -npx openapi-typescript docs/api-contracts/meta/openapi.yaml --output docs/api-contracts/generated/typescript/meta.ts -``` - -### Switch S3: ai + knowledge API 集成切换 - -**前置条件检查:** -- P1 ai+knowledge API 全部集成测试通过 → 可开始 S3 - -**依赖:** muse-cloud ai + knowledge API 自测通过 - -1. AI 生成 SSE 流验证(断线重连、超时) -2. 候选接受/拒绝流程验证 -3. 知识库文档上传/解析/确认流程 -4. 知识绑定/解绑跨模块测试 -5. SSE 端点专项验证: - - [ ] **AI 生成 SSE 流** - - 连接 AI 生成 SSE 端点,接收完整 chunk 序列 - - 模拟断线后使用 lastEventId 重连,验证断点续传 - - 验证超时处理(服务端无响应时客户端行为) - - [ ] **事件流 SSE** - - 连接事件流端点,接收通知事件 - - 使用 lastEventId 验证断线后事件不丢失 - - 验证心跳保活机制 - - [ ] **错误状态处理** - - 网络断开时客户端降级提示 - - 服务端 5xx 错误时重试策略 - - 超时后的自动重连 -6. 修正不一致,更新 OpenAPI - -- [ ] **更新 OpenAPI 契约(如有不一致)** - -```bash -# 编辑对应的 openapi.yaml 修正 -git add docs/api-contracts/ -git commit -m "fix(api): ai+knowledge 联调后契约修正" -# 重新生成 TypeScript 类型 -npx openapi-typescript docs/api-contracts/ai/openapi.yaml --output docs/api-contracts/generated/typescript/ai.ts -npx openapi-typescript docs/api-contracts/knowledge/openapi.yaml --output docs/api-contracts/generated/typescript/knowledge.ts -``` - -### Switch S4: market API 集成切换 - -**前置条件检查:** -- P1 market API 全部集成测试通过 + S1/S2/S3 全部完成 → 可开始 S4 - -**依赖:** 前面所有模块已切换 - -1. 市场浏览/推荐验证 -2. Handoff 跨空间跳转测试 -3. 发布/审核流程验证 -4. 修正不一致,更新 OpenAPI - -- [ ] **更新 OpenAPI 契约(如有不一致)** - -```bash -# 编辑对应的 openapi.yaml 修正 -git add docs/api-contracts/ -git commit -m "fix(api): market 联调后契约修正" -# 重新生成 TypeScript 类型 -npx openapi-typescript docs/api-contracts/market/openapi.yaml --output docs/api-contracts/generated/typescript/market.ts -``` - ---- - -## Phase 2: 质量加固 - -### Quality G1: 后端全量测试 - -- [ ] **单元测试补充** - - Domain 层 edge case 覆盖 - - Service 层 mock 测试(JUnit 5 + Mockito) - - 目标: Domain ≥90%, Application ≥80% - -- [ ] **集成测试补充** - - Testcontainers + PostgreSQL - - 跨模块事务边界测试(内容保存 → 知识提取 → AI 候选) - - 并发冲突测试(乐观锁) - -- [ ] **契约测试** - - Spring Cloud Contract 或 Pact - - 验证 API 响应与 OpenAPI 定义一致 - -### Quality G2: 前端全量测试 - -- [ ] **muse-studio 单元测试补充** - - 组件测试 (Testing Library) ≥75% - - Hook 测试 ≥80% - -- [ ] **muse-admin 单元测试补充** - - 组件测试 (Vue Test Utils) ≥70% - - Composable 测试 ≥75% - -- [ ] **E2E 测试** - - Playwright 覆盖核心流程 - - studio: 写作流程、作品管理、知识确认 - - admin: MetaSchema 管理、资产审核 - -### Quality G3: CI/CD - -- [ ] **后端 CI 流水线** (GitHub Actions / Jenkins) - ```yaml - # .github/workflows/muse-cloud.yml - steps: - - compile (Maven) - - unit-test (JUnit + Mockito) - - integration-test (Testcontainers) - - lint (Checkstyle + SpotBugs) - - build-image (Docker) - ``` - -- [ ] **前端 CI 流水线** - ```yaml - # .github/workflows/muse-studio.yml - steps: - - typecheck (tsc --noEmit) - - lint (ESLint + Prettier) - - test (Vitest) - - build (Vite) - ``` - -### Quality G4: 安全加固 - -- [ ] **依赖漏洞扫描** (npm audit, OWASP Dependency Check) -- [ ] **XSS 防护 + CSP 头配置** -- [ ] **敏感数据脱敏**(日志中 token/密码脱敏) -- [ ] **SSRF 防护**(Knowledge linkUrl allowlist + 私网拦截) -- [ ] **AI 安全**(Prompt injection 防护、输出合规检查) - ---- - -## 完成标准 - -- [ ] 6 个模块全部切换真实 API -- [ ] 全量端到端冒烟测试通过 -- [ ] 后端测试覆盖率达标: Domain ≥90%, Application ≥80% -- [ ] 前端测试覆盖率达标: Studio ≥75%, Admin ≥70% -- [ ] CI 流水线全部通过 -- [ ] OpenAPI 契约与实现一致 -- [ ] 安全扫描无 Critical/High 新发现 \ No newline at end of file diff --git a/docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md b/docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md deleted file mode 100644 index 09415dbf..00000000 --- a/docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md +++ /dev/null @@ -1,669 +0,0 @@ -# P1R-0 Baseline Gate Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build a repeatable P1R API coverage gate that audits OpenAPI operations against current `muse-cloud/` implementation and produces a machine-readable completion matrix. - -**Architecture:** A Python audit script parses OpenAPI YAML files and scans Java source conservatively for dedicated controllers, catch-all contract handlers, generic persistence handlers, and placeholder SSE handlers. It writes JSON and Markdown reports under `docs/superpowers/reports/`, and a Maven test verifies the report shape and ensures non-real implementations remain `incomplete` or `blocked`. - -**Tech Stack:** Python 3 + PyYAML, Java 21, JUnit 5, Jackson, Maven, Spring Boot 3 / Yudao Cloud. - ---- - -## File Structure - -- Create: `muse-cloud/scripts/p1r-audit-api-coverage.py` - - Parses `docs/api-contracts/*/openapi.yaml`. - - Scans Java files under `muse-cloud/`. - - Writes `docs/superpowers/reports/p1r-api-coverage.json`. - - Writes `docs/superpowers/reports/p1r-api-coverage.md`. - - Exits non-zero in `--check` mode if required fields are missing, or non-real implementation entries are not `incomplete` / `blocked`. -- Create: `docs/superpowers/reports/p1r-api-coverage.json` - - Generated baseline JSON report. -- Create: `docs/superpowers/reports/p1r-api-coverage.md` - - Generated human-readable report. -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` - - Maven gate that parses the JSON report and checks structure and status rules. -- No production source file should be modified in P1R-0. - -## Task 1: Add The API Coverage Audit Script - -**Files:** -- Create: `muse-cloud/scripts/p1r-audit-api-coverage.py` - -- [ ] **Step 1: Create the script with OpenAPI parsing and Java scanning** - -Use `apply_patch` to create `muse-cloud/scripts/p1r-audit-api-coverage.py` with this content: - -```python -#!/usr/bin/env python3 -"""Generate the P1R API coverage matrix. - -The script is intentionally conservative: if it cannot prove an operation has a -dedicated business implementation, it marks the operation as incomplete or -blocked. This prevents catch-all contract handlers from being counted as done. -""" - -from __future__ import annotations - -import argparse -import json -import re -import sys -from dataclasses import dataclass, asdict -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - -try: - import yaml -except ImportError as exc: - raise SystemExit("PyYAML is required. Install it with: python3 -m pip install PyYAML") from exc - - -REPO_ROOT = Path(__file__).resolve().parents[2] -CONTRACT_ROOT = REPO_ROOT / "docs" / "api-contracts" -MUSE_CLOUD_ROOT = REPO_ROOT / "muse-cloud" -REPORT_ROOT = REPO_ROOT / "docs" / "superpowers" / "reports" -JSON_REPORT = REPORT_ROOT / "p1r-api-coverage.json" -MARKDOWN_REPORT = REPORT_ROOT / "p1r-api-coverage.md" -HTTP_METHODS = {"get", "post", "put", "patch", "delete"} - - -@dataclass(frozen=True) -class Operation: - domain: str - side: str - method: str - path: str - operationId: str - isWrite: bool - requiresCommandId: bool - externalDependencies: list[str] - targetStage: str - implementationStatus: str - completionStatus: str - controllerFiles: list[str] - serviceFiles: list[str] - notes: str - - -def main() -> int: - parser = argparse.ArgumentParser(description="Generate P1R API coverage matrix") - parser.add_argument("--check", action="store_true", help="validate generated report after writing it") - args = parser.parse_args() - - operations = collect_operations() - REPORT_ROOT.mkdir(parents=True, exist_ok=True) - report = build_report(operations) - JSON_REPORT.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - MARKDOWN_REPORT.write_text(render_markdown(report), encoding="utf-8") - - if args.check: - validate_report(report) - print(f"Generated {JSON_REPORT.relative_to(REPO_ROOT)}") - print(f"Generated {MARKDOWN_REPORT.relative_to(REPO_ROOT)}") - return 0 - - -def collect_operations() -> list[Operation]: - source_index = build_source_index() - operations: list[Operation] = [] - for contract_path in sorted(CONTRACT_ROOT.glob("*/openapi.yaml")): - domain = contract_path.parent.name - with contract_path.open("r", encoding="utf-8") as handle: - document = yaml.safe_load(handle) - for path, path_item in sorted((document.get("paths") or {}).items()): - if not isinstance(path_item, dict): - continue - for method, operation_doc in sorted(path_item.items()): - if method.lower() not in HTTP_METHODS or not isinstance(operation_doc, dict): - continue - operation_id = operation_doc.get("operationId") - if not operation_id: - raise SystemExit(f"Missing operationId in {contract_path}:{path}:{method}") - full_path = normalize_path(path) - side = detect_side(full_path) - is_write = method.upper() not in {"GET", "HEAD", "OPTIONS"} - requires_command_id = operation_requires_command_id(operation_doc) - implementation = classify_implementation(full_path, operation_id, domain, source_index) - operations.append(Operation( - domain=domain, - side=side, - method=method.upper(), - path=full_path, - operationId=operation_id, - isWrite=is_write, - requiresCommandId=requires_command_id, - externalDependencies=detect_external_dependencies(domain, full_path, operation_id), - targetStage=target_stage(domain), - implementationStatus=implementation["implementationStatus"], - completionStatus=implementation["completionStatus"], - controllerFiles=implementation["controllerFiles"], - serviceFiles=implementation["serviceFiles"], - notes=implementation["notes"], - )) - return operations - - -def build_source_index() -> dict[str, Any]: - java_files = sorted((MUSE_CLOUD_ROOT).glob("**/src/main/java/**/*.java")) - contract_support_files: list[str] = [] - generic_persistence_files: list[str] = [] - sse_placeholder_files: list[str] = [] - dedicated_controllers: list[str] = [] - service_files: list[str] = [] - all_text: dict[str, str] = {} - - for java_file in java_files: - text = java_file.read_text(encoding="utf-8", errors="ignore") - rel = str(java_file.relative_to(REPO_ROOT)) - all_text[rel] = text - if "MuseApiContractSupport.handle" in text: - contract_support_files.append(rel) - if "MuseContractPersistenceService" in text or "contractPersistenceService.handle" in text: - generic_persistence_files.append(rel) - if "SseEmitter" in text and ("muse ai task stream ready" in text or ".name(\"done\")" in text): - sse_placeholder_files.append(rel) - if "@RestController" in text and "MuseApiContractSupport.handle" not in text and "contractPersistenceService.handle" not in text: - dedicated_controllers.append(rel) - if "/application/" in rel or "/service/" in rel: - service_files.append(rel) - - return { - "allText": all_text, - "contractSupportFiles": contract_support_files, - "genericPersistenceFiles": generic_persistence_files, - "ssePlaceholderFiles": sse_placeholder_files, - "dedicatedControllers": dedicated_controllers, - "serviceFiles": service_files, - } - - -def classify_implementation(path: str, operation_id: str, domain: str, source_index: dict[str, Any]) -> dict[str, Any]: - matching_dedicated = files_containing_route_or_operation(source_index["dedicatedControllers"], source_index["allText"], path, operation_id) - matching_services = files_matching_domain_services(source_index["serviceFiles"], domain) - matching_catch_all = files_containing_route_or_operation(source_index["contractSupportFiles"], source_index["allText"], path, operation_id) - matching_generic = files_containing_route_or_operation(source_index["genericPersistenceFiles"], source_index["allText"], path, operation_id) - matching_sse = files_containing_route_or_operation(source_index["ssePlaceholderFiles"], source_index["allText"], path, operation_id) - - if matching_sse: - return status("sse_placeholder", "incomplete", matching_sse, matching_services, "SSE endpoint returns placeholder events and needs P1R real task stream implementation.") - if matching_dedicated: - return status("dedicated", "needs_verification", matching_dedicated, matching_services, "Dedicated entry exists; later P1R stage must verify DTO, state machine, persistence, and real external closure.") - if matching_generic: - return status("generic_persistence", "incomplete", matching_generic, matching_services, "Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.") - if matching_catch_all: - return status("catch_all", "incomplete", matching_catch_all, matching_services, "Handled by MuseApiContractSupport catch-all, which is not P1R completion.") - return status("missing", "incomplete", [], matching_services, "No backend route was identified by the conservative scanner.") - - -def status(implementation: str, completion: str, controllers: list[str], services: list[str], notes: str) -> dict[str, Any]: - return { - "implementationStatus": implementation, - "completionStatus": completion, - "controllerFiles": controllers, - "serviceFiles": services, - "notes": notes, - } - - -def files_containing_route_or_operation(files: list[str], text_by_file: dict[str, str], path: str, operation_id: str) -> list[str]: - route_fragments = route_fragments_for(path) - matches: list[str] = [] - for file_path in files: - text = text_by_file[file_path] - if operation_id in text or any(fragment in text for fragment in route_fragments): - matches.append(file_path) - return matches - - -def route_fragments_for(path: str) -> list[str]: - path_without_prefix = path - path_without_prefix = path_without_prefix.replace("/app-api", "") - path_without_prefix = path_without_prefix.replace("/admin-api", "") - fragments = [path_without_prefix] - parts = [part for part in path_without_prefix.split("/") if part] - if len(parts) >= 2: - fragments.append("/" + "/".join(parts[:2])) - if len(parts) >= 3: - fragments.append("/" + "/".join(parts[:3])) - return sorted(set(fragments), key=len, reverse=True) - - -def files_matching_domain_services(files: list[str], domain: str) -> list[str]: - domain_path = "/module/" + ("member" if domain == "account" else domain) + "/" - return [file_path for file_path in files if domain_path in file_path] - - -def normalize_path(path: str) -> str: - if path.startswith("/app-api/") or path.startswith("/admin-api/"): - return path - return path - - -def detect_side(path: str) -> str: - if path.startswith("/admin-api/"): - return "admin" - if path.startswith("/app-api/"): - return "app" - return "unknown" - - -def operation_requires_command_id(operation_doc: dict[str, Any]) -> bool: - text = json.dumps(operation_doc, ensure_ascii=False) - return "commandId" in text or "X-Command-Id" in text - - -def detect_external_dependencies(domain: str, path: str, operation_id: str) -> list[str]: - haystack = f"{domain} {path} {operation_id}".lower() - dependencies: set[str] = set() - if any(word in haystack for word in ["ai", "task", "suggestion", "agent", "job", "source-event"]): - dependencies.update(["New-API", "SSE"]) - if any(word in haystack for word in ["knowledge", "kb", "document", "entity", "relation", "graph", "reindex"]): - dependencies.add("RAGFlow") - if any(word in haystack for word in ["import", "export", "download", "file", "document"]): - dependencies.add("FileService") - if any(word in haystack for word in ["marketplace", "purchase", "install", "handoff", "license"]): - dependencies.update(["Account", "MarketAuthorization"]) - if any(word in haystack for word in ["account", "usage", "quota", "balance", "correlation", "new-api-binding"]): - dependencies.update(["New-API", "AccountLedger"]) - return sorted(dependencies) - - -def target_stage(domain: str) -> str: - return { - "content": "P1R-1 Content Real API", - "meta": "P1R-2 Meta Real API", - "account": "P1R-3 Account Real API", - "ai": "P1R-4 AI Real API", - "knowledge": "P1R-5 Knowledge Real API", - "market": "P1R-6 Market Real API", - "events": "P1R-7 End-to-End Acceptance", - }.get(domain, "P1R-0 Baseline Gate") - - -def build_report(operations: list[Operation]) -> dict[str, Any]: - summary = { - "totalOperations": len(operations), - "completedOperations": sum(1 for operation in operations if operation.completionStatus == "completed"), - "incompleteOperations": sum(1 for operation in operations if operation.completionStatus == "incomplete"), - "needsVerificationOperations": sum(1 for operation in operations if operation.completionStatus == "needs_verification"), - "catchAllOperations": sum(1 for operation in operations if operation.implementationStatus == "catch_all"), - "genericPersistenceOperations": sum(1 for operation in operations if operation.implementationStatus == "generic_persistence"), - "ssePlaceholderOperations": sum(1 for operation in operations if operation.implementationStatus == "sse_placeholder"), - "missingOperations": sum(1 for operation in operations if operation.implementationStatus == "missing"), - "blockedOperations": sum(1 for operation in operations if operation.completionStatus == "blocked"), - } - return { - "generatedAt": datetime.now(timezone.utc).replace(microsecond=0).isoformat(), - "sourceContracts": [str(path.relative_to(REPO_ROOT)) for path in sorted(CONTRACT_ROOT.glob("*/openapi.yaml"))], - "summary": summary, - "operations": [asdict(operation) for operation in operations], - } - - -def render_markdown(report: dict[str, Any]) -> str: - lines = [ - "# P1R API Coverage Report", - "", - f"- Generated at: `{report['generatedAt']}`", - f"- Total operations: `{report['summary']['totalOperations']}`", - f"- Needs verification: `{report['summary']['needsVerificationOperations']}`", - f"- Incomplete: `{report['summary']['incompleteOperations']}`", - f"- Catch-all: `{report['summary']['catchAllOperations']}`", - f"- Generic persistence: `{report['summary']['genericPersistenceOperations']}`", - f"- SSE placeholder: `{report['summary']['ssePlaceholderOperations']}`", - f"- Missing: `{report['summary']['missingOperations']}`", - "", - "## Operations", - "", - "| Domain | Side | Method | Path | Operation | Implementation | Completion | Target Stage |", - "|--------|------|--------|------|-----------|----------------|------------|--------------|", - ] - for operation in report["operations"]: - lines.append( - "| {domain} | {side} | {method} | `{path}` | `{operationId}` | {implementationStatus} | {completionStatus} | {targetStage} |".format(**operation) - ) - lines.extend(["", "## Incomplete Or Blocked", ""]) - for operation in report["operations"]: - if operation["completionStatus"] in {"incomplete", "blocked"}: - lines.append(f"- `{operation['operationId']}` `{operation['method']} {operation['path']}`: {operation['implementationStatus']} - {operation['notes']}") - lines.append("") - return "\n".join(lines) - - -def validate_report(report: dict[str, Any]) -> None: - required_fields = { - "domain", "side", "method", "path", "operationId", "isWrite", - "requiresCommandId", "externalDependencies", "targetStage", - "implementationStatus", "completionStatus", "controllerFiles", - "serviceFiles", "notes", - } - if report["summary"]["totalOperations"] <= 0: - raise SystemExit("No operations found") - for operation in report["operations"]: - missing = required_fields - operation.keys() - if missing: - raise SystemExit(f"Operation {operation.get('operationId')} missing fields: {sorted(missing)}") - if operation["implementationStatus"] in {"catch_all", "generic_persistence", "sse_placeholder", "missing"}: - if operation["completionStatus"] not in {"incomplete", "blocked"}: - raise SystemExit(f"{operation['operationId']} non-real implementation must be incomplete or blocked") - if "message" in json.dumps(operation, ensure_ascii=False): - raise SystemExit(f"{operation['operationId']} contains forbidden response evidence field: message") - - -if __name__ == "__main__": - raise SystemExit(main()) -``` - -- [ ] **Step 2: Make the script executable** - -Run: - -```bash -chmod +x muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -Expected: command exits `0`. - -- [ ] **Step 3: Run the script in check mode** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected output includes: - -```text -Generated docs/superpowers/reports/p1r-api-coverage.json -Generated docs/superpowers/reports/p1r-api-coverage.md -``` - -If it exits with `PyYAML is required`, install PyYAML only after confirming the current Python environment: - -```bash -python3 -m pip show PyYAML || python3 -m pip install PyYAML -``` - -- [ ] **Step 4: Inspect the generated summary** - -Run: - -```bash -python3 - <<'PY' -import json -from pathlib import Path -report = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -print(json.dumps(report["summary"], indent=2, ensure_ascii=False)) -PY -``` - -Expected: `totalOperations` is greater than `0`, and `completedOperations` is `0` for P1R-0. - -- [ ] **Step 5: Commit script and generated reports** - -Run: - -```bash -git status --short -git add \ - muse-cloud/scripts/p1r-audit-api-coverage.py \ - docs/superpowers/reports/p1r-api-coverage.json \ - docs/superpowers/reports/p1r-api-coverage.md -git commit -m "chore(p1r): 生成 API 覆盖基线矩阵" -- \ - muse-cloud/scripts/p1r-audit-api-coverage.py \ - docs/superpowers/reports/p1r-api-coverage.json \ - docs/superpowers/reports/p1r-api-coverage.md -``` - -Expected commit includes exactly the script and two report files. - -## Task 2: Add Maven Gate For The Coverage Report - -**Files:** -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- Read: `docs/superpowers/reports/p1r-api-coverage.json` - -- [ ] **Step 1: Add the report test** - -Use `apply_patch` to create `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` with this content: - -```java -package cn.iocoder.muse.server.framework.api; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * P1R API 覆盖矩阵门禁测试。 - * - *

该测试不证明业务 API 已完成,只防止覆盖矩阵缺字段,或把 catch-all / 通用持久化入口误标为完成。

- */ -class P1rApiCoverageReportTest { - - /** 不能被标记为完成的实现状态。 */ - private static final Set INCOMPLETE_IMPLEMENTATION_STATUSES = Set.of( - "catch_all", "generic_persistence", "sse_placeholder", "missing"); - - private final ObjectMapper objectMapper = new ObjectMapper(); - - /** - * 验证报告存在且至少包含一个合同 operation。 - */ - @Test - void should_load_p1r_api_coverage_report() throws Exception { - Path reportPath = findReportPath(); - assertThat(Files.exists(reportPath)).as("P1R API coverage report must exist").isTrue(); - - JsonNode report = objectMapper.readTree(reportPath.toFile()); - - assertThat(report.path("summary").path("totalOperations").asInt()).isGreaterThan(0); - assertThat(report.path("operations").isArray()).isTrue(); - assertThat(report.path("operations").size()).isEqualTo(report.path("summary").path("totalOperations").asInt()); - } - - /** - * 验证每个 operation 都包含后续阶段执行所需的稳定字段。 - */ - @Test - void should_require_stable_fields_for_every_operation() throws Exception { - JsonNode operations = objectMapper.readTree(findReportPath().toFile()).path("operations"); - - for (JsonNode operation : operations) { - assertThat(operation.path("domain").asText()).isNotBlank(); - assertThat(operation.path("side").asText()).isNotBlank(); - assertThat(operation.path("method").asText()).isNotBlank(); - assertThat(operation.path("path").asText()).startsWith("/"); - assertThat(operation.path("operationId").asText()).isNotBlank(); - assertThat(operation.has("isWrite")).isTrue(); - assertThat(operation.has("requiresCommandId")).isTrue(); - assertThat(operation.path("externalDependencies").isArray()).isTrue(); - assertThat(operation.path("targetStage").asText()).startsWith("P1R-"); - assertThat(operation.path("implementationStatus").asText()).isNotBlank(); - assertThat(operation.path("completionStatus").asText()).isNotBlank(); - assertThat(operation.path("controllerFiles").isArray()).isTrue(); - assertThat(operation.path("serviceFiles").isArray()).isTrue(); - assertThat(operation.path("notes").asText()).isNotBlank(); - } - } - - /** - * 验证 catch-all、通用持久化、SSE 占位和缺入口只能保持 incomplete 或 blocked。 - */ - @Test - void should_keep_non_real_implementations_incomplete_or_blocked() throws Exception { - JsonNode operations = objectMapper.readTree(findReportPath().toFile()).path("operations"); - - for (JsonNode operation : operations) { - String implementationStatus = operation.path("implementationStatus").asText(); - if (INCOMPLETE_IMPLEMENTATION_STATUSES.contains(implementationStatus)) { - assertThat(operation.path("completionStatus").asText()) - .as(operation.path("operationId").asText()) - .isIn("incomplete", "blocked"); - } - } - } - - /** - * 验证报告不会把错误响应字段写成 message,P1R 必须坚持 code/data/msg。 - */ - @Test - void should_not_use_message_as_completion_evidence() throws Exception { - String report = Files.readString(findReportPath()); - - assertThat(report).doesNotContain("\"message\""); - } - - /** - * 从当前 Maven 执行目录向上查找仓库根目录,避免 surefire 在不同模块目录执行时路径失效。 - */ - private static Path findReportPath() { - Path current = Path.of("").toAbsolutePath(); - for (Path candidate = current; candidate != null; candidate = candidate.getParent()) { - Path reportPath = candidate.resolve("docs/superpowers/reports/p1r-api-coverage.json"); - if (Files.exists(reportPath)) { - return reportPath; - } - } - return current.resolve("docs/superpowers/reports/p1r-api-coverage.json"); - } - -} -``` - -- [ ] **Step 2: Run the focused Maven test** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: `BUILD SUCCESS`. - -`-am` 会让 Maven reactor 进入上游依赖模块的 `test` phase;这些模块没有 `P1rApiCoverageReportTest` 时不应导致本门禁失败,因此该聚焦命令需要显式设置 `-Dsurefire.failIfNoSpecifiedTests=false`。 - -If Maven cannot resolve dependencies in offline mode, rerun without `-o`: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -- [ ] **Step 3: Run the script again after the test** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: script still succeeds and report files remain valid. - -- [ ] **Step 4: Commit the Maven gate** - -Run: - -```bash -git status --short -git add muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -git commit -m "test(p1r): 增加 API 覆盖矩阵门禁" -- \ - muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -``` - -Expected commit includes only the test file. - -## Task 3: Final P1R-0 Verification And Handoff - -**Files:** -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` -- Read: `docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md` - -- [ ] **Step 1: Verify generated reports are internally consistent** - -Run: - -```bash -python3 - <<'PY' -import json -from pathlib import Path -report = json.loads(Path("docs/superpowers/reports/p1r-api-coverage.json").read_text()) -summary = report["summary"] -count = len(report["operations"]) -assert summary["totalOperations"] == count, (summary["totalOperations"], count) -assert summary["completedOperations"] == 0, summary["completedOperations"] -for op in report["operations"]: - assert op["completionStatus"] != "completed", op["operationId"] -print("P1R-0 report consistency OK:", count, "operations") -PY -``` - -Expected output: - -```text -P1R-0 report consistency OK: operations -``` - -- [ ] **Step 2: Run all P1R-0 checks** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: script succeeds and Maven prints `BUILD SUCCESS`. - -- [ ] **Step 3: Inspect git status before final summary** - -Run: - -```bash -git status --short -``` - -Expected: no staged P1R-0 files. Unrelated files from parallel P2/P3/admin sessions may still appear; do not modify or commit them. - -- [ ] **Step 4: Write handoff summary in the final response** - -Include: - -```text -P1R-0 completed: -- Generated API coverage matrix JSON and Markdown. -- Added Maven gate for matrix shape and non-real implementation status rules. -- Verified script and focused Maven test. - -Important result: -- completedOperations remains 0 by design. -- catch-all/generic persistence/SSE placeholder/missing operations remain incomplete or blocked. -- Next stage is P1R-1 Content Real API spec + plan. -``` - -- [ ] **Step 5: Do not commit unrelated files** - -Before any final commit, verify: - -```bash -git diff --cached --name-status -``` - -Expected: empty, unless you intentionally staged only P1R-0 files. diff --git a/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md b/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md deleted file mode 100644 index 2435f152..00000000 --- a/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md +++ /dev/null @@ -1,439 +0,0 @@ -# muse-cloud Explainer Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build a HTML-first `docs/explainers/muse-cloud/` architecture explainer that helps junior backend engineers understand the P1R muse-cloud architecture, OpenAPI contract paths, request lifecycle, domain ownership, stage map, and troubleshooting flow. - -**Architecture:** `index.html` is the complete reading surface. `content/*.md` files are non-authoritative maintenance notes. HTML sections carry source markers that point back to the note file and upstream spec/contract evidence. Static checks verify the required teaching structures, not just keyword presence. - -**Tech Stack:** Static HTML5, CSS custom properties, vanilla JavaScript, HTML/CSS/SVG-style diagrams, no build step, no backend API calls. - ---- - -## Corrected Decisions From Review - -1. OpenAPI `paths` entries are already full external paths such as `/app-api/muse/works`; do not teach them as `prefix + resource path` post-processing. -2. HTML is the authoritative explainer. Markdown files are non-authoritative notes for review and maintenance. -3. The page must contain the minimum diagnostic payload from the spec: - - Architecture layers: `负责什么` / `不应负责什么` / `定位证据` - - Request lifecycle nodes: `常见问题` / `定位证据` / `下一步` - - P1R stage cards: `负责领域` / `真实能力` / `假完成形态` / `验收证据` - - Troubleshooting rows: `症状` / `优先定位层` / `检查项` / `下一步` / `证据` -4. Keep commit strategy simple: no task-level commits. Make one final commit after validation. -5. Local server verification must be non-blocking, use an available port, record PID, and clean up. - -## File Structure - -- Create: `docs/explainers/muse-cloud/README.md` -- Create: `docs/explainers/muse-cloud/index.html` -- Create: `docs/explainers/muse-cloud/content/architecture.md` -- Create: `docs/explainers/muse-cloud/content/openapi-paths.md` -- Create: `docs/explainers/muse-cloud/content/request-lifecycle.md` -- Create: `docs/explainers/muse-cloud/content/domain-owners.md` -- Create: `docs/explainers/muse-cloud/content/p1r-stages.md` -- Create: `docs/explainers/muse-cloud/content/troubleshooting.md` -- Create: `docs/explainers/muse-cloud/content/sources.md` -- Create: `docs/explainers/muse-cloud/assets/styles.css` -- Create: `docs/explainers/muse-cloud/assets/app.js` - -Do not modify: - -- `muse-cloud/**` -- `muse-admin/**` -- `muse-studio/**` -- `docs/api-contracts/**` -- `.superpowers/**` -- `docs/superpowers/plans/P1-execution-prompt.md` -- `docs/superpowers/plans/P3-execution-prompt.md` -- `docs/superpowers/reports/**` - -## Task 1: Verify Source Facts and Scaffold Notes - -**Files:** -- Create: `docs/explainers/muse-cloud/README.md` -- Create: `docs/explainers/muse-cloud/content/*.md` - -- [ ] **Step 1: Inspect current worktree** - -Run: - -```bash -git status --short -``` - -Expected: unrelated untracked files may exist. Do not stage or edit them. - -- [ ] **Step 2: Verify OpenAPI full-path fact** - -Run: - -```bash -rg -n "^ /app-api/muse/works:" docs/api-contracts/content/openapi.yaml -rg -n "operationId: listWorks" docs/api-contracts/content/openapi.yaml -rg -n "^ /admin-api/muse/account/users:" docs/api-contracts/account/openapi.yaml -``` - -Expected: all three commands find matches. This proves the explainer must say contract `paths` are complete external paths. - -- [ ] **Step 3: Verify code example facts** - -Run: - -```bash -rg -n "class AppContentController|interface ContentAppService|class ContentAppServiceImpl" \ - muse-cloud/muse-module-content/muse-module-content-server/src/main/java -``` - -Expected: matches for `AppContentController`, `ContentAppService`, and `ContentAppServiceImpl`. If any class is missing, mark controller/service names in the explainer as examples instead of verified facts. - -- [ ] **Step 4: Create directories** - -Run: - -```bash -mkdir -p docs/explainers/muse-cloud/content docs/explainers/muse-cloud/assets -``` - -Expected: command exits with status `0`. - -- [ ] **Step 5: Create README and note files** - -Create these files with `apply_patch`: - -- `README.md`: explain purpose, point to `index.html`, state HTML is authoritative, state Markdown notes are non-authoritative, list source priority, and warn that catch-all / generic persistence / placeholder SSE / empty list / accepted task are not P1R completion. -- `content/architecture.md`: short notes for Controller, Application Service, Domain, Query/Assembler, Persistence Mapper, External Adapter, CommonResult. -- `content/openapi-paths.md`: state that OpenAPI `paths` are already full external paths; include `/app-api/muse/works` and `listWorks`. -- `content/request-lifecycle.md`: list the lifecycle from request to `CommonResult(code, data, msg)`. -- `content/domain-owners.md`: list Content, Meta, Account, AI, Knowledge, Market, Events owner boundaries. -- `content/p1r-stages.md`: list P1R-0 through P1R-7. -- `content/troubleshooting.md`: list all eight required symptoms. -- `content/sources.md`: map each HTML section to its note file and upstream source. - -Required source mapping in `content/sources.md`: - -```markdown -| HTML section | Maintenance note | Upstream source | -|--------------|------------------|-----------------| -| architecture | content/architecture.md | P1R real API design section 7 | -| openapi | content/openapi-paths.md | docs/api-contracts/**/openapi.yaml | -| lifecycle | content/request-lifecycle.md | P1R real API design section 5 and 7 | -| owners | content/domain-owners.md | P1R real API design section 5.3 | -| stages | content/p1r-stages.md | P1R real API design section 10 | -| troubleshooting | content/troubleshooting.md | explainer design section 7.6 | -``` - -- [ ] **Step 6: Verify note files** - -Run: - -```bash -for f in architecture openapi-paths request-lifecycle domain-owners p1r-stages troubleshooting sources; do - test -f "docs/explainers/muse-cloud/content/$f.md" || exit 1 -done -rg -n "TBD|TODO|待定" docs/explainers/muse-cloud/content && exit 1 || true -rg -n "prefix \\+ resource|side prefix \\+ resource|路径由三段组成" docs/explainers/muse-cloud/content && exit 1 || true -``` - -Expected: files exist; no placeholders; no old wrong OpenAPI path-composition phrasing. - -## Task 2: Build the HTML, CSS, and JS - -**Files:** -- Create: `docs/explainers/muse-cloud/index.html` -- Create: `docs/explainers/muse-cloud/assets/styles.css` -- Create: `docs/explainers/muse-cloud/assets/app.js` - -- [ ] **Step 1: Create `index.html`** - -Create a complete standalone HTML page. It must satisfy this content contract: - -1. Navigation: - - Desktop layout has left nav, main content, and right quick panel. - - Mobile layout keeps quick triage before main content, not after all content. -2. `#overview`: - - States this is an explainer, not proof of API completion. - - Explicitly says catch-all, generic persistence, placeholder SSE, empty list, and accepted task are not complete. -3. `#architecture`: - - Contains at least seven `.architecture-layer` blocks for Controller, Application Service, Domain, Query/Assembler, Persistence Mapper, External Adapter, CommonResult. - - Each block contains labels `负责什么`, `不应负责什么`, and `定位证据`. -4. `#openapi`: - - Says OpenAPI `paths` are complete external paths. - - Contains `/app-api/muse/works`, `listWorks`, `AppContentController`, and `ContentAppService`. - - Does not say paths are assembled by appending a side prefix to a resource path. - - Includes a source note pointing to `docs/api-contracts/content/openapi.yaml`. -5. `#lifecycle`: - - Contains eight `.lifecycle-node` blocks: HTTP Request, Version/Auth, Controller DTO, Application, Domain, DB/Adapter, Assembler, CommonResult. - - Each block contains labels `常见问题`, `定位证据`, and `下一步`. -6. `#owners`: - - Covers Content, Meta, Account, AI, Knowledge, Market, Events. - - Desktop may use a table; mobile must have readable owner cards or definition lists without horizontal scrolling as the only access path. -7. `#stages`: - - Contains eight `.stage-card` blocks for P1R-0 through P1R-7. - - Each block contains labels `负责领域`, `真实能力`, `假完成形态`, and `验收证据`. -8. `#troubleshooting`: - - Contains eight `.diagnostic-row` blocks for 404, 400, 401/403, 409, 空列表但应有数据, accepted 但不终态, 外部服务失败, 响应字段不对. - - Each block contains labels `症状`, `优先定位层`, `检查项`, `下一步`, and `证据`. -9. `#maintenance`: - - States HTML is authoritative and Markdown files are non-authoritative notes. - - Shows section-to-source mapping. - -Every major section must include `data-source="content/.md"` and `data-upstream="..."` attributes. - -- [ ] **Step 2: Create `assets/styles.css`** - -Create a calm engineering-manual style: - -1. Use CSS custom properties for `--bg`, `--surface`, `--text`, `--muted`, `--line`, `--accent`. -2. Avoid heavy card nesting. Sections should read like document bands; reserve bordered surfaces for diagrams, diagnostic rows, and source notes. -3. Use 8px border radius or less. -4. Use `letter-spacing: 0` or no letter spacing; no negative letter spacing. -5. Desktop: three-column layout. -6. Tablet/mobile: nav and quick triage appear before main content; content is one column; owner cards are readable without requiring table horizontal scroll. -7. Add visible focus states for links and buttons. -8. Add print styles that hide nav, quick triage, and copy buttons. - -- [ ] **Step 3: Create `assets/app.js`** - -Implement local-only interactions: - -1. Active section highlighting for nav links. -2. Copy buttons for sample paths. -3. Quick triage filter buttons for all eight symptom groups. -4. A clear-filter button. -5. No `fetch`, `XMLHttpRequest`, `localStorage`, or `sessionStorage`. -6. Clipboard failure must degrade by showing the copied value in the button text. - -## Task 3: Validate the Explainer - -**Files:** -- Verify: `docs/explainers/muse-cloud/**` - -- [ ] **Step 1: Run structural validation** - -Run: - -```bash -node <<'NODE' -const fs = require('fs'); -const html = fs.readFileSync('docs/explainers/muse-cloud/index.html', 'utf8'); -const css = fs.readFileSync('docs/explainers/muse-cloud/assets/styles.css', 'utf8'); -const js = fs.readFileSync('docs/explainers/muse-cloud/assets/app.js', 'utf8'); - -const requiredFiles = [ - 'README.md', - 'content/architecture.md', - 'content/openapi-paths.md', - 'content/request-lifecycle.md', - 'content/domain-owners.md', - 'content/p1r-stages.md', - 'content/troubleshooting.md', - 'content/sources.md', - 'assets/styles.css', - 'assets/app.js', - 'index.html', -]; -for (const file of requiredFiles) { - if (!fs.existsSync(`docs/explainers/muse-cloud/${file}`)) throw new Error(`missing ${file}`); -} - -for (const token of ['TBD', 'TODO', '待定']) { - if (html.includes(token) || css.includes(token) || js.includes(token)) throw new Error(`placeholder ${token}`); -} - -if (html.includes('prefix + resource') || html.includes('side prefix + resource') || html.includes('路径由三段组成')) { - throw new Error('old OpenAPI path composition phrasing found'); -} -for (const token of ['/app-api/muse/works', 'listWorks', 'AppContentController', 'ContentAppService', 'X-API-Version: 1', 'code/data/msg']) { - if (!html.includes(token)) throw new Error(`missing ${token}`); -} - -const count = (pattern) => (html.match(pattern) || []).length; -if (count(/class="[^"]*architecture-layer/g) < 7) throw new Error('architecture layers missing'); -if (count(/class="[^"]*lifecycle-node/g) < 8) throw new Error('lifecycle nodes missing'); -if (count(/class="[^"]*stage-card/g) < 8) throw new Error('stage cards missing'); -if (count(/class="[^"]*diagnostic-row/g) < 8) throw new Error('diagnostic rows missing'); - -for (const token of ['负责什么', '不应负责什么', '定位证据', '常见问题', '下一步', '负责领域', '真实能力', '假完成形态', '验收证据', '优先定位层', '检查项', '证据']) { - if (!html.includes(token)) throw new Error(`missing structural label ${token}`); -} - -for (const forbidden of ['fetch(', 'XMLHttpRequest', 'localStorage', 'sessionStorage']) { - if (js.includes(forbidden)) throw new Error(`forbidden browser API ${forbidden}`); -} -if (/letter-spacing:\s*-[0-9.]/.test(css)) throw new Error('negative letter spacing'); -if (/border-radius:\s*(1[0-9]|[2-9][0-9])px/.test(css)) throw new Error('radius above 8px'); -NODE -``` - -Expected: command exits with status `0`. - -- [ ] **Step 2: Verify examples against real source** - -Run: - -```bash -rg -n "^ /app-api/muse/works:" docs/api-contracts/content/openapi.yaml -rg -n "operationId: listWorks" docs/api-contracts/content/openapi.yaml -rg -n "class AppContentController|interface ContentAppService|class ContentAppServiceImpl" \ - muse-cloud/muse-module-content/muse-module-content-server/src/main/java -``` - -Expected: all commands find matches. - -- [ ] **Step 3: Run whitespace checks** - -Run: - -```bash -git diff --check -- docs/explainers/muse-cloud -``` - -Expected: no output and exit status `0`. - -- [ ] **Step 4: Verify direct file opening path** - -Run: - -```bash -node <<'NODE' -const path = require('path'); -const htmlPath = path.resolve('docs/explainers/muse-cloud/index.html'); -console.log(`file://${htmlPath}`); -NODE -``` - -Open the printed `file://` URL in a browser. Expected: CSS and JS load because they use relative paths; page is readable without a local server. - -- [ ] **Step 5: Start a non-blocking local server** - -Run: - -```bash -PORT=4173 -while lsof -nP -iTCP:$PORT -sTCP:LISTEN >/dev/null 2>&1; do - PORT=$((PORT + 1)) -done -python3 -m http.server "$PORT" --directory docs/explainers/muse-cloud > /tmp/muse-cloud-explainer-http.log 2>&1 & -SERVER_PID=$! -echo "$SERVER_PID" > /tmp/muse-cloud-explainer-http.pid -echo "http://localhost:$PORT/index.html" -``` - -Expected: command prints a URL and returns to the shell. Keep the server running until browser and screenshot checks finish. - -- [ ] **Step 6: Browser check** - -Open the printed URL. Check: - -1. Desktop: nav, main content, and quick triage do not overlap. -2. Mobile: quick triage appears before main content and remains usable. -3. Owner boundaries are readable on mobile without relying only on a wide table. -4. Quick triage filters and clear filter work. -5. Copy buttons either copy or reveal the copied value. -6. The page reads like an engineering manual, not a marketing landing page. - -- [ ] **Step 7: Screenshot automation or explicit fallback** - -Run preflight: - -```bash -node -e "require.resolve('playwright')" -``` - -If it succeeds, run screenshots against the local server URL: - -```bash -URL="$(grep -o 'http://localhost:[0-9]*/index.html' /tmp/muse-cloud-explainer-http.log 2>/dev/null || true)" -if [ -z "$URL" ]; then - PORT="$(lsof -nP -iTCP -sTCP:LISTEN | awk '/Python/ && /LISTEN/ {print $9}' | sed -n 's/.*://p' | tail -1)" - URL="http://localhost:${PORT}/index.html" -fi -node < { - const browser = await chromium.launch(); - const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); - await page.goto('$URL'); - await page.screenshot({ path: 'docs/explainers/muse-cloud/desktop-check.png', fullPage: true }); - await page.setViewportSize({ width: 390, height: 900 }); - await page.screenshot({ path: 'docs/explainers/muse-cloud/mobile-check.png', fullPage: true }); - await browser.close(); -})(); -NODE -rm -f docs/explainers/muse-cloud/desktop-check.png docs/explainers/muse-cloud/mobile-check.png -``` - -If preflight fails, do not install dependencies. Use the available browser or visual MCP tooling and record in the final summary that Playwright screenshot automation was skipped because `require.resolve('playwright')` failed. - -- [ ] **Step 8: Stop local server** - -Run: - -```bash -if [ -f /tmp/muse-cloud-explainer-http.pid ]; then - kill "$(cat /tmp/muse-cloud-explainer-http.pid)" 2>/dev/null || true - rm -f /tmp/muse-cloud-explainer-http.pid -fi -``` - -Expected: local server stops. - -## Task 4: Final Review and Commit - -**Files:** -- Commit only: `docs/explainers/muse-cloud/**` - -- [ ] **Step 1: Inspect status** - -Run: - -```bash -git status --short -``` - -Expected: unrelated files may still exist, but only `docs/explainers/muse-cloud/**` should be part of this implementation. - -- [ ] **Step 2: Stage only explainer files** - -Run: - -```bash -git add -- docs/explainers/muse-cloud -git diff --cached --name-only -``` - -Expected: every staged file path starts with `docs/explainers/muse-cloud/`. - -- [ ] **Step 3: Enforce staged whitelist** - -Run: - -```bash -git diff --cached --name-only | awk ' - !/^docs\/explainers\/muse-cloud\// { print "unexpected staged file: " $0; bad=1 } - END { exit bad } -' -git diff --cached --check -- docs/explainers/muse-cloud -``` - -Expected: both commands exit with status `0`. - -- [ ] **Step 4: Commit** - -Run: - -```bash -git commit -m "docs(explainer): 完成 muse-cloud 架构讲解包" -- docs/explainers/muse-cloud -``` - -Expected: commit succeeds and includes only `docs/explainers/muse-cloud/**`. - -## Completion Criteria - -- `index.html` is complete enough to read without opening Markdown. -- OpenAPI section says contract `paths` are full external paths. -- The page includes all required architecture, lifecycle, stage, and troubleshooting structures. -- The page includes `/app-api/muse/**`, `/admin-api/muse/**`, `X-API-Version: 1`, and `code/data/msg`. -- The page does not request backend APIs or external credentials. -- Direct `file://` open path and local server path are both checked. -- No files under `muse-cloud/`, `muse-admin/`, `muse-studio/`, `docs/api-contracts/**`, `.superpowers/**`, `docs/superpowers/reports/**`, or P1/P3 prompt files were modified or committed. diff --git a/docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md b/docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md deleted file mode 100644 index e6af8738..00000000 --- a/docs/superpowers/plans/2026-05-26-P1R-1-content-real-api.md +++ /dev/null @@ -1,709 +0,0 @@ -# P1R-1 Content Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 Content 域 51 个 operation 从 P1R-0 覆盖矩阵推进到真实业务 API 可验收状态。 - -**Architecture:** 专用 Controller 调用 Content Application Service,Application Service 持有事务、幂等、owner guard、审计和外部 facade 编排。Content 只写 Content 自有事实;AI、Meta、Knowledge、FileService 通过 facade / adapter 协作,未接通时明确 blocked,不伪造成功。 - -**Tech Stack:** Java 21、Spring Boot 3、Yudao Cloud、MyBatis Plus、PostgreSQL、JUnit 5、Maven、OpenAPI 合同、P1R 覆盖矩阵脚本。 - ---- - -## 0. 执行边界 - -### 必须遵守 - -- 不修改 `docs/api-contracts/**/openapi.yaml`;例外:用户已批准将 `RiskActionRequest.required` 增补 `expectedVersion`,用于对齐治理动作服务端版本校验。 -- 不修改 P1R-0 扫描脚本来掩盖真实实现缺口。 -- 不把 `MuseApiContractSupport.handle(...)`、`MuseContractPersistenceService`、operation record、workflow task 原始行当完成。 -- 所有新增代码必须有中文注释,关键业务逻辑解释 WHY。 -- 每个任务完成后运行任务内最小验证。 - -### 当前输入 - -- `docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md` -- `docs/superpowers/reports/p1r-api-coverage.json` -- `docs/api-contracts/content/openapi.yaml` -- `muse-cloud/muse-module-content/muse-module-content-server/**` -- `muse-cloud/sql/muse/V1__init_content_schema.sql` - -## 1. 目标文件结构 - -### 新增或修改的主要代码文件 - -| 类型 | 路径 | 职责 | -|---|---|---| -| Modify | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentController.java` | 保留并校准核心 CRUD | -| Modify | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseContentContractController.java` | 随任务逐步移除 catch-all 路径 | -| Modify | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/admin/AdminMuseContentContractController.java` | 随任务逐步移除 admin catch-all 路径 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentStructureController.java` | reorder/split/merge | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentSourceController.java` | source attribution / suggestion merge | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentPlanningController.java` | planning / candidate / style check | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentImportParseController.java` | import / parse / parse review | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentExportController.java` | export / download | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/admin/AdminContentController.java` | admin read / risk action | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/*Service.java` | Content 应用服务接口 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/*ServiceImpl.java` | Content 应用服务实现 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/facade/*.java` | AI/Meta/Knowledge/File facade 合同 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/vo/*.java` | app OpenAPI DTO | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/admin/vo/*.java` | admin OpenAPI DTO | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/convert/ContentConvert.java` | DO/facade DTO 到 VO 的组装 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/dal/dataobject/*.java` | import/export/planning/governance DO | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/dal/mysql/*.java` | import/export/planning/governance Mapper | -| Create | `muse-cloud/sql/muse/V9__extend_content_real_api_schema.sql` | P1R-1 需要的新表或字段 | -| Create | `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/**` | 单元、服务、Controller 契约测试 | - -## 2. Operation 分组 - -| 分组 | operation 数量 | 目标 | -|---|---:|---| -| 核心作品/章节/Block | 16 | 15 个 dedicated 回归验收,`reorderChapters` 退役 catch-all | -| Block 结构编辑 | 2 | `splitBlock` / `mergeBlocks` 真实修改 Block 结构 | -| 来源归因与 Suggestion Merge | 2 | 来源归因读模型,AI suggestion 合并写 Canonical | -| Meta 投影与动态字段校验 | 3 | Content owner guard + Meta facade,未接通时 blocked | -| 作品规划与候选 | 9 | Content 正式 planning,AI candidate/style check facade | -| 导入与解析审阅 | 9 | Content import task,AI parse job facade,确认写入 owner 边界 | -| 导出与下载 | 4 | Content export task,FileService 产物和凭证 | -| 管理端治理 | 6 | admin read model、任务列表、risk action | - -## Task 1: Preflight 和覆盖矩阵冻结 - -**Files:** -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/api-contracts/content/openapi.yaml` -- Read: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseContentContractController.java` -- Read: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/admin/AdminMuseContentContractController.java` - -- [x] **Step 1: 确认分支和工作树** - -Run: - -```bash -git branch --show-current -git status --short -``` - -Expected: 在 `dev/1.0.0` 或 P1R-1 专用分支,且没有非本任务脏改。 - -- [x] **Step 2: 重新生成并检查 P1R 报告** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: 命令成功,Content 仍是 51 个 operation。 - -- [x] **Step 3: 导出 Content operation 分桶** - -Run: - -```bash -jq -r '.operations[] | select(.domain=="content") | [.implementationStatus,.completionStatus] | @tsv' \ - docs/superpowers/reports/p1r-api-coverage.json | sort | uniq -c -``` - -Expected: - -```text -36 catch_all incomplete -15 dedicated needs_verification -``` - -- [x] **Step 4: 记录任务输入** - -Run: - -```bash -jq -r '.operations[] | select(.domain=="content") | [.method,.path,.operationId,.implementationStatus,.completionStatus] | @tsv' \ - docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 51 行,作为本阶段后续对账表。 - -## Task 2: Content API 基础设施 - -**Files:** -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/domain/ContentApiVersionGuard.java` -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/domain/ContentRevisionGuard.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentCommandService.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentCommandServiceImpl.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentAuditService.java` -- Create: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentAuditServiceImpl.java` -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/dal/dataobject/ContentCommandDO.java` -- Test: `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/domain/ContentApiVersionGuardTest.java` -- Test: `muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentCommandServiceTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- 缺少 `X-API-Version` 返回版本错误。 -- `X-API-Version != 1` 返回版本错误。 -- commandId 为空返回 commandId required。 -- 相同 commandId + 相同 requestHash 返回已保存 resultSnapshot。 -- 相同 commandId + 不同 requestHash 返回幂等冲突。 - -- [x] **Step 2: 实现错误码和 guard** - -要求: - -- 错误码常量集中到 `ErrorCodeConstants`。 -- `ContentApiVersionGuard.requireVersion(String version)` 只接受 `"1"`。 -- `ContentRevisionGuard` 复用错误码常量,不再散落 magic number。 - -- [x] **Step 3: 实现 command/audit 服务** - -要求: - -- `ContentCommandService` 负责 commandId、requestHash、resultSnapshot。 -- `ContentAuditService` 负责写命令审计语义,短期可以落 `muse_content_command_log` 的扩展字段;若字段不足,在 Task 3 的 DDL 中补齐。 -- 中文注释解释幂等和审计的区别:幂等用于重复请求一致性,审计用于追踪业务责任。 - -- [x] **Step 4: 运行聚焦测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-content/muse-module-content-server -am \ - -Dtest=ContentApiVersionGuardTest,ContentCommandServiceTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 相关测试通过。 - -## Task 3: 核心 CRUD 验收回归和 DTO 对齐 - -**Files:** -- Modify: `AppContentController.java` -- Modify: `ContentAppService.java` -- Modify: `ContentAppServiceImpl.java` -- Modify/Create: `controller/app/vo/*` -- Create: `convert/ContentConvert.java` -- Test: `application/ContentAppServiceTest.java` -- Test: `controller/app/AppContentControllerTest.java` - -- [x] **Step 1: 写 Controller 契约测试** - -覆盖 15 个 dedicated operation: - -- `listWorks` -- `createWork` -- `getWork` -- `updateWork` -- `deleteWork` -- `listChapters` -- `createChapter` -- `getChapter` -- `updateChapter` -- `deleteChapter` -- `listBlocks` -- `createBlock` -- `getBlock` -- `saveBlock` -- `deleteBlock` - -每个写接口至少断言: - -- 需要 `X-API-Version: 1`。 -- 请求体缺 commandId 失败。 -- owner 不匹配失败。 -- 响应 JSON 使用 `code/data/msg`。 - -- [x] **Step 2: 写应用服务测试** - -覆盖: - -- 创建作品重复 commandId 返回同一作品。 -- 更新作品 revision 冲突失败。 -- 删除章节跨 workId 失败。 -- 保存 Block 写入 source attribution。 -- 删除 Block 强制 sourceSnapshot。 - -- [x] **Step 3: 对齐 DTO 和 assembler** - -要求: - -- `WorkRespVO` 对齐 OpenAPI `Work`。 -- 列表项对齐 `WorkSummary`,不要在列表里泄漏无关字段。 -- `ChapterRespVO` 对齐 `Chapter` / `ChapterDetail`。 -- `BlockRespVO` 对齐 `Block`。 -- 返回对象由 `ContentConvert` 组装,避免 Controller 或 Service 零散拼 VO。 - -- [x] **Step 4: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-content/muse-module-content-server -am \ - -Dtest=ContentAppServiceTest,AppContentControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: dedicated operation 回归测试通过。 - -## Task 4: 章节重排与 Block split/merge - -**Files:** -- Create: `controller/app/AppContentStructureController.java` -- Create: `controller/app/vo/ReorderChapterReqVO.java` -- Create: `controller/app/vo/SplitBlockReqVO.java` -- Create: `controller/app/vo/MergeBlockReqVO.java` -- Create: `application/ContentStructureService.java` -- Create: `application/ContentStructureServiceImpl.java` -- Modify: `dal/mysql/ChapterMapper.java` -- Modify: `dal/mysql/BlockMapper.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentStructureServiceTest.java` -- Test: `controller/app/AppContentStructureControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- `reorderChapters` 校验 work owner、chapter 从属、expectedRevision。 -- `splitBlock` 在 splitPosition 分割正文,原 block 和新 block 排序正确。 -- `mergeBlocks` 只能合并同 chapter 相邻 block。 -- 重复 commandId 返回同一结果。 -- 不同 requestHash 的重复 commandId 失败。 - -- [x] **Step 2: 实现专用 Controller 和 Service** - -要求: - -- Controller 路径完全匹配 OpenAPI。 -- Service 事务内更新 orderNo、revision、source attribution、command log、audit。 -- 复杂排序逻辑写中文注释说明为什么要整体调整同 chapter orderNo。 - -- [x] **Step 3: 移除 catch-all 路径** - -从 `AppMuseContentContractController` 移除: - -- `/works/{workId}/chapters/{chapterId}/reorder` -- `/works/{workId}/blocks/{blockId}/split` -- `/works/{workId}/blocks/{blockId}/merge` - -- [x] **Step 4: 验证覆盖矩阵** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -jq -r '.operations[] | select(.domain=="content" and (.operationId=="reorderChapters" or .operationId=="splitBlock" or .operationId=="mergeBlocks")) | [.operationId,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 三个 operation 不再是 `catch_all`。 - -## Task 5: 来源归因和 Suggestion Merge - -**Files:** -- Create: `controller/app/AppContentSourceController.java` -- Create: `controller/app/vo/SourceAttributionRespVO.java` -- Create: `controller/app/vo/MergeBlockSuggestionReqVO.java` -- Create: `controller/app/vo/MergeBlockSuggestionRespVO.java` -- Create: `application/ContentSourceService.java` -- Create: `application/ContentSourceServiceImpl.java` -- Create: `application/facade/ContentAiSuggestionFacade.java` -- Modify: `dal/mysql/BlockSourceAttributionMapper.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentSourceServiceTest.java` -- Test: `controller/app/AppContentSourceControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- 来源归因查询必须 owner guard。 -- 不存在 source attribution 返回空 sources 而不是 catch-all。 -- suggestion facade 返回 unavailable 时,`mergeBlockSuggestion` 返回 blocked 错误。 -- suggestion revision/status 不匹配时失败。 -- merge 成功时更新 Block revision,并写 source attribution。 - -- [x] **Step 2: 实现 AI suggestion facade 合同** - -要求: - -- facade 只定义 Content 需要的 projection:suggestionId、status、revision、content、sourceSnapshot、authorizationSnapshot。 -- 默认实现不能伪造成功。没有真实 AI provider 时返回 unavailable,Service 转换为 blocked 错误。 - -- [x] **Step 3: 实现来源归因读模型** - -要求: - -- 从 `muse_content_block_source_attribution` 组装 OpenAPI `SourceAttribution`。 -- 不返回 DO 原始字段名。 - -- [x] **Step 4: 移除 catch-all 路径并验证** - -移除: - -- `/works/{workId}/blocks/{blockId}/source-attribution` -- `/works/{workId}/blocks/{blockId}/suggestion-merges` - -Run P1R 脚本并确认两个 operation 不再是 `catch_all`。 - -## Task 6: Planning 正式稿和 AI 候选 facade - -**Files:** -- Create: `muse-cloud/sql/muse/V9__extend_content_real_api_schema.sql` -- Create: `dal/dataobject/PlanningSectionDO.java` -- Create: `dal/mysql/PlanningSectionMapper.java` -- Create: `controller/app/AppContentPlanningController.java` -- Create: `controller/app/vo/Planning*.java` -- Create: `application/ContentPlanningService.java` -- Create: `application/ContentPlanningServiceImpl.java` -- Create: `application/facade/ContentPlanningCandidateFacade.java` -- Create: `application/facade/ContentStyleCheckFacade.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentPlanningServiceTest.java` -- Test: `controller/app/AppContentPlanningControllerTest.java` - -- [x] **Step 1: 写 DDL** - -最小表: - -- `muse_content_planning_section` - - `id` - - `work_id` - - `section_key` - - `content_payload` - - `schema_version` - - `projection_version` - - `source_snapshot` - - `revision` - - Yudao 标准字段和 `tenant_id` - - 唯一键 `(tenant_id, work_id, section_key)` - -- [x] **Step 2: 写失败测试** - -覆盖: - -- `getPlanning` 返回正式 planning section。 -- `savePlanningItem` 校验 owner、commandId、expectedRevision。 -- `confirmPlanningCandidate` 在 AI facade unavailable 时 blocked。 -- 候选确认成功后写正式 planning section。 -- `discardPlanningCandidate` 不写 Content 正式事实,只记录用户决策审计。 - -- [x] **Step 3: 实现 Service** - -要求: - -- Content 只拥有正式 planning section。 -- AI candidate 和 style check 运行事实必须通过 facade 获取。 -- facade 不可用时明确 blocked。 - -- [x] **Step 4: 移除 catch-all 路径并验证** - -移除 `/works/{workId}/planning` 和 `/works/{workId}/planning/**` 中已实现的具体路径。 - -Run P1R 脚本确认 planning 相关 operation 不再是 catch-all,或在真实外部 owner 未完成时被明确记录为 blocked。 - -## Task 7: Import task 和 Parse job 投影 - -**Files:** -- Create: `dal/dataobject/ImportTaskDO.java` -- Create: `dal/mysql/ImportTaskMapper.java` -- Create: `controller/app/AppContentImportParseController.java` -- Create: `controller/app/vo/Import*.java` -- Create: `controller/app/vo/Parse*.java` -- Create: `application/ContentImportParseService.java` -- Create: `application/ContentImportParseServiceImpl.java` -- Create: `application/facade/ContentFileFacade.java` -- Create: `application/facade/ContentParseJobFacade.java` -- Create: `application/facade/ContentKnowledgeDraftFacade.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentImportParseServiceTest.java` -- Test: `controller/app/AppContentImportParseControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- `createImportTask` 校验 fileName/fileSize/fileHash/format/uploadUrl。 -- 文件 facade 不可用时返回 blocked,不写 completed。 -- `getImportTask` 只能查询自己的 task。 -- `createParseJob` 必须引用已存在且 owner 匹配的 importTask。 -- parse facade 不可用时返回 blocked。 -- confirm/reject/batchConfirm 必须校验 parse result projection revision。 - -- [x] **Step 2: 实现 ImportTask 持久化** - -要求: - -- 使用已有 `muse_content_import_task` 表。 -- 状态只能在 queued/processing/completed/failed 间合法流转。 -- result DTO 包含 progress、failedStage、retryable、nextActions。 - -- [x] **Step 3: 实现 Parse facade 边界** - -要求: - -- Parse job 运行事实由 AI owner 负责。 -- Content 负责 owner guard、importTask 上下文、确认结果后的 Content/Knowledge 写入编排。 -- Knowledge facade 不可用时,不能宣称 `createdDraftIds` 成功。 - -- [x] **Step 4: 移除 catch-all 路径并验证** - -移除: - -- `/works/{workId}/import-tasks` -- `/import-tasks/**` -- `/works/{workId}/parse-jobs` -- `/parse-jobs/**` -- `/chapter-parse-results/**` - -Run P1R 脚本确认相关 operation 状态变化符合事实。 - -## Task 8: Export task 和下载凭证 - -**Files:** -- Create: `dal/dataobject/ExportTaskDO.java` -- Create: `dal/mysql/ExportTaskMapper.java` -- Create: `controller/app/AppContentExportController.java` -- Create: `controller/app/vo/Export*.java` -- Create: `application/ContentExportService.java` -- Create: `application/ContentExportServiceImpl.java` -- Modify: `application/facade/ContentFileFacade.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentExportServiceTest.java` -- Test: `controller/app/AppContentExportControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- `createExportTask` 校验 owner、format、includeChapters 从属关系。 -- 重复 commandId 返回同一 export task。 -- FileService 不可用时返回 blocked。 -- `getExportTask` 跨 owner 失败。 -- `downloadExportPackage` 校验 credentialId、owner、过期时间、任务 completed。 - -- [x] **Step 2: 实现 ExportTask 持久化** - -要求: - -- 使用已有 `muse_content_export_task` 表。 -- 如下载凭证字段不足,在 `V9` 增加 `download_expires_at`、`completed_at`、`progress`、`source_snapshot`。 - -- [x] **Step 3: 实现 download** - -要求: - -- 返回真实文件响应或 Yudao 文件服务可下载响应。 -- 记录下载审计。 -- 不返回固定 URL 或空响应。 - -- [x] **Step 4: 移除 catch-all 路径并验证** - -移除: - -- `/works/{workId}/export` -- `/works/{workId}/export-tasks` -- `/export-tasks/**` -- `/downloads/**` - -Run P1R 脚本确认 export/download operation 状态变化符合事实。 - -## Task 9: Meta 投影和动态字段校验 - -**Files:** -- Create: `controller/app/AppContentMetaProjectionController.java` -- Create: `controller/app/vo/MetaProjection*.java` -- Create: `controller/app/vo/DynamicField*.java` -- Create: `application/ContentMetaProjectionService.java` -- Create: `application/ContentMetaProjectionServiceImpl.java` -- Create: `application/facade/ContentMetaFacade.java` -- Modify: `controller/app/AppMuseContentContractController.java` -- Test: `application/ContentMetaProjectionServiceTest.java` -- Test: `controller/app/AppContentMetaProjectionControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- 所有接口先校验 work owner。 -- Meta facade unavailable 时返回 blocked。 -- facade 成功时 DTO 对齐 OpenAPI。 -- `validateDynamicFields` 不写 Content 表。 - -- [x] **Step 2: 实现 Meta facade 合同** - -要求: - -- Content 不复制 MetaSchema 规则。 -- facade 返回 projection summary/detail 和 field validation result。 -- 默认 unavailable 实现必须让 operation 显式 blocked。 - -- [x] **Step 3: 移除 catch-all 路径并验证** - -移除: - -- `/works/{workId}/meta-projections` -- `/works/{workId}/meta-projections/{projectionKey}` -- `/works/{workId}/dynamic-fields/validate` - -Run P1R 脚本确认不再由 catch-all 处理。 - -## Task 10: Admin Content 治理 API - -**Files:** -- Create: `controller/admin/AdminContentController.java` -- Create: `controller/admin/vo/AdminWork*.java` -- Create: `controller/admin/vo/AdminChapter*.java` -- Create: `controller/admin/vo/RiskAction*.java` -- Create: `controller/admin/vo/AdminImportTask*.java` -- Create: `controller/admin/vo/AdminExportTask*.java` -- Create: `application/ContentAdminService.java` -- Create: `application/ContentAdminServiceImpl.java` -- Create: `dal/dataobject/ContentGovernanceActionDO.java` -- Create: `dal/mysql/ContentGovernanceActionMapper.java` -- Modify: `muse-cloud/sql/muse/V9__extend_content_real_api_schema.sql` -- Modify: `controller/admin/AdminMuseContentContractController.java` -- Test: `application/ContentAdminServiceTest.java` -- Test: `controller/admin/AdminContentControllerTest.java` - -- [x] **Step 1: 写失败测试** - -覆盖: - -- admin list/get/chapter 接口需要 RBAC。 -- admin 读模型不返回正文全文。 -- `adminRiskAction` 缺 reason 失败。 -- `adminRiskAction` expectedVersion 冲突失败。 -- 重复 commandId 返回同一 actionId。 - -- [x] **Step 2: 实现治理动作持久化** - -DDL 最小表:`muse_content_governance_action` - -- `id` -- `work_id` -- `operator_user_id` -- `action` -- `reason` -- `target_scope` -- `target_ids` -- `expected_version` -- `status` -- `audit_log_id` -- Yudao 标准字段和 `tenant_id` - -- [x] **Step 3: 实现 admin read model** - -要求: - -- work summary/detail 从 Content 表和治理动作组装。 -- import/export task summary 从对应任务表组装。 -- 不使用 catch-all 或 operation record。 - -- [x] **Step 4: 移除 admin catch-all 路径并验证** - -移除: - -- `/content/works` -- `/content/works/**` -- `/content/import-tasks` -- `/content/export-tasks` - -Run P1R 脚本确认 admin Content operation 不再是 catch-all。 - -## Task 11: P1R-1 完成门禁和报告更新 - -**Files:** -- Modify: `docs/superpowers/reports/p1r-api-coverage.json` -- Modify: `docs/superpowers/reports/p1r-api-coverage.md` -- Modify/Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java` -- Create: `docs/memorys/YYYY-MM-DD-P1R1内容真实API收口.md` only if user asks for memory trace - -- [x] **Step 1: 重新运行 P1R 审计** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: - -- 报告可生成。 -- Content 51 个 operation 仍完整。 -- 已退役 operation 不再是 `catch_all`。 -- 外部 owner 未接通的 operation 明确显示 blocked 或仍 incomplete,不能伪装 completed。 - -- [x] **Step 2: 增加 Content 阶段门禁测试** - -`P1rContentRealApiGateTest` 至少校验: - -- Content operation 数量为 51。 -- Content operation 不允许 `generic_persistence` 或 `sse_placeholder`。 -- Content operation 如果 `implementationStatus=catch_all`,必须有明确 blocked/incomplete 说明,不能是 `completed`。 -- 已列入 P1R-1 完成清单的 operation 必须不是 catch-all。 - -- [x] **Step 3: 运行 Maven 门禁** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: P1R 门禁测试通过。 - -- [x] **Step 4: 运行 Content 模块测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-content/muse-module-content-server -am \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: Content 模块测试通过。 - -- [x] **Step 5: 最终对账** - -Run: - -```bash -jq -r '.operations[] | select(.domain=="content") | [.operationId,.implementationStatus,.completionStatus,.notes] | @tsv' \ - docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -- 每个 operation 状态可解释。 -- 没有任何 Content operation 被误标为完成。 -- blocked 项有真实 owner 和下一步。 - -## 3. 回滚策略 - -1. 每个任务单独提交,回滚到上一任务不影响更早任务。 -2. DDL 只追加 Flyway 文件,不修改历史 V1-V8。 -3. catch-all 路径移除必须在专用 Controller 和测试通过后做;如回滚,恢复对应 catch-all 路径即可恢复合同保护网。 -4. 外部 facade 默认 blocked,不产生不可逆外部副作用。 - -## 4. 完成定义 - -P1R-1 只有在以下条件满足时才可确认完成: - -1. 规格中 51 个 Content operation 全部在实现对账表中有状态。 -2. 所有 Content 自有事实 operation 已退出 catch-all 并有测试。 -3. 跨领域 operation 不伪造成功;未接通的真实 owner 明确 blocked。 -4. P1R 覆盖脚本通过。 -5. Content 模块测试通过。 -6. P1R 门禁测试通过。 -7. 用户确认是否需要写 `docs/memorys/YYYY-MM-DD-P1R1内容真实API收口.md` 留痕。 diff --git a/docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md b/docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md deleted file mode 100644 index 9da33fed..00000000 --- a/docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md +++ /dev/null @@ -1,787 +0,0 @@ -# P1R-2 Meta Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 Meta 16 个 operation 从 `generic_persistence / incomplete` 推进到专用 Meta Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`。 - -**Architecture:** 以 `muse-module-meta` 为唯一 Meta owner,新增 MetaSchema、ProtectionNode、FunctionChain 专用入口和应用服务。通用 `MuseContractPersistenceService` 只能作为迁移保护网,不能作为 P1R-2 完成路径;跨 owner 影响通过 facade、投影读模型或 outbox/job 表达,外部不可用时返回合同内 `CommonResult` 业务错误。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 - ---- - -## 0. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的 `feature/p1-muse-cloud-backend-20260524` checkout 上落实现。 -- 不修改 `muse-cloud/scripts/**` 来改变 P1R 判断口径。 -- 不修改 `docs/api-contracts/meta/openapi.yaml`,除非先单独说明合同纠偏原因、影响范围和迁移风险。 -- 不破坏 Content 当前 51 个 `dedicated / needs_verification`。 -- 不把 Meta operation 标为 `completed`。 - -## 1. 当前事实盘点 - -| operationId | Method | Path | 当前状态 | 当前实现 owner | 计划处理 | -|---|---|---|---|---|---| -| `listMetaSchemas` | GET | `/admin-api/muse/governance/meta-schemas` | `generic_persistence / incomplete` | `AdminMuseMetaContractController` + `MuseContractPersistenceService` | Task 4 | -| `getMetaSchema` | GET | `/admin-api/muse/governance/meta-schemas/{schemaKey}` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `getMetaSchemaVersion` | GET | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `saveMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `validateMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/validate` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `previewMetaSchemaDraftImpact` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/impact-preview` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `publishMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/publish` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `activateMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/activate` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `rollbackMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/rollback` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `deprecateMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/deprecate` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `setMetaSchemaGrayRules` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/gray-rules` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `listProtectionNodes` | GET | `/admin-api/muse/governance/protection-nodes` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `getProtectionNode` | GET | `/admin-api/muse/governance/protection-nodes/{nodeKey}` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `listFunctionChains` | GET | `/admin-api/muse/governance/function-chains` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `previewFunctionChainImpact` | POST | `/admin-api/muse/governance/function-chains/{chainKey}/impact-preview` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `activateFunctionChainVersion` | POST | `/admin-api/muse/governance/function-chains/{chainKey}/versions/{version}/activate` | `generic_persistence / incomplete` | 同上 | Task 8 | - -## 2. 目标文件结构 - -| 操作 | 路径 | 责任 | -|---|---|---| -| Modify | `muse-cloud/muse-module-meta/muse-module-meta-server/pom.xml` | 补 validation/test 依赖,按需补 tenant starter | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaController.java` | MetaSchema 专用 admin API | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminProtectionNodeController.java` | ProtectionNode 专用 admin API | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminFunctionChainController.java` | FunctionChain 专用 admin API | -| Modify | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` | 移除或避让 P1R-2 已 dedicated 路径 | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/vo/*.java` | OpenAPI DTO | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/*.java` | Meta 应用服务 | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/facade/*.java` | 跨 owner 影响 facade | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/*.java` | 版本、权限、保护节点、功能链 guard | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/*.java` | Meta DO | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/mysql/*.java` | Meta Mapper | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/convert/MetaConvert.java` | DO/facade 到 VO 组装 | -| Create | `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/enums/ErrorCodeConstants.java` | Meta 错误码 | -| Create | `muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql` | 追加 Meta 真实 API 表和约束 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java` | P1R-2 阶段门禁 | - -## 3. 子代理执行规则 - -每个 Task 使用 fresh subagent,模型 `gpt-5.5`,reasoning `xhigh`,等待超时 30 分钟。 - -每个实现 Task 完成后必须两轮 review: - -1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md` 和本计划。 -2. code quality review:检查最小改动、可维护性、测试质量、无脚本掩盖。 - -review 未通过不能进入下一 Task。所有 review 发现的问题必须由新的修复子代理处理,再重新 review。 - -## Task 1: Preflight 与覆盖基线冻结 - -**Files:** - -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/api-contracts/meta/openapi.yaml` -- Read: `muse-cloud/muse-module-meta/**` -- Read: `muse-cloud/sql/muse/**` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -除本规格和本计划尚未提交时可能出现的两行 untracked 文档外,无其他输出: - -```text -?? docs/superpowers/plans/2026-05-27-P1R-2-meta-real-api.md -?? docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md -``` - -- [ ] **Step 2: 统计 Meta operation 状态** - -Run: - -```bash -jq -r '[.operations[] | select(.domain == "meta")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -generic_persistence/incomplete 16 -``` - -- [ ] **Step 3: 冻结 operation 清单** - -Run: - -```bash -jq -r '.operations[] | select(.domain == "meta") | [.operationId, .method, .path, .implementationStatus, .completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 16 行,且 operationId 与本计划第 1 节完全一致。 - -- [ ] **Step 4: 检查脚本目录无改动** - -Run: - -```bash -git status --short -- muse-cloud/scripts -``` - -Expected: 无输出。 - -## Task 2: Meta 基础设施 - -**Files:** - -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/pom.xml` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/MetaApiVersionGuard.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/MetaVersionStateGuard.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/MetaProtectionNodeGuard.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/FunctionChainGuard.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaCommandService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaCommandServiceImpl.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaAuditService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaAuditServiceImpl.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaCommandDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaGovernanceAuditDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/mysql/MetaCommandMapper.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/mysql/MetaGovernanceAuditMapper.java` -- Create: `muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql` -- Test: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaMigrationSqlTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/domain/*Test.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaCommandServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaAuditServiceTest.java` - -- [ ] **Step 1: 补模块依赖** - -确保 `muse-module-meta-server/pom.xml` 包含: - -- `muse-spring-boot-starter-security` -- `muse-spring-boot-starter-mybatis` -- `spring-boot-starter-validation` -- `muse-spring-boot-starter-test`,scope 为 test - -- [ ] **Step 2: 实现 API version guard** - -`MetaApiVersionGuard.requireVersion(String version)` 只接受 `"1"`,缺失或未知版本抛 `META_API_VERSION_UNSUPPORTED`。 - -- [ ] **Step 3: 实现 command service** - -规则: - -- `buildRequestHash(Object payload)` 使用稳定 JSON + SHA-256。 -- `reserveCommand(...)` 预占 commandId。 -- `getReplayCommand(...)` 相同 hash 返回旧记录。 -- 相同 commandId 不同 hash 抛 `META_COMMAND_ID_CONFLICT`。 -- envelope 必须校验 operationId、actor、targetType、targetKey、targetVersion。 - -- [ ] **Step 4: 实现 audit service** - -记录字段必须覆盖 spec 第 10 节,不记录敏感全文。 - -- [ ] **Step 5: 编写 V10 迁移** - -至少新增: - -- `muse_meta_command` -- `muse_meta_governance_audit` -- `muse_meta_validation_result` -- `muse_meta_impact_preview` -- `muse_meta_gray_rule` -- `muse_meta_function_chain_version` -- `muse_meta_function_chain_slot` -- `muse_meta_function_chain_node` - -并追加: - -- `muse_meta_schema_version` active 唯一 partial index,唯一边界必须是 `(tenant_id, schema_id)`,不能只按 `(tenant_id, schema_key)` 约束。 -- `muse_meta_function_chain_version` active 唯一 partial index。 -- ProtectionNode 缺失结构化字段。 - -- [ ] **Step 6: 运行聚焦测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=MetaApiVersionGuardTest,MetaVersionStateGuardTest,MetaProtectionNodeGuardTest,FunctionChainGuardTest,MetaCommandServiceTest,MetaAuditServiceTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -- [ ] **Step 7: 验证迁移语法和 schema identity** - -Run: - -```bash -rg -n "schema_key.*active|active_flag|schema_id|muse_meta_schema_version" muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql -``` - -Expected: - -- active 唯一索引使用 `tenant_id, schema_id`。 -- 没有新增 `tenant_id, schema_key` 的 active 唯一索引。 -- 如果实现需要变更 V3 既有 `uk_muse_meta_schema_version_key`,必须先在 review 中单独说明兼容风险,不能静默替换历史迁移。 - -- [ ] **Step 8: 增加迁移 SQL 聚焦门禁** - -新增 `P1rMetaMigrationSqlTest`,至少断言: - -- `V10__extend_meta_real_api_schema.sql` 存在。 -- V10 包含 `muse_meta_command`、`muse_meta_governance_audit`、`muse_meta_validation_result`、`muse_meta_impact_preview`、`muse_meta_function_chain_version`。 -- V10 的 MetaSchema active partial unique index 使用 `tenant_id, schema_id`。 -- V10 没有新增 `tenant_id, schema_key` 的 active partial unique index。 - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rMetaMigrationSqlTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -如本机或远端提供 PostgreSQL 验证库,再执行: - -```bash -psql "$MUSE_TEST_DATABASE_URL" -v ON_ERROR_STOP=1 -f muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql -``` - -Expected: PostgreSQL 接受迁移。若缺少 `MUSE_TEST_DATABASE_URL`,最终汇报必须说明未做真实 PG/Flyway 迁移验证,Meta 只能保持 `needs_verification`。 - -## Task 3: Meta DAL 与 DTO 骨架 - -**Files:** - -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaSchemaDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaSchemaVersionDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaFieldDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaVisibilityPolicyDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaValidationResultDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaImpactPreviewDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/MetaGrayRuleDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/ProtectionNodeDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/FunctionChainDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/FunctionChainVersionDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/FunctionChainSlotDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/dataobject/FunctionChainNodeDO.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/dal/mysql/*.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/vo/*.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/convert/MetaConvert.java` - -- [ ] **Step 1: 建 DO / Mapper** - -DO 必须对齐 V3 + V10 表字段。JSONB 字段复用项目已有 JSON 类型处理方式,不能在业务层手写脆弱字符串拼接。 - -- [ ] **Step 2: 建 OpenAPI VO** - -VO 必须覆盖 spec 第 6.3 节的 DTO 列表,字段命名对齐 `docs/api-contracts/meta/openapi.yaml`。 - -- [ ] **Step 3: 建转换器** - -`MetaConvert` 负责 DO / facade DTO 到 VO 的组装。Controller 不直接拼 JSON。 - -- [ ] **Step 4: 编译验证** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -DskipTests \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 编译通过。 - -## Task 4: MetaSchema 查询 API - -**Files:** - -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaController.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaServiceImpl.java` -- Read: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaControllerTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaSchemaServiceTest.java` - -- [ ] **Step 1: 实现 list/get/version 查询** - -覆盖: - -- `listMetaSchemas` -- `getMetaSchema` -- `getMetaSchemaVersion` - -要求: - -- 校验 `X-API-Version: 1`。 -- `@PreAuthorize("@ss.hasPermission('muse:meta:query')")`。 -- 返回 OpenAPI DTO,不返回 DO 或 Map。 -- Controller 测试必须断言响应 envelope 是 `code/data/msg`,且不存在 `message` 字段。 -- 详情必须包含 active version、gray rules、fields、impact summary。 -- version 详情必须包含 fields、diff、publishRecord。 - -- [ ] **Step 2: 标记待退役路由** - -本 Task 不单独改 `AdminMuseMetaContractController`。所有 P1R-2 路由统一在 Task 9 退役,避免半退役导致 coverage 误判。 - -- [ ] **Step 3: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=AdminMetaSchemaControllerTest,MetaSchemaServiceTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -## Task 5: 草稿、校验、影响预览 - -**Files:** - -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaController.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaService.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaServiceImpl.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaValidationService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaValidationServiceImpl.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaImpactPreviewService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaImpactPreviewServiceImpl.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/facade/MetaImpactFacade.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaSchemaValidationServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaSchemaImpactPreviewServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaControllerTest.java` - -- [ ] **Step 1: 实现 save draft** - -覆盖 `saveMetaSchemaDraft`。 - -要求: - -- 必填 `commandId`。 -- requestHash 幂等。 -- 权限 `muse:meta:draft`。 -- `expectedVersion` 与当前 active 或 draft 基线冲突时返回版本冲突。 -- 保存 fields / visibility / effective scope。 -- 返回 `draftVersion`、`validationSummary`、`currentActiveVersion`。 - -- [ ] **Step 2: 实现 validate draft** - -覆盖 `validateMetaSchemaDraft`。 - -要求: - -- 校验字段类型、必填、枚举、引用、兼容性、保护节点边界。 -- 持久化 `validationResultId`。 -- 当前 OpenAPI 无 body;使用 `schemaKey + draftVersion + actor + draftHash` 作为等价幂等键。 -- 权限 `muse:meta:draft`。 -- Controller 测试必须断言响应 envelope 是 `code/data/msg`,且不存在 `message` 字段。 - -- [ ] **Step 3: 实现 impact preview** - -覆盖 `previewMetaSchemaDraftImpact`。 - -要求: - -- 持久化 `impactPreviewId`。 -- 返回 work/planning/knowledge/ai/export 影响摘要。 -- 使用 `schemaKey + draftVersion + actor + draftHash` 作为等价幂等键;重复请求返回同一个 `impactPreviewId`。 -- 外部 owner 不可用时返回 `CommonResult` 业务错误 `META_EXTERNAL_OWNER_UNAVAILABLE`,不往 OpenAPI DTO 添加 blocked 字段,不伪造影响数量。 -- 预览绑定 draftHash,draft 更新后旧 preview 不可用于发布。 -- 权限 `muse:meta:draft`。 - -- [ ] **Step 4: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=MetaSchemaServiceTest,MetaSchemaValidationServiceTest,MetaSchemaImpactPreviewServiceTest,AdminMetaSchemaControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -## Task 6: 发布、激活、回滚、废弃、灰度 - -**Files:** - -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaController.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaService.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/MetaSchemaServiceImpl.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/MetaVersionStateGuard.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/MetaSchemaServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaControllerTest.java` - -- [ ] **Step 1: 实现 publish** - -覆盖 `publishMetaSchemaDraft`。 - -要求: - -- 必填 `commandId`、`reason`、`expectedVersion`、`validationResultId`、`impactPreviewId`。 -- validation / preview 必须同源同 draftHash。 -- draft 必须 valid。 -- 记录 audit。 -- 权限 `muse:meta:publish`。 - -- [ ] **Step 2: 实现 activate** - -覆盖 `activateMetaSchemaVersion`。 - -要求: - -- 必填 `commandId`、`reason`、`expectedActiveVersion`、`validationResultId`、`impactPreviewId`。 -- 只能激活 published 版本。 -- 数据库和服务层都保证唯一 active。 -- 生成 projection rebuild job id 时只能记录 Meta outbox/job,不直接写其他 owner。 -- 权限 `muse:meta:activate`。 - -- [ ] **Step 3: 实现 rollback** - -覆盖 `rollbackMetaSchemaVersion`。 - -要求: - -- 只能回滚到已发布且未废弃版本。 -- 必填 `commandId`、`reason`、`expectedActiveVersion`、`validationResultId`、`impactPreviewId`。 -- 校验 `expectedActiveVersion`,validation / preview 必须同源同目标版本。 -- 记录 rollback_from / rollback_to。 -- projection invalidate/rebuild 外部 owner 不可用时返回 `META_EXTERNAL_OWNER_UNAVAILABLE` 或记录 Meta outbox/job pending,不能伪造完成。 -- 权限 `muse:meta:rollback`。 - -- [ ] **Step 4: 实现 deprecate** - -覆盖 `deprecateMetaSchemaVersion`。 - -要求: - -- 支持字段级和版本级废弃。 -- 必填 `commandId`、`reason`、`expectedVersion`、`validationResultId`、`impactPreviewId`。 -- 字段废弃必须有 replacement 或 retention/migration 提示。 -- 权限 `muse:meta:deprecate`。 - -- [ ] **Step 5: 实现 gray rules** - -覆盖 `setMetaSchemaGrayRules`。 - -要求: - -- 必填 `commandId`、`reason`。 -- 支持 set / clear / adjust。 -- 返回 affectedScope 和 rollbackEntry。 -- 权限 `muse:meta:gray-rule`。 - -- [ ] **Step 6: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=MetaSchemaServiceTest,AdminMetaSchemaControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过,覆盖重复 commandId、hash 冲突、expectedVersion / expectedActiveVersion 冲突、非法状态流转、权限和 audit;Controller 测试必须断言 `code/data/msg` envelope。 - -## Task 7: ProtectionNode API - -**Files:** - -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminProtectionNodeController.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/ProtectionNodeService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/ProtectionNodeServiceImpl.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/MetaProtectionNodeGuard.java` -- Read: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/ProtectionNodeServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminProtectionNodeControllerTest.java` - -- [ ] **Step 1: 实现 list/get** - -覆盖: - -- `listProtectionNodes` -- `getProtectionNode` - -要求: - -- 权限 `muse:meta:query`。 -- 返回 OpenAPI DTO。 -- Controller 测试必须断言响应 envelope 是 `code/data/msg`,且不存在 `message` 字段。 -- `irreplaceable`、`irreplaceableReason`、`permissionRequirement`、`auditRequirement`、`shadowToCanonicalBoundary` 必须来自持久化或明确治理配置。 - -- [ ] **Step 2: 实现不可替换 guard** - -ProtectionNode 被 FunctionChain 或草稿字段影响时,必须能被 guard 阻断。 - -- [ ] **Step 3: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=ProtectionNodeServiceTest,AdminProtectionNodeControllerTest,MetaProtectionNodeGuardTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -## Task 8: FunctionChain API - -**Files:** - -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminFunctionChainController.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/FunctionChainService.java` -- Create: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/application/FunctionChainServiceImpl.java` -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/domain/FunctionChainGuard.java` -- Read: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/application/FunctionChainServiceTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminFunctionChainControllerTest.java` - -- [ ] **Step 1: 实现 list** - -覆盖 `listFunctionChains`。 - -要求: - -- 返回 activeVersion、openSlotCount、protectionNodeCount。 -- 开放槽位不能包含保护节点降级结果。 -- 权限 `muse:meta:query`。 - -- [ ] **Step 2: 实现 impact preview** - -覆盖 `previewFunctionChainImpact`。 - -要求: - -- 必填 `commandId`。 -- 返回 `impactPreviewId`、`validationResultId`、affectedAgentSlotBindings、affectedAIRuntimeTasks、ProtectionNode 影响。 -- New-API/SSE/AI runtime 无真实 owner 时,返回 `META_EXTERNAL_OWNER_UNAVAILABLE` 或只记录 Meta outbox/job pending,不往 OpenAPI DTO 添加 blocked 字段。 -- 权限 `muse:meta:function-chain`。 - -- [ ] **Step 3: 实现 activate** - -覆盖 `activateFunctionChainVersion`。 - -要求: - -- 必填 `commandId`、`reason`、`validationResultId`、`impactPreviewId`、`expectedActiveVersion`。 -- 影响预览必须同源同 version。 -- 保护节点降级必须阻断。 -- 外部 runtime 副作用只记录 outbox/job pending 或返回 `META_EXTERNAL_OWNER_UNAVAILABLE`,不直接伪造完成。 -- 权限 `muse:meta:function-chain`。 - -- [ ] **Step 4: 运行测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=FunctionChainServiceTest,AdminFunctionChainControllerTest,FunctionChainGuardTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过;Controller 测试必须断言 `code/data/msg` envelope。 - -## Task 9: Meta 合同兜底路由退役 - -**Files:** - -- Modify: `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractControllerTest.java` -- Test: `muse-cloud/muse-module-meta/muse-module-meta-server/src/test/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaRouteOwnershipTest.java` - -- [ ] **Step 1: 删除或收窄 P1R-2 路由兜底** - -`AdminMuseMetaContractController` 不得继续匹配以下 16 个 OpenAPI 路径: - -- `/muse/governance/meta-schemas` -- `/muse/governance/meta-schemas/{schemaKey}` -- `/muse/governance/meta-schemas/{schemaKey}/versions/{version}` -- `/muse/governance/meta-schemas/{schemaKey}/drafts` -- `/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/validate` -- `/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/impact-preview` -- `/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/publish` -- `/muse/governance/meta-schemas/{schemaKey}/versions/{version}/activate` -- `/muse/governance/meta-schemas/{schemaKey}/versions/{version}/rollback` -- `/muse/governance/meta-schemas/{schemaKey}/versions/{version}/deprecate` -- `/muse/governance/meta-schemas/{schemaKey}/versions/{version}/gray-rules` -- `/muse/governance/protection-nodes` -- `/muse/governance/protection-nodes/{nodeKey}` -- `/muse/governance/function-chains` -- `/muse/governance/function-chains/{chainKey}/impact-preview` -- `/muse/governance/function-chains/{chainKey}/versions/{version}/activate` - -优先方案:删除 `AdminMuseMetaContractController`。如果还有非 P1R-2 Meta 合同路径必须保留,必须把兜底路径收窄到这些非 P1R-2 路径,并在测试里证明 P1R-2 路径不会进入 `MuseContractPersistenceService`。 - -- [ ] **Step 2: 增加路由 owner 测试** - -`AdminMetaRouteOwnershipTest` 至少证明: - -- 16 个 P1R-2 路由都命中 dedicated Controller。 -- 16 个 P1R-2 路由不会调用 `MuseContractPersistenceService.handle(...)`。 -- 没有 wildcard `/**` 把 dedicated 路由重新兜住。 - -- [ ] **Step 3: 运行路由测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dtest=AdminMetaRouteOwnershipTest,AdminMuseMetaContractControllerTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过,且 `AdminMuseMetaContractController` 不再接管 P1R-2 路由。 - -## Task 10: P1R-2 覆盖门禁与报告对账 - -**Files:** - -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java` -- Modify: `docs/superpowers/reports/p1r-api-coverage.json` -- Modify: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `muse-cloud/scripts/p1r-audit-api-coverage.py` - -- [ ] **Step 1: 运行覆盖审计** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: - -- 脚本成功。 -- Content 仍是 51 个 `dedicated / needs_verification`。 -- Meta 16 个 operation 是 `dedicated / needs_verification`。 -- Meta 不出现 `completed`。 - -如果脚本不能识别 dedicated Meta 实现,先分析脚本识别规则和代码结构差异;不能直接改脚本掩盖缺口。需要修改脚本时必须先说明这是扫描规则纠偏还是状态口径变更,并单独取得确认。 - -- [ ] **Step 2: 增加 Meta 阶段门禁测试** - -`P1rMetaRealApiGateTest` 至少校验: - -- Meta operation 数量为 16。 -- Meta operation 清单与本计划第 1 节一致。 -- Meta operation 不允许 `generic_persistence`、`sse_placeholder`、`missing`。 -- Meta operation 不允许 `completionStatus=completed`。 -- Meta operation 必须全部保持 `dedicated / needs_verification`,不能使用 `blocked` 或 `completed` 作为 P1R-2 收口状态。 - -- [ ] **Step 3: 运行 P1R 门禁** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -- [ ] **Step 4: 运行 Meta 模块全量测试** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-meta/muse-module-meta-server -am \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -Expected: 测试通过。 - -- [ ] **Step 5: 基础卫生检查** - -Run: - -```bash -git diff --check -git status --short -- muse-cloud/scripts -``` - -Expected: - -- `git diff --check` 无输出。 -- `muse-cloud/scripts/**` 无改动,除非用户已批准扫描规则纠偏。 - -## 4. Review 门禁 - -每个 Task 完成后交给两个 fresh subagent: - -### Spec compliance review - -检查: - -- 是否完全覆盖对应 operation。 -- 是否符合 P1R-2 spec。 -- 是否没有标 `completed`。 -- 是否没有用 generic persistence / operation record / sample JSON 伪装完成。 -- 是否没有越权写其他 owner。 - -### Code quality review - -检查: - -- 改动是否最小。 -- Controller / Service / Domain / DAL 是否职责清晰。 -- DTO 是否对齐 OpenAPI。 -- 测试是否覆盖成功、冲突、非法状态、权限、幂等、审计。 -- 是否无无关重构。 -- 是否无 `muse-cloud/scripts/**` 掩盖性修改。 - -## 5. 回滚策略 - -- 文档与测试回滚:回退本阶段新增 spec/plan/gate test。 -- 代码回滚:回退 `muse-module-meta` 新增 Controller/Service/DTO/DAL/Domain。 -- DDL 回滚:开发阶段可删除未合入迁移;迁移合入后不得修改历史 V10,只能新增 V11 修正。 -- 覆盖报告回滚:重新运行 P1R audit,确保 Content 保持 P1R-1 状态。 - -## 6. 完成条件 - -P1R-2 实现阶段完成时必须同时满足: - -1. Meta 16 个 operation 有专用实现。 -2. Meta 不存在 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing` 残留。 -3. Meta 全部保持 `dedicated / needs_verification`,不存在 `completed` 或 `blocked` 收口状态。 -4. Meta 模块测试通过。 -5. P1R 门禁测试通过。 -6. `muse-cloud/scripts/**` 未被用来掩盖缺口。 -7. Content 51 个 operation 仍为 `dedicated / needs_verification`。 -8. 写入 `docs/memorys/YYYY-MM-DD-P1R2元治理真实API收口.md` 留痕。 diff --git a/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md b/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md deleted file mode 100644 index 67800ee9..00000000 --- a/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md +++ /dev/null @@ -1,895 +0,0 @@ -# P1R-3 Account Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. `superpowers:executing-plans` is acceptable only when subagents are unavailable. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 Account 33 个 operation 从 `generic_persistence / incomplete` 推进到专用 Account Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`。 - -**Architecture:** 以 `muse-module-member` 内 Account 子域为 owner,新增 Account 专用 app/admin 入口和应用服务。通用 `MuseContractPersistenceService` 只能作为迁移保护网,不能作为 P1R-3 完成路径;New-API、Market、AI、Knowledge、FileService 等外部 owner 不可用时返回合同内 `CommonResult` 业务错误或 pending job,不伪造完成。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 - -**Implementation Status:** 2026-05-29 已按 Task 1-12 执行并完成 review 收口;Account 33 个 operation 当前为 `dedicated / needs_verification`,真实 runtime owner 未闭合前不得写成 `completed`。实现阶段留痕见 `docs/memorys/2026-05-28-P1R3账户真实API规格计划.md`。 - ---- - -## 0. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的 `feature/p1-muse-cloud-backend-20260524` checkout 上落实现。 -- 不修改 `muse-cloud/scripts/**` 来改变 P1R 判断口径。 -- 不修改 `docs/api-contracts/account/openapi.yaml`,除非先单独说明合同纠偏原因、影响范围和迁移风险。 -- 不破坏 Content 当前 51 个 `dedicated / needs_verification`。 -- 不破坏 Meta 当前 16 个 `dedicated / needs_verification`。 -- 不把 Account operation 标为 `completed`。 -- 本文件是后续实现阶段的执行计划;本次 P1R-3 规格计划会话只产出 spec + plan + review + memory,收到实现指令后必须按 Task 1-12 执行。 - -## 1. 当前事实盘点 - -| operationId | Method | Path | 当前状态 | 当前实现 owner | 计划处理 | -|---|---|---|---|---|---| -| `adminListAccountUsers` | GET | `/admin-api/muse/account/users` | `generic_persistence / incomplete` | `AdminMuseAccountContractController` + `MuseContractPersistenceService` | Task 4 | -| `adminGetUserEntitlements` | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminCreateQuotaAdjustment` | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminListQuotaAdjustments` | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminListNewApiBindings` | GET | `/admin-api/muse/account/new-api-bindings` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `adminCreateNewApiBinding` | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `adminCreateQuotaRequest` | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `adminGetBalanceSnapshots` | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminCreateCallAttributionJob` | POST | `/admin-api/muse/account/call-attribution-jobs` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `adminGetCallAttributionJob` | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `adminGetIntegrationCallByCorrelation` | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `adminListUsageRecords` | GET | `/admin-api/muse/account/usage-records` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminListPurchaseRecords` | GET | `/admin-api/muse/account/purchase-records` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `getCurrentUser` | GET | `/app-api/muse/me` | `generic_persistence / incomplete` | `AppMuseAccountContractController` + `MuseContractPersistenceService` | Task 4 | -| `getProfile` | GET | `/app-api/muse/profile` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `updateProfile` | PATCH | `/app-api/muse/profile` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `getAppEntitlements` | GET | `/app-api/muse/account/entitlements` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `getAppUsage` | GET | `/app-api/muse/account/usage` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `getAppNewApiBinding` | GET | `/app-api/muse/account/new-api-binding` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `appRecheckNewApiBinding` | POST | `/app-api/muse/account/new-api-binding/recheck` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `getAppBalanceSnapshots` | GET | `/app-api/muse/account/balance-snapshots` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `appCreateQuotaRequest` | POST | `/app-api/muse/account/quota-requests` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `appGetQuotaRequest` | GET | `/app-api/muse/account/quota-requests/{requestId}` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `appGetIntegrationCallByCorrelation` | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | 同上 | Task 6 | -| `appListPurchases` | GET | `/app-api/muse/account/purchases` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `appListLicenses` | GET | `/app-api/muse/account/licenses` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `appListPublishRecords` | GET | `/app-api/muse/account/publish-records` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `appListSecurityEvents` | GET | `/app-api/muse/account/security-events` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `appGetSecurityEvent` | GET | `/app-api/muse/account/security-events/{eventId}` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `appAcknowledgeSecurityEvent` | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `appCreateExportTask` | POST | `/app-api/muse/account/export-tasks` | `generic_persistence / incomplete` | 同上 | Task 10 | -| `appGetExportTask` | GET | `/app-api/muse/account/export-tasks/{taskId}` | `generic_persistence / incomplete` | 同上 | Task 10 | -| `appDownloadExport` | GET | `/app-api/muse/account/downloads/{credentialId}` | `generic_persistence / incomplete` | 同上 | Task 10 | - -## 2. 目标文件结构 - -| 操作 | 路径 | 责任 | -|---|---|---| -| Modify | `muse-cloud/muse-module-member/muse-module-member-server/pom.xml` | 确认已有 validation/security/mybatis/test 依赖,按需补测试依赖 | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/*.java` | Account 管理端 dedicated API | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/*.java` | Account 用户端 dedicated API | -| Modify | `AdminMuseAccountContractController.java` | 移除或避让 P1R-3 已 dedicated 路径 | -| Modify | `AppMuseAccountContractController.java` | 移除或避让 P1R-3 已 dedicated 路径 | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/*/account/vo/*.java` | Account OpenAPI DTO | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/*.java` | Account 应用服务 | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/*.java` | New-API / Market projection / FileService / AttributionSource 外部边界;AI / Knowledge 只能经 `AccountAttributionSourceFacade` 的 unavailable 默认实现表达,不新增独立 AI / Knowledge facade | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/*.java` | version、owner、permission、quota、download guard | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/*.java` | Account DO | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/*.java` | Account Mapper | -| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/convert/account/AccountConvert.java` | DO/facade 到 VO 组装 | -| Modify | `muse-cloud/muse-module-member/muse-module-member-api/src/main/java/cn/iocoder/muse/module/member/enums/ErrorCodeConstants.java` | Account 错误码 | -| Create | `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql` | Account 真实 API 追加表和约束 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java` | P1R-3 阶段门禁 | - -## 3. 子代理执行规则 - -每个 Task 使用 fresh subagent,模型 `gpt-5.5`,reasoning `xhigh`,等待超时 30 分钟。 - -每个实现 Task 完成后必须两轮 review: - -1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md` 和本计划。 -2. code quality review:检查最小改动、可维护性、测试质量、无脚本掩盖。 - -review 未通过不能进入下一 Task。所有 review 发现的问题必须由新的修复子代理处理,再重新 review。 - -## Task 1: Preflight 与覆盖基线冻结 - -**Files:** - -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/api-contracts/account/openapi.yaml` -- Read: `muse-cloud/muse-module-member/**` -- Read: `muse-cloud/sql/muse/**` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -除本规格、本计划和本阶段 memory 尚未提交时可能出现的文档外,无其他输出。 - -- [ ] **Step 2: 统计 Account operation 状态** - -Run: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-3 Account Real API" or .domain == "account")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -generic_persistence/incomplete 33 -``` - -- [ ] **Step 3: 冻结 operation 清单** - -Run: - -```bash -jq -r '.operations[] | select(.targetStage == "P1R-3 Account Real API" or .domain == "account") | [.operationId, .method, .path, .implementationStatus, .completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 33 行,且 operationId 与本计划第 1 节完全一致。 - -- [ ] **Step 4: 检查脚本目录无改动** - -Run: - -```bash -git status --short -- muse-cloud/scripts -``` - -Expected: 无输出。 - -## Task 2: Account 基础设施与 DDL - -**Files:** - -- Read/Modify: `muse-cloud/muse-module-member/muse-module-member-server/pom.xml` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuard.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuard.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuard.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuard.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountCommandService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAuditService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-api/src/main/java/cn/iocoder/muse/module/member/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCommandDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountAuditDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCommandMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountAuditMapper.java` -- Create: `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountMigrationSqlTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuardTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuardTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuardTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuardTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceTest.java` - -- [ ] **Step 1: 确认模块依赖** - -`muse-module-member-server/pom.xml` 已有: - -- `muse-spring-boot-starter-security` -- `muse-spring-boot-starter-mybatis` -- `spring-boot-starter-validation` -- `muse-spring-boot-starter-test` - -如需新增依赖,先说明原因。 - -- [ ] **Step 2: 增加 Account 错误码** - -在 `ErrorCodeConstants.java` 增加本阶段使用的 Account 错误码,至少覆盖 `ACCOUNT_API_VERSION_UNSUPPORTED`、`ACCOUNT_USER_NOT_EXISTS`、`ACCOUNT_RESOURCE_FORBIDDEN`、`ACCOUNT_COMMAND_ID_REQUIRED`、`ACCOUNT_COMMAND_ID_CONFLICT`、`ACCOUNT_VERSION_CONFLICT`、`ACCOUNT_NEW_API_BINDING_NOT_EXISTS`、`ACCOUNT_NEW_API_UNAVAILABLE`、`ACCOUNT_INTEGRATION_CALL_NOT_EXISTS`、`ACCOUNT_QUOTA_EXHAUSTED`、`ACCOUNT_DOWNLOAD_CREDENTIAL_INVALID`、`ACCOUNT_EXPORT_SOURCE_BLOCKED`。 - -- [ ] **Step 3: 实现 API version guard** - -`AccountApiVersionGuard.requireVersion(String version)` 只接受 `"1"`,缺失或未知版本抛 `ACCOUNT_API_VERSION_UNSUPPORTED`。 - -- [ ] **Step 4: 实现 command service** - -规则: - -- `buildRequestHash(Object payload)` 使用稳定 JSON + SHA-256。 -- `reserveCommand(...)` 预占 commandId。 -- `getReplayCommand(...)` 相同 hash 返回旧记录。 -- 相同 commandId 不同 hash 抛 `ACCOUNT_COMMAND_ID_CONFLICT`。 -- envelope 必须校验 operationId、actor、owner、targetType、targetId。 - -- [ ] **Step 5: 实现 audit service** - -记录字段必须覆盖 spec 第 10 节,不记录 New-API token、Prompt/Response 全文、外部订单原文、敏感 IP 明文。 - -- [ ] **Step 6: 编写 V11 迁移** - -至少新增: - -- `muse_account_command` -- `muse_account_audit` -- `muse_account_profile` -- `muse_account_balance_snapshot` -- `muse_account_quota_request` -- `muse_account_integration_call` -- `muse_account_call_attribution_job` -- `muse_account_call_attribution_item` -- `muse_account_export_task` -- `muse_account_download_credential` -- `muse_account_security_event_ack` -- `muse_account_record_projection` - -关键约束: - -- command 唯一键 `(tenant_id, command_id)`。 -- account audit 必须索引 `account_user_id`、`operation_id`、`correlation_id`。 -- account profile 必须以 `account_user_id` 唯一,并用 `profile_version` 支撑 `expectedVersion`。 -- record projection 必须用 `record_type` 区分 `purchase`、`license`、`publish`,并保存 `source_table` / `source_id` / `source_revision`。 -- app 可见事实必须索引 `account_user_id`。 -- correlation 查询必须索引 `correlation_id`。 -- download credential 必须 hash 存储,不落明文 token。 - -- [ ] **Step 7: 创建迁移 SQL 门禁测试** - -`P1rAccountMigrationSqlTest` 至少校验: - -- `V11__extend_account_real_api_schema.sql` 存在。 -- V11 包含 `muse_account_command`、`muse_account_audit`、`muse_account_profile`、`muse_account_balance_snapshot`、`muse_account_quota_request`、`muse_account_integration_call`、`muse_account_call_attribution_job`、`muse_account_call_attribution_item`、`muse_account_export_task`、`muse_account_download_credential`、`muse_account_security_event_ack`、`muse_account_record_projection`。 -- command 唯一键、profile version、record projection `record_type`、download credential hash、account owner 索引和 correlation 索引均存在。 -- 测试不得只检查文件名,必须检查关键表名和约束片段。 - -- [ ] **Step 8: 运行迁移 SQL 门禁** - -Run: - -```bash -set -e -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountMigrationSqlTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountMigrationSqlTest -``` - -Expected: V11 迁移表、索引、唯一键、hash credential 字段、profile version、audit 字段和 record projection 约束均通过门禁;若 `P1rAccountMigrationSqlTest` 不存在,必须失败。 - -- [ ] **Step 9: 运行聚焦测试** - -Run: - -```bash -set -e -cd muse-cloud -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuardTest.java -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuardTest.java -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuardTest.java -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuardTest.java -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceTest.java -test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-member/muse-module-member-server -am \ - -Dtest=AccountApiVersionGuardTest,AccountOwnerGuardTest,AccountQuotaGuardTest,AccountDownloadGuardTest,AccountCommandServiceTest,AccountAuditServiceTest -``` - -Expected: 测试通过。 - -## Task 3: DTO / DAL / Mapper 骨架 - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUserEntitlementDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentLedgerEntryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingCreateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestCreateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminBalanceSnapshotEntryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminIntegrationCallDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobCreateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUsageRecordRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminPurchaseRecordRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppCurrentUserSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileUpdateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppEntitlementRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppUsageSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestCreateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/QuotaRequestStatusRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppBalanceSnapshotEntryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppIntegrationCallDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPurchaseRecordRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppLicenseRecordRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPublishRecordRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventSummaryRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskCreateReqVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskResultRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskDetailRespVO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountProfileDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberEntitlementDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberEntitlementAuditLogDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberQuotaDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberUsageRecordDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberSecurityEventDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberNewApiBindingDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountBalanceSnapshotDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountQuotaRequestDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionJobDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionItemDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountSecurityEventAckDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountExportTaskDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountDownloadCredentialDO.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountProfileMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberEntitlementMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberEntitlementAuditLogMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberQuotaMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberUsageRecordMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberSecurityEventMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberNewApiBindingMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountBalanceSnapshotMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountQuotaRequestMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionJobMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionItemMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountSecurityEventAckMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountExportTaskMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountDownloadCredentialMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/convert/account/AccountConvert.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/convert/account/AccountConvertTest.java` - -- [ ] **Step 1: 按 OpenAPI 创建 VO** - -覆盖 spec 第 6.3 节列出的最小 DTO,命名与已有项目 VO 风格一致。 - -- [ ] **Step 2: 创建 DO / Mapper** - -覆盖 V2 表和 V11 新表;V2 表包括 `muse_member_entitlement`、`muse_member_entitlement_audit_log`、`muse_member_quota`、`muse_member_usage_record`、`muse_member_security_event`、`muse_member_new_api_binding`。对 `member_user`、Market 上游来源只创建必要 Mapper 方法,不复制其他模块状态机。 - -- [ ] **Step 3: 创建 Account assembler** - -统一处理 ID 字符串化、分页、脱敏、枚举映射、时间字段。 - -- [ ] **Step 4: DTO 对齐测试** - -新增单元测试校验关键 response shape,不返回 `Map`。 - -## Task 4: App me / profile 与 Admin 用户摘要 - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountProfileController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountProfileService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountProfileServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountProfileDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountProfileMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppCurrentUserSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileUpdateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountProfileServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountProfileControllerTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserControllerTest.java` - -- [ ] **Step 1: 实现 `/app-api/muse/me`** - -返回 `CurrentUserSummary` 的基础账户资料,来源为当前登录用户和 `muse_account_profile`。权益摘要、安全风险数、配额状态、New-API binding 等跨服务聚合字段在 Task 5、Task 6、Task 9 中回填;Task 4 不得为了完整摘要提前实现后续 task 的 service。 - -- [ ] **Step 2: 实现 `/app-api/muse/profile` GET/PATCH** - -PATCH 必须校验 `commandId` 和 `expectedVersion`,联系方式不在本接口直接改。 -profile 扩展事实写入 `muse_account_profile`,不得通过扩字段污染 `member_user` 基础会员表。 - -- [ ] **Step 3: 实现 `/admin-api/muse/account/users`** - -查询用户基础摘要,来源为 `member_user`、`muse_account_profile` 和已存在的账号状态字段。权益来源、配额状态、New-API binding、风险标记在 Task 5、Task 6、Task 9 中通过修改 `AdminAccountUserController` 和 `AdminAccountUserSummaryRespVO` 回填;Task 4 只预留可空字段,不跨 task 实现聚合服务。 - -- [ ] **Step 4: 测试** - -覆盖当前用户、profile 更新、owner 只能是自己、版本冲突、command replay/conflict、admin 权限。 - -## Task 5: 权益 / 配额 / 余额 / 用量 - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountEntitlementController.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountBalanceSnapshotDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountBalanceSnapshotMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUserEntitlementDetailRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentLedgerEntryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUsageRecordRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminBalanceSnapshotEntryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppEntitlementRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppUsageSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppBalanceSnapshotEntryRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageControllerTest.java` - -- [ ] **Step 1: 权益查询** - -实现: - -- `adminGetUserEntitlements` -- `getAppEntitlements` - -必须从 `muse_member_entitlement` / `muse_member_quota` 装配 OpenAPI DTO。 -同时回填 `AdminAccountUserSummaryRespVO` 的权益来源和配额状态字段,不改 Task 4 的基础 owner 逻辑。 - -- [ ] **Step 2: 配额调整** - -实现: - -- `adminCreateQuotaAdjustment` -- `adminListQuotaAdjustments` - -必须记录 before/after snapshot、operator summary、reason、correlationId、auditStatus。 - -- [ ] **Step 3: 余额快照** - -实现: - -- `adminGetBalanceSnapshots` -- `getAppBalanceSnapshots` - -必须从 `muse_account_balance_snapshot` 读取;没有 New-API runtime 时只返回已有快照,不即时伪造远端余额。 - -- [ ] **Step 4: 用量摘要** - -实现: - -- `adminListUsageRecords` -- `getAppUsage` - -必须从 `muse_member_usage_record` 聚合,app 只查本人。 - -- [ ] **Step 5: 测试** - -覆盖分页、owner guard、quota delta、before/after snapshot、usage aggregation。 - -## Task 6: New-API binding / quota request / integration call - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountNewApiController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountNewApiController.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaService.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageService.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/NewApiAccountFacade.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableNewApiAccountFacade.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountQuotaRequestDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountQuotaRequestMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingCreateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestCreateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminIntegrationCallDetailRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestCreateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/QuotaRequestStatusRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppIntegrationCallDetailRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountNewApiControllerTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountNewApiControllerTest.java` - -- [ ] **Step 1: New-API binding 查询与创建** - -实现: - -- `adminListNewApiBindings` -- `adminCreateNewApiBinding` -- `getAppNewApiBinding` - -不得暴露 token/provider route。 -`adminCreateNewApiBinding` 必须使用 `NewApiBindingCreateReqVO` 接收 inline requestBody,校验 `commandId`、`forceRefresh`、`reason`,不得用 `Map` 或裸对象承接请求。 -创建或刷新 binding 时必须写 `muse_account_integration_call`,内部 `correlation_id` 与 `binding_id` 关联;响应只能返回 OpenAPI 已定义的 `NewApiBindingResult` 字段,不新增 `correlationId`。 -同时回填 `AdminAccountUserSummaryRespVO` 的 New-API binding 摘要字段。 -默认 `UnavailableNewApiAccountFacade` 不得伪造远端同步成功:创建/刷新无法满足 OpenAPI `created/refreshed/idempotent_hit/idempotency_conflict` 时,HTTP 响应返回合同内业务错误 `ACCOUNT_NEW_API_UNAVAILABLE`;内部 integration call 可记录 `unavailable` / `failed` 状态,但这些状态不得进入 `NewApiBindingResult.status`。 - -- [ ] **Step 2: recheck** - -实现: - -- `appRecheckNewApiBinding` - -必须使用 `AppNewApiBindingRecheckReqVO` 接收 inline requestBody,校验 `commandId`,不得用 `Map` 或裸对象承接请求。 -若真实 New-API 不可用,按合同返回业务错误 `ACCOUNT_NEW_API_UNAVAILABLE`,或在能够创建异步任务时只返回 OpenAPI 已定义的 `jobId/status=queued|processing`。服务端必须写 `muse_account_integration_call`,内部 `correlation_id` 与 `job_id` 关联;不得向响应新增 `correlationId`,也不得把内部 `unavailable/failed` 写入响应 `status`。 -测试必须断言 unavailable facade 不会产生 fake success,且不会返回 OpenAPI 未声明的状态值。 - -- [ ] **Step 3: quota request** - -实现: - -- `adminCreateQuotaRequest` -- `appCreateQuotaRequest` -- `appGetQuotaRequest` - -`appCreateQuotaRequest` 必须使用 `AppQuotaRequestCreateReqVO` 接收 inline requestBody,校验 app 侧允许的 `requestType`,不得复用 admin DTO。 -必须生成 requestId、correlationId,并写 integration call 初始记录。 - -- [ ] **Step 4: integration call 查询** - -实现: - -- `adminGetIntegrationCallByCorrelation` -- `appGetIntegrationCallByCorrelation` - -app 必须按 owner 限制。 - -- [ ] **Step 5: 测试** - -覆盖 command replay/conflict、quota request 响应 correlation、binding/recheck 内部 correlation 写入但不改响应合同、app 越权、New-API unavailable。 - -## Task 7: Call attribution job - -**Files:** - -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAttributionService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAttributionServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionJobDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionItemDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionJobMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionItemMapper.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/AccountAttributionSourceFacade.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableAccountAttributionSourceFacade.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobCreateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobDetailRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAttributionServiceTest.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageControllerTest.java` - -- [ ] **Step 1: 创建 job** - -实现 `adminCreateCallAttributionJob`。只能基于已存在 `correlationId` 和可选 callIds;不能信任请求体指定归因事实。 - -- [ ] **Step 2: 查询 job** - -实现 `adminGetCallAttributionJob`,返回 completed/failed/partially_completed 等状态和补偿建议。 - -- [ ] **Step 3: 归因边界** - -服务端只能通过 `AccountAttributionSourceFacade` 读取 call、task、work、agent、asset、license 关系;P1R-3 默认实现为 `UnavailableAccountAttributionSourceFacade`。外部 owner 未接入时,内部 job 可保持不可执行或 failed;响应状态只能使用 OpenAPI 已声明的 `queued`、`processing`、`completed`、`failed`、`partially_completed`、`idempotent_hit`,不得返回 `pending`,不得伪造归因成功,也不跨入 AI/Content/Market/Knowledge 主流程。 - -- [ ] **Step 4: 测试** - -覆盖 correlation 不存在、callIds 白名单、幂等、归因失败、状态查询、unavailable facade 不 fake success,且响应 `status` 不出现 OpenAPI 未声明状态。 - -## Task 8: Purchase / License / Publish records - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountPurchaseController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountMarketRecordController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/MarketAccountProjectionFacade.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableMarketAccountProjectionFacade.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminPurchaseRecordRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPurchaseRecordRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppLicenseRecordRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPublishRecordRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountMarketRecordControllerTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountPurchaseControllerTest.java` - -- [ ] **Step 1: purchase records** - -实现: - -- `adminListPurchaseRecords` -- `appListPurchases` - -来源为 `muse_account_record_projection(record_type=purchase)`,上游同步自 `muse_market_purchase`;不实现购买/退款/发票。 -默认 `UnavailableMarketAccountProjectionFacade` 必须返回明确 projection unavailable 边界,不能回填伪造购买记录。 - -- [ ] **Step 2: license records** - -实现 `appListLicenses`。来源为 `muse_account_record_projection(record_type=license)`,上游同步自 `muse_market_installation`;不修改 licenseStatus。 - -- [ ] **Step 3: publish records** - -实现 `appListPublishRecords`。来源为 `muse_account_record_projection(record_type=publish)`,上游同步自 `muse_market_publish_request`;不审核、不上架、不下架、不申诉。 - -- [ ] **Step 4: 测试** - -覆盖 app owner、admin query、脱敏、record_type 过滤、Market projection unavailable,且 unavailable facade 不产生 fake record。 - -## Task 9: Security events - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountSecurityService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountSecurityServiceImpl.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountSecurityEventAckDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountSecurityEventAckMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventSummaryRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventDetailRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountSecurityServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityControllerTest.java` - -- [ ] **Step 1: 列表与详情** - -实现: - -- `appListSecurityEvents` -- `appGetSecurityEvent` - -只能查看本人安全事件,敏感值脱敏。 -同时回填 `AdminAccountUserSummaryRespVO` 的风险标记或安全事件计数字段,不提前接入 session 管理。 - -- [ ] **Step 2: acknowledge** - -实现 `appAcknowledgeSecurityEvent`。必须 append-only 写确认动作、note、riskSummary;不得删除事件。 - -- [ ] **Step 3: session_revoked 边界** - -若 session 管理服务未接入,返回明确业务错误或 pending action,不伪造 session 已撤销。 - -- [ ] **Step 4: 测试** - -覆盖 owner guard、ack 幂等、重复确认、session_revoked external unavailable。 - -## Task 10: Export task / download credential - -**Files:** - -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountExportController.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountExportService.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountExportServiceImpl.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/AccountFileServiceFacade.java` -- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableAccountFileServiceFacade.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountExportTaskDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountDownloadCredentialDO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountExportTaskMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountDownloadCredentialMapper.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskCreateReqVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskResultRespVO.java` -- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskDetailRespVO.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountExportServiceTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountExportControllerTest.java` - -- [ ] **Step 1: 创建导出任务** - -实现 `appCreateExportTask`。必须校验 exportType、dateRange、step-up requirement,并写 command/audit。 - -- [ ] **Step 2: 查询导出任务** - -实现 `appGetExportTask`。只能查本人 task。 - -- [ ] **Step 3: 下载凭证** - -实现 `appDownloadExport`。必须校验 credential hash、owner、expiresAt、revoked/consumed/source blocked。成功响应必须返回 OpenAPI 定义的 `application/octet-stream` 文件流;错误响应仍走项目统一 `code/data/msg`。 - -- [ ] **Step 4: FileService 边界** - -默认 `UnavailableAccountFileServiceFacade` 表示 FileService 未接入;内部 task 可保持不可执行或 failed,不返回伪造文件流。响应状态只能使用 OpenAPI 已声明的 `queued`、`processing`、`completed`、`failed`、`idempotent_hit`,不得返回 `pending`。 - -- [ ] **Step 5: 测试** - -覆盖高敏导出、安全事件生成、过期凭证、跨用户凭证、source blocked、成功下载响应不是 `CommonResult`,unavailable facade 不返回 fake stream,且响应 `status` 不出现 OpenAPI 未声明状态。 - -## Task 11: Account 合同兜底退役 - -**Files:** - -- Modify/Delete: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/AdminMuseAccountContractController.java` -- Modify/Delete: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/AppMuseAccountContractController.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java` - -- [ ] **Step 1: 删除或收窄 P1R-3 路由兜底** - -优先方案:删除两个 Account 合同兜底 Controller。若还有非 P1R-3 Account 合同路径必须保留,必须把兜底路径收窄到这些非 P1R-3 路径,并在测试里证明 P1R-3 路径不会进入 `MuseContractPersistenceService`。 - -- [ ] **Step 2: route ownership 测试** - -测试必须证明: - -- 33 个 P1R-3 路由都命中 dedicated Controller。 -- 33 个 P1R-3 路由不会调用 `MuseContractPersistenceService.handle(...)`。 -- 没有 wildcard `/**` 把 dedicated 路由重新兜住。 - -- [ ] **Step 3: 运行 route ownership 测试** - -Run: - -```bash -set -e -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAccountRouteOwnershipTest -``` - -Expected: `P1rAccountRouteOwnershipTest` 通过;若测试文件不存在,必须失败。 - -## Task 12: P1R-3 覆盖门禁与报告对账 - -**Files:** - -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java` -- Modify: `docs/superpowers/reports/p1r-api-coverage.json` -- Modify: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `muse-cloud/scripts/p1r-audit-api-coverage.py` - -- [ ] **Step 1: 运行覆盖审计** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: - -- 脚本成功。 -- Content 仍是 51 个 `dedicated / needs_verification`。 -- Meta 仍是 16 个 `dedicated / needs_verification`。 -- Account 33 个 operation 是 `dedicated / needs_verification`。 -- Account 不出现 `completed`。 - -如果脚本不能识别 dedicated Account 实现,先分析脚本识别规则和代码结构差异;不能直接改脚本掩盖缺口。需要修改脚本时必须先说明这是扫描规则纠偏还是状态口径变更,并单独取得确认。 - -- [ ] **Step 2: 增加 Account 阶段门禁测试** - -`P1rAccountRealApiGateTest` 至少校验: - -- Account operation 数量为 33。 -- Account operation 清单与本计划第 1 节一致。 -- Account operation 不允许 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing`。 -- Account operation 不允许 `completionStatus=completed`。 -- Account operation 必须全部保持 `dedicated / needs_verification`,不能使用 `blocked` 或 `completed` 作为 P1R-3 收口状态。 - -- [ ] **Step 3: 运行 P1R 门禁** - -Run: - -```bash -set -e -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest -``` - -Expected: 测试通过。 - -- [ ] **Step 4: 运行 Account/Member 模块聚焦测试** - -Run: - -```bash -set -e -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-member/muse-module-member-server -am -``` - -Expected: 测试通过。 - -- [ ] **Step 5: 基础卫生检查** - -Run: - -```bash -git diff --check -git status --short -- muse-cloud/scripts -``` - -Expected: - -- `git diff --check` 无输出。 -- `muse-cloud/scripts/**` 无改动,除非用户已批准扫描规则纠偏。 - -## 4. Review 门禁 - -每个 Task 完成后交给两个 fresh subagent: - -### Spec compliance review - -检查: - -- 是否完全覆盖对应 operation。 -- 是否符合 P1R-3 spec。 -- 是否没有标 `completed`。 -- 是否没有用 generic persistence / operation record / workflow task / sample JSON 伪装完成。 -- 是否没有越权写其他 owner。 -- 是否没有跨入 P1R-4/P1R-5/P1R-6/P1R-7。 - -### Code quality review - -检查: - -- 改动是否最小。 -- Controller / Service / Domain / DAL 是否职责清晰。 -- DTO 是否对齐 OpenAPI。 -- 测试是否覆盖成功、冲突、非法状态、权限、幂等、审计。 -- 是否无无关重构。 -- 是否无 `muse-cloud/scripts/**` 掩盖性修改。 - -## 5. 回滚策略 - -- 文档与测试回滚:回退本阶段新增 spec/plan/gate test。 -- 代码回滚:回退 `muse-module-member` 新增 Account Controller/Service/DTO/DAL/Domain。 -- DDL 回滚:开发阶段可删除未合入迁移;迁移合入后不得修改历史 V11,只能新增 V12 修正。 -- 覆盖报告回滚:重新运行 P1R audit,确保 Content 和 Meta 保持当前状态。 - -## 6. 完成条件 - -P1R-3 实现阶段完成时必须同时满足: - -1. Account 33 个 operation 有专用实现。 -2. Account 不存在 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing` 残留。 -3. Account 全部保持 `dedicated / needs_verification`,不存在 `completed` 或 `blocked` 收口状态。 -4. Account/Member 模块测试通过。 -5. `P1rAccountMigrationSqlTest` 通过,证明 V11 迁移包含 `muse_account_audit`、`muse_account_profile`、`muse_account_record_projection` 等必要表和约束。 -6. `P1rAccountRouteOwnershipTest` 通过,证明 33 个 P1R-3 路由不再进入 `MuseContractPersistenceService`。 -7. P1R 门禁测试通过。 -8. `muse-cloud/scripts/**` 未被用来掩盖缺口。 -9. Content 51 个 operation 仍为 `dedicated / needs_verification`。 -10. Meta 16 个 operation 仍为 `dedicated / needs_verification`。 -11. 若未运行真实 PostgreSQL / Flyway / New-API runtime / FileService 验证,必须在 `docs/memorys/YYYY-MM-DD-P1R3账户真实API规格计划.md` 或实现阶段留痕中记录残余 `needs_verification` 风险。 diff --git a/docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md b/docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md deleted file mode 100644 index e8607eba..00000000 --- a/docs/superpowers/plans/2026-05-30-P1R-4-ai-real-api.md +++ /dev/null @@ -1,600 +0,0 @@ -# P1R-4 AI Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. `superpowers:executing-plans` is acceptable only when subagents are unavailable. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 AI 41 个 operation 从 40 个 `generic_persistence / incomplete` 和 1 个 `sse_placeholder / incomplete` 推进到专用 AI Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`。 - -**Architecture:** 以 `muse-module-ai` 为 owner,新增 Muse AI 合同专用 app/admin 入口和应用服务。通用 `MuseContractPersistenceService` 只能作为迁移保护网,不能作为 P1R-4 完成路径;AI task SSE 必须基于真实 task event 序列,不再发送占位 `done`。New-API、Security、Content、Meta、Account、Knowledge、Market 等外部 owner 不可用时返回合同内业务错误或真实失败状态,不伪造完成。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、SseEmitter、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 - -**Implementation Status:** 本文件是 P1R-4 规格计划阶段产物;review gate 已通过;当前不进入实现代码。收到“进入 P1R-4 实现阶段”后才允许按 Task 1-12 执行。 - ---- - -## 0. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的其他 checkout 上写阶段文档或实现代码。 -- 不修改 `docs/api-contracts/ai/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/**` 改变 P1R 判断口径。 -- 不破坏 Content 当前 51 个 `dedicated / needs_verification`。 -- 不破坏 Meta 当前 16 个 `dedicated / needs_verification`。 -- 不破坏 Account 当前 33 个 `dedicated / needs_verification`。 -- 不把 AI operation 标为 `completed`。 -- 如果新增或修改 DDL,阶段收口前必须在同一 PostgreSQL 实例的 `_test` 数据库跑真实 Flyway。 - -## 1. 当前事实盘点 - -| operationId | Method | Path | 当前状态 | 当前实现 owner | 计划处理 | -|---|---|---|---|---|---| -| `adminListAgents` | GET | `/admin-api/muse/ai/agents` | `generic_persistence / incomplete` | `AdminMuseAiContractController` + `MuseContractPersistenceService` | Task 4 | -| `adminCreateAgent` | POST | `/admin-api/muse/ai/agents` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `adminCreateAgentVersion` | POST | `/admin-api/muse/ai/agents/{agentId}/versions` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `adminStartEvaluationRun` | POST | `/admin-api/muse/ai/evaluation-runs` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminGetEvaluationRun` | GET | `/admin-api/muse/ai/evaluation-runs/{runId}` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminListPrompts` | GET | `/admin-api/muse/ai/prompts` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminCreatePromptVersion` | POST | `/admin-api/muse/ai/prompts/{promptKey}/versions` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminActivatePromptVersion` | POST | `/admin-api/muse/ai/prompts/{promptKey}/versions/{version}/activate` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminListQualityPolicies` | GET | `/admin-api/muse/ai/quality-policies` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminCreateQualityPolicyVersion` | POST | `/admin-api/muse/ai/quality-policies/{policyKey}/versions` | `generic_persistence / incomplete` | 同上 | Task 3 | -| `adminListAiTasks` | GET | `/admin-api/muse/ai/tasks` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `adminListToolGrants` | GET | `/admin-api/muse/ai/tool-grants` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `adminCreateOrAdjustToolGrant` | POST | `/admin-api/muse/ai/tool-grants` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `adminListApiAccessLogs` | GET | `/admin-api/muse/audit/api-logs` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `adminGetApiAccessLog` | GET | `/admin-api/muse/audit/api-logs/{logId}` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `adminListBusinessAuditEvents` | GET | `/admin-api/muse/audit/business-events` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `adminGetBusinessAuditEvent` | GET | `/admin-api/muse/audit/business-events/{eventId}` | `generic_persistence / incomplete` | 同上 | Task 9 | -| `adminListJobs` | GET | `/admin-api/muse/jobs` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `adminGetJob` | GET | `/admin-api/muse/jobs/{jobId}` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `adminCancelJob` | POST | `/admin-api/muse/jobs/{jobId}/cancel` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `adminRetryJob` | POST | `/admin-api/muse/jobs/{jobId}/retry` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `adminListSourceEvents` | GET | `/admin-api/muse/source-events` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `adminGetSourceEvent` | GET | `/admin-api/muse/source-events/{eventId}` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `adminRetrySourceEvent` | POST | `/admin-api/muse/source-events/{eventId}/retry` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `listUserAgents` | GET | `/app-api/muse/agents` | `generic_persistence / incomplete` | `AppMuseAiContractController` + `MuseContractPersistenceService` | Task 4 | -| `createUserAgent` | POST | `/app-api/muse/agents` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `testAgent` | POST | `/app-api/muse/agents/{agentId}/test` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `createAgentVersion` | POST | `/app-api/muse/agents/{agentId}/versions` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `createAiTask` | POST | `/app-api/muse/ai/tasks` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `getAiTask` | GET | `/app-api/muse/ai/tasks/{taskId}` | `generic_persistence / incomplete` | 同上 | Task 5 | -| `streamAiTask` | GET | `/app-api/muse/ai/tasks/{taskId}/stream` | `sse_placeholder / incomplete` | `AppMuseAiContractController` 占位 SSE | Task 6 | -| `getUserJob` | GET | `/app-api/muse/jobs/{jobId}` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `cancelUserJob` | POST | `/app-api/muse/jobs/{jobId}/cancel` | `generic_persistence / incomplete` | 同上 | Task 8 | -| `querySourceStatus` | POST | `/app-api/muse/source-status/query` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `recheckSourceStatus` | POST | `/app-api/muse/source-status/recheck` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `getSuggestion` | GET | `/app-api/muse/suggestions/{suggestionId}` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `rejectSuggestion` | POST | `/app-api/muse/suggestions/{suggestionId}/reject` | `generic_persistence / incomplete` | 同上 | Task 7 | -| `listWorkAgentSlots` | GET | `/app-api/muse/works/{workId}/agent-slots` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `bindAgentSlot` | POST | `/app-api/muse/works/{workId}/agent-slots/{slotKey}/bind` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `precheckAgentSlot` | POST | `/app-api/muse/works/{workId}/agent-slots/{slotKey}/prechecks` | `generic_persistence / incomplete` | 同上 | Task 4 | -| `listWorkSuggestions` | GET | `/app-api/muse/works/{workId}/suggestions` | `generic_persistence / incomplete` | 同上 | Task 7 | - -## 2. 目标文件结构 - -| 操作 | 路径 | 责任 | -|---|---|---| -| Modify | `muse-cloud/muse-server/pom.xml` | 将聚合服务从 AI 合同兜底入口推进到完整 AI runtime,或显式声明短期双依赖避让策略 | -| Modify | `muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` | 确认 validation/security/mybatis/web/test 依赖,按需补测试依赖 | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/controller/admin/muse/*.java` | AI 管理端 dedicated API | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/controller/app/muse/*.java` | AI 用户端 dedicated API | -| Modify | `AdminMuseAiContractController.java` | 移除或避让 P1R-4 已 dedicated 路径 | -| Modify | `AppMuseAiContractController.java` | 移除或避让 P1R-4 已 dedicated 路径,删除占位 SSE | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/controller/*/muse/vo/*.java` | AI OpenAPI VO | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/*.java` | AI 应用服务 | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/*.java` | New-API / Security / Content / Meta / Account / Knowledge / Market 边界 | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/domain/muse/*.java` | version、owner、permission、source、job、SSE guard | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/dataobject/muse/*.java` | AI P1R DO | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/dal/mysql/muse/*.java` | AI P1R Mapper | -| Create | `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/convert/muse/MuseAiConvert.java` | DO/facade 到 VO 组装 | -| Modify | `muse-cloud/muse-module-ai/muse-module-ai-api/src/main/java/cn/iocoder/muse/module/ai/enums/ErrorCodeConstants.java` | AI P1R 错误码 | -| Create | `muse-cloud/sql/muse/V12__extend_ai_real_api_schema.sql` | AI 真实 API 追加表和约束 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java` | P1R-4 coverage 门禁 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRouteOwnershipTest.java` | P1R-4 route ownership 门禁 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiMigrationSqlTest.java` | P1R-4 DDL 门禁 | -| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiFlywayMigrationIT.java` | P1R-4 真实 PostgreSQL / Flyway `_test` 迁移验收 | - -## 3. 子代理执行规则 - -每个实现 Task 使用 fresh subagent,模型 `gpt-5.5`,reasoning `xhigh`,等待超时 30 分钟。 - -每个实现 Task 完成后必须两轮 review: - -1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md` 和本计划。 -2. quality / feasibility review:检查最小改动、可维护性、测试质量、运行态失败路径、无脚本掩盖。 - -review 未通过不能进入下一 Task。所有 review 发现的问题必须由新的修复子代理处理,再重新 review。 - -## Task 1: Preflight 与覆盖基线冻结 - -**Files:** - -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/api-contracts/ai/openapi.yaml` -- Read: `muse-cloud/muse-module-ai/**` -- Read: `muse-cloud/sql/muse/**` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -除本阶段 spec、plan、memory 尚未提交时可能出现的文档外,无其他输出。 - -- [ ] **Step 2: 统计 AI operation 状态** - -Run: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-4 AI Real API" or .domain == "ai")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -generic_persistence/incomplete 40 -sse_placeholder/incomplete 1 -``` - -- [ ] **Step 3: 冻结 operation 清单** - -Run: - -```bash -jq -r '.operations[] | select(.targetStage == "P1R-4 AI Real API" or .domain == "ai") | [.operationId, .method, .path, .implementationStatus, .completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 41 行,且 operationId 与本计划第 1 节完全一致。 - -- [ ] **Step 4: 检查禁止修改面** - -Run: - -```bash -git status --short -- docs/api-contracts/ai/openapi.yaml muse-cloud/scripts -``` - -Expected: 无输出。 - -## Task 2: AI 基础设施与 V12 DDL - -**Files:** - -- Read/Modify: `muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml` -- Read/Modify: `muse-cloud/muse-server/pom.xml` -- Modify: `muse-cloud/muse-module-ai/muse-module-ai-api/src/main/java/cn/iocoder/muse/module/ai/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/domain/muse/MuseAiApiVersionGuard.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/domain/muse/MuseAiOwnerGuard.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/domain/muse/MuseAiRuntimePermissionGuard.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiCommandService.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiCommandServiceImpl.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiAuditService.java` -- Create: `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/MuseAiAuditServiceImpl.java` -- Create: `muse-cloud/sql/muse/V12__extend_ai_real_api_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiMigrationSqlTest.java` - -- [ ] **Step 1: 切换聚合服务 AI runtime 装配** - -当前 `muse-server/pom.xml` 只依赖 `muse-module-ai-contract-server`,注释说明“不拉起完整 AI Runtime”。P1R-4 实现阶段必须先做显式装配决策,禁止把 dedicated Controller 写进 `muse-module-ai-server` 后仍让主服务只加载合同兜底模块。 - -推荐选择: - -1. `muse-server/pom.xml` 引入 `muse-module-ai-server`。 -2. 删除或注释 `muse-module-ai-contract-server` 依赖,避免 41 个 P1R-4 路由被合同兜底 Controller 抢占。 -3. 如短期必须双依赖,必须同步让 `AdminMuseAiContractController` / `AppMuseAiContractController` 精确避让 41 个 AI 路由,并用 `P1rAiRouteOwnershipTest` 证明主服务依赖图下 dedicated Controller 生效且不调用 `MuseContractPersistenceService`。 - -验证要求: - -- `git diff` 必须包含 `muse-server/pom.xml` 的 AI dependency 变更。 -- `P1rAiRouteOwnershipTest` 必须在 `muse-server` 模块下运行,证明 41 个 P1R-4 路由的 HandlerMethod 不属于 `*MuseAiContractController`。 -- 回滚时恢复 `muse-module-ai-contract-server` 依赖,并且 coverage 不得残留半截 `dedicated` 状态。 - -- [ ] **Step 2: 确认 AI server 模块依赖** - -确认 `muse-module-ai-server/pom.xml` 已有或补齐: - -- `muse-spring-boot-starter-security` -- `muse-spring-boot-starter-mybatis` -- `spring-boot-starter-validation` -- `spring-boot-starter-web` -- `muse-spring-boot-starter-test` - -如需新增依赖,先说明原因。 - -- [ ] **Step 3: 增加 AI 错误码** - -至少覆盖: - -- `AI_API_VERSION_UNSUPPORTED` -- `AI_COMMAND_ID_REQUIRED` -- `AI_COMMAND_ID_CONFLICT` -- `AI_RESOURCE_FORBIDDEN` -- `AI_AGENT_NOT_EXISTS` -- `AI_AGENT_SCOPE_FORBIDDEN` -- `AI_PROMPT_NOT_EXISTS` -- `AI_PROMPT_VERSION_NOT_EXISTS` -- `AI_QUALITY_POLICY_NOT_EXISTS` -- `AI_TOOL_GRANT_NOT_APPROVED` -- `AI_RUNTIME_PERMISSION_DENIED` -- `AI_NEW_API_UNAVAILABLE` -- `AI_TASK_NOT_EXISTS` -- `AI_TASK_NOT_CANCELLABLE` -- `AI_TASK_NOT_RETRYABLE` -- `AI_SOURCE_BLOCKED` -- `AI_SOURCE_RECHECK_REQUIRED` -- `AI_SSE_TASK_NOT_VISIBLE` - -- [ ] **Step 4: 实现 API version guard** - -`MuseAiApiVersionGuard.requireVersion(String version)` 只接受 `"1"`,缺失或未知版本抛 `AI_API_VERSION_UNSUPPORTED`。 - -- [ ] **Step 5: 实现 command service** - -规则: - -- `buildRequestHash(Object payload)` 使用稳定 JSON + SHA-256。 -- `reserveCommand(...)` 预占 commandId。 -- `getReplayCommand(...)` 相同 hash 返回旧记录。 -- 相同 commandId 不同 hash 抛 `AI_COMMAND_ID_CONFLICT`。 -- envelope 必须校验 operationId、actor、owner、targetType、targetId 或 targetKey。 - -- [ ] **Step 6: 实现 audit service** - -审计只存脱敏摘要,不能存: - -- New-API token / secret。 -- Prompt/Response 全文。 -- 用户私有正文全文。 -- 完整 header、cookie、Authorization。 -- 未脱敏 IP、外部 provider 原始错误体。 - -- [ ] **Step 7: 编写 V12 迁移** - -至少新增或扩展: - -- `muse_ai_command` -- `ALTER TABLE muse_ai_generation` 补 command/runtime/source/job 字段。 -- `muse_ai_runtime_call` -- `muse_ai_task_event` -- `muse_ai_job` -- `muse_ai_source_event` -- `muse_ai_source_status_projection` -- `muse_ai_suggestion_decision` -- `muse_ai_evaluation_run` -- `muse_ai_evaluation_sample_result` -- `muse_api_access_log_projection` -- `muse_business_audit_event` - -关键约束: - -- command 唯一键 `(tenant_id, command_id)`。 -- runtime call 唯一键 `(tenant_id, correlation_id)`。 -- SSE event 唯一键 `(tenant_id, task_id, sequence_no)`。 -- Prompt active version partial unique index。 -- Quality Policy active version partial unique index。 -- app 可见事实必须索引 owner user。 -- job/source/task 必须索引 owner、status、create_time。 - -- [ ] **Step 8: 编写迁移 SQL 门禁测试** - -`P1rAiMigrationSqlTest` 至少校验: - -- V12 文件存在。 -- V12 包含上述表和关键 alter。 -- command/runtime/SSE/job/source/audit 关键约束存在。 -- 没有明文 token、secret、bearer、api_key 字段。 -- active partial unique index 存在。 - -- [ ] **Step 9: 运行迁移 SQL 门禁** - -Run: - -```bash -set -e -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiMigrationSqlTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAiMigrationSqlTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -命令中的 `-Dsurefire.failIfNoSpecifiedTests=false` 只用于规避 `-am` 上游模块没有同名测试时的 Maven 行为;前置 `test -f` 和 surefire 输出必须证明 `P1rAiMigrationSqlTest` 在 `muse-server` 模块实际执行。若输出不包含目标测试类或测试数量,视为验证失败。 - -## Task 3: Prompt / Quality Policy / Evaluation Run - -**Files:** - -- Create: `controller/admin/muse/AdminMusePromptController.java` -- Create: `controller/admin/muse/AdminMuseQualityController.java` -- Create: `application/muse/MusePromptService*.java` -- Create: `application/muse/MuseQualityPolicyService*.java` -- Create: related VO / DO / Mapper / tests - -- [ ] 实现 `adminListPrompts`、`adminCreatePromptVersion`、`adminActivatePromptVersion`。 -- [ ] 实现 `adminListQualityPolicies`、`adminCreateQualityPolicyVersion`。 -- [ ] 实现 `adminStartEvaluationRun`、`adminGetEvaluationRun`,evaluation run 创建 `muse_ai_job`,未接真实评估执行器时保持可查询的 `queued` / `failed` 边界,不伪造 completed。 -- [ ] Prompt 版本创建必须幂等;激活必须校验版本存在、active 唯一和 reason。 -- [ ] Quality Policy 版本创建必须幂等;active version 不可多重。 -- [ ] DTO 只能返回 OpenAPI 声明字段。 -- [ ] 测试覆盖 command replay、conflict、active 唯一、缺失 prompt/policy、admin 权限。 - -## Task 4: Agent / Tool Grant / Agent Slot - -**Files:** - -- Create: `controller/admin/muse/AdminMuseAgentController.java` -- Create: `controller/admin/muse/AdminMuseToolGrantController.java` -- Create: `controller/app/muse/AppMuseAgentController.java` -- Create: `controller/app/muse/AppMuseAgentSlotController.java` -- Create: `application/muse/MuseAgentService*.java` -- Create: `application/muse/MuseToolGrantService*.java` -- Create: `application/muse/MuseAgentSlotService*.java` -- Create: related facade / VO / tests - -- [ ] 实现 admin system agent list/create/version。 -- [ ] 实现 app user agent list/create/version。 -- [ ] admin 不能创建或修改 user scope agent;app 不能修改 system agent。 -- [ ] Tool Grant 只能消费 Security facade 已批准结果;Security unavailable 时返回合同内业务错误,不得自批准。 -- [ ] 实现 slot list/precheck/bind;保护节点不可替换,revision 冲突返回合同内 409。 -- [ ] precheck 产生短期凭证,bind 原子消费,重复 command 返回同一结果。 -- [ ] 测试覆盖 scope owner、权限、precheck 过期、revision 冲突、source blocked。 - -## Task 5: AI Task / Runtime / Suggestion 创建基础 - -**Files:** - -- Create: `controller/app/muse/AppMuseAiTaskController.java` -- Create: `controller/admin/muse/AdminMuseAiTaskController.java` -- Create: `application/muse/MuseAiTaskService*.java` -- Create: `application/muse/MuseAiRuntimeService*.java` -- Create: `application/muse/facade/MuseAiRuntimeClient.java` -- Create: `application/muse/facade/UnavailableMuseAiRuntimeClient.java` -- Create: related VO / DO / Mapper / tests - -- [ ] 实现 `createAiTask`、`getAiTask`、`adminListAiTasks`。 -- [ ] 服务端按 work/chapter/block/agentSlotKey 生成 source snapshot 和 runtime permission envelope。 -- [ ] 客户端提交上下文正文、权限包或授权快照时必须拒绝或忽略,不能信任。 -- [ ] 先定义 `RuntimeCommand`、`RuntimeResult`、`RuntimeFailure`,字段必须对齐规格第 6.7 节,不能让实现代理临场发明 runtime 合同。 -- [ ] `RuntimeCommand` 必须包含 `commandId`、`taskId`、`jobId`、`correlationId`、`attemptNo`、`modelKey`、`promptTemplateVersion`、`inputSummary`、`sourceSnapshotId`、`runtimePermissionEnvelopeId`、`timeoutPolicy`、`retryPolicy`。 -- [ ] `RuntimeResult` 只有 `status=success` 时才允许推进 task/job 到 `completed` 并创建 suggestion / `done` event。 -- [ ] `RuntimeFailure` 必须包含 `failureType`、`providerStatusCode`、`errorCode`、`sanitizedMessage`、`retryable`、`nextRetryAt`,不得保存 provider 原始错误体、token、Prompt/Response 全文。 -- [ ] New-API runtime client 必须有 timeout、失败分类、重试预算、correlationId、脱敏 request/response summary;默认 connect timeout 5s、first byte timeout 15s、non-stream read timeout 60s、stream idle timeout 30s、total task runtime timeout 180s。 -- [ ] retry budget 默认最多 3 次,指数退避 1s / 2s / 4s;network timeout、连接失败、HTTP 408、HTTP 429、HTTP 5xx 可重试;认证失败、权限失败、模型不存在、请求参数错误、内容安全阻断、runtime permission denied 不可重试。 -- [ ] runtime call 事务边界:task 创建事务只写 command/task/job/snapshot/envelope;job executor 每次外部调用前先提交 `muse_ai_runtime_call(status=requested, attemptNo=...)`;调用后追加成功或失败状态,再推进 task/job/SSE;重试必须追加新 attempt,不能覆盖旧 attempt。 -- [ ] unavailable runtime 默认 fail closed:task/job 进入 `failed` 或可重试状态,不能返回 completed。 -- [ ] 成功 runtime 才允许生成 suggestion、quality scores 和 done event。 -- [ ] `testAgent` 走同一 runtime 和 permission 边界;preview_only 不落 suggestion,shadow 才落 suggestion。 -- [ ] 测试覆盖 create replay、runtime unavailable、owner forbidden、source revision stale、agent override forbidden。 - -## Task 6: AI Task SSE Stream - -**Files:** - -- Modify: `AppMuseAiContractController.java` -- Create/Modify: `controller/app/muse/AppMuseAiTaskController.java` -- Create: `application/muse/MuseAiTaskStreamService*.java` -- Create: related event DO / Mapper / tests - -- [ ] 删除或避让 `AppMuseAiContractController.streamAiTask` 占位实现。 -- [ ] `streamAiTask` 必须校验 task owner、tenant、API version。 -- [ ] SSE 只能发送 `chunk`、`quality_check`、`done`、`error`。 -- [ ] 每个事件必须有递增 `sequenceNo` 或可恢复顺序;done/error 必须是终态。 -- [ ] 断线重连可 replay 已持久化事件;没有事件时只能发送 SSE comment / keepalive 心跳,不得新增 OpenAPI 未声明 event,也不得发送假 done。 -- [ ] task failed 时发送 OpenAPI 声明的 error event;不得泄露 provider 原始错误体。 -- [ ] 测试覆盖 owner forbidden、placeholder token 不存在、event 顺序、done/error 互斥。 - -## Task 7: Suggestion / Source Status / Source Event - -**Files:** - -- Create: `controller/app/muse/AppMuseSuggestionController.java` -- Create: `controller/app/muse/AppMuseSourceStatusController.java` -- Create: `controller/admin/muse/AdminMuseSourceEventController.java` -- Create: `application/muse/MuseSuggestionService*.java` -- Create: `application/muse/MuseSourceStatusService*.java` -- Create: related facade / VO / tests - -- [ ] 实现 `listWorkSuggestions`、`getSuggestion`、`rejectSuggestion`。 -- [ ] reject 必须幂等,不能写 Content 正文。 -- [ ] 实现 `querySourceStatus`、`recheckSourceStatus`。 -- [ ] 实现 `adminListSourceEvents`、`adminGetSourceEvent`、`adminRetrySourceEvent`。 -- [ ] 对 Content/Meta/Account 调用 dedicated facade 获取摘要;对 Knowledge/Market unavailable 使用合同内 `owner_missing` / `blocked` 等 `sourceStatus`,`needs_recheck` 只能作为 `actionPolicy`,不能写入 `sourceStatus`。 -- [ ] source event retry 创建 `muse_ai_job`,不伪造传播完成。 -- [ ] 测试覆盖来源阻断、重验任务、reject replay、跨 owner unavailable。 - -## Task 8: Job Admin/App Query、Cancel、Retry - -**Files:** - -- Create: `controller/admin/muse/AdminMuseJobController.java` -- Create: `controller/app/muse/AppMuseJobController.java` -- Create: `application/muse/MuseJobService*.java` -- Create: related VO / tests - -- [ ] 实现 `adminListJobs`、`adminGetJob`、`adminCancelJob`、`adminRetryJob`。 -- [ ] 实现 `getUserJob`、`cancelUserJob`。 -- [ ] `/admin-api/muse/jobs` 是通用路径,但 P1R-4 只实现 AI owner job projection;Knowledge / Market job 主流程仍属 P1R-5 / P1R-6,不能把 AI job 表冒充全域 job owner。 -- [ ] Admin 可按 owner/status 查询;App 只能查当前用户可见 job。 -- [ ] cancel 只允许 queued/running/streaming;completed/failed 后按合同错误返回。 -- [ ] retry 只允许 retryable failed job;新 job 必须保留 retryGroupId。 -- [ ] 不直接返回 `muse_domain_workflow_task` 原始行。 -- [ ] 测试覆盖权限、状态机、retry group、重复 cancel。 - -## Task 9: API Access Log / Business Audit - -**Files:** - -- Create: `controller/admin/muse/AdminMuseAuditController.java` -- Create: `application/muse/MuseApiAccessLogQueryService*.java` -- Create: `application/muse/MuseBusinessAuditQueryService*.java` -- Create: related projection DO / Mapper / tests - -- [ ] 实现 `adminListApiAccessLogs`、`adminGetApiAccessLog`。 -- [ ] 实现 `adminListBusinessAuditEvents`、`adminGetBusinessAuditEvent`。 -- [ ] 列表和详情必须脱敏,不包含 token、secret、cookie、完整 header、完整 body、Prompt/Response 全文。 -- [ ] 读取敏感详情本身必须追加 business audit event。 -- [ ] API access log projection 可从 Infra log 输入,但对外只暴露 Muse OpenAPI 声明字段。 -- [ ] 测试覆盖脱敏、详情审计、权限、筛选。 - -## Task 10: Retire AI 合同兜底与覆盖门禁 - -**Files:** - -- Modify/Delete: `AdminMuseAiContractController.java` -- Modify/Delete: `AppMuseAiContractController.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRouteOwnershipTest.java` - -- [ ] 移除或精确避让 41 个 P1R-4 AI 路由的通用持久化兜底。 -- [ ] route ownership test 必须证明 41 个路由由 dedicated Controller 持有。 -- [ ] route ownership test 必须证明这些路由不依赖 `MuseContractPersistenceService`。 -- [ ] coverage gate 必须证明 AI 41 个 operation 为 `dedicated / needs_verification`。 -- [ ] coverage gate 必须证明 AI 不允许 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing`、`blocked`、`completed`。 -- [ ] coverage gate 必须证明 Content/Meta/Account 状态保持 `dedicated / needs_verification`。 - -Run: - -```bash -set -e -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiRouteOwnershipTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAiRealApiGateTest,P1rAiRouteOwnershipTest \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -## Task 11: PostgreSQL / Flyway `_test` 迁移验收 - -**Files:** - -- Read: `~/.config/muse-repo/infra.env` -- Read: `muse-cloud/sql/muse/**` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiFlywayMigrationIT.java` -- Optional Create: `docs/memorys/YYYY-MM-DD-P1R4AI真实API规格计划.md` 追加实现阶段结果 - -- [ ] 使用 P1R-3 相同 PostgreSQL 实例。 -- [ ] 默认使用同实例隔离库 `muse_local_p1r4_test` 执行 V12 migrate;如需要复用现有 `muse_local_test`,不得未确认就清理或删除已有 test DB。 -- [ ] Flyway locations 必须使用 `filesystem:sql/muse`。 -- [ ] 记录 `flyway_success`、`migrations_executed`、`target_schema_version`、`flyway_latest`、`v12_tables` 或关键表数量。 -- [ ] 失败时定位 SQL/环境根因,不允许只靠 SQL grep 收口。 - -可执行验收步骤: - -```bash -set -euo pipefail -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 - -test -f ~/.config/muse-repo/infra.env -set -a -. ~/.config/muse-repo/infra.env -set +a - -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAiFlywayMigrationIT.java - -# 默认复用 P1R-3 同实例;数据库名优先使用隔离库,避免误清理已有 muse_local_test。 -: "${MUSE_POSTGRES_HOST:?missing MUSE_POSTGRES_HOST}" -: "${MUSE_POSTGRES_PORT:?missing MUSE_POSTGRES_PORT}" -: "${MUSE_POSTGRES_USERNAME:?missing MUSE_POSTGRES_USERNAME}" -: "${MUSE_POSTGRES_PASSWORD:?missing MUSE_POSTGRES_PASSWORD}" -P1R4_TEST_DB="${P1R4_TEST_DB:-muse_local_p1r4_test}" -case "$P1R4_TEST_DB" in - *[!a-zA-Z0-9_]*|"") echo "invalid P1R4_TEST_DB: $P1R4_TEST_DB" >&2; exit 2 ;; -esac - -if ! PGPASSWORD="$MUSE_POSTGRES_PASSWORD" psql \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - -d postgres \ - -tAc "SELECT 1 FROM pg_database WHERE datname = '${P1R4_TEST_DB}'" | grep -qx 1; then - PGPASSWORD="$MUSE_POSTGRES_PASSWORD" createdb \ - -h "$MUSE_POSTGRES_HOST" \ - -p "$MUSE_POSTGRES_PORT" \ - -U "$MUSE_POSTGRES_USERNAME" \ - "$P1R4_TEST_DB" -fi - -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am \ - -Dtest=P1rAiFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R4_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.password="$MUSE_POSTGRES_PASSWORD" \ - -Dp1r.flyway.locations="filesystem:sql/muse" \ - -Dsurefire.failIfNoSpecifiedTests=false -``` - -`P1rAiFlywayMigrationIT` 必须用 Flyway API 执行 `filesystem:sql/muse`,并断言: - -- `flyway_success=true` -- `target_schema_version=12` -- `flyway_latest=12:extend ai real api schema` -- V1-V12 全链路实际执行,或在已有 schema history 中显示 V12 成功补齐。 -- V12 关键表存在:`muse_ai_command`、`muse_ai_runtime_call`、`muse_ai_task_event`、`muse_ai_job`、`muse_ai_source_event`、`muse_ai_source_status_projection`、`muse_ai_evaluation_run`、`muse_api_access_log_projection`、`muse_business_audit_event`。 - -如果实现阶段不新增或不修改 DDL,仍需在收口报告中明确说明“未触发 Flyway `_test` 必跑条件”的证据;只靠 SQL grep 或 `P1rAiMigrationSqlTest` 不能替代真实数据库验收。 - -## Task 12: 最终验证、报告对账与 memory - -- [ ] 运行 AI 模块相关单元测试和 server gate 测试。 -- [ ] 运行 `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check`。 -- [ ] 运行 `git diff --check`。 -- [ ] 核对 `docs/superpowers/reports/p1r-api-coverage.json`: - -Expected: - -```text -AI 41 dedicated / needs_verification -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -Account 33 dedicated / needs_verification -completedOperations = 0 -``` - -- [ ] 更新 `docs/memorys/YYYY-MM-DD-P1R4AI真实API规格计划.md`,记录实现阶段事实、验证命令和 residual risk。 -- [ ] `git status --short` 只应包含 P1R-4 相关文件。 - -## 4. 回滚策略 - -- 文档阶段回滚:只撤销本 spec、plan、memory 文档。 -- 实现阶段回滚:按任务小步提交回滚;优先回滚 dedicated Controller 注册与 coverage 报告,再回滚 DDL/Java。 -- DDL 回滚:未进入共享环境前可通过新迁移反向处理;不得直接删除历史 Flyway 文件。 -- 外部 runtime 回滚:关闭 New-API runtime adapter 配置后必须 fail closed,不能恢复成假 success。 - -## 5. 完成条件 - -本规格计划会话完成条件: - -- 已产出 P1R-4 spec。 -- 已产出 P1R-4 implementation plan。 -- 已产出 P1R-4 memory。 -- 已通过 spec compliance review 和 quality / feasibility review。 -- 未实现代码、未改 OpenAPI、未改 scanner、未改 Java / SQL。 - -后续实现阶段完成条件: - -- AI 41 个 operation 全部为 `dedicated / needs_verification`。 -- `streamAiTask` 不再是 `sse_placeholder`。 -- Content / Meta / Account 不退化。 -- DDL/Flyway 在同实例 `_test` 数据库真实通过。 -- 没有真实端到端验收证据前,仍不标记任何 operation 为 `completed`。 diff --git a/docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md b/docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md deleted file mode 100644 index dfa65915..00000000 --- a/docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md +++ /dev/null @@ -1,514 +0,0 @@ -# P1R-5 Knowledge / RAGFlow Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. `superpowers:executing-plans` is acceptable only when subagents are unavailable. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 Knowledge 59 个 operation 从 `generic_persistence / incomplete` 推进到专用 Knowledge Controller / Service / DTO / DAL / DDL / RAGFlow adapter / 测试可验收的 `dedicated / needs_verification`。 - -**Architecture:** 以 `muse-module-knowledge` 为 owner,新增 Knowledge 合同专用 app/admin 入口和应用服务。通用 `MuseContractPersistenceService` 只能作为迁移保护网,不能作为 P1R-5 完成路径;RAGFlow 是外部知识引擎,失败必须真实暴露为业务任务状态或 source action policy,不伪造成功。New-API 负责 LLM 调用,不负责知识库索引和检索。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、RAGFlow HTTP API、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 - -**Implementation Status:** 本文件是 P1R-5 规格计划阶段产物;review gate 待执行;当前不进入实现代码。收到“进入 P1R-5 实现阶段”后才允许按 Task 1-12 执行。 - ---- - -## 0. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的其他 checkout 上写阶段文档或实现代码。 -- 不修改 `docs/api-contracts/knowledge/openapi.yaml`。 -- 不修改 `docs/api-contracts/ai/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/**` 改变 P1R 判断口径。 -- 不破坏 AI 41、Content 51、Meta 16、Account 33 的 `dedicated / needs_verification`。 -- 不把任何 operation 标为 `completed`。 -- 不跨入 P1R-6 Market 主流程。 -- 不跨入 P1R-7 统一 `/app-api/muse/events`。 -- 如果新增或修改 DDL,阶段收口前必须在同一 PostgreSQL 实例的 `_test` 数据库跑真实 Flyway。 - -## 1. 当前事实盘点 - -当前 Knowledge operation 清单: - -```text -listKnowledgeDraftsGovernance GET /admin-api/muse/knowledge/drafts -listGlobalKnowledgeBases GET /admin-api/muse/knowledge/global-kbs -createGlobalKnowledgeBase POST /admin-api/muse/knowledge/global-kbs -getGlobalKnowledgeBase GET /admin-api/muse/knowledge/global-kbs/{kbId} -updateGlobalKnowledgeBase PATCH /admin-api/muse/knowledge/global-kbs/{kbId} -listGlobalKBAccessPolicies GET /admin-api/muse/knowledge/global-kbs/{kbId}/access-policies -saveGlobalKBAccessPolicyDraft POST /admin-api/muse/knowledge/global-kbs/{kbId}/access-policies/drafts -publishGlobalKBAccessPolicy POST /admin-api/muse/knowledge/global-kbs/{kbId}/access-policies/drafts/{draftId}/publish -disableGlobalKnowledgeBase POST /admin-api/muse/knowledge/global-kbs/{kbId}/disable -listGlobalKBDocuments GET /admin-api/muse/knowledge/global-kbs/{kbId}/documents -uploadGlobalKBDocument POST /admin-api/muse/knowledge/global-kbs/{kbId}/documents -deleteGlobalKBDocument DELETE /admin-api/muse/knowledge/global-kbs/{kbId}/documents/{documentId} -getGlobalKBDocument GET /admin-api/muse/knowledge/global-kbs/{kbId}/documents/{documentId} -createGlobalKBDocumentVersion POST /admin-api/muse/knowledge/global-kbs/{kbId}/documents/{documentId}/versions -enableGlobalKnowledgeBase POST /admin-api/muse/knowledge/global-kbs/{kbId}/enable -previewGlobalKBImpact POST /admin-api/muse/knowledge/global-kbs/{kbId}/impact-preview -listGlobalKBProcessingTasks GET /admin-api/muse/knowledge/global-kbs/{kbId}/processing-tasks -reindexGlobalKnowledgeBase POST /admin-api/muse/knowledge/global-kbs/{kbId}/reindex -triggerGlobalKBSourceEvent POST /admin-api/muse/knowledge/global-kbs/{kbId}/source-events -listGlobalKBVersions GET /admin-api/muse/knowledge/global-kbs/{kbId}/versions -activateGlobalKBVersion POST /admin-api/muse/knowledge/global-kbs/{kbId}/versions/{version}/activate -listProjectionTasks GET /admin-api/muse/knowledge/projection-tasks -listSourceBindings GET /admin-api/muse/knowledge/source-bindings -getEntity GET /app-api/muse/entities/{entityId} -updateEntity PUT /app-api/muse/entities/{entityId} -listRelations GET /app-api/muse/entities/{entityId}/relations -createRelation POST /app-api/muse/entities/{entityId}/relations -listInstalledKnowledgeBases GET /app-api/muse/installed-knowledge-bases -deleteInstalledKnowledgeBase DELETE /app-api/muse/installed-knowledge-bases/{installId} -disableInstalledKnowledgeBase POST /app-api/muse/installed-knowledge-bases/{installId}/disable -restoreInstalledKnowledgeBase POST /app-api/muse/installed-knowledge-bases/{installId}/restore -listKnowledgeBases GET /app-api/muse/knowledge-bases -createKnowledgeBase POST /app-api/muse/knowledge-bases -deleteKnowledgeBase DELETE /app-api/muse/knowledge-bases/{kbId} -getKnowledgeBase GET /app-api/muse/knowledge-bases/{kbId} -updateKnowledgeBase PATCH /app-api/muse/knowledge-bases/{kbId} -disableKnowledgeBase POST /app-api/muse/knowledge-bases/{kbId}/disable -listKBDocuments GET /app-api/muse/knowledge-bases/{kbId}/documents -uploadKBDocument POST /app-api/muse/knowledge-bases/{kbId}/documents -deleteKBDocument DELETE /app-api/muse/knowledge-bases/{kbId}/documents/{documentId} -listKBDocumentVersions GET /app-api/muse/knowledge-bases/{kbId}/documents/{documentId}/versions -createKBDocumentVersion POST /app-api/muse/knowledge-bases/{kbId}/documents/{documentId}/versions -createKBExportTask POST /app-api/muse/knowledge-bases/{kbId}/export-tasks -getKBProcessingTask GET /app-api/muse/knowledge-bases/{kbId}/processing-tasks/{taskId} -createKBPublishReadiness POST /app-api/muse/knowledge-bases/{kbId}/publish-prechecks -createKBMarketPublishReadiness POST /app-api/muse/knowledge-bases/{kbId}/publish-readiness -createKBPublishSnapshot POST /app-api/muse/knowledge-bases/{kbId}/publish-snapshots -reindexKnowledgeBase POST /app-api/muse/knowledge-bases/{kbId}/reindex -restoreKnowledgeBase POST /app-api/muse/knowledge-bases/{kbId}/restore -confirmKnowledgeDraft POST /app-api/muse/knowledge-drafts/{draftId}/confirm -ignoreKnowledgeDraft POST /app-api/muse/knowledge-drafts/{draftId}/ignore -recheckKnowledgeDraft POST /app-api/muse/knowledge-drafts/{draftId}/recheck -listEntities GET /app-api/muse/works/{workId}/entities -getKnowledgeGraph GET /app-api/muse/works/{workId}/graph -createKnowledgeBinding POST /app-api/muse/works/{workId}/knowledge-bindings -createKnowledgeBindingPrecheck POST /app-api/muse/works/{workId}/knowledge-bindings/prechecks -deleteKnowledgeBinding DELETE /app-api/muse/works/{workId}/knowledge-bindings/{bindingId} -listKnowledgeDrafts GET /app-api/muse/works/{workId}/knowledge-drafts -getLocalKnowledge GET /app-api/muse/works/{workId}/local-knowledge -``` - -全部 59 个当前状态均为 `generic_persistence / incomplete`,当前实现 owner 是 `AdminMuseKnowledgeContractController` / `AppMuseKnowledgeContractController` + `MuseContractPersistenceService`。 - -## 2. 子代理执行规则 - -每个实现 Task 使用 fresh subagent,模型 `gpt-5.5`,reasoning `xhigh`,等待超时 30 分钟。 - -每个实现 Task 完成后必须两轮 fresh review: - -1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md` 和本计划。 -2. quality / feasibility review:检查最小改动、可维护性、测试质量、RAGFlow/外部失败路径、无脚本掩盖。 - -review 未通过不能进入下一 Task。所有 review 发现的问题必须由新的修复子代理处理,再重新 review。 - -## Task 1: Preflight 与覆盖基线冻结 - -**Files:** - -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/api-contracts/knowledge/openapi.yaml` -- Read: `docs/api-contracts/ai/openapi.yaml` -- Read: `muse-cloud/muse-module-knowledge/**` -- Read: `muse-cloud/muse-module-ai/**` -- Read: `muse-cloud/sql/muse/**` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -仅在规格/计划 review 阶段,允许以下未跟踪文件存在,并且它们必须作为阶段交付物被 review: - -```text -?? docs/memorys/2026-06-01-P1R5KnowledgeRAGFlow规格计划.md -?? docs/superpowers/plans/2026-06-01-P1R-5-knowledge-ragflow-real-api.md -?? docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md -``` - -进入实现前,必须提交这些 review 文档,或明确确认它们仍是唯一未跟踪文件。此时如存在任何代码、DDL、OpenAPI 或脚本 diff,均为 blocker。 - -- [ ] **Step 2: 统计 Knowledge operation 状态** - -Run: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-5 Knowledge / RAGFlow Real API" or .domain == "knowledge")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -generic_persistence/incomplete 59 -``` - -- [ ] **Step 3: 冻结 operation 清单** - -Run: - -```bash -jq -r '.operations[] | select(.domain == "knowledge") | [.operationId, .method, .path, .implementationStatus, .completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 59 行,且 operationId 与本计划第 1 节一致。 - -- [ ] **Step 4: 检查禁止修改面** - -Run: - -```bash -git status --short -- docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/ai/openapi.yaml muse-cloud/scripts -``` - -Expected: 无输出。 - -## Task 2: Knowledge 基础设施与 V14 DDL - -**Files:** - -- Modify: `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/pom.xml` -- Create: `muse-cloud/muse-module-knowledge/muse-module-knowledge-api/src/main/java/cn/iocoder/muse/module/knowledge/enums/ErrorCodeConstants.java` 或复用项目既有错误码位置 -- Create: `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/domain/muse/*.java` -- Create: `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/application/muse/*.java` -- Create: `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/dataobject/muse/*.java` -- Create: `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/dal/mysql/muse/*.java` -- Create: `muse-cloud/sql/muse/V14__extend_knowledge_ragflow_real_api_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeMigrationSqlTest.java` - -- [ ] **Step 1: 确认 Knowledge server 模块依赖** - -确认或补齐 validation、web、security、mybatis、test 依赖。不要引入 AI module 作为 owner 依赖;如需复用 RAGFlow/New-API DTO,必须通过本模块 facade/adapter 明确隔离。 - -- [ ] **Step 2: 增加 Knowledge 错误码** - -至少覆盖: - -- `KNOWLEDGE_API_VERSION_UNSUPPORTED` -- `KNOWLEDGE_COMMAND_ID_REQUIRED` -- `KNOWLEDGE_COMMAND_ID_CONFLICT` -- `KNOWLEDGE_RESOURCE_FORBIDDEN` -- `KNOWLEDGE_BASE_NOT_EXISTS` -- `KNOWLEDGE_DOCUMENT_NOT_EXISTS` -- `KNOWLEDGE_DOCUMENT_NOT_PROCESSABLE` -- `KNOWLEDGE_RAGFLOW_UNAVAILABLE` -- `KNOWLEDGE_RAGFLOW_AUTH_FAILED` -- `KNOWLEDGE_RAGFLOW_TIMEOUT` -- `KNOWLEDGE_RAGFLOW_INDEX_NOT_READY` -- `KNOWLEDGE_DRAFT_NOT_EXISTS` -- `KNOWLEDGE_DRAFT_REVISION_CONFLICT` -- `KNOWLEDGE_SOURCE_STALE` -- `KNOWLEDGE_AUTHORIZATION_EXPIRED` -- `KNOWLEDGE_BIND_PRECHECK_NOT_EXISTS` -- `KNOWLEDGE_BIND_PRECHECK_EXPIRED` -- `KNOWLEDGE_MARKET_HANDOFF_UNAVAILABLE` - -- [ ] **Step 3: 编写 V14 迁移** - -至少新增或扩展: - -- `muse_knowledge_command` -- `muse_knowledge_processing_task` -- `muse_knowledge_ragflow_binding` -- `muse_knowledge_ragflow_call` -- `muse_knowledge_source_event` -- `muse_knowledge_source_binding_projection` -- `muse_knowledge_projection_task` -- `muse_knowledge_access_policy` -- `muse_knowledge_access_policy_draft` -- `muse_knowledge_impact_preview` -- `muse_knowledge_bind_precheck` -- `muse_knowledge_publish_snapshot` -- `muse_knowledge_publish_readiness` -- `muse_knowledge_draft_decision` - -关键约束: - -- command 唯一键 `(tenant_id, command_id)`。 -- RAGFlow call correlation 唯一或索引。 -- active KB version / active policy 使用 partial unique index 或等价约束。 -- task、binding、draft、source projection 均有 tenant/owner/status/create_time 索引。 -- SQL 不出现明文 token、secret、bearer、api_key 字段。 - -- [ ] **Step 4: 编写迁移 SQL 门禁测试** - -`P1rKnowledgeMigrationSqlTest` 至少校验 V14 文件存在、关键表/索引/约束存在、未出现明文密钥字段名、V5 基础表未被破坏。 - -## Task 3: Command / Guard / Audit / RAGFlow Adapter 基础 - -**Files:** - -- Create: `domain/muse/MuseKnowledgeApiVersionGuard.java` -- Create: `domain/muse/MuseKnowledgeOwnerGuard.java` -- Create: `application/muse/MuseKnowledgeCommandService.java` -- Create: `application/muse/MuseKnowledgeAuditService.java` -- Create: `application/muse/facade/RagFlowKnowledgeRuntimeClient.java` -- Create: `application/muse/facade/HttpRagFlowKnowledgeRuntimeClient.java` -- Create: `application/muse/facade/UnavailableRagFlowKnowledgeRuntimeClient.java` - -- [ ] 实现 `X-API-Version` 只接受 `"1"`。 -- [ ] 实现 commandId 幂等、requestHash replay、冲突检测。 -- [ ] 实现 owner/tenant/work/kb 权限 guard。 -- [ ] 实现 RAGFlow command/result/failure 类型:dataset、document、parse、chunk、retrieve、GraphRAG、knowledge graph、health。 -- [ ] RAGFlow adapter command 至少覆盖: - - `createDataset` - - `updateDatasetConfig` - - `listDatasets` - - `uploadDocuments` - - `startParseDocuments` - - `pollDocumentStatuses` - - `listChunks` - - `retrieveChunks` - - `runGraphRag` - - `traceGraphRag` - - `getKnowledgeGraph` -- [ ] RAGFlow adapter result 必须携带 external ID、operation、correlationId、requestHash、attempt、duration、status、脱敏摘要。 -- [ ] RAGFlow parse 成功响应不提供外部 task id;`startParseDocuments` 只能返回 accepted + Muse 内部 `processingTaskId/correlationId`,`pollDocumentStatuses` 必须通过 RAGFlow document list/detail 的 `run/progress/progress_msg` 轮询并归一 Muse 状态。 -- [ ] RAGFlow adapter failure enum 至少覆盖:`CONFIG_MISSING`、`AUTH_MISSING`、`AUTH_FAILED`、`RAGFLOW_UNAVAILABLE`、`TIMEOUT`、`RATE_LIMITED`、`VALIDATION_ERROR`、`CONFLICT`、`FILE_REJECTED`、`SSRF_BLOCKED`、`INDEX_NOT_READY`、`NO_CHUNK`、`GRAPH_NOT_READY`、`GRAPH_NOT_SUPPORTED`、`ATTRIBUTION_NOT_CONFIGURED`、`TASK_ALREADY_RUNNING`、`TASK_NOT_FOUND`、`RESPONSE_SCHEMA_CHANGED`、`UNKNOWN_FAILURE`。 -- [ ] RAGFlow adapter 必须有 timeout、retry budget、failureClass、correlation、脱敏摘要。 -- [ ] 没有 RAGFlow 配置时 fail closed,不返回假成功。 -- [ ] 默认 RAGFlow health base URL 候选来自配置;当前 live preflight 已验证 `http://100.64.0.8:9380/v1/system/healthz` 和 `http://100.64.0.8/v1/system/healthz` 返回 ok,`100.64.0.11:9380` 超时。实现不能把这些地址硬编码进业务代码。 -- [ ] dataset/document/retrieval/GraphRAG smoke 需要 API key 和测试 dataset;缺 key 时只能产出 `AUTH_MISSING` 或 blocked/needs_verification 证据。 -- [ ] 不实现 RAGFlow chat/session/assistant 问答路径;RAGFlow 检索结果只能作为 Knowledge/RAG source,LLM 生成仍由 New-API/AI owner 负责。 -- [ ] GraphRAG run 默认必须先检查 RAGFlow 默认模型是否经 New-API 且可归因;未确认时返回 `GRAPH_NOT_SUPPORTED` 或 `ATTRIBUTION_NOT_CONFIGURED` 并记录 task/call,不调用真实 `run_graphrag`。 - -## Task 4: Knowledge Base / Access Policy / Version - -**Files:** - -- Create: `controller/admin/muse/AdminMuseKnowledgeBaseController.java` -- Create: `controller/app/muse/AppMuseKnowledgeBaseController.java` -- Create: `controller/*/muse/vo/*KnowledgeBase*.java` -- Create: `application/muse/MuseKnowledgeBaseService.java` -- Create: `application/muse/MuseKnowledgeAccessPolicyService.java` -- Modify: `AdminMuseKnowledgeContractController.java` -- Modify: `AppMuseKnowledgeContractController.java` - -- [ ] 实现 admin global KB 列表、创建、详情、更新、启用、停用、影响预览、版本列表、版本激活。 -- [ ] 实现 global KB access policy 列表、草稿保存、发布。 -- [ ] 实现 app user KB 列表、创建、详情、更新、删除、停用、恢复。 -- [ ] 所有写命令必须走 command service。 -- [ ] 读接口必须返回 OpenAPI DTO 形态,不暴露 DB row。 - -## Task 5: Document / Processing / RAGFlow Ingestion - -**Files:** - -- Create: `controller/admin/muse/AdminMuseKnowledgeDocumentController.java` -- Create: `controller/app/muse/AppMuseKnowledgeDocumentController.java` -- Create: `application/muse/MuseKnowledgeDocumentService.java` -- Create: `application/muse/MuseKnowledgeProcessingTaskService.java` -- Create: `application/muse/facade/KnowledgeFileFacade.java` -- Create: `application/muse/MuseKnowledgeMaterializationService.java` -- Create: `controller/*/muse/vo/*Document*.java` - -- [ ] 实现 admin/app document list、upload/register、detail/version、delete/disable。 -- [ ] 上传入口支持 file、entryContent、linkUrl;三种来源必须先通过 `KnowledgeFileFacade` 或等价边界材料化为 fileRef。 -- [ ] file 来源记录 hash、size、contentType、scanStatus;entryContent 生成可审计文本 fileRef;linkUrl 先做 SSRF/DNS rebinding/类型/大小检查,再抓取、扫描、存储并转 fileRef。 -- [ ] FileService/对象存储/扫描不可用时,document version 和 processing task 必须进入 `failed` 或 blocked/needs_verification,不允许跳过材料化直接写 RAGFlow 成功。 -- [ ] 文档创建后写入 Muse document/version、materialization task 和 processing task。 -- [ ] RAGFlow dataset/document 映射必须持久化。 -- [ ] RAGFlow document upload/parse 失败必须落 `failed` 或可重试状态,不吞错;parse 状态由 Muse 内部 task + RAGFlow document `run/progress/progress_msg` 轮询归一。 -- [ ] `processingStatus` 映射只能使用 OpenAPI 声明值。 - -## Task 6: Processing Tasks / Reindex / Source Events / Projection - -Status note: Task 5 fresh spec compliance review PASS;fresh quality / feasibility re-review PASS。此处仅表示 Task 5 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -**Files:** - -- Create: `controller/admin/muse/AdminMuseKnowledgeTaskController.java` -- Create: `application/muse/MuseKnowledgeSourceEventService.java` -- Create: `application/muse/MuseKnowledgeProjectionTaskService.java` - -- [ ] 实现 `listGlobalKBProcessingTasks`、`getKBProcessingTask`。 -- [ ] 实现 admin/app reindex,重建索引只创建真实 Muse task 并调用 RAGFlow parse/retrieval adapter;GraphRAG 仅在 New-API 归因和预算已确认时调用,否则落 `GRAPH_NOT_SUPPORTED` / `ATTRIBUTION_NOT_CONFIGURED`。 -- [ ] 实现 `triggerGlobalKBSourceEvent` 和 `listSourceBindings`。 -- [ ] 实现 `listProjectionTasks`。 -- [ ] source event 必须更新 binding/projection,不只写日志。 - -## Task 7: Entity / Relation / Graph / Local Knowledge - -Status note: Task 6 fresh spec compliance review PASS;fresh quality / feasibility re-review PASS。此处仅表示 Task 6 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -**Files:** - -- Create: `controller/app/muse/AppMuseKnowledgeEntityController.java` -- Create: `controller/app/muse/AppMuseKnowledgeGraphController.java` -- Create: `application/muse/MuseKnowledgeEntityService.java` -- Create: `application/muse/MuseKnowledgeGraphQueryService.java` - -- [ ] 实现 `listEntities`、`getEntity`、`updateEntity`。 -- [ ] 实现 `listRelations`、`createRelation`。 -- [ ] 实现 `getKnowledgeGraph`,优先消费 canonical entity/relation,并在可用时合并 RAGFlow knowledge graph projection。 -- [ ] 实现 `getLocalKnowledge`。 -- [ ] GraphRAG 不可用时返回合同内明确状态,不伪造图谱。 - -## Task 8: Knowledge Draft Lifecycle - -Status note: Task 7 fresh spec compliance review PASS;fresh quality / feasibility re-review PASS。此处仅表示 Task 7 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -**Files:** - -- Create: `controller/admin/muse/AdminMuseKnowledgeDraftController.java` -- Create: `controller/app/muse/AppMuseKnowledgeDraftController.java` -- Create: `application/muse/MuseKnowledgeDraftService.java` - -- [ ] 实现 admin `listKnowledgeDraftsGovernance`。 -- [ ] 实现 app `listKnowledgeDrafts`。 -- [ ] 实现 `confirmKnowledgeDraft`:校验 commandId、expectedDraftRevision、sourceSnapshotId、authorizationSnapshotId、confirmMode、decisionType、riskAcknowledgement。 -- [ ] `confirmMode`、`decisionType` 必须按 OpenAPI 枚举和必填约束做 VO 校验、状态机分支和测试断言,不能只透传保存。 -- [ ] confirm 成功必须写 canonical `muse_knowledge_entity` / `muse_knowledge_relation` 和 draft decision。 -- [ ] stale/source hash 或授权快照不匹配必须失败或转 conflicted,不允许绕过。 -- [ ] 实现 `ignoreKnowledgeDraft`、`recheckKnowledgeDraft`。 - -Status note: Task 8 fresh spec compliance final re-review PASS;fresh quality / feasibility re-review PASS。此处仅表示 Task 8 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -## Task 9: Binding / Installed KB / Publish Readiness / Export - -**Files:** - -- Create: `controller/app/muse/AppMuseKnowledgeBindingController.java` -- Create: `controller/app/muse/AppMuseInstalledKnowledgeBaseController.java` -- Create: `application/muse/MuseKnowledgeBindingService.java` -- Create: `application/muse/MuseInstalledKnowledgeBaseService.java` -- Create: `application/muse/MuseKnowledgePublishService.java` - -- [ ] 实现 installed KB 列表、停用、删除、恢复。 -- [ ] 实现 binding precheck,消费 Market handoff/source authorization summary 并生成服务端快照。 -- [ ] 实现 binding,只消费 `kbBindPrecheckId`、`commandId`、`expectedWorkRevision`。 -- [ ] 实现 unbind。 -- [ ] 实现 `createKBPublishReadiness`、`createKBPublishSnapshot`、`createKBMarketPublishReadiness`,只产出源侧材料,不进入 Market 主流程。 -- [ ] 实现 `createKBExportTask` 为真实 task;文件/对象存储未就绪时标 blocked/needs_verification,不假成功。 - -Status note: Task 9 fresh spec compliance final re-review PASS;fresh quality / feasibility re-review PASS。此处仅表示 Task 9 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -## Task 10: Retire Knowledge 合同兜底与覆盖门禁 - -**Files:** - -- Modify/Delete: `AdminMuseKnowledgeContractController.java` -- Modify/Delete: `AppMuseKnowledgeContractController.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRealApiGateTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRouteOwnershipTest.java` - -- [ ] 移除或精确避让 P1R-5 59 个 Knowledge 路由的合同兜底。 -- [ ] `P1rKnowledgeRealApiGateTest` 固定 59 个 operation 清单,断言全部 `dedicated / needs_verification`。 -- [ ] 断言 `completedOperations = 0`。 -- [ ] 断言 AI 41、Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`。 -- [ ] 断言剩余 `sse_placeholder` 只能是 P1R-7 `events / streamEvents`。 -- [ ] `P1rKnowledgeRouteOwnershipTest` 在 Spring runtime 下证明 59 个 route 归属 `muse-module-knowledge` dedicated controller,不归属合同 Controller 或 `MuseContractPersistenceService`。 - -Status note: Task 10 fresh spec compliance review PASS;fresh quality / feasibility review PASS。此处仅表示 Task 10 review gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -## Task 11: PostgreSQL / Flyway `_test` 迁移验收 - -**Files:** - -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeFlywayMigrationIT.java` - -- [ ] 使用同一 PostgreSQL 实例的隔离 `_test` 库跑 V1-V14 全量迁移。 -- [ ] 验证 V14 关键表、索引、约束存在。 -- [ ] 数据库密码只从环境变量读取,不通过 Maven/JVM 参数传递;不输出数据库密码。 - -Run: - -```bash -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeFlywayMigrationIT.java -set -a -. ~/.config/muse-repo/infra.env -set +a -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -P1R5_TEST_DB="${P1R5_TEST_DB:-muse_local_p1r5_test}" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server \ - -Dtest=P1rKnowledgeFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R5_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -Expected: PostgreSQL/Flyway V1-V14 全量迁移成功,测试输出包含目标测试类和非零测试数;测试类缺失或未匹配必须失败。 - -Status note: Task 11 PostgreSQL/Flyway `_test` migration gate PASS。已在同一 PostgreSQL 实例隔离库 `muse_local_p1r5_test` 执行 V1-V14 全量迁移,`P1rKnowledgeFlywayMigrationIT` 1 test / 0 failures / 0 errors,输出仅包含 masked JDBC URL;此处仅表示 Task 11 migration gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 - -## Task 12: 最终验证、报告对账与 memory - -- [ ] 运行 coverage: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -- [ ] 对账 Knowledge: - -```bash -jq -r '[.operations[] | select(.domain == "knowledge")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -dedicated/needs_verification 59 -``` - -- [ ] 对账其他阶段: - -```bash -jq -r '["ai","content","meta","account"][] as $d | [$d, ([.operations[] | select(.domain==$d)] | group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring)))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.summary | [.completedOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -ai dedicated/needs_verification:41 -content dedicated/needs_verification:51 -meta dedicated/needs_verification:16 -account dedicated/needs_verification:33 -0 1 -``` - -- [ ] 运行 Maven gate: - -```bash -cd muse-cloud -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRealApiGateTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeRouteOwnershipTest.java -test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rKnowledgeMigrationSqlTest.java -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server \ - -Dtest=P1rApiCoverageReportTest,P1rKnowledgeRealApiGateTest,P1rKnowledgeRouteOwnershipTest,P1rKnowledgeMigrationSqlTest -``` - -- [ ] 运行 Knowledge focused tests,目标测试类和非零测试数必须明确。 -- [ ] 运行 `git diff --check`。 -- [ ] 确认禁止修改面: - -```bash -git diff -- docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/ai/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -Expected: 无输出。 - -- [ ] 更新 `docs/memorys/YYYY-MM-DD-P1R5KnowledgeRAGFlow*.md`,只写 dedicated gate 证据,不写 completed。 - -Status note: Task 12 final verification gate PASS。`p1r-audit-api-coverage.py --check` 通过并重新生成 coverage reports;Knowledge 59 = `dedicated / needs_verification`;AI 41、Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`;`completedOperations = 0`,`ssePlaceholderOperations = 1`。Maven gate `P1rApiCoverageReportTest,P1rKnowledgeRealApiGateTest,P1rKnowledgeRouteOwnershipTest,P1rKnowledgeMigrationSqlTest` 为 27 tests / 0 failures / 0 errors;Knowledge focused `muse-module-knowledge-server` 为 194 tests / 0 failures / 0 errors;`git diff --check` 无输出;禁止修改面 `docs/api-contracts/knowledge/openapi.yaml`、`docs/api-contracts/ai/openapi.yaml`、`muse-cloud/scripts/p1r-audit-api-coverage.py` 无 diff。此处仅表示 Task 12 verification gate 通过,不表示 P1R-5 或任何 Knowledge operation completed。 diff --git a/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md b/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md deleted file mode 100644 index 56c3a810..00000000 --- a/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md +++ /dev/null @@ -1,534 +0,0 @@ -# P1R-6 Market Real API Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. `superpowers:executing-plans` is acceptable only when subagents are unavailable. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将 Market 32 个 operation 从 `generic_persistence / incomplete` 推进到专用 Market Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`。 - -**Architecture:** 以 `muse-module-market` 为 owner,新增 Market 合同专用 app/admin 入口和应用服务。Market 只持有来源侧市场事实、授权摘要、安装、handoff、发布审核和治理状态;AI / Knowledge / Content 目标 owner 事实必须通过各自 owner 完成,Market 不直接写入。Account 只消费 Market 写入 `muse_account_record_projection` 的投影,并通过 member 模块既有 `MarketAccountProjectionFacade` 判断投影是否可用,不成为 Market 的运行时代码 owner。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 - -**Implementation Status:** 本文件是 P1R-6 规格计划阶段产物;review gate 待执行;当前不进入实现代码。收到“进入 P1R-6 实现阶段”后才允许按 Task 1-13 执行。 - ---- - -## 0. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的其他 checkout 上写阶段文档或实现代码。 -- 不修改 `docs/api-contracts/market/openapi.yaml`。 -- 不修改 `docs/api-contracts/ai/openapi.yaml`、`docs/api-contracts/knowledge/openapi.yaml`。 -- 不修改 `muse-cloud/scripts/**` 把 Market 伪装成 `completed`。 -- 不破坏 AI 41、Knowledge 59 当前 `dedicated / completed`。 -- 不破坏 Content 51、Meta 16、Account 33 当前 `dedicated / needs_verification`。 -- 不把 Market operation 标为 `completed`。 -- 不跨入 P1R-7 统一 `/app-api/muse/events`。 -- 如果新增或修改 DDL,阶段收口前必须在同一 PostgreSQL 实例的 `_test` 数据库跑真实 Flyway。 - -## 1. 当前事实盘点 - -Market operation 清单: - -```text -listMarketplaceAssets GET /app-api/muse/marketplace/assets -listMarketplaceCategories GET /app-api/muse/marketplace/categories -listMarketplaceRecommendations GET /app-api/muse/marketplace/recommendations -getMarketplaceAsset GET /app-api/muse/marketplace/assets/{assetId} -favoriteAsset POST /app-api/muse/marketplace/assets/{assetId}/favorite -unfavoriteAsset DELETE /app-api/muse/marketplace/assets/{assetId}/favorite -purchaseAsset POST /app-api/muse/marketplace/assets/{assetId}/purchase -installMarketplaceAsset POST /app-api/muse/marketplace/assets/{assetId}/install -createBindPrecheck POST /app-api/muse/marketplace/assets/{assetId}/bind-precheck -createMarketplaceHandoff POST /app-api/muse/marketplace/handoffs -getHandoffStatus GET /app-api/muse/marketplace/handoffs/{handoffToken} -cancelHandoff POST /app-api/muse/marketplace/handoffs/{handoffToken}/cancel -savePublishDraft POST /app-api/muse/marketplace/publish-drafts -runPublishCheck POST /app-api/muse/marketplace/publish-drafts/{draftId}/checks -submitPublishRequest POST /app-api/muse/marketplace/publish-requests -withdrawPublishRequest POST /app-api/muse/marketplace/publish-requests/{requestId}/withdraw -listMyPublishRecords GET /app-api/muse/marketplace/my-publish-records -getGovernanceImpact GET /app-api/muse/marketplace/assets/{assetId}/governance-impact -submitAppeal POST /app-api/muse/marketplace/appeals -supplementAppeal POST /app-api/muse/marketplace/appeals/{appealId}/supplements -withdrawAppeal POST /app-api/muse/marketplace/appeals/{appealId}/withdraw -adminListMarketAssets GET /admin-api/muse/market/assets -adminGetMarketAsset GET /admin-api/muse/market/assets/{assetId} -adminPreviewGovernanceImpact POST /admin-api/muse/market/assets/{assetId}/governance-impact -adminDelistAsset POST /admin-api/muse/market/assets/{assetId}/delist -adminRecallAsset POST /admin-api/muse/market/assets/{assetId}/recall -adminListPublishRequests GET /admin-api/muse/market/publish-requests -adminApprovePublishRequest POST /admin-api/muse/market/publish-requests/{requestId}/approve -adminRejectPublishRequest POST /admin-api/muse/market/publish-requests/{requestId}/reject -adminListAppeals GET /admin-api/muse/market/appeals -adminGetAppeal GET /admin-api/muse/market/appeals/{appealId} -adminResolveAppeal POST /admin-api/muse/market/appeals/{appealId}/resolve -``` - -全部 32 个当前状态均为 `generic_persistence / incomplete`,当前实现 owner 是 `AdminMuseMarketContractController` / `AppMuseMarketContractController` + `MuseContractPersistenceService`。 - -## 2. 子代理执行规则 - -同一实现 Task 内,复用最近一次负责该任务的 implementer;只有进入新的实现 Task 时,才启用 fresh implementer。 - -每个实现 Task 完成后必须两轮 fresh review: - -1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md` 和本计划。 -2. quality / feasibility review:检查最小改动、可维护性、测试质量、外部 owner 失败路径、无脚本掩盖。 - -review 未通过不能进入下一 Task。review 修复仍属于同一实现 Task,复用最近 implementer;重新 review 必须 fresh reviewer。 - -## Task 1: Preflight 与覆盖基线冻结 - -**Files:** - -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/superpowers/reports/p1r-api-coverage.md` -- Read: `docs/api-contracts/market/openapi.yaml` -- Read: `muse-cloud/muse-module-market/**` -- Read: `muse-cloud/sql/muse/**` -- Read: `docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -``` - -进入实现前,P1R-4 / P1R-5 coverage 状态推进和 P1R-6 spec/plan 必须已提交,或明确作为唯一待提交文档/coverage diff 存在。不得混入其它代码 diff。 - -- [ ] **Step 2: 统计 Market operation 状态** - -Run: - -```bash -jq -r '.operations[] | select(.domain == "market") | [.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json | sort | uniq -c -``` - -Expected: - -```text -32 generic_persistence incomplete -``` - -- [ ] **Step 3: 冻结 operation 清单** - -Run: - -```bash -jq -r '.operations[] | select(.domain == "market") | [.operationId,.method,.path,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: 输出 32 行,且 operationId 与本计划第 1 节一致。 - -- [ ] **Step 4: 检查禁止修改面** - -Run: - -```bash -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml muse-cloud/scripts -``` - -Expected: 无输出;如果存在 P1R-4 / P1R-5 coverage 脚本已批准改动,必须在进入 P1R-6 实现前先提交。 - -## Task 2: Market 基础设施与 V15 DDL - -**Files:** - -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/pom.xml` -- Create/Modify: `muse-cloud/muse-module-market/muse-module-market-api/src/main/java/cn/iocoder/muse/module/market/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/domain/muse/*.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/*.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/dataobject/muse/*.java` -- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/*.java` -- Create: `muse-cloud/sql/muse/V15__extend_market_real_api_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketMigrationSqlTest.java` - -- [ ] **Step 1: 确认 Market server 模块依赖** - -确认或补齐 validation、web、security、mybatis、test 依赖。P1R-6 Account 投影采用最小可执行接线方案:`muse-module-market-server` 显式依赖 `muse-module-member-server`,复用 member 侧既有 `MarketAccountProjectionFacade`、`AccountRecordProjectionDO` 和 `AccountRecordProjectionMapper`。当前 `member-server` 不依赖 `market-server`,该依赖方向不得形成 Maven 循环;实现时必须用 Maven reactor 或 dependency-tree 验证。不要引入 AI / Knowledge / Content 作为强 owner 依赖;跨 owner 能力通过本模块 facade 明确隔离。 - -- [ ] **Step 2: 增加 Market 错误码** - -至少覆盖: - -- `MARKET_API_VERSION_UNSUPPORTED` -- `MARKET_COMMAND_ID_REQUIRED` -- `MARKET_COMMAND_ID_CONFLICT` -- `MARKET_ASSET_NOT_EXISTS` -- `MARKET_ASSET_NOT_LISTED` -- `MARKET_ASSET_GOVERNANCE_BLOCKED` -- `MARKET_LICENSE_NOT_EXISTS` -- `MARKET_LICENSE_NOT_PURCHASABLE` -- `MARKET_INSTALL_NOT_EXISTS` -- `MARKET_INSTALL_NOT_BINDABLE` -- `MARKET_HANDOFF_NOT_EXISTS` -- `MARKET_HANDOFF_EXPIRED` -- `MARKET_HANDOFF_NOT_CANCELLABLE` -- `MARKET_PUBLISH_DRAFT_NOT_EXISTS` -- `MARKET_PUBLISH_CHECK_NOT_EXISTS` -- `MARKET_PUBLISH_CHECK_EXPIRED` -- `MARKET_PUBLISH_REQUEST_NOT_EXISTS` -- `MARKET_PUBLISH_REQUEST_STATUS_CONFLICT` -- `MARKET_GOVERNANCE_PREVIEW_REQUIRED` -- `MARKET_APPEAL_NOT_EXISTS` -- `MARKET_APPEAL_STATUS_CONFLICT` -- `MARKET_TARGET_OWNER_UNAVAILABLE` -- `MARKET_ACCOUNT_PROJECTION_UNAVAILABLE` - -- [ ] **Step 3: 编写 V15 迁移** - -至少新增或扩展: - -- `muse_market_command` -- `muse_market_authorization_snapshot` -- `muse_market_authorization_summary` -- `muse_market_handoff_event` -- `muse_market_publish_draft` -- `muse_market_publish_check` -- `muse_market_review_event` -- `muse_market_governance_preview` -- `muse_market_governance_action` -- `muse_market_governance_impact` -- `muse_market_appeal_material` -- `muse_market_appeal_event` -- `muse_market_account_projection` -- `muse_market_source_status_event` - -DDL 必须有 tenant、command、status、revision、audit 时间、必要唯一约束和查询索引。其中 `muse_market_account_projection` 只表达 Market 侧投影同步/outbox 状态,不是 Account 查询读模型;Account 查询读模型只能是 V11 已有的 `muse_account_record_projection`。 - -- [ ] **Step 4: DDL 静态测试** - -`P1rMarketMigrationSqlTest` 至少验证: - -- V15 文件存在。 -- 新表名存在。 -- `muse_market_command` 有 `command_id`、`request_hash` 和唯一约束。 -- handoff token 不以明文作为唯一安全依据,必须存在 token hash 或等价字段。 -- governance action / preview / impact 三类事实不被压成一个字段。 - -## Task 3: Market command、version、owner guard - -**Files:** - -- Create: `.../domain/muse/MarketApiVersionGuard.java` -- Create: `.../domain/muse/MarketCommandGuard.java` -- Create: `.../domain/muse/MarketOwnerGuard.java` -- Create: `.../application/muse/MarketCommandService.java` -- Create: `.../application/muse/MarketCommandServiceImpl.java` -- Test: `.../application/muse/MarketCommandServiceTest.java` - -- [ ] **Step 1: 实现 API version guard** - -所有 dedicated Controller 入口必须校验 `X-API-Version`,不支持版本返回业务错误,不进入应用服务写事实。 - -- [ ] **Step 2: 实现 command 幂等 guard** - -相同 command + 相同 request hash 返回已保存结果;相同 command + 不同 request hash 返回冲突。 - -- [ ] **Step 3: 实现 owner/tenant guard** - -App 查询只能看当前用户可见资产、授权、安装、发布记录和申诉。Admin 查询必须保留权限注解,不能用 app user 可见性替代。 - -## Task 4: 资产发现、分类、推荐、详情、收藏 - -**Files:** - -- Create: `.../controller/app/muse/AppMuseMarketplaceAssetController.java` -- Create: `.../controller/app/muse/vo/*.java` -- Create: `.../application/muse/MarketAssetQueryService.java` -- Create: `.../application/muse/MarketFavoriteService.java` -- Test: `.../controller/app/muse/AppMuseMarketplaceAssetControllerTest.java` -- Test: `.../application/muse/MarketFavoriteServiceTest.java` - -- [ ] **Step 1: 实现资产列表、分类、推荐、详情** - -读取 Market 资产、版本和展示摘要。无独立推荐数据时可以使用可解释 fallback 排序,但必须在响应摘要中体现来源,不允许 fake 个性化。 - -- [ ] **Step 2: 实现收藏/取消收藏** - -写 `muse_market_favorite`,保持幂等。取消收藏不能删除审计事实,只能更新状态。 - -- [ ] **Step 3: 测试合同字段和可见性** - -测试覆盖 listed/delisted/recall、当前用户收藏状态、分页结构、未登录和不可见资产。 - -## Task 5: 授权购买、安装和 Account 投影 - -**Files:** - -- Create: `.../controller/app/muse/AppMuseMarketLicenseController.java` -- Create: `.../application/muse/MarketLicenseService.java` -- Create: `.../application/muse/MarketInstallService.java` -- Create: `.../application/muse/MarketAccountProjectionProvider.java` -- Implement: `cn.iocoder.muse.module.member.application.account.facade.MarketAccountProjectionFacade` provider bean -- Use: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java` -- Use: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java` -- Test: `.../application/muse/MarketLicenseServiceTest.java` -- Test: `.../application/muse/MarketInstallServiceTest.java` -- Test: `.../application/muse/MarketAccountProjectionProviderTest.java` -- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceTest.java` - -- [ ] **Step 1: 实现 purchaseAsset** - -校验资产可获取、许可可用、治理策略允许新获取。写购买/授权快照,并向既有 `muse_account_record_projection` 写入 `purchase` / `license` 投影。重复 command 返回同一授权结果。 - -- [ ] **Step 2: 实现 installMarketplaceAsset** - -安装必须消费有效授权。安装智能体和知识库只进入账户可用列表,并更新 Account `license` 投影状态;不写 AI / Knowledge 目标事实。作品资产不走安装流程。 - -- [ ] **Step 3: Account projection fail-closed** - -不得在 Market 模块另建同名 `MarketAccountProjectionFacade` 或 `UnavailableMarketAccountProjectionFacade` 作为运行态契约。Market 必须通过 `market-server -> member-server` 依赖对接 member 模块既有 `cn.iocoder.muse.module.member.application.account.facade.MarketAccountProjectionFacade`,并提供真实 provider bean 替换 member 默认 auto-configuration 注册的 unavailable bean:当 Market projection provider 未接入时,Account 查询继续 fail-closed;当 provider 接入且投影写入成功后,测试必须证明 Account `purchase` / `license` / `publish` 查询不再命中默认 unavailable 实现。投影写入失败时,Market 业务事实只能记录为 projection pending / blocked,不能伪造成 Account 已同步。 - -## Task 6: 来源授权摘要与 Handoff - -**Files:** - -- Create: `.../controller/app/muse/AppMuseMarketHandoffController.java` -- Create: `.../application/muse/MarketHandoffService.java` -- Create: `.../application/muse/facade/MarketTargetOwnerFacade.java` -- Create: `.../application/muse/facade/UnavailableMarketTargetOwnerFacade.java` -- Test: `.../application/muse/MarketHandoffServiceTest.java` -- Test: `.../controller/app/muse/AppMuseMarketHandoffControllerTest.java` - -- [ ] **Step 1: 实现 createBindPrecheck** - -生成来源侧 authorization summary 和 authorization snapshot。结果不能包含目标 owner precheck ID。 - -- [ ] **Step 2: 实现 createMarketplaceHandoff** - -生成一次性 handoff token、token hash、过期时间和目标页。Market 不消费 token,不写目标 owner。 - -- [ ] **Step 3: 实现 getHandoffStatus / cancelHandoff** - -查询不改变状态。取消只能取消 pending / owner_precheck_created 前的 token;已 completed、expired、cancelled 不可取消。这里的 `completed` 只表示 handoff token 业务生命周期状态,不等于 coverage `completionStatus=completed`,不得据此推进 Market operation completed。 - -## Task 7: 发布草稿、发布检查、提交与撤回 - -**Files:** - -- Create: `.../controller/app/muse/AppMuseMarketPublishController.java` -- Create: `.../application/muse/MarketPublishService.java` -- Test: `.../application/muse/MarketPublishServiceTest.java` -- Test: `.../controller/app/muse/AppMuseMarketPublishControllerTest.java` - -- [ ] **Step 1: 实现 savePublishDraft** - -保存发布材料草稿,不提交审核、不上架。作品资产高阶模式只允许保存为 `read_only_favorite_license`。 - -- [ ] **Step 2: 实现 runPublishCheck** - -检查权利、隐私、许可、治理和材料 hash。检查通过只允许 submit,不上架。 - -- [ ] **Step 3: 实现 submitPublishRequest / withdrawPublishRequest / listMyPublishRecords** - -提交必须原子消费未过期、匹配草稿版本和材料 hash 的检查结果。撤回只允许非终态申请。 - -## Task 8: 管理端审核、上架、拒绝 - -**Files:** - -- Create: `.../controller/admin/muse/AdminMuseMarketReviewController.java` -- Create: `.../application/muse/AdminMarketReviewService.java` -- Test: `.../application/muse/AdminMarketReviewServiceTest.java` -- Test: `.../controller/admin/muse/AdminMuseMarketReviewControllerTest.java` - -- [ ] **Step 1: 实现 adminListPublishRequests** - -支持审核状态、资产类型、风险标签、发布者筛选。 - -- [ ] **Step 2: 实现 adminApprovePublishRequest** - -通过必须消费审核校验结果,写审核事件,更新资产/版本为 listed,可见状态必须可查询。 - -- [ ] **Step 3: 实现 adminRejectPublishRequest** - -拒绝必须写理由和证据标签,发布者可后续申诉。终态申请不可重复审核。 - -## Task 9: 管理端资产治理、下架、召回和影响预览 - -**Files:** - -- Create: `.../controller/admin/muse/AdminMuseMarketAssetController.java` -- Create: `.../application/muse/AdminMarketGovernanceService.java` -- Test: `.../application/muse/AdminMarketGovernanceServiceTest.java` -- Test: `.../controller/admin/muse/AdminMuseMarketAssetControllerTest.java` - -- [ ] **Step 1: 实现 adminListMarketAssets / adminGetMarketAsset** - -管理端详情必须包含版本、授权、安装、治理和来源摘要。 - -- [ ] **Step 2: 实现 adminPreviewGovernanceImpact** - -生成影响预览,覆盖授权、安装、handoff、绑定摘要、运行中任务、Shadow 候选、Knowledge Draft 和导出限制。 - -- [ ] **Step 3: 实现 adminDelistAsset / adminRecallAsset** - -必须消费有效 `impactPreviewId` 和 expectedStatus。召回要写 source status propagation 事实,owner facade 不可用时记录 blocked,并把需要重验的信息写入 action policy / recheck reasons;`needs_recheck` 不能作为 Market `SourceStatus` 值。 - -## Task 10: 申诉提交、补充、撤回与管理端处理 - -**Files:** - -- Create: `.../controller/app/muse/AppMuseMarketAppealController.java` -- Create: `.../controller/admin/muse/AdminMuseMarketAppealController.java` -- Create: `.../application/muse/MarketAppealService.java` -- Test: `.../application/muse/MarketAppealServiceTest.java` -- Test: `.../controller/app/muse/AppMuseMarketAppealControllerTest.java` -- Test: `.../controller/admin/muse/AdminMuseMarketAppealControllerTest.java` - -- [ ] **Step 1: 实现用户端申诉** - -提交、补充、撤回必须校验申诉期限、关联关系和 expectedStatus。补充材料保存脱敏摘要,不记录密钥或无关正文全文。 - -- [ ] **Step 2: 实现管理端申诉查询与处理** - -只读取申诉材料和发布资产,不读取用户私有副本。恢复上架必须消费治理影响预览。 - -## Task 11: Retire Market 合同兜底与 coverage gate - -**Files:** - -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/admin/AdminMuseMarketContractController.java` -- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/app/AppMuseMarketContractController.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRealApiGateTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRouteOwnershipTest.java` - -- [ ] **Step 1: 精确退役 32 个 Market 路由兜底** - -合同兜底 Controller 不能继续捕获 P1R-6 32 个路由。可以删除或精确避让,但必须由 route ownership test 证明。 - -- [ ] **Step 2: coverage gate** - -`P1rMarketRealApiGateTest` 必须证明: - -- Market operation 数量为 32。 -- 32 个 operation 全部 `dedicated / needs_verification`。 -- Market 不包含 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing`。 -- `completedOperations` 仍为 100。 -- AI / Knowledge 仍是 `dedicated / completed`。 -- Content / Meta / Account 仍是 `dedicated / needs_verification`。 -- Events P1R-7 `streamEvents` 仍是唯一 SSE placeholder。 - -- [ ] **Step 3: route ownership gate** - -`P1rMarketRouteOwnershipTest` 必须证明 32 个 Market 路由 handler 不属于 `*MarketContractController`,且不调用 `MuseContractPersistenceService`。 - -## Task 12: PostgreSQL / Flyway `_test` 迁移验收 - -**Files:** - -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketFlywayMigrationIT.java` - -- [ ] **Step 1: 构建 reactor** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -``` - -- [ ] **Step 2: 跑真实 PostgreSQL / Flyway** - -Run: - -```bash -cd muse-cloud -set -a -. ~/.config/muse-repo/infra.env -set +a -export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" -P1R6_TEST_DB="${P1R6_TEST_DB:-muse_local_p1r6_test}" -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server \ - -Dtest=P1rMarketFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R6_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" -``` - -Expected: - -```text -migrations_executed >= 15 -target_schema_version = 15 -flyway_latest = 15:extend market real api schema -``` - -## Task 13: 最终验证、报告对账与 memory - -**Files:** - -- Modify: `docs/superpowers/reports/p1r-api-coverage.json` -- Modify: `docs/superpowers/reports/p1r-api-coverage.md` -- Create: `docs/memorys/YYYY-MM-DD-P1R6Market真实API收口.md` - -- [ ] **Step 1: 重新生成 coverage** - -Run: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -- [ ] **Step 2: 对账 coverage** - -Run: - -```bash -jq -r '.operations | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -market dedicated/needs_verification:32 -completedOperations = 100 -genericPersistenceOperations = 0 -ssePlaceholderOperations = 1 -``` - -- [ ] **Step 3: Maven gate** - -Run: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rMarketRealApiGateTest,P1rMarketRouteOwnershipTest,P1rMarketMigrationSqlTest test -``` - -- [ ] **Step 4: focused tests** - -Run the focused Market service/controller tests created in Tasks 3-11. - -- [ ] **Step 5: diff hygiene** - -Run: - -```bash -git diff --check -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml -``` - -Expected: no OpenAPI diff and no whitespace errors. - -## 3. Review handoff - -完成本计划文档后必须执行两轮 fresh review: - -1. spec compliance review:确认本计划覆盖 `docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md`,没有跨入目标 owner 事实写入。 -2. quality / feasibility review:确认任务拆分、DDL、测试门禁、失败路径和验证命令足够可执行。 - -review 通过前不得进入 P1R-6 代码实现。 diff --git a/docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md b/docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md deleted file mode 100644 index 9d9c3f00..00000000 --- a/docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md +++ /dev/null @@ -1,796 +0,0 @@ -# P1R-7a Events SSE Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 将唯一 Events operation `streamEvents GET /app-api/muse/events` 从 `sse_placeholder / incomplete` 推进到独立 Events owner 可验收的 `dedicated / needs_verification`。 - -**Architecture:** 新增 `muse-module-events-api/server` 作为统一 Events owner,持有 `/app-api/muse/events`、`muse_unified_event`、全局 `muse:` 游标、source owner 发布契约、SSE replay / heartbeat / error event。跨 owner 发布模式固定为 source owner 本域 outbox + worker 调用 Events API;但 P1R-7a 只实现 Events owner、发布契约、统一事件流和“已有成熟 outbox/job 的可选最小接入”,不在本阶段强制改造 AI、Knowledge、Market、Member/Account 四个 owner。Events server 不允许反向依赖 source owner server,也不允许统一 SSE Controller 直接扫多域业务表。 - -**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult` / app security、MyBatis Plus、PostgreSQL/Flyway、Spring `SseEmitter`、JUnit 5、Mockito/MockMvc、TypeScript fetch stream、P1R coverage JSON。 - -**Implementation Status:** 本文件是 P1R-7a 执行版计划;review gate 待执行;review PASS 前不进入实现。 - ---- - -## 0. 已确认决策 - -1. 本阶段命名为 `P1R-7a Events SSE`,是总 P1R-7 End-to-End Acceptance 的前置子任务。 -2. 目标只到 `streamEvents = dedicated / needs_verification`,不标 `completed`。 -3. 新增独立 `muse-module-events-api/server`。 -4. 新增 `muse-cloud/sql/muse/V16__extend_events_sse_schema.sql`。 -5. `lastEventId` 采用 `muse:`,执行版需兼容非法 cursor 的 SSE `error` event。 -6. 首批 source mapping 限定为 AI task event 和 notification 最小集合,不扩大 OpenAPI event 类型;只有 source owner 已存在安全可公开事实和可复用 outbox/job 时才接入,否则列为 P1R-7b / `needs_verification` 风险。 -7. 前端全局事件流从 `EventSource` 改为 fetch 流式 SSE,保留 `close()`、`on(event, handler)`、`lastEventId`、事件名分发。 - -## 1. 执行边界 - -- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0` -- 分支:`dev/1.0.0` -- 不在 `/Users/qingse/Sync/local-git/oh-my-muse` 的其他 checkout 写阶段文档或实现代码。 -- 不修改 `docs/api-contracts/market/openapi.yaml`、`docs/api-contracts/ai/openapi.yaml`、`docs/api-contracts/knowledge/openapi.yaml`。 -- 不修改 `docs/api-contracts/events/openapi.yaml`;若发现合同必须变更,停止实现并单独提出合同变更。 -- 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py` 掩盖缺口。 -- 不把 Market 或 Events operation 标为 `completed`。 -- 每个实现 Task 必须 fresh implementer;每个 Task 后必须 fresh spec compliance review + fresh quality / feasibility review。 - -## 2. 当前事实基线 - -已验证 coverage summary: - -```text -completedOperations 100 -needsVerificationOperations 132 -incompleteOperations 1 -genericPersistenceOperations 0 -ssePlaceholderOperations 1 -``` - -Events operation: - -```text -streamEvents GET /app-api/muse/events -targetStage: P1R-7 End-to-End Acceptance -implementationStatus: sse_placeholder -completionStatus: incomplete -controller: muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java -``` - -当前占位 controller 会发送 `muse event stream ready` 后立即 `complete()`。scanner 识别该模式为 `sse_placeholder`。 - -## 3. 文件结构规划 - -### 3.1 后端 Maven 与模块 - -- Modify: `muse-cloud/pom.xml` -- Create: `muse-cloud/muse-module-events/pom.xml` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/pom.xml` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/pom.xml` -- Modify: `muse-cloud/muse-server/pom.xml` - -### 3.2 Events API 契约 - -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApi.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/dto/EventsPublishReqDTO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/dto/EventsPublishRespDTO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/enums/ErrorCodeConstants.java` - -### 3.3 Events server 实现 - -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/controller/app/AppMuseEventsController.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/stream/EventsStreamService.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/stream/EventsStreamServiceImpl.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/publish/EventsPublishService.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/publish/EventsPublishServiceImpl.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApiImpl.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/domain/EventsCursor.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/domain/EventsPayloadSanitizer.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/dal/dataobject/UnifiedEventDO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/dal/mysql/UnifiedEventMapper.java` - -### 3.4 旧路由避让 - -- Modify: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java` - -要求:旧 Content controller 不能再拥有 `/muse/events` 路由。推荐删除该类或改为非 Spring bean;执行时必须确认 route ownership test 能证明只有 Events module controller 持有 `streamEvents`。 - -### 3.5 DDL 与门禁 - -- Create: `muse-cloud/sql/muse/V16__extend_events_sse_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsMigrationSqlTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsFlywayMigrationIT.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRealApiGateTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRouteOwnershipTest.java` - -### 3.6 Frontend fetch SSE - -- Modify: `muse-studio/src/lib/sse.ts` -- Create or modify focused tests under `muse-studio/src/**/__tests__` or existing test location discovered during implementation. - -## Task 1: Preflight 与执行基线冻结 - -**Files:** - -- Read: `docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md` -- Read: `docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md` -- Read: `docs/superpowers/reports/p1r-api-coverage.json` -- Read: `docs/api-contracts/events/openapi.yaml` -- Read: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java` -- Read: `muse-studio/src/lib/sse.ts` - -- [ ] **Step 1: 核实工作面** - -Run: - -```bash -cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -git status --short --branch -``` - -Expected: - -```text -## dev/1.0.0...origin/dev/1.0.0 -?? docs/memorys/2026-06-05-P1R7aEventsSSE审阅版.md -?? docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md -?? docs/superpowers/plans/2026-06-05-P1R-7a-events-sse.md -``` - -如果存在其他 dirty / untracked,必须先判断来源;不要清理或回退非本任务文件。 - -- [ ] **Step 2: 冻结 Events coverage** - -Run: - -```bash -jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -jq -r '.operations[] | select(.domain == "events" or .operationId == "streamEvents") | [.domain,.operationId,.method,.path,.implementationStatus,.completionStatus,.targetStage] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -Expected: - -```text -100 132 1 0 1 -events streamEvents GET /app-api/muse/events sse_placeholder incomplete P1R-7 End-to-End Acceptance -``` - -- [ ] **Step 3: 核实受保护文件无改动** - -Run: - -```bash -git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml docs/api-contracts/events/openapi.yaml muse-cloud/scripts/p1r-audit-api-coverage.py -``` - -Expected: 无输出。 - -## Task 2: Events Maven 模块与路由 owner 骨架 - -**Files:** - -- Modify: `muse-cloud/pom.xml` -- Create: `muse-cloud/muse-module-events/pom.xml` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/pom.xml` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/pom.xml` -- Modify: `muse-cloud/muse-server/pom.xml` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRouteOwnershipTest.java` - -- [ ] **Step 1: 新增 Maven 模块骨架** - -按 `muse-module-meta` / `muse-module-market` 现有结构新增: - -```text -muse-module-events - muse-module-events-api - muse-module-events-server -``` - -`events-api` 只依赖 `muse-common`。`events-server` 允许依赖: - -```text -muse-module-events-api -muse-spring-boot-starter-web -muse-spring-boot-starter-security -muse-spring-boot-starter-biz-tenant -muse-spring-boot-starter-mybatis -spring-boot-starter-validation -muse-spring-boot-starter-test (test) -``` - -禁止 `events-server` 依赖 `ai-server`、`knowledge-server`、`market-server`、`member-server`、`content-server`。 - -- [ ] **Step 2: 装配 muse-server** - -在 `muse-cloud/muse-server/pom.xml` 引入: - -```xml - - cn.iocoder.cloud - muse-module-events-server - ${revision} - -``` - -- [ ] **Step 3: 增加 route ownership test** - -`P1rEventsRouteOwnershipTest` 必须断言: - -1. `streamEvents` 只能由 Events module controller 持有。 -2. Content 旧 `AppMuseEventsController` 不能继续声明 `/muse/events`。 -3. Events server 不依赖 source owner server。 - -- [ ] **Step 4: 运行 Maven 接线验证** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am -DskipTests dependency:tree -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rEventsRouteOwnershipTest test -``` - -Expected: - -```text -BUILD SUCCESS -``` - -## Task 3: V16 统一事件投影 DDL 与 SQL/Flyway 门禁 - -**Files:** - -- Create: `muse-cloud/sql/muse/V16__extend_events_sse_schema.sql` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsMigrationSqlTest.java` -- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsFlywayMigrationIT.java` - -- [ ] **Step 1: 编写 V16 DDL** - -必须创建 `muse_unified_event`,字段至少包含: - -```text -id BIGSERIAL -tenant_id BIGINT NOT NULL -command_id VARCHAR NOT NULL -event_id VARCHAR NOT NULL -sequence_no BIGINT NOT NULL DEFAULT nextval('muse_unified_event_sequence_no_seq') -event_type VARCHAR NOT NULL -owner_user_id BIGINT NOT NULL -source_owner VARCHAR NOT NULL -source_type VARCHAR NOT NULL -source_id VARCHAR NOT NULL -source_revision VARCHAR NOT NULL DEFAULT '__none__' -resource_type VARCHAR -resource_id VARCHAR -payload_summary JSONB NOT NULL DEFAULT '{}'::jsonb -publish_status VARCHAR NOT NULL -publish_error_code VARCHAR -visible_from TIMESTAMP NOT NULL -emitted_at TIMESTAMP NOT NULL -deleted BOOLEAN NOT NULL DEFAULT FALSE -create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -``` - -必须创建 `muse_unified_event_sequence_no_seq`,并让 `sequence_no` 在插入 accepted / rejected / blocked 记录时由 PostgreSQL sequence 分配;禁止使用 `max(sequence_no)+1` 或应用内自增。`source_revision` 没有业务版本时统一写入 sentinel `__none__`,避免 PostgreSQL 普通唯一约束对 `NULL` 不去重。 - -必须有约束 / 索引: - -```text -UNIQUE (tenant_id, sequence_no) -UNIQUE (tenant_id, command_id) -UNIQUE (tenant_id, event_id) -UNIQUE (tenant_id, source_owner, source_type, source_id, source_revision, event_type) -INDEX (tenant_id, owner_user_id, sequence_no) -CHECK event_type IN ('chunk','quality_check','done','error','notification') -CHECK publish_status IN ('accepted','rejected','blocked') -``` - -- [ ] **Step 2: SQL 静态门禁** - -`P1rEventsMigrationSqlTest` 至少验证: - -1. V16 文件存在。 -2. 表、sequence、默认值、唯一约束、索引、check constraints 存在。 -3. `payload_summary` 是 JSONB。 -4. 没有明文 token / authorization / secret 字段。 -5. 存在 `P1rEventsFlywayMigrationIT`。 -6. `command_id` 是 `NOT NULL`,且存在 `UNIQUE (tenant_id, command_id)`。 -7. `source_revision` 是 `NOT NULL DEFAULT '__none__'`,且 source tuple 唯一约束覆盖该字段。 -8. `sequence_no` 使用 PostgreSQL sequence / default 分配,测试必须拒绝 `max(sequence_no)+1` 文本模式。 - -- [ ] **Step 3: Flyway _test 门禁** - -`P1rEventsFlywayMigrationIT` 参考 `P1rMarketFlywayMigrationIT`,必须: - -1. 只允许 `_test` 后缀数据库。 -2. 不从 JDBC URL 读取密码。 -3. `clean + migrate` 到 V16。 -4. 输出 `flyway_latest=16:extend events sse schema`。 - -- [ ] **Step 4: 运行验证** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rEventsMigrationSqlTest test -``` - -Flyway IT 在最终验证阶段运行;如果本 Task 有测试库凭据,也可以提前运行。 - -## Task 4: Events publish contract 与幂等投影 - -**Files:** - -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApi.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/dto/EventsPublishReqDTO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/api/publish/dto/EventsPublishRespDTO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-api/src/main/java/cn/iocoder/muse/module/events/enums/ErrorCodeConstants.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/api/publish/EventsPublishApiImpl.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/publish/EventsPublishService*.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/domain/EventsPayloadSanitizer.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/dal/dataobject/UnifiedEventDO.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/dal/mysql/UnifiedEventMapper.java` -- Create: focused tests under `muse-cloud/muse-module-events/muse-module-events-server/src/test/java/**` - -- [ ] **Step 1: 定义 API DTO** - -`EventsPublishReqDTO` 必须包含: - -```text -commandId -tenantId -ownerUserId -sourceOwner -sourceType -sourceId -sourceRevision -eventType -resourceType -resourceId -payloadSummary -emittedAt -``` - -`EventsPublishRespDTO` 必须包含: - -```text -eventId -sequenceNo -publishStatus -publishErrorCode -duplicate -``` - -- [ ] **Step 2: 实现 publish 幂等** - -语义: - -1. 同一 `commandId` 或同一 source tuple 重复发布返回同一 `eventId / sequenceNo`,`duplicate=true`。 -2. payload 不符合 OpenAPI event schema 时写入 `publish_status=rejected`,不进入 SSE 可见查询。 -3. source owner 未接入或调用失败不得伪造事件。 -4. payload sanitizer 必须过滤 `authorization`、`bearer`、`token`、`secret`、provider raw body。 -5. `commandId` 必填并落入 `command_id`;幂等查重必须先按 `(tenant_id, command_id)`,再按 source tuple。 -6. `sourceRevision` 为空时必须规范化为 `__none__` 再落库和查重;不得把 Java `null` 直接写入 source tuple 唯一约束字段。 -7. `sequenceNo` 只能读取数据库 sequence/default 分配结果;不得由应用通过 `max(sequence_no)+1`、本地计数器或无锁查询生成。 - -P1R-7a 的跨 owner 发布契约采用 **source owner 本域 outbox + worker publish** 策略,不采用同步 facade 直接阻塞业务提交。Task 4 只实现 Events 侧 API、幂等、状态记录和 SSE 可见性过滤;source owner worker 的具体接入范围由 Task 6 按最小可行条件筛选: - -1. source owner 先在本域事务内写入业务事实和本域 outbox 事实;主业务事实不得因为 EventsPublishApi 临时失败而回滚。 -2. source owner worker 读取本域 outbox 后调用 `EventsPublishApi`;EventsPublishApi 只负责验证、幂等落库和返回 publish 结果,不反向拉取 source owner 数据。 -3. `EventsPublishApi` 调用失败时,source owner outbox 保持可重试状态;失败期间不得伪造浏览器可见事件。 -4. 重试达到本域上限后,source owner outbox 进入 `dead_letter` 或等价阻塞状态;Events 侧如果已经收到但 payload 不合规,则记录 `publish_status=rejected` 或 `blocked` 并不进入 SSE 可见查询。 -5. replay 由 source owner worker 基于本域 outbox 的 source tuple / commandId 重新调用 `EventsPublishApi`;Events 侧必须通过 `commandId` 和 source tuple 幂等返回同一 `eventId / sequenceNo`,不能生成重复事件。 -6. operator 或后续补偿任务只能重放本域 outbox 事实,不能绕过 source owner 直接向 Events 注入伪造 payload。 - -- [ ] **Step 3: focused tests** - -至少覆盖: - -```text -should_publishAcceptedEvent_when_payloadMatchesOpenApiSchema -should_returnSameEvent_when_duplicateCommandPublished -should_rejectPayload_when_containsSecret -should_rejectPayload_when_eventTypeNotDeclared -should_notExposeRejectedEventToStreamQuery -should_returnSameEvent_when_sourceOwnerReplaysOutboxFact -should_notExposeBlockedEvent_when_publishValidationFailsAfterRetry -should_returnSameEvent_when_sourceRevisionIsNullAndReplayed -should_allocateMonotonicSequenceNo_whenPublishConcurrentEvents -``` - -- [ ] **Step 4: 运行验证** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-events/muse-module-events-server -am -Dtest='*EventsPublish*Test,*UnifiedEvent*Test' test -``` - -## Task 5: Events SSE stream service - -**Files:** - -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/controller/app/AppMuseEventsController.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/application/stream/EventsStreamService*.java` -- Create: `muse-cloud/muse-module-events/muse-module-events-server/src/main/java/cn/iocoder/muse/module/events/domain/EventsCursor.java` -- Modify/Delete: `muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java` -- Create: focused stream tests under `muse-cloud/muse-module-events/muse-module-events-server/src/test/java/**` - -- [ ] **Step 1: Controller** - -`AppMuseEventsController` 必须: - -1. 位于 events module。 -2. 仍映射 `/muse/events`,与网关 `/app-api` 前缀保持现有模式一致。 -3. 读取并校验 `X-API-Version`,支持版本为 `1`。 -4. 在进入 SSE 生命周期前取得登录用户。 -5. 读取 `lastEventId`。 -6. 委托 `EventsStreamService.streamEvents(loginUserId, apiVersion, lastEventId)`。 -7. 不支持的 `X-API-Version` 不能返回 OpenAPI 未声明的普通 400;必须建立 SSE 后发送已声明 `error` event,然后 complete,并写安全日志。 - -- [ ] **Step 2: Cursor** - -`EventsCursor` 必须: - -1. 接受空 cursor。 -2. 接受 `muse:`。 -3. 非法 cursor 不能抛出未脱敏异常;应生成 OpenAPI 声明的 `error` event。 -4. 空 cursor 的语义是“从当前连接时刻之后推送”,不是 replay 历史。 - -- [ ] **Step 3: Stream service** - -必须实现: - -1. 只查询 `publish_status=accepted` 且 tenant / ownerUserId 匹配的事件。 -2. 如果 `lastEventId` 为空,连接建立时读取当前可见最大 `sequence_no` 作为起点,后续只推送更大的事件;已有历史事件只能导致 heartbeat,不能被首连 replay。 -3. 如果 `lastEventId` 是 `muse:`,replay `sequence_no > lastSequenceNo`。 -4. SSE `id` 使用 `muse:`。 -5. event 名称只允许 OpenAPI 声明的 5 种。 -6. 无事件时发送 SSE comment heartbeat,不发送假 event。 -7. 不支持的 API version、非法 cursor、线程池拒绝和 DB 异常都 fail-closed,写安全日志;除 401 鉴权失败外,进入 SSE 生命周期后的业务错误统一用 OpenAPI 声明的 `error` event 收敛。 -8. 客户端断开、timeout、error 时取消后台轮询。 - -- [ ] **Step 4: focused tests** - -至少覆盖: - -```text -should_replayAcceptedEventsAfterCursor_forCurrentTenantAndOwner -should_notReplayHistoricalEvents_whenLastEventIdMissing -should_notReplayCrossTenantOrCrossOwnerEvents -should_sendHeartbeatOnly_whenNoVisibleEvents -should_emitErrorEventAndComplete_whenCursorInvalid -should_emitErrorEventAndComplete_whenApiVersionUnsupported -should_completeWithError_whenExecutorRejectsPolling -should_cancelPollingFuture_whenEmitterCompletes -should_notLeakSecretFields_whenStreamingErrorEvent -``` - -- [ ] **Step 5: 运行验证** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-events/muse-module-events-server -am -Dtest='*EventsStream*Test,*EventsCursor*Test' test -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rEventsRouteOwnershipTest test -``` - -## Task 6: source owner publish 兼容性盘点与最小接入 - -**Files:** - -- Inspect source owner services and modify only when they already have safe public facts and reusable outbox / job foundations: - - AI task event path under `muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/**` - - Knowledge source event path under `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/**` - - Market source/governance path under `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/**` - - Account security path under `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/**` -- Modify source owner `pom.xml` only to depend on `muse-module-events-api`, never `events-server`. -- Add focused tests only for actually touched source owners. -- If no source owner satisfies the minimal conditions, do not modify source owner code; record all source-owner propagation as P1R-7b / `needs_verification` in the final evidence. - -- [ ] **Step 1: 兼容性盘点** - -先只读盘点每个候选 source owner 是否同时满足: - -1. 已有 owner-visible 的安全事实或已脱敏摘要字段。 -2. 已有可复用 outbox / job / retry 入口,或已有等价异步补偿模式。 -3. 能生成稳定 `commandId` 或 source tuple,不需要新增跨域 ID 体系。 -4. 新增依赖只需要 `muse-module-events-api`,不会引入 server 循环依赖。 -5. focused tests 可以在 owner 模块内稳定覆盖成功、失败、重复和 blocked / dead_letter 语义。 - -不满足任一条件的 owner 不在 P1R-7a 新增 worker,不阻塞 Events `dedicated / needs_verification`;必须在最终报告列为 P1R-7b 待办。 - -- [ ] **Step 2: 可选最小接入策略** - -P1R-7a 的 source owner 发布策略固定为: - -1. source owner 在本域事务内写业务事实和本域 outbox,不直接在业务事务内依赖 Events server 成功。 -2. source owner worker 调用 `EventsPublishApi`,只依赖 `muse-module-events-api`。 -3. `EventsPublishApi` 成功返回 `accepted` 或 `duplicate=true` 时,本域 outbox 标记为 published。 -4. `EventsPublishApi` 临时失败时,本域 outbox 保持 retryable;重试次数和下次重试时间按现有本域 outbox / job 模式实现,没有现成模式时使用最小固定退避并写中文注释说明。 -5. `EventsPublishApi` 返回 `rejected` 或 payload 安全校验失败时,本域 outbox 标记为 blocked / dead_letter,必须记录安全日志,不能继续推送到 SSE。 -6. 人工 replay 或补偿任务只能重放本域 outbox 记录;重复 replay 必须依赖 `commandId` 或 source tuple 得到同一 Events 事件。 -7. 如果某 source owner 当前没有可复用 outbox / job 入口,P1R-7a 不为该 owner 新建 worker;该 owner 的统一事件传播进入 P1R-7b。 - -本阶段只允许接入满足 Step 1 条件的首批最小 mapping: - -```text -AI task chunk/quality_check/done/error -> 同名 SSE event -AI source status -> notification(source_status_change) -Knowledge source/projection done -> notification(knowledge_projection_done) -Market governance/source action -> notification(governance_action) -Account quota/security alert -> notification(quota_alert) only when existing source fact supports owner-visible notification -``` - -如果某 source owner 当前没有安全可公开 payload 或没有可复用 outbox / job,就跳过接入并在验证报告中列为 P1R-7b / `needs_verification`,不得伪造。 - -- [ ] **Step 3: source owner 只依赖 events-api** - -每个实际接入模块的 `pom.xml` 只允许新增: - -```xml - - cn.iocoder.cloud - muse-module-events-api - ${revision} - -``` - -- [ ] **Step 4: focused tests** - -对每个实际接入的 source owner,至少覆盖: - -1. source owner 成功发布时调用 `EventsPublishApi`。 -2. source owner 失败路径不伪造事件。 -3. payload 只包含 OpenAPI 允许字段。 -4. source owner 重复业务动作不重复发布事件。 -5. `EventsPublishApi` 临时失败时 source owner 业务事实已提交、outbox 仍可重试。 -6. retry 成功后 outbox 标记 published,且 Events 返回同一 `eventId / sequenceNo`。 -7. retry 超过上限或 payload 被拒绝时 outbox 进入 blocked / dead_letter,并且 SSE 不可见。 -8. replay 同一 outbox 记录不会产生重复事件。 - -如果本阶段没有实际接入 source owner,则以 Task 4 EventsPublishApi focused tests 和 Task 8 coverage gate 作为 P1R-7a 必跑证据,并在最终报告列明 source owner 接入为 P1R-7b。 - -- [ ] **Step 5: 运行验证** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-module-ai/muse-module-ai-server,muse-module-knowledge/muse-module-knowledge-server,muse-module-market/muse-module-market-server,muse-module-member/muse-module-member-server -am -Dtest='*Event*Test,*EventsPublish*Test,*Source*Event*Test' test -``` - -如果没有实际接入 source owner,跳过本命令并在最终报告说明原因。若通配测试选中过多无关测试,收窄到实际新增/修改测试类。 - -## Task 7: muse-studio 全局 fetch SSE - -**Files:** - -- Modify: `muse-studio/src/lib/sse.ts` -- Create/Modify: focused frontend tests for `connectEventStream` - -- [ ] **Step 1: 保留公开 API** - -`connectEventStream(lastEventId?)` 返回结构保持: - -```ts -{ - close: () => void; - on: (event: string, handler: (data: unknown) => void) => void; -} -``` - -- [ ] **Step 2: 改为 fetch stream** - -必须发送: - -```text -Accept: text/event-stream -X-API-Version: 1 -Authorization: Bearer // 具体 token 来源按 muse-studio 现有 auth 工具读取 -``` - -禁止 query token。 - -- [ ] **Step 3: SSE parser** - -必须支持: - -1. `event:` 行决定事件名。 -2. `id:` 行更新 lastEventId。 -3. `data:` 支持 JSON parse。 -4. `:` comment heartbeat 忽略。 -5. `close()` abort fetch。 -6. 网络错误派发 `error` handler 或保留现有错误回调语义。 - -- [ ] **Step 4: 断线重连语义** - -fetch SSE 必须保留原全局事件流的长期连接能力: - -1. `connectEventStream(lastEventId?)` 内部维护 `lastSeenEventId`,初始值来自入参。 -2. 每次解析到 SSE `id:` 后更新 `lastSeenEventId`。 -3. fetch stream 在非 `close()` 场景下断开或 reader 报错时,必须重新发起 fetch。 -4. 重连 URL 必须带 `lastEventId=lastSeenEventId`;如果尚未收到 id,则沿用入参或不带 cursor。 -5. 重连退避必须有上限;建议 `1000ms -> 2000ms -> 5000ms -> 10000ms`,之后封顶 `10000ms`。 -6. `close()` 必须设置 closed 标志、abort 当前 fetch、清理 pending timer,且关闭后不得再重连。 -7. 重连期间可派发 `error` handler,但不得把网络断开当成业务 `error` SSE event 注入。 - -- [ ] **Step 5: focused tests** - -至少覆盖: - -```text -should_sendAuthorizationAndXApiVersionHeaders -should_dispatchNamedEventsFromFetchStream -should_ignoreHeartbeatComments -should_abortFetch_whenCloseCalled -should_passLastEventIdQuery -should_reconnectWithLastSeenEventId_whenFetchStreamDisconnects -should_capReconnectBackoff -should_notStartSecondFetch_whenReconnectTimerAlreadyPending -should_notReconnect_afterCloseCalled -should_clearPendingReconnectTimer_whenCloseCalled -``` - -- [ ] **Step 6: 运行验证** - -Run from `muse-studio`: - -```bash -pnpm test -- --run sse -pnpm build -``` - -`muse-studio/package.json` 当前没有 `typecheck` 脚本;`build` 会执行 `tsc -b && vite build`。 - -## Task 8: Events coverage gate 与报告推进 - -**Files:** - -- Create/Modify: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rEventsRealApiGateTest.java` -- Modify generated reports only by running scanner: - - `docs/superpowers/reports/p1r-api-coverage.json` - - `docs/superpowers/reports/p1r-api-coverage.md` - -- [ ] **Step 1: Events real API gate** - -Gate 必须断言: - -1. Events 只有 `streamEvents`。 -2. `streamEvents` 是 `dedicated / needs_verification`。 -3. Events 不存在 `sse_placeholder`、`generic_persistence`、`catch_all`、`missing`。 -4. `ssePlaceholderOperations = 0`。 -5. `completedOperations` 仍为 100,不因 P1R-7a 自动提升。 -6. Market 仍是 `dedicated / needs_verification`。 - -- [ ] **Step 2: 运行 scanner** - -Run from repo root: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -Expected: - -```text -scanner_exit=0 -completedOperations=100 -needsVerificationOperations=133 -incompleteOperations=0 -genericPersistenceOperations=0 -ssePlaceholderOperations=0 -``` - -如果 scanner 输出格式不同,以 JSON 报告为准,但数字必须匹配。 - -- [ ] **Step 3: 运行 P1R gates** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rEventsRealApiGateTest,P1rEventsRouteOwnershipTest test -``` - -Expected: `BUILD SUCCESS`。 - -## Task 9: Reactor build 与 Flyway _test - -**Files:** - -- No source changes unless verification exposes a real defect. - -- [ ] **Step 1: Reactor build** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am -``` - -Expected: `BUILD SUCCESS`。 - -- [ ] **Step 2: Dependency tree** - -Run from `muse-cloud`: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -am dependency:tree | rg 'muse-module-events|muse-module-(ai|knowledge|market|member|content)-server' -``` - -Expected: - -1. `muse-server` depends on `muse-module-events-server`. -2. source owner server may depend on `muse-module-events-api`. -3. `muse-module-events-server` does not depend on source owner server. - -- [ ] **Step 3: Flyway _test** - -Run from `muse-cloud` with existing P1R Flyway env: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server \ - -Dtest=P1rEventsFlywayMigrationIT \ - -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R7A_TEST_DB}" \ - -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ - -Dp1r.flyway.locations="filesystem:sql/muse" \ - test -``` - -Expected: - -```text -flyway_success=true -flyway_latest=16:extend events sse schema -``` - -## Task 10: 收口、留痕与状态边界 - -**Files:** - -- Modify/Create: `docs/memorys/YYYY-MM-DD-P1R7aEventsSSE真实API收口.md` -- Review: all changed files - -- [ ] **Step 1: 最终状态核验** - -Run: - -```bash -git status --short --branch -git diff --name-status -git diff --check -``` - -- [ ] **Step 2: 汇总 evidence** - -必须汇总: - -1. Events focused tests。 -2. source owner publish tests。 -3. frontend SSE tests。 -4. P1R gates。 -5. coverage scanner 数字。 -6. reactor build。 -7. dependency tree。 -8. Flyway `_test`。 -9. 受保护文件未修改。 - -- [ ] **Step 3: 留痕** - -写 `docs/memorys/YYYY-MM-DD-P1R7aEventsSSE真实API收口.md`,必须包含: - -1. `streamEvents` 状态:`dedicated / needs_verification`。 -2. 不标 `completed`。 -3. coverage 数字。 -4. review PASS 序列。 -5. warning 和未完成的总 P1R-7 End-to-End Acceptance 范围。 - -- [ ] **Step 4: 不自动提交** - -除非用户明确要求,否则不 commit、不 push。 - -## 4. Review Gate - -本执行版计划必须先完成两轮 fresh review: - -1. spec compliance review:检查是否符合审阅版、P1R 总 spec、coverage 边界、OpenAPI/scanner 保护边界。 -2. quality / feasibility review:检查任务是否可落地、是否最小、测试是否覆盖关键失败路径、是否存在 Maven 循环或前端不可行方案。 - -review 未 PASS 前,不进入 Task 1 实现。 diff --git a/docs/superpowers/specs/2026-05-24-Muse四仓开发计划-design.md b/docs/superpowers/specs/2026-05-24-Muse四仓开发计划-design.md deleted file mode 100644 index 339731c5..00000000 --- a/docs/superpowers/specs/2026-05-24-Muse四仓开发计划-design.md +++ /dev/null @@ -1,295 +0,0 @@ -# Muse 四仓开发计划 - -- 版本:v1 -- 日期:2026-05-24 -- 状态:待实施 -- 目标读者:产品 / 架构 / 前端 / 后端 - -## 1. 背景 - -Muse 是一个 AI 驱动的长篇创作工具,采用四仓架构,已完成完整的设计文档 SSOT(产品、架构、流程、前端、后端)和 AI 开发管理基线。当前进入实现阶段,从零搭建四个代码仓库。 - -## 2. 目标 - -1. 产出可执行的 API 契约文件(OpenAPI 3.0) -2. 搭建 muse-cloud / muse-studio / muse-admin 三仓工程脚手架 -3. 实现全量 API(6 个业务模块) -4. 实现双前端(用户端 + 管理端) -5. 集成切换 + 质量加固 - -## 3. 非目标 - -- 不修改已确认的架构决策(22 项 ADR) -- 不在此计划中处理部署和运维(按全局发布规范单独执行) -- 不包含产品运营和市场推广 - -## 4. 总体策略 - -**API-first + 三仓并行**:先冻结 API 契约,然后后端和双前端同时开发。前端用 Mock API 独立开发,后端逐步产出真实 API,前端按模块逐个切换。 - -## 5. Phase 0 — API 契约冻结 - -**目标**:产出可执行的 OpenAPI 3.0 契约文件,三仓以此为准并行开发。 - -**输入**:`design-docs/后端-05-统一API契约-v1.md`(v8,146+ 接口) - -### 5.1 工作内容 - -| # | 任务 | 输出 | -|---|------|------| -| 1 | 从设计文档提取接口,生成 OpenAPI 3.0 规范 | `docs/api-contracts/*/openapi.yaml` | -| 2 | 按模块拆分契约,每个模块独立 YAML | content / ai / knowledge / market / account / meta | -| 3 | 补充 JSON Schema(request/response body、error codes、pagination) | 各 openapi.yaml | -| 4 | 从 OpenAPI 生成 TypeScript 类型包 | generated/typescript/ | -| 5 | 从 OpenAPI 生成 Java DTO 骨架 | generated/java/ | - -### 5.2 产出物结构 - -``` -muse-repo/docs/api-contracts/ -├── openapi-base.yaml # 全局配置(认证、分页、错误格式、X-API-Version) -├── content/openapi.yaml # Content 模块(作品、章节、Block、导出) -├── ai/openapi.yaml # AI 模块(生成、候选、智能体、质量门控) -├── knowledge/openapi.yaml # Knowledge 模块(知识实体、关系、草稿、确认) -├── market/openapi.yaml # Market 模块(资产、安装、绑定、审核) -├── account/openapi.yaml # Account 模块(权益、配额、用量、安全事件) -├── meta/openapi.yaml # MetaSchema 模块(字段定义、版本、scope) -├── generated/ -│ ├── typescript/ # 前端类型包(muse-studio + muse-admin 共享) -│ └── java/ # 后端 DTO 骨架 -``` - -### 5.3 不做什么 - -- 不实现任何业务逻辑 -- 不写数据库 migration -- 不做接口性能优化 -- 不处理 Yudao Cloud 集成 - ---- - -## 6. Phase 1 — 三仓并行启动 - -### 6.1 muse-cloud(后端) - -**Step 1: Yudao Cloud 基础搭建** - -| 任务 | 说明 | -|------|------| -| Fork 确认 | 调整包名 `cn.iocoder.yudao` → `com.muse` | -| 保留基础模块 | system / infra / framework / gateway | -| 新建业务模块目录 | content / ai / knowledge / market / account / meta | -| Docker Compose | PostgreSQL 16 + Redis 开发环境 | -| .idea 维护 | 团队共享 IDE 配置,纳入版本管理 | - -**Step 2: 数据库 Migration** - -| 任务 | 说明 | -|------|------| -| 全量 DDL | 按 `后端-04` Schema 文档生成全部建表 SQL | -| Flyway 迁移 | V1__init_schema.sql 起,按模块分批 | -| 索引 | 关键查询路径的索引 | - -**Step 3: 模块骨架** - -| 模块 | 核心职责 | 依赖 | -|------|----------|------| -| yudao-module-content | 作品、章节、Block 保存与版本管理 | system(用户) | -| yudao-module-account | 权益、配额、用量统计、安全事件 | system | -| yudao-module-meta | MetaSchema 定义、版本、scope | system | -| yudao-module-ai | AI 生成、候选管理、grant/runtime 包隔离、质量门控 | content + meta | -| yudao-module-knowledge | 知识实体、关系、草稿、自动确认 | content + meta | -| yudao-module-market | 资产发布、安装绑定、审核治理 | account + ai + knowledge | - -**Step 4: API 实现顺序(按依赖关系)** - -``` -Phase 1.1: content + account(无外部依赖,最独立) - ↓ -Phase 1.2: meta(被 AI 和 knowledge 依赖) - ↓ -Phase 1.3: ai + knowledge(依赖 content + meta 的投影) - ↓ -Phase 1.4: market(依赖 account + ai + knowledge) -``` - -### 6.2 muse-studio(用户端) - -**技术栈**:React + Vite + TypeScript SPA,TanStack Query,Zustand,React Hook Form + Zod,Tiptap/ProseMirror,Tailwind CSS,MSW - -**Step 1: 工程脚手架** - -| 任务 | 说明 | -|------|------| -| Vite + React + TS 初始化 | 严格模式,路径别名 | -| ESLint + Prettier + Tailwind | 统一代码风格 | -| 路由框架 + 布局组件 | 6 个顶级空间的路由 | -| TanStack Query 全局配置 | query client、错误处理、乐观更新 | -| 类型包集成 | 从 OpenAPI 生成类型,放入 shared-types 包 | - -**Step 2: 核心基础设施** - -| 任务 | 说明 | -|------|------| -| API 客户端 + MSW | 所有 API 调用的 mock handler | -| SSE 客户端 | AI stream(独立连接)+ 事件 stream(长连接)+ lastEventId 续传 | -| IndexedDB 持久化层 | 正文自动保存的安全网 | -| Zustand store | 编辑器状态、UI 状态、用户偏好 | - -**Step 3: 功能域开发** - -``` -优先级 1: 写作台(核心路径) - - Tiptap 编辑器集成 - - Block 保存(自动保存 + IndexedDB 安全网) - - AI 生成 + 候选面板(SSE streaming) - - 候选接受/拒绝 - -优先级 2: 我的作品 + 作品工作台 - - 作品列表、创建、删除 - - 章节管理、Block 分割 - - 作品规划台(大纲、角色关系) - -优先级 3: 知识库工作台 - - 知识面板(自动确认 + 手动确认) - - 实体/关系可视化 - - 知识草稿管理 - -优先级 4: 智能体工作台 - - 智能体列表、创建 - - 配置型智能体编辑 - - 槽位绑定 - -优先级 5: 市场 + 个人中心 - - 市场浏览、安装、绑定 - - 个人中心(Token 用量、配额) -``` - -### 6.3 muse-admin(管理端) - -**技术栈**:Vue 3 + Vben Admin + TypeScript,Vitest + Vue Test Utils - -**Step 1: 工程脚手架** - -| 任务 | 说明 | -|------|------| -| Vben Admin 初始化 | 调整项目配置 | -| Muse 业务目录结构 | 按 governance/ai/knowledge/market/account/newapi/jobs/audit 组织 | -| 类型包集成 | 从 OpenAPI 生成类型 | -| Mock API 配置 | MSW 或 Vben 内置 mock | - -**Step 2: 管理页面开发** - -``` -优先级 1: MetaSchema 管理 - - 字段定义 CRUD - - 版本管理 - - scope 配置(全局/租户/用户/作品) - -优先级 2: 系统治理 - - 用户管理 - - 角色与权限配置 - - 审计日志查看 - -优先级 3: AI 配置 - - Prompt 模板管理 - - 质量门控维度配置 - - 保护节点注册与管理 - -优先级 4: 市场治理 - - 资产审核(上架/驳回) - - 下架与召回 - - 申诉处理 - -优先级 5: 全局知识管理 - - 全局知识库维护 - - 知识来源管理 -``` - ---- - -## 7. Phase 2 — 集成切换 + 质量加固 - -### 7.1 集成切换顺序 - -``` -① content + account API 就绪 → studio 写作台/作品台切换真实 API -② meta API 就绪 → admin MetaSchema 管理切换真实 API -③ ai + knowledge API 就绪 → studio 知识/智能体 + admin AI 配置切换真实 API -④ market API 就绪 → studio 市场 + admin 治理切换真实 API -``` - -### 7.2 每模块集成动作 - -1. 后端 API 自测通过(契约测试 + 集成测试) -2. 前端移除该模块的 MSW handler -3. 前后端联调,修正接口不一致 -4. 端到端冒烟测试(写作流程、管理流程) -5. 修复联调中发现的契约缺陷,更新 OpenAPI - -### 7.3 质量加固 - -| 维度 | 后端(muse-cloud) | 前端(muse-studio) | 前端(muse-admin) | -|------|-------------------|---------------------|---------------------| -| 单元测试 | JUnit 5 + Mockito,Domain 层 ≥90% | Vitest + Testing Library,组件 ≥75% | Vitest + Vue Test Utils,组件 ≥70% | -| 集成测试 | Testcontainers + PostgreSQL,Application 层 ≥80% | Playwright(写作 + 配置关键流程) | Playwright(管理关键流程) | -| Lint | Checkstyle / SpotBugs | ESLint + Prettier + Stylelint | ESLint + Prettier | -| 构建 | Maven + Docker 镜像 | Vite build + 静态资源 | Vite build + 静态资源 | -| CI | 编译 → 测试 → lint → 镜像 | 类型检查 → lint → 测试 → 构建 | 类型检查 → lint → 测试 → 构建 | -| 安全 | 依赖漏洞扫描 + 敏感数据脱敏 | XSS 防护 + CSP | XSS 防护 + CSP | - ---- - -## 8. 里程碑与并行关系 - -``` -Week 1-2: Phase 0(API 契约冻结) - ↓ -Week 3-4: 三仓 Step 1 全部并行 - muse-cloud Step 1 (Yudao + DB) ─┐ - muse-studio Step 1 (脚手架) ├─ 完全并行 - muse-admin Step 1 (脚手架) ─┘ - ↓ -Week 5-8: 三仓 Step 2-3 并行 - muse-cloud Step 2-3 (模块骨架) ─┐ - muse-studio Step 2 (基础设施) ├─ 并行,前端用 Mock - muse-admin Step 2 (基础设施) ─┘ - ↓ -Week 9-12: 核心功能开发 + 开始集成 - muse-cloud Step 4.1 (content+account) ─→ 产出真实 API ─→ studio 写作台切换 - muse-cloud Step 4.2 (meta) ─→ admin MetaSchema 切换 - muse-studio Step 3-①② (写作台) ─→ 等待 API 切换 - muse-admin Step 2-①② (MetaSchema) ─→ 等待 API 切换 - ↓ -Week 13-16: 扩展功能开发 + 持续集成 - muse-cloud Step 4.3-4.4 (ai+kb+market) - muse-studio Step 3-③④⑤ (知识/智能体/市场) - muse-admin Step 2-③④⑤ (AI 配置/治理) - ↓ -Week 17-18: 全量集成 + 质量加固 -``` - ---- - -## 9. 风险与缓解 - -| 风险 | 概率 | 缓解措施 | -|------|------|----------| -| Yudao Cloud 框架过重,集成困难 | 中 | Phase 0 期间同步评估 Yudao 剥离策略 | -| 前后端 API 契约理解不一致 | 高 | Mock API 用相同 OpenAPI 生成;联调前先做契约测试 | -| AI 模块 grant/runtime 隔离实现复杂 | 中 | 先实现 ArchUnit 规则,跑 CI 时强制检查 | -| SSE 实时通信在不同环境下表现不一致 | 低 | 早期写集成测试覆盖断线重连、超时场景 | -| 团队 size 小,三仓同时推进压力大 | 高 | 各 Phase 内任务严格按优先级排序;低优先级可延后 | - ---- - -## 10. 关键引用 - -- `design-docs/架构-01-系统全貌与边界上下文.md` — 有界上下文与协作规则 -- `design-docs/架构-02-核心数据结构与双轨模型.md` — 核心数据模型 -- `design-docs/架构-03-关键决策与原则(ADR).md` — 22 项架构决策 -- `design-docs/后端-04-统一数据库Schema-v1.md` — 数据库表结构 -- `design-docs/后端-05-统一API契约-v1.md` — API 契约 SSOT -- `design-docs/前端-01` ~ `05` — 前端设计文档 -- `docs/dev-baseline/global/01-AI开发管理总则.md` — 全局开发规则 -- `docs/dev-baseline/global/02-跨仓库协作规范.md` — 四仓协作规则 \ No newline at end of file diff --git a/docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md b/docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md deleted file mode 100644 index 0fe9482e..00000000 --- a/docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md +++ /dev/null @@ -1,195 +0,0 @@ -# P1R-0:muse-cloud API 基线门禁设计 - -- 版本:v1 -- 日期:2026-05-25 -- 状态:待实施 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 范围:`muse-cloud/` 实现状态审计、`docs/api-contracts/**/openapi.yaml` 合同清点、P1R API 完成矩阵 -- 非范围:业务 API 实现、OpenAPI 合同修改、`muse-admin/`、`muse-studio/` - -## 1. 背景 - -P1R 总规格要求全量 Muse API 从合同兜底入口迁移到真实业务实现。当前仍缺一个可重复执行的基线门禁来回答三个问题: - -1. 合同里到底有多少 operation。 -2. 每个 operation 当前由谁实现,是否仍由 catch-all 或通用持久化处理。 -3. 每个 operation 应归属哪个 P1R 阶段,后续执行需要补哪些真实业务能力。 - -P1R-0 的目标不是写业务代码,而是建立可复跑、可审计、可阻断后续阶段误判的完成矩阵。 - -## 2. 目标 - -1. 解析 `docs/api-contracts/**/openapi.yaml`,生成全量 operation 清单。 -2. 扫描 `muse-cloud/` 当前 Controller 和关键服务,识别真实专用实现、catch-all 合同入口、通用持久化入口、SSE 占位入口。 -3. 为每个 operation 标注 owner、side、method、path、operationId、是否写命令、外部依赖、目标 P1R 阶段、当前状态和阻塞说明。 -4. 输出机器可读 JSON 和人类可审 Markdown,作为后续 P1R-1 到 P1R-7 的输入。 -5. 在 Maven 测试中加入最小门禁,确保矩阵生成脚本可运行、矩阵结构稳定、catch-all 操作不会被误标为完成。 - -## 3. 非目标 - -1. 不修改任何业务 Controller、Service、Mapper 或 DDL。 -2. 不修复 P1R-1 以后才应处理的 API 实现缺口。 -3. 不修改 `docs/api-contracts/**/openapi.yaml`。 -4. 不连接真实 New-API、RAGFlow 或文件服务;P1R-0 只记录这些依赖和后续验收要求。 -5. 不把当前任何 catch-all operation 重新解释为完成。 - -## 4. 产物 - -### 4.1 审计脚本 - -路径:`muse-cloud/scripts/p1r-audit-api-coverage.py` - -职责: - -1. 读取所有合同文件。 -2. 提取 paths、methods、operationId、parameters、requestBody required 字段。 -3. 读取 `muse-cloud` Java 源码,识别 Controller 路由和是否调用 `MuseApiContractSupport` / `MuseContractPersistenceService`。 -4. 根据规则生成每个 operation 的状态。 -5. 输出 JSON 和 Markdown。 - -脚本必须只读扫描,不能修改业务文件。 - -### 4.2 JSON 完成矩阵 - -路径:`docs/superpowers/reports/p1r-api-coverage.json` - -字段: - -```json -{ - "generatedAt": "2026-05-25T00:00:00Z", - "sourceContracts": ["docs/api-contracts/content/openapi.yaml"], - "summary": { - "totalOperations": 0, - "completedOperations": 0, - "incompleteOperations": 0, - "catchAllOperations": 0, - "genericPersistenceOperations": 0, - "blockedOperations": 0 - }, - "operations": [ - { - "domain": "content", - "side": "app", - "method": "GET", - "path": "/app-api/muse/works", - "operationId": "listWorks", - "isWrite": false, - "requiresCommandId": false, - "externalDependencies": [], - "targetStage": "P1R-1 Content Real API", - "implementationStatus": "dedicated", - "completionStatus": "needs_verification", - "controllerFiles": [ - "muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppContentController.java" - ], - "serviceFiles": [ - "muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentAppService.java" - ], - "notes": "专用入口存在,仍需后续阶段验证 DTO 与真实环境闭环。" - } - ] -} -``` - -### 4.3 Markdown 报告 - -路径:`docs/superpowers/reports/p1r-api-coverage.md` - -内容: - -1. 总览统计。 -2. 按领域和阶段分组的 operation 表。 -3. catch-all / 通用持久化 / SSE 占位清单。 -4. 外部依赖清单。 -5. 后续阶段建议顺序。 - -### 4.4 Maven 门禁测试 - -路径:`muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java` - -职责: - -1. 校验 JSON 报告存在并可解析。 -2. 校验 `totalOperations > 0`。 -3. 校验每个 operation 都有 domain、side、method、path、operationId、targetStage、completionStatus。 -4. 校验被识别为 catch-all、generic persistence、SSE 占位或缺入口的 operation 只能保持 `completionStatus=incomplete` 或 `completionStatus=blocked`。 -5. 校验 report 中没有 `message` 作为响应字段完成证据。 -6. 测试必须从当前 Maven 模块目录向上查找仓库根目录,不能依赖固定 `user.dir`。 - -## 5. 状态判定规则 - -### 5.1 implementationStatus - -| 值 | 含义 | -|----|------| -| `dedicated` | 找到专用 Controller 或 Application Service 路径,未经过 catch-all | -| `catch_all` | 由 `MuseApiContractSupport.handle(...)` 处理 | -| `generic_persistence` | 由 `MuseContractPersistenceService` 处理 | -| `sse_placeholder` | 返回固定或非真实业务 SSE 事件 | -| `missing` | 合同存在,但未找到后端入口 | - -### 5.2 completionStatus - -| 值 | 含义 | -|----|------| -| `needs_verification` | 有专用实现,但 P1R-0 不证明完整业务闭环,需要领域阶段继续验收 | -| `incomplete` | 当前仍是 catch-all、generic persistence、SSE 占位或缺入口 | -| `blocked` | 合同或源码扫描遇到结构性问题,需要先修扫描或确认 owner | - -P1R-0 不输出 `completed`。原因是 P1R-0 只做基线审计,不跑领域真实外部闭环。 - -## 6. 阶段归属规则 - -| domain | 默认目标阶段 | -|--------|--------------| -| `content` | P1R-1 Content Real API | -| `meta` | P1R-2 Meta Real API | -| `account` | P1R-3 Account Real API | -| `ai` | P1R-4 AI Real API | -| `knowledge` | P1R-5 Knowledge Real API | -| `market` | P1R-6 Market Real API | -| `events` | P1R-7 End-to-End Acceptance | - -如果 operation 跨领域协作,仍按合同 owner domain 归属阶段,跨领域依赖写入 notes。 - -## 7. 外部依赖标注规则 - -脚本根据路径、operationId 和 domain 标注外部依赖: - -| 触发词 | externalDependencies | -|--------|----------------------| -| `ai`, `task`, `suggestion`, `agent`, `job`, `source-event` | `New-API`, `SSE` | -| `knowledge`, `kb`, `document`, `entity`, `relation`, `graph`, `reindex` | `RAGFlow` | -| `import`, `export`, `download`, `file`, `document` | `FileService` | -| `marketplace`, `purchase`, `install`, `handoff`, `license` | `Account`, `MarketAuthorization` | -| `account`, `usage`, `quota`, `balance`, `correlation`, `new-api-binding` | `New-API`, `AccountLedger` | - -这些标注用于后续阶段计划,不代表 P1R-0 要连接外部服务。 - -## 8. 验收标准 - -1. `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 成功。 -2. `docs/superpowers/reports/p1r-api-coverage.json` 生成成功。 -3. `docs/superpowers/reports/p1r-api-coverage.md` 生成成功。 -4. JSON 报告中全部 operation 有完整字段。 -5. catch-all、generic persistence、sse placeholder、missing operation 全部标为 `incomplete` 或 `blocked`。 -6. Maven 门禁测试通过: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -该命令使用 `-am` 拉起 `muse-server` 的上游 reactor 模块;这些上游模块没有指定测试类时不应导致本门禁失败,因此聚焦执行时显式设置 `-Dsurefire.failIfNoSpecifiedTests=false`。 - -7. 不修改 `muse-admin/`、`muse-studio/`、`docs/api-contracts/**`。 -8. 提交只包含 P1R-0 相关脚本、报告、测试和文档。 - -## 9. 风险与处理 - -1. OpenAPI YAML 里 `$ref` 很多,P1R-0 不需要完全解析 schema,只需要 operation 元数据;脚本只做浅解析。 -2. Java 路由匹配不应追求完美 AST。P1R-0 用保守规则:无法确定专用实现时标为 `blocked` 或 `incomplete`,不能误标完成。 -3. 当前工作树有并行会话文件。提交时必须显式 pathspec,只提交 P1R-0 文件。 -4. 如果 PyYAML 不存在,执行代理应先确认本机是否可安装;不能改用脆弱文本解析。可选方案是在 `muse-cloud/scripts/requirements-p1r.txt` 固定 `PyYAML`,但是否引入依赖由执行时确认。 diff --git a/docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md b/docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md deleted file mode 100644 index 409eaf29..00000000 --- a/docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md +++ /dev/null @@ -1,438 +0,0 @@ -# P1R:muse-cloud 真实业务 API 完成规格 - -- 版本:v1 -- 日期:2026-05-25 -- 状态:待用户复核 -- 目标读者:后端执行代理 / 主代理 / 架构评审 / 验收人员 -- 范围:`muse-cloud/` -- 非范围:`muse-admin/`、`muse-studio/`、`docs/api-contracts/**` 合同修改、Yudao 基座重构 - -## 1. 背景 - -原 P1 计划目标是在 `muse-cloud/` 这个 Yudao Cloud fork 上完成 Muse 后端业务模块,覆盖 `docs/api-contracts/**/openapi.yaml` 中的全量 Muse API。 - -当前 P1 已完成后端可调用基线:DDL、模块入口、Content 核心 API、统一合同路由、幂等审计和部分写命令持久化。但这仍不足以称为真实业务 API 完成,因为 Meta / Knowledge / Market / AI / Account 等模块仍大量依赖合同兜底入口和通用持久化服务,很多接口没有领域 Application Service、Domain 状态机、OpenAPI DTO 组装和真实外部闭环。 - -P1R 的目标是纠正这个偏差:P1 不再以“接口能被调用”作为完成标准,而以“全量 API 有真实业务行为、真实外部闭环、真实验收证据”作为完成标准。 - -## 2. 目标 - -1. 覆盖 `docs/api-contracts/**/openapi.yaml` 中属于 P1 的全部 Muse API。 -2. 每个 API 都必须有真实 Controller / Application Service / Domain 规则 / Persistence / 测试。 -3. 写命令必须真实推进领域事实、状态机、审计、幂等和外部副作用。 -4. 读接口必须返回对应领域读模型 DTO,不允许返回通用 operation record、workflow task 原始行或占位 JSON。 -5. AI、知识解析、导入导出、市场安装绑定、New-API / RAGFlow / 文件服务等外部链路必须跑通真实闭环。 -6. 遇到外部依赖、环境、认证、数据、测试阻塞时,目标是解决阻塞,不是降级为 mock、跳过或伪成功。 -7. 总 spec 只定义共同架构、真实 API 标准、外部服务硬依赖、验收门禁和阶段拆分原则;每个领域的详细实现再通过后续阶段 spec + plan 承接。 - -## 3. 非目标 - -1. 不修改 `muse-admin/`、`muse-studio/`。 -2. 不重构 Yudao 基座,不删除保留模块。 -3. 不借 P1R 顺手修改 API 合同。若发现合同与设计 SSOT 冲突,必须单独提出合同变更并先获得确认。 -4. 不用 catch-all shim 扩大完成口径。`MuseApiContractSupport` 和 `MuseContractPersistenceService` 只能作为迁移中间态或未完成保护网,不能作为最终业务实现。 -5. 不用本地替身、controller mock、内存 Map、固定样例数据替代真实外部服务闭环。 -6. 不把前端可见性、按钮隐藏或路由限制当作权限、安全、状态机和功能 gate 的可信边界。 - -## 4. 当前事实基线 - -### 4.1 已完成基线 - -1. `muse-cloud/` 已纳入主仓管理,并完成 P1 后端基础提交。 -2. `muse-cloud/sql/muse/` 已有 V1-V8 Muse 业务 DDL。 -3. Content 核心作品、章节、Block API 已有专用 `ContentAppService`。 -4. Meta / Knowledge / Market / AI / Account 合同入口已从纯占位响应推进到通用持久化层。 -5. 全量 `mvn test` 曾在 P1 收口时通过。 -6. 远端 PG15 已验证可执行 V1-V8;用户已允许以 PG15 推进,不再因 PG16 未就绪阻塞当前进度。 - -### 4.2 仍未完成的事实 - -1. 多数非 Content API 仍不是领域 Application Service,而是合同兜底入口。 -2. 多数读接口还不是 OpenAPI DTO 对应的读模型。 -3. Content 扩展 API 如规划、导入、解析、导出、MetaProjection、SuggestionMerge 仍缺专用业务实现。 -4. `muse_domain_workflow_task` 当前只表示任务事实,不等于真实异步执行器、外部副作用和终态闭环。 -5. New-API、RAGFlow、文件服务、导出器、SSE 等真实外部闭环还没有形成全量验收证据。 -6. 原计划中的 Domain 覆盖率、Application 集成测试、ArchUnit、Checkstyle / SpotBugs 等门禁没有完整验收证据。 - -## 5. 真实 API 标准 - -P1R 中,一个 API 只有同时满足以下条件,才能标记为完成。 - -### 5.1 入口标准 - -1. 每个 OpenAPI operation 有明确 Controller 方法或明确路由映射。 -2. 所有 Muse API 支持并校验 `X-API-Version: 1`。 -3. 响应统一为 Yudao `CommonResult` 形态,对外 JSON 字段为 `code/data/msg`。 -4. 不得返回 `message` 替代 `msg`。 -5. Controller 只做鉴权入口、参数校验、版本头检查、DTO 转换和统一响应,不拼业务规则。 - -### 5.2 DTO 标准 - -1. 请求和响应必须使用领域 DTO / VO / assembler。 -2. 读接口必须返回 OpenAPI 合同对应的业务读模型。 -3. 禁止用 `Map`、operation record 原始行、workflow task 原始行、`persisted: true`、`status: accepted` 这类通用结构替代合同 DTO。 -4. 异步任务创建接口可以返回任务摘要,但任务必须能通过后续查询接口进入真实成功、失败、取消或超时终态。 - -### 5.3 领域 owner 标准 - -1. 业务事实只能由所属领域模块写入。 -2. `admin-api` 和 `app-api` 只是入口差异,不能形成两套事实。 -3. 跨模块读取必须通过 facade、Application Service、事件投影或读模型,不能直接绕过 owner 写其他模块表。 -4. Market 只负责资产、授权、安装记录、发布申请、申诉、来源侧授权摘要、handoff token 和跳转审计;目标绑定事实必须由目标 owner API 消费授权后写入。 -5. Account 负责个人资料、权益、配额、用量、购买/授权/发布记录聚合和 New-API 调用归因;不得允许请求体任意伪造归因事实。 -6. Meta 负责 MetaSchema、保护节点和功能链治理;保护节点不能被降级为用户可替换槽位。 - -### 5.4 写命令标准 - -1. 所有写命令必须有 `commandId` 或合同指定的等价幂等键。 -2. 重复请求必须返回同一业务结果,不重复扣费、不重复安装、不重复发布、不重复外部调用。 -3. 覆盖事实的写命令必须校验 revision / expectedVersion / expectedStatus / expectedActiveVersion。 -4. 所有写命令必须记录审计字段:操作者、入口、请求摘要、业务目标、状态变化、时间、结果。 -5. 写命令不能只落 operation record;必须真实改变领域事实,或创建真实可执行任务并最终进入业务终态。 - -### 5.5 状态机标准 - -1. 状态变化必须由领域服务推进。 -2. 状态机前置条件必须在后端强制校验。 -3. 非法状态转移必须返回明确错误码。 -4. 状态字段不能只作为可任意更新的字符串。 -5. 状态机测试必须覆盖成功路径、重复命令、并发冲突、非法转移、外部失败恢复。 - -### 5.6 安全标准 - -1. 后端强制登录态、RBAC、owner 校验、tenant 隔离、来源授权和高危动作权限。 -2. 前端隐藏入口不是安全边界。 -3. 敏感操作必须审计;高危治理必须包含 reason、影响预览或校验引用、期望状态或版本。 -4. 用户数据查询必须带 owner 和 tenant 约束。 -5. 外部 URL、导入文件和下载凭证必须有服务端安全校验,包含私网地址阻断、文件大小限制、类型限制、病毒或内容扫描接入点、凭证过期和访问审计。 - -## 6. 明确禁止事项 - -1. 禁止用 `MuseApiContractSupport.handle(...)` 的占位结果作为完成。 -2. 禁止用 `MuseContractPersistenceService` 的通用持久化响应替代领域服务。 -3. 禁止将仍由 catch-all 合同入口处理的 operation 标记为完成。 -4. 禁止 controller 层 mock、内存 Map、固定样例数据、伪造外部服务成功。 -5. 禁止只在前端实现权限、状态机或功能 gate。 -6. 禁止为了通过测试跳过安全、审计、幂等、外部服务调用。 -7. 禁止用空列表、accepted task、operation record、通用 JSON 行作为真实读模型完成证据。 -8. 禁止未验证就声明 P1R 完成。 - -catch-all 入口可以临时保留为 404 / 501 保护网,但不能计入任何 operation 的完成范围。 - -## 7. 目标架构 - -```mermaid -flowchart TD - C[Controller] --> A[Application Service] - A --> D[Domain Service / Aggregate] - A --> Q[Query Service / Assembler] - A --> O[Outbox / Job Service] - D --> P[Persistence Mapper] - O --> E[External Adapter] - E --> N[New-API] - E --> R[RAGFlow / Knowledge Engine] - E --> F[File / Object Storage] - E --> S[SSE Event Stream] - Q --> V[OpenAPI DTO] -``` - -### 7.1 Controller - -Controller 只负责入口转换:鉴权、参数校验、`X-API-Version` 校验、请求 DTO、响应封装。Controller 不直接访问数据库,不直接拼业务 JSON,不直接调用外部服务。 - -### 7.2 Application Service - -Application Service 是用例和事务边界。它负责幂等、权限摘要、跨模块 facade、任务创建、外部调用编排、outbox 发布、错误归一和最终响应组装。 - -### 7.3 Domain - -Domain 持有聚合状态机、不变式、版本冲突判断、授权快照消费规则和状态变化决策。Domain 不依赖 Controller、Yudao Web DTO 或外部 API DTO。 - -### 7.4 Infrastructure - -Infrastructure 负责 MyBatis Mapper、Redis、文件服务、New-API client、RAGFlow client、SSE 推送、任务执行器和审计持久化。外部 client 必须暴露超时、错误分类、重试和补偿语义。 - -### 7.5 Query Service / Assembler - -Query Service 负责读模型查询和 DTO 组装。读接口不能由 Controller 直接拼 JSON,也不能把数据库表行原样暴露给前端。 - -## 8. 外部服务硬依赖 - -P1R 的外部服务是验收硬门槛,不允许因为复杂而降级为 mock。 - -| 依赖 | P1R 必须证明的真实能力 | -|------|------------------------| -| PostgreSQL | 真实持久化、事务、唯一约束、幂等、状态查询、DDL/Flyway 可执行 | -| Redis | 幂等辅助、短期锁、SSE/job 状态缓存、限流或任务协调中实际需要的部分 | -| New-API | AI 调用、调用归因、成本/用量回查、绑定重验 | -| RAGFlow / 知识引擎 | 文档入库、切片、索引、检索、GraphRAG 或项目确认的图查询能力 | -| 文件/对象存储 | 导入文件、解析原件、导出产物、下载凭证 | -| SSE | AI stream、任务事件和统一事件流真实推送 | -| Yudao 基础能力 | 登录、RBAC、租户、审计、文件、任务、权限菜单 | - -## 9. 阻塞处理规则 - -1. 连接失败、认证失败、版本不符、缺容器、缺账号、缺配置,必须先诊断 root cause。 -2. 能通过服务器 Docker 补齐基础设施的,补齐并记录命令。 -3. 能通过现有远端环境解决的,优先复用远端环境,但必须验证版本、权限、数据隔离和清理策略。 -4. 缺凭据时,只说明需要哪个服务、哪个账号、哪类权限,不输出已有密钥。 -5. 外部服务暂不可用时,相关 API 不能标为完成,只能标为 blocked,并列出下一步验证命令。 -6. 测试可以构造测试数据,但不能绕过真实 adapter。 -7. 测试失败要修服务、配置或代码,不能以跳过测试收尾。 -8. 对不可逆或高危环境操作,如清库、重置远端服务、删除索引,必须先单独确认。 - -## 10. 阶段拆分 - -P1R 采用“总 spec + 阶段 spec + 阶段 plan”的执行模型。总 spec 定义硬标准,后续每个阶段必须单独补充领域 spec 和实现计划。 - -### P1R-0 Baseline Gate - -目标:清点全部 operation,建立 API 完成矩阵。 - -输出: - -1. operation 清单,来源为 `docs/api-contracts/**/openapi.yaml`。 -2. 每个 operation 标注 owner、side、HTTP method、路径、是否写命令、是否外部依赖、是否异步。 -3. 每个 operation 标注当前实现状态:真实实现、catch-all、通用持久化、缺实现、阻塞。 -4. 每个 operation 标注目标阶段和验收证据。 - -### P1R-1 Content Real API - -目标:补齐 Content 真实业务 API。 - -范围: - -1. 作品、章节、Block 核心能力回归到 OpenAPI DTO。 -2. 规划、候选、导入解析、导出、动态字段校验、来源归因、Suggestion Merge。 -3. 管理端 content read、risk action、导入导出任务治理。 - -外部闭环: - -1. 文件导入产生真实文件和解析任务。 -2. 导出产生真实产物和下载凭证。 -3. Suggestion Merge 与 AI suggestion、知识草稿、来源归因形成真实链路。 - -### P1R-2 Meta Real API - -目标:实现 MetaSchema 和治理真实 API。 - -范围: - -1. MetaSchema 列表、详情、版本、草稿。 -2. 验证、影响预览、发布、激活、回滚、废弃、灰度规则。 -3. 保护节点和功能链治理。 - -关键要求: - -1. 版本激活必须有唯一 active 约束。 -2. 高危治理必须有 validationResultId、impactPreviewId、reason、expectedVersion 或 expectedActiveVersion。 -3. 保护节点不可被用户替换槽位覆盖。 - -### P1R-3 Account Real API - -目标:实现账户、权益、配额、用量和归因真实 API。 - -范围: - -1. 当前用户、资料、权益、配额、余额快照。 -2. New-API 绑定、绑定重验、调用归因 job、correlation 查询。 -3. 用量、购买记录、授权记录、发布记录、安全事件、导出下载。 - -外部闭环: - -1. New-API 调用产生真实 correlation/call 记录。 -2. Account 归因接口基于真实调用记录校验 user/work/asset/license。 -3. 导出任务生成真实下载凭证。 - -### P1R-4 AI Real API - -目标:实现 AI 编排、任务、智能体和质量治理真实 API。 - -范围: - -1. Prompt、Agent、Tool Grant、Quality Policy。 -2. AI task/job、source event、agent slot、suggestion。 -3. API 访问日志和业务审计。 - -外部闭环: - -1. AI task 调用真实 New-API。 -2. SSE 能推送真实任务事件或流式结果。 -3. 任务支持成功、失败、取消、重试。 -4. runtime 不能自授权,grant/runtime 包隔离由 ArchUnit 验证。 - -### P1R-5 Knowledge Real API - -目标:实现全局知识库、用户知识库、局域知识和 RAG 检索真实 API。 - -范围: - -1. 全局知识库、用户知识库、文档版本、访问策略。 -2. 文档上传、链接抓取、解析、切片、索引、重建。 -3. 实体、关系、知识图谱、知识草稿、绑定预检、发布快照、安装知识库。 - -外部闭环: - -1. 文档能进入 RAGFlow / 知识引擎。 -2. 检索结果能回到 Knowledge API。 -3. 知识草稿确认能真实写入 Canonical 事实。 -4. Market 安装的知识库只能通过授权快照消费。 - -### P1R-6 Market Real API - -目标:实现市场资产、交易、安装、handoff 和治理真实 API。 - -范围: - -1. 资产、分类、推荐、详情、收藏。 -2. 购买、安装、bind-precheck、handoff 查询/取消。 -3. 发布草稿、发布检查、发布申请、撤回。 -4. 管理端审核、驳回、下架、召回、申诉处理。 - -外部闭环: - -1. 购买和安装必须写授权、安装记录和账户聚合。 -2. Handoff 只能产生来源授权摘要和 token,目标 owner 必须重新生成并消费目标 precheck。 -3. 作品资产高阶模式默认关闭,未完成 ADR、Schema/API、来源 lineage 和授权快照前不能开放。 - -### P1R-7 End-to-End Acceptance - -目标:在真实环境中证明全量 P1R 业务闭环。 - -验收链路: - -1. 应用连接真实 PG/Redis 启动。 -2. 用户创建作品、章节、Block,保存正文并生成来源归因。 -3. 用户创建 AI task,真实调用 New-API,SSE 或轮询得到终态。 -4. AI suggestion 被接受或拒绝,正文和审计事实一致。 -5. 文档上传到知识库,RAGFlow / 知识引擎完成索引和检索。 -6. 知识草稿确认后写入 Canonical 知识事实。 -7. 市场发布、审核、购买、安装、handoff、目标绑定形成跨领域闭环。 -8. Account 能查询真实用量、授权、购买、发布、导出和安全事件。 -9. 管理端治理动作能改变状态并留下审计。 - -## 11. 验收门禁 - -### 11.1 API 覆盖门禁 - -从 `docs/api-contracts/**/openapi.yaml` 生成 operation 清单。每个 operation 必须有: - -1. owner module。 -2. Controller 方法。 -3. Application Service 方法。 -4. Domain 规则或状态机说明。 -5. 持久化表、读模型或外部服务。 -6. 单元测试、集成测试、API 契约测试。 -7. 真实环境验收记录。 - -### 11.2 Catch-all 清零门禁 - -任何仍由 `MuseApiContractSupport.handle(...)` 或通用 `MuseContractPersistenceService` 处理的 operation,不能标记完成。 - -### 11.3 响应合同门禁 - -1. 所有接口统一 `code/data/msg`。 -2. 响应 DTO 与 OpenAPI 匹配。 -3. 分页统一使用项目现有分页结构或合同明确结构。 -4. 错误码必须可追踪到领域模块。 - -### 11.4 写命令门禁 - -1. 所有写命令校验 `commandId`。 -2. 所有覆盖事实的写命令校验 revision / expectedVersion / expectedStatus。 -3. 重复命令返回同一业务结果。 -4. 所有写命令落审计。 -5. 外部副作用必须有真实调用记录、状态和失败恢复路径。 - -### 11.5 外部闭环门禁 - -1. New-API 调用能产生可查询的真实 correlation/call 记录,并被 Account 归因接口校验。 -2. RAGFlow / 知识引擎能完成文档入库、索引、检索,并驱动 Knowledge API 状态变化。 -3. 文件导入、导出和下载凭证能产生真实文件或对象。 -4. AI task 能从创建、执行、流式或轮询查看,到完成、失败或取消形成闭环。 -5. 市场购买、安装、handoff、绑定能跨 Market、AI、Knowledge、Content owner 完成真实授权消费。 - -### 11.6 测试门禁 - -1. Domain 层单元测试覆盖状态机、不变式、冲突和非法转移。 -2. Application 层集成测试覆盖事务、幂等、审计、外部调用和失败恢复。 -3. API 契约测试覆盖请求响应、错误码、权限和版本头。 -4. 真实环境冒烟测试覆盖 P1R-7 验收链路。 -5. 若全量 `mvn test` 被非 P1 模块阻断,必须定位并修复或隔离到明确 owner,不能用“基座问题”直接跳过最终验收。 - -最低命令: - -```bash -cd muse-cloud -mvn test -``` - -阶段命令: - -```bash -cd muse-cloud -mvn test -pl <领域模块> -am -mvn test -Dtest=<领域 API 契约测试/集成测试> -``` - -真实环境验收必须补充对应 curl、HTTP test、Maven profile、脚本或手工命令记录。 - -### 11.7 提交门禁 - -1. 小步提交,每个 commit 只做一件事。 -2. 只提交 P1R 相关文件。 -3. 不提交 `muse-admin/`、`muse-studio/`、`.DS_Store`、并行计划文件或 unrelated SQL。 -4. 提交前必须看 `git status --short`。 -5. 涉及安全扫描或 pre-commit hook 跳过时,必须说明项目约定和原因。 - -## 12. 完成定义 - -P1R 只有满足以下条件才能宣称完成: - -1. 全部 P1 operation 都从 catch-all 迁移到真实业务实现。 -2. 全部 OpenAPI 响应 DTO 和错误模型有测试证据。 -3. 全部写命令有幂等、审计、权限和状态机测试。 -4. 全部外部闭环在真实服务上验收通过。 -5. 全量 `mvn test` 通过。 -6. DDL/Flyway 在目标 PG 环境执行通过。 -7. API 完成矩阵中没有未完成、阻塞、未验收项。 - -以下情况不能算完成: - -1. 只有入口,没有领域服务。 -2. 只有 accepted task,没有执行器和终态。 -3. 只有 operation record,没有业务事实。 -4. 只有空列表或固定样例。 -5. 只有 controller mock 或测试替身。 -6. 外部服务未跑通。 -7. 全量测试未跑或失败。 - -## 13. 后续产出要求 - -本 spec 通过后,不直接进入编码。下一步必须先为 P1R-0 写阶段 spec 和 implementation plan。 - -建议产出顺序: - -1. `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` -2. `docs/superpowers/plans/2026-05-25-P1R-0-baseline-gate.md` -3. 完成 P1R-0 API 完成矩阵和验收口径后,再进入 P1R-1 Content。 - -每个阶段 spec 必须包含: - -1. 阶段目标和非目标。 -2. API operation 列表。 -3. 领域 owner 和状态机。 -4. 数据表、读模型和外部服务依赖。 -5. 权限、幂等、审计和错误码。 -6. 测试和真实环境验收命令。 -7. 阻塞处理策略。 - -每个阶段 plan 必须包含: - -1. 任务顺序。 -2. 涉及路径。 -3. 测试先行要求。 -4. 小步提交点。 -5. 回滚策略。 -6. 完成证据。 - diff --git a/docs/superpowers/specs/2026-05-25-muse-cloud-explainer-design.md b/docs/superpowers/specs/2026-05-25-muse-cloud-explainer-design.md deleted file mode 100644 index 8a12b12e..00000000 --- a/docs/superpowers/specs/2026-05-25-muse-cloud-explainer-design.md +++ /dev/null @@ -1,333 +0,0 @@ -# muse-cloud 架构讲解包设计 - -- 版本:v1 -- 日期:2026-05-25 -- 状态:待用户复核 -- 目标读者:初级后端工程师 / 后端执行代理 / 主代理 / 代码评审人员 -- 输出目录:`docs/explainers/muse-cloud/` -- 上游规格: - - `docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` - - `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` -- 非范围:`muse-cloud/` 业务代码实现、`muse-admin/`、`muse-studio/`、`docs/api-contracts/**` 合同修改 - -## 1. 背景 - -P1R 的目标是把 `muse-cloud/` 从“接口可调用”推进到“全量 API 有真实业务行为、真实外部闭环、真实验收证据”。这对初级后端不容易理解,因为当前系统同时存在四类事实: - -1. OpenAPI 合同定义的目标接口。 -2. Yudao Cloud fork 的后端入口和基础能力。 -3. P1 已落地的 Content 核心能力、DDL、合同入口持久化。 -4. P1R 要淘汰的 catch-all、通用持久化和占位式完成口径。 - -讲解包的目标不是替代 P1R spec,而是把这些事实转换成可以被初级后端快速学习、排障和维护的工程手册。 - -## 2. 目标 - -1. 在 `docs/explainers/muse-cloud/` 下维护一套 HTML-first 的 muse-cloud 讲解资料。 -2. `index.html` 必须是完整可读的主文档,读者不需要先打开 Markdown 才能理解系统。 -3. Markdown 只作为轻量内容源、审查文本和简单图维护稿,不能承载复杂交互和复杂架构解释。 -4. 讲清当前 P1R spec 的 cloud 架构、OpenAPI 组合路径、领域 owner、请求生命周期、P1R 阶段和排障路径。 -5. 让初级后端遇到业务问题时,可以按“合同路径 -> Controller -> Application -> Domain -> DB/外部 Adapter -> 审计/任务”的顺序定位问题。 -6. 页面视觉风格必须简洁、舒适、偏工程手册,不做营销页,不做装饰性堆叠。 - -## 3. 非目标 - -1. 不实现任何 P1R 业务 API。 -2. 不修改 `muse-cloud/` 源码、DDL、测试或运行配置。 -3. 不修改 `docs/api-contracts/**/openapi.yaml`。 -4. 不把当前 catch-all、通用持久化、占位 SSE 或空响应描述成完成态。 -5. 不建设复杂文档站框架,不引入构建系统。 -6. 不依赖后端服务才能阅读 HTML。HTML 应可直接通过浏览器打开。 - -## 4. 内容形态 - -讲解包采用 HTML-first 模型: - -```mermaid -flowchart LR - M[content/*.md
轻量正文和简单 Mermaid] --> H[index.html
完整主阅读入口] - H --> V[复杂 HTML/CSS/SVG 图] - H --> T[排障流程和路径定位卡片] - H --> P[OpenAPI 路径组合示例] -``` - -### 4.1 HTML - -HTML 是面向初级后端的完整教学产品,必须包含完整正文、图、示例、定位流程和关键提醒。 - -HTML 中复杂内容使用 HTML/CSS/SVG/少量 JavaScript 表达,包括: - -1. cloud 架构总览。 -2. OpenAPI 路径组合器。 -3. 请求生命周期图。 -4. 领域 owner 边界图。 -5. P1R 阶段地图。 -6. 症状到层级的排障流程。 - -### 4.2 Markdown - -Markdown 是维护辅助材料,不是主阅读入口。它用于: - -1. 存放可 diff 的短正文。 -2. 存放简单 Mermaid 图。 -3. 存放术语表、路径规则和排障条目的简版。 -4. 给后续 agent 或人类维护 HTML 提供文本来源。 - -Markdown 可以被 HTML 复制、引用或人工同步,但本阶段不要求实现运行时 Markdown 渲染器。 - -## 5. 目录结构 - -目标目录: - -```text -docs/explainers/muse-cloud/ -├── README.md -├── index.html -├── content/ -│ ├── architecture.md -│ ├── openapi-paths.md -│ ├── request-lifecycle.md -│ ├── domain-owners.md -│ ├── p1r-stages.md -│ └── troubleshooting.md -└── assets/ - ├── styles.css - └── app.js -``` - -### 5.1 `README.md` - -职责: - -1. 说明讲解包用途。 -2. 指向 `index.html` 作为主入口。 -3. 说明 Markdown 与 HTML 的维护关系。 -4. 列出资料来源和边界:P1R spec、P1R-0 spec、OpenAPI 合同、muse-cloud 代码事实。 - -### 5.2 `index.html` - -职责: - -1. 提供完整阅读体验。 -2. 使用固定左侧目录、中间正文、右侧快速定位面板。 -3. 在页面内完整解释架构、路径、生命周期、owner、阶段和排障。 -4. 用清晰卡片和图形展示复杂关系。 -5. 不把读者引到 Markdown 才能完成理解。 - -### 5.3 `content/*.md` - -职责: - -1. 维护各主题的简版正文。 -2. 保存简单 Mermaid 图。 -3. 为后续内容更新提供易审查文本。 - -### 5.4 `assets/styles.css` - -职责: - -1. 承载 HTML 页面视觉系统。 -2. 定义布局、色彩、排版、响应式、打印和可访问性样式。 - -### 5.5 `assets/app.js` - -职责: - -1. 承载轻量交互:目录定位、路径组合示例切换、排障筛选、复制路径。 -2. 不引入复杂前端框架。 -3. 不请求后端 API,不收集用户输入。 - -## 6. 页面信息架构 - -`index.html` 按以下顺序组织: - -1. **你正在看的是什么**:讲解包目标、适用读者、不要误读为 API 完成证明。 -2. **系统一张图**:muse-cloud 目标架构和真实 API 标准。 -3. **OpenAPI 路径怎么组合**:从合同文件到 URL、operationId、Controller 和 Application。 -4. **一次请求怎么走**:版本头、鉴权、DTO、幂等、Domain、Persistence、Adapter、CommonResult。 -5. **领域 owner 怎么分**:Content、Meta、Account、AI、Knowledge、Market、Events。 -6. **P1R 阶段地图**:P1R-0 到 P1R-7 的任务和验收口径。 -7. **问题怎么定位**:按症状进入排障流程。 -8. **维护规则**:后续新增阶段 spec 或 API 完成矩阵后如何更新讲解包。 - -## 7. 关键图和交互 - -### 7.1 目标架构图 - -核心事实: - -```mermaid -flowchart TD - C[Controller] --> A[Application Service] - A --> D[Domain Service / Aggregate] - A --> Q[Query Service / Assembler] - A --> O[Outbox / Job Service] - D --> P[Persistence Mapper] - O --> E[External Adapter] - E --> N[New-API] - E --> R[RAGFlow / Knowledge Engine] - E --> F[File / Object Storage] - E --> S[SSE Event Stream] - Q --> V[OpenAPI DTO] -``` - -HTML 中应把这张图扩展成分层图,并在每一层旁边写明: - -1. 这一层负责什么。 -2. 这一层不应该负责什么。 -3. 出问题时先查什么文件或证据。 - -### 7.2 OpenAPI 路径组合器 - -HTML 中必须展示以下组合规则: - -```text -docs/api-contracts//openapi.yaml - + side prefix: /app-api/muse 或 /admin-api/muse - + resource path: 合同 paths 下的资源路径 - + operationId: 后端映射和完成矩阵识别键 -``` - -示例: - -```text -docs/api-contracts/content/openapi.yaml - -> /app-api/muse/works - -> listWorks - -> AppContentController - -> ContentAppService -``` - -页面要明确: - -1. `/app-api/muse/**` 是 app 入口。 -2. `/admin-api/muse/**` 是 admin 入口。 -3. app/admin 不是领域 owner。 -4. 领域事实只能由领域模块拥有。 -5. `X-API-Version: 1` 是 Muse API 的入口要求。 -6. 对外响应字段是 `code/data/msg`,不是 `message`。 - -### 7.3 请求生命周期图 - -HTML 中用横向流程解释: - -```text -HTTP Request - -> X-API-Version 校验 - -> 登录 / RBAC / owner / tenant 校验 - -> Controller DTO - -> Application Service 事务与幂等 - -> Domain 状态机和不变式 - -> Mapper / External Adapter - -> Query / Assembler - -> CommonResult(code, data, msg) -``` - -每个节点必须带“常见问题”和“定位证据”: - -1. 404:路径、前缀、Controller 映射、catch-all。 -2. 400:版本头、DTO、参数校验。 -3. 401/403:登录态、RBAC、owner、tenant。 -4. 409:`commandId`、revision、expectedVersion、状态机前置条件。 -5. 500:数据库、外部 adapter、任务执行器、错误分类。 - -### 7.4 领域 owner 边界图 - -HTML 中展示: - -| 领域 | owner 事实 | 典型外部依赖 | 不应越界 | -|------|------------|--------------|----------| -| Content | 作品、章节、Block、导入导出、Suggestion Merge | File、AI suggestion | 不直接拥有市场授权 | -| Meta | MetaSchema、保护节点、功能链治理 | PostgreSQL、审计 | 不被用户槽位覆盖保护节点 | -| Account | 用户资料、权益、配额、用量、New-API 归因 | New-API、账本 | 不允许请求体伪造归因 | -| AI | Prompt、Agent、Tool Grant、任务、质量治理 | New-API、SSE | 不伪造 AI 调用成功 | -| Knowledge | 知识库、文档、切片、索引、图谱 | RAGFlow、File | 不用空列表代替索引结果 | -| Market | 资产、授权、安装、发布、申诉、handoff | Account、授权快照 | 不写目标领域绑定事实 | -| Events | SSE、任务事件、跨端事件 | Redis、SSE | 不返回固定假事件 | - -### 7.5 P1R 阶段地图 - -HTML 中用阶段轨道展示: - -1. P1R-0:API 基线门禁。 -2. P1R-1:Content Real API。 -3. P1R-2:Meta Real API。 -4. P1R-3:Account Real API。 -5. P1R-4:AI Real API。 -6. P1R-5:Knowledge Real API。 -7. P1R-6:Market Real API。 -8. P1R-7:End-to-End Acceptance。 - -每个阶段显示: - -1. 负责的领域。 -2. 必须补齐的真实业务能力。 -3. 不能计入完成的假完成形态。 -4. 需要的验收证据。 - -### 7.6 排障流程 - -HTML 中必须有“症状 -> 层级 -> 检查项 -> 下一步”的诊断结构: - -| 症状 | 优先定位层 | 检查项 | -|------|------------|--------| -| 404 | OpenAPI 路径 / Controller | side prefix、`/muse`、资源路径、Controller mapping | -| 400 | Header / DTO | `X-API-Version`、必填字段、schema、参数校验 | -| 401/403 | Security | 登录、RBAC、owner、tenant、来源授权 | -| 409 | Domain / Idempotency | `commandId`、revision、expectedVersion、状态机 | -| 空列表但应有数据 | Query / Persistence | owner 条件、tenant 条件、读模型、mapper SQL | -| accepted 但不终态 | Job / Adapter | workflow task、outbox、外部服务回调、重试 | -| 外部服务失败 | Adapter | New-API、RAGFlow、File、SSE、超时和错误分类 | -| 响应字段不对 | DTO / CommonResult | `code/data/msg`、assembler、禁止 `message` | - -## 8. 视觉设计要求 - -HTML 页面必须按工程手册风格设计: - -1. 布局:桌面端使用左侧目录、中间正文、右侧快速定位;移动端折叠为单列。 -2. 色彩:低饱和、清晰分区、避免单一紫色/蓝紫渐变、避免装饰性光斑。 -3. 排版:正文可读,标题层级克制,代码和路径使用等宽字体。 -4. 卡片:只用于独立诊断项、阶段项和图例,不做卡片套卡片。 -5. 图:复杂关系优先用 HTML/CSS/SVG 绘制,简单关系可保留 Mermaid。 -6. 交互:只做理解服务,例如锚点导航、筛选诊断项、复制路径示例。 -7. 可访问性:对比度足够,键盘可导航,交互按钮有文本或 `aria-label`。 -8. 响应式:移动端不允许文字溢出、图表重叠或导航遮挡正文。 - -实现前应使用前端设计审查口径检查页面,避免 AI 风格的营销化、花哨化和信息空泛。 - -## 9. 维护规则 - -1. P1R spec、P1R-0 报告或 OpenAPI 合同更新后,必须检查讲解包是否需要同步。 -2. 如果 HTML 和 Markdown 内容冲突,以 P1R spec 和 OpenAPI 合同为准,修正文档。 -3. Markdown 只维护简版文本,HTML 承载完整讲解,不能要求读者跳转 Markdown 才能读懂主流程。 -4. 不在讲解包中记录真实凭据、环境密码或内部 token。 -5. 不把未验证实现写成已完成。 -6. 页面中的代码路径、命令和接口示例必须来自当前仓库事实或明确标注为示例。 - -## 10. 验收标准 - -1. `docs/explainers/muse-cloud/index.html` 可直接在浏览器打开。 -2. `index.html` 内完整解释 cloud 架构、OpenAPI 路径组合、请求生命周期、领域 owner、P1R 阶段和排障流程。 -3. `content/*.md` 覆盖各主题轻量正文。 -4. Mermaid 图语法可渲染,HTML/CSS/SVG 图不遮挡、不空白。 -5. 页面在桌面和移动宽度下不出现文字溢出、元素重叠或导航遮挡正文。 -6. 页面不依赖后端服务,不请求真实 API。 -7. 页面不修改或生成 `docs/api-contracts/**`、`muse-cloud/`、`muse-admin/`、`muse-studio/`。 -8. 页面明确声明 catch-all、通用持久化和占位响应不是 P1R 完成态。 - -## 11. 后续执行计划范围 - -后续 implementation plan 应只覆盖文档讲解包实现,不覆盖业务 API 实现。建议拆为: - -1. 建立 `docs/explainers/muse-cloud/` 目录和 Markdown 内容源。 -2. 设计并实现 `index.html`、`assets/styles.css`、`assets/app.js`。 -3. 补齐复杂 HTML 图和 OpenAPI 路径组合示例。 -4. 补齐排障流程和 P1R 阶段地图。 -5. 本地浏览器检查桌面和移动宽度。 -6. 最终只提交讲解包相关文件,不提交 `.superpowers/` 临时预览。 - -## 12. 待用户复核项 - -1. 是否接受 `index.html` 作为完整主阅读入口,Markdown 只作为轻量维护源。 -2. 是否接受 `docs/explainers/muse-cloud/` 作为正式讲解包路径。 -3. 是否接受不引入文档站构建系统,只使用静态 HTML/CSS/JS。 diff --git a/docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md b/docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md deleted file mode 100644 index fd860571..00000000 --- a/docs/superpowers/specs/2026-05-26-P1R-1-content-real-api-design.md +++ /dev/null @@ -1,441 +0,0 @@ -# P1R-1:Content Real API 规格 - -- 版本:v1 -- 日期:2026-05-26 -- 状态:待实施 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-content/**`、Content 相关共享支撑、Content 相关测试与迁移 -- 非范围:修改 `docs/api-contracts/**/openapi.yaml`、实现 AI / Meta / Knowledge / Market / Account 全领域 owner、修改 `muse-admin/` 或 `muse-studio/` - -## 1. 结论 - -P1R-1 的目标不是继续补扫描脚本,也不是把 `MuseApiContractSupport` 的 catch-all 响应包装得更像业务结果。 - -本阶段要把 Content 域从“合同路由可响应”推进到“真实业务 API 可验收”: - -1. 51 个 Content operation 必须逐项有真实 Controller / Service / DTO / persistence 或明确 blocked 证据。 -2. 36 个 `catch_all/incomplete` 必须退出 catch-all 兜底路径,进入专用 Controller 和 Content 应用服务。 -3. 15 个 `dedicated/needs_verification` 不能直接视为完成,必须补齐 API 版本、DTO 对齐、owner guard、幂等、审计、错误响应和测试证据。 -4. Content owner 只写 Content 事实。AI、Meta、Knowledge、FileService 等外部 owner 必须通过 facade / adapter / 投影输入协作,不能由 Content 直接伪造对方事实。 - -## 2. 已验证事实 - -### 2.1 P1R-0 覆盖矩阵 - -来自 `docs/superpowers/reports/p1r-api-coverage.json`: - -| 指标 | 数量 | -|---|---:| -| 全量 operation | 233 | -| Content operation | 51 | -| Content `dedicated/needs_verification` | 15 | -| Content `catch_all/incomplete` | 36 | -| Content `generic_persistence` | 0 | -| Content `sse_placeholder` | 0 | -| Content `missing` | 0 | -| Content `completed` | 0 | - -推论:P1R-1 的主要工作不是清理 generic persistence、SSE placeholder 或 missing,而是退役 Content catch-all,并验证已有 dedicated 入口是否真的满足 OpenAPI 和业务验收。 - -### 2.2 当前代码事实 - -已存在: - -- `AppContentController`:覆盖作品、章节、Block 基础 CRUD。 -- `ContentAppService` / `ContentAppServiceImpl`:覆盖 15 个 dedicated operation 的核心持久化。 -- `muse_content_work`、`muse_content_chapter`、`muse_content_block`、`muse_content_block_source_attribution`、`muse_content_command_log`。 -- `muse_content_import_task`、`muse_content_export_task` 表已在 SQL 中存在,但当前没有对应 DO / Mapper / Service。 -- `ContentRevisionGuard` 已有 commandId 和 revision 基础校验。 - -仍未满足 P1R-1: - -- `AppMuseContentContractController` 和 `AdminMuseContentContractController` 仍接管 36 个 Content operation。 -- 当前没有 Content 级 `X-API-Version: 1` 统一校验。 -- 当前 `muse_content_command_log` 只是幂等日志,不等于完整审计链。 -- 当前 admin content 读模型、risk action、规划、Meta 投影、导入解析、导出下载、Suggestion Merge 都没有专用业务实现。 -- 当前跨领域依赖没有真实 facade 合同和 blocked/完成判定。 - -## 3. 目标 - -1. 为 51 个 Content operation 定义真实 API 落地边界。 -2. 保留 Content 单一 owner:作品、章节、Block、规划正式稿、来源归因、导入任务、导出任务由 Content 写入。 -3. 对 AI / Meta / Knowledge / FileService 依赖建立明确 facade / adapter 边界。 -4. 所有写命令强制 commandId、revision 或 expectedVersion、owner guard、审计。 -5. 所有响应保持 Yudao `CommonResult`,对外 JSON 字段为 `code/data/msg`。 -6. 每个接口都有可执行验收方式:单元测试、应用服务测试、Controller 契约测试、P1R 覆盖门禁。 - -## 4. 非目标 - -1. 不修改 OpenAPI 合同来适配当前实现。 -2. 不把 catch-all、operation record、workflow task 原始行、空列表、固定样例响应作为完成证据。 -3. 不在 Content 阶段实现 AI Orchestration、MetaSchema、Knowledge Graph 的完整领域 owner。 -4. 不为跨领域依赖写 mock 成功闭环。真实依赖不可用时,该 operation 只能 `blocked`,必须写明下一步验证命令。 -5. 不把前端隐藏入口当作权限、状态机或风控边界。 - -## 5. 目标架构 - -```mermaid -flowchart TD - A[App/Admin Content Controller] --> B[Content Application Service] - B --> C[Content Domain Guard] - B --> D[Content Query/Assembler] - B --> E[Content Command/Audit] - B --> F[Content Persistence] - B --> G[External Facades] - G --> H[FileService] - G --> I[AI Orchestration] - G --> J[Meta Projection] - G --> K[Knowledge Draft] - F --> L[(muse_content_* tables)] - E --> M[(command log / audit log)] -``` - -### 5.1 Controller - -Controller 只承担入口职责: - -- 登录态和 RBAC 注解。 -- `X-API-Version: 1` 校验。 -- path/query/body 参数校验。 -- 调用 Content 应用服务。 -- 用 `CommonResult` 返回 `code/data/msg`。 - -禁止事项: - -- Controller 不访问 Mapper。 -- Controller 不拼业务 JSON。 -- Controller 不直接调用外部服务。 -- Controller 不调用 `MuseApiContractSupport.handle(...)` 作为完成路径。 - -推荐 Controller 拆分: - -| Controller | 职责 | -|---|---| -| `AppContentController` | 保留作品、章节、Block 基础 CRUD | -| `AppContentStructureController` | 章节重排、Block split/merge | -| `AppContentSourceController` | 来源归因、Suggestion Merge | -| `AppContentPlanningController` | 规划正式稿、规划候选投影、文风检查投影 | -| `AppContentImportParseController` | 导入任务、解析任务投影、解析结果确认 | -| `AppContentExportController` | 导出任务和下载凭证 | -| `AdminContentController` | 管理端作品读模型、任务列表、risk action | - -### 5.2 Service - -Application Service 是事务、幂等、权限、审计和外部编排边界。 - -推荐服务: - -| Service | 职责 | -|---|---| -| `ContentAppService` | 保留已有作品、章节、Block CRUD,补齐验收缺口 | -| `ContentStructureService` | reorder、split、merge,保证排序、revision 和来源归因 | -| `ContentSourceService` | 当前 revision 来源归因查询,AI suggestion 合并写入 Canonical Block | -| `ContentPlanningService` | 规划正式稿读写、候选确认进入正式规划、候选丢弃决策 | -| `ContentImportParseService` | 导入任务、解析任务投影、章节解析结果确认/驳回/批量确认 | -| `ContentExportService` | 导出任务、导出产物、下载凭证 | -| `ContentAdminService` | 管理端读模型和治理动作 | -| `ContentCommandService` | commandId 幂等、requestHash、resultSnapshot | -| `ContentAuditService` | 写命令审计和 admin risk action 审计 | - -### 5.3 Persistence - -已有表可直接复用: - -- `muse_content_work` -- `muse_content_chapter` -- `muse_content_block` -- `muse_content_block_source_attribution` -- `muse_content_import_task` -- `muse_content_export_task` -- `muse_content_command_log` - -需要补齐的持久化模型: - -| 模型 | 建议落点 | 说明 | -|---|---|---| -| 导入任务 DO/Mapper | `ImportTaskDO` / `ImportTaskMapper` | 对齐已有 `muse_content_import_task` | -| 导出任务 DO/Mapper | `ExportTaskDO` / `ExportTaskMapper` | 对齐已有 `muse_content_export_task` | -| 规划正式稿 | 新增 `muse_content_planning_section` | Content 拥有正式规划,不拥有 AI 候选运行事实 | -| 治理动作 | 新增 `muse_content_governance_action` | admin risk action 的审计与影响范围 | -| 下载凭证 | 可扩展 `muse_content_export_task` 或新增凭证表 | 必须有过期时间和 owner 校验 | -| command result snapshot | 扩展 `muse_content_command_log` 使用方式 | 重复 commandId 返回同一业务结果 | - -如新增 DDL,文件应使用下一个 Flyway 版本,例如 `muse-cloud/sql/muse/V9__extend_content_real_api_schema.sql`。 - -### 5.4 DTO / Assembler - -每个 OpenAPI schema 对应专用 VO / DTO,不返回 `Map`。 - -要求: - -- app VO 放在 `controller/app/vo`。 -- admin VO 放在 `controller/admin/vo`。 -- 复杂读模型通过 assembler 从 DO / facade DTO 组装。 -- 读接口不得直接暴露 DO。 -- 异步任务详情不能只返回 `status: accepted`,必须包含真实状态、进度、失败阶段、retryable、nextActions。 - -### 5.5 Owner Guard / 权限 - -app 入口: - -- 所有 workId 必须校验 `owner_user_id == getLoginUserId()`。 -- chapterId、blockId 必须校验从属于 workId。 -- import/export/parse/planning/source 任务必须校验 owner 和 workId 归属。 - -admin 入口: - -- 必须使用 `@PreAuthorize` 绑定 `muse:content:*` 权限。 -- admin 可跨 owner 读治理摘要,但写治理动作必须有 reason、expectedVersion、targetScope。 -- admin risk action 不得绕过 Content 状态机直接改其他领域事实。 - -tenant: - -- 依赖 Yudao tenant plugin,但查询条件和唯一键仍要保持 tenant 维度。 -- 测试需要覆盖跨 owner 访问负例;如当前测试基座难以覆盖 tenant,也要至少验证 owner guard。 - -### 5.6 commandId / 幂等 - -所有写命令必须满足: - -1. 请求体包含 `commandId`,或合同明确使用等价幂等键。 -2. 首次执行记录 requestHash、业务结果 resultSnapshot、targetType、targetId。 -3. 重复 commandId + 相同 requestHash 返回同一业务结果。 -4. 重复 commandId + 不同 requestHash 返回幂等冲突错误。 -5. 写入前校验 revision / expectedVersion / expectedStatus。 - -适用操作包括: - -- create/update/delete work。 -- create/update/delete/reorder chapter。 -- create/save/delete/split/merge block。 -- mergeBlockSuggestion。 -- savePlanningItem、confirm/discard planning candidate、createStyleCheck。 -- createImportTask、createParseJob、retryParseJob、confirm/reject/batchConfirm chapters。 -- createExportTask、exportWork。 -- adminRiskAction。 - -### 5.7 Audit - -`muse_content_command_log` 可以作为幂等基座,但 P1R-1 还需要完整审计语义: - -| 字段 | 说明 | -|---|---| -| actorUserId | 操作者 | -| side | app/admin | -| operationId | OpenAPI operationId | -| commandId | 幂等键 | -| targetType / targetId | 业务目标 | -| beforeRevision / afterRevision | 状态变化 | -| requestSummary | 请求摘要,不能记录敏感全文 | -| resultSummary | 结果摘要 | -| reason | 用户或管理员原因 | -| status | succeeded / failed / blocked | - -高危操作必须审计: - -- deleteWork / deleteChapter / deleteBlock。 -- splitBlock / mergeBlocks。 -- mergeBlockSuggestion。 -- import / parse / export 任务创建和重试。 -- adminRiskAction。 - -### 5.8 错误响应 - -统一使用 `ServiceException` 和 Yudao `CommonResult` 输出。 - -错误码建议: - -| 场景 | 建议错误码语义 | -|---|---| -| API 版本错误 | `CONTENT_API_VERSION_UNSUPPORTED` | -| commandId 缺失 | `CONTENT_COMMAND_ID_REQUIRED` | -| commandId 请求体冲突 | `CONTENT_COMMAND_ID_CONFLICT` | -| owner 校验失败 | `CONTENT_FORBIDDEN` | -| 资源不存在 | `CONTENT_NOT_FOUND` | -| revision 冲突 | `CONTENT_REVISION_CONFLICT` | -| 非法状态转移 | `CONTENT_INVALID_STATUS_TRANSITION` | -| 外部 owner 不可用 | `CONTENT_EXTERNAL_OWNER_UNAVAILABLE` | -| 文件凭证无效或过期 | `CONTENT_DOWNLOAD_CREDENTIAL_INVALID` | -| admin 权限不足 | 复用 Yudao 权限错误 | - -对外 JSON 字段必须是 `code/data/msg`,禁止以 `message` 替代 `msg`。 - -## 6. Operation 覆盖矩阵 - -### 核心作品/章节/Block - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| listWorks | GET | `/app-api/muse/works` | `dedicated` / `needs_verification` | - | `WorkSummary` page | - | -| createWork | POST | `/app-api/muse/works` | `dedicated` / `needs_verification` | create work body | `Work` | - | -| getWork | GET | `/app-api/muse/works/{workId}` | `dedicated` / `needs_verification` | - | `Work` | - | -| updateWork | PUT | `/app-api/muse/works/{workId}` | `dedicated` / `needs_verification` | update work body | boolean/empty success | - | -| deleteWork | DELETE | `/app-api/muse/works/{workId}` | `dedicated` / `needs_verification` | delete body | empty success | - | -| listChapters | GET | `/app-api/muse/works/{workId}/chapters` | `dedicated` / `needs_verification` | - | `Chapter[]` | - | -| createChapter | POST | `/app-api/muse/works/{workId}/chapters` | `dedicated` / `needs_verification` | create chapter body | `Chapter` | - | -| getChapter | GET | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated` / `needs_verification` | - | `ChapterDetail` | - | -| updateChapter | PUT | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated` / `needs_verification` | `UpdateChapterRequest` | empty success | - | -| deleteChapter | DELETE | `/app-api/muse/works/{workId}/chapters/{chapterId}` | `dedicated` / `needs_verification` | `DeleteChapterRequest` | empty success | - | -| reorderChapters | PUT | `/app-api/muse/works/{workId}/chapters/{chapterId}/reorder` | `catch_all` / `incomplete` | `ReorderChapterRequest` | empty success | - | -| listBlocks | GET | `/app-api/muse/works/{workId}/chapters/{chapterId}/blocks` | `dedicated` / `needs_verification` | - | `Block[]` | - | -| createBlock | POST | `/app-api/muse/works/{workId}/chapters/{chapterId}/blocks` | `dedicated` / `needs_verification` | `CreateBlockRequest` | `Block` | - | -| getBlock | GET | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated` / `needs_verification` | - | `Block` | - | -| saveBlock | PUT | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated` / `needs_verification` | `SaveBlockRequest` | `newRevision` | - | -| deleteBlock | DELETE | `/app-api/muse/works/{workId}/blocks/{blockId}` | `dedicated` / `needs_verification` | `DeleteBlockRequest` | empty success | - | - -验收要求:15 个 dedicated operation 必须通过契约测试证明 DTO、owner guard、revision、commandId、audit、`code/data/msg` 均正确;`reorderChapters` 必须退出 catch-all。 - -### Block 结构编辑 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| splitBlock | POST | `/app-api/muse/works/{workId}/blocks/{blockId}/split` | `catch_all` / `incomplete` | `SplitBlockRequest` | empty success 或 block 结果 | - | -| mergeBlocks | POST | `/app-api/muse/works/{workId}/blocks/{blockId}/merge` | `catch_all` / `incomplete` | `MergeBlockRequest` | empty success 或 block 结果 | - | - -验收要求:必须校验相邻关系、排序、revision、sourceSnapshot,并写入来源归因和审计。 - -### 来源归因与 Suggestion Merge - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| getBlockSourceAttribution | GET | `/app-api/muse/works/{workId}/blocks/{blockId}/source-attribution` | `catch_all` / `incomplete` | - | `SourceAttribution` | - | -| mergeBlockSuggestion | POST | `/app-api/muse/works/{workId}/blocks/{blockId}/suggestion-merges` | `catch_all` / `incomplete` | `MergeBlockSuggestionRequest` | `MergeBlockSuggestionResult` | New-API, SSE | - -验收要求:Content 写 Canonical Block,AI suggestion 事实由 AI owner 提供投影。AI owner 不可用时,不能伪造成功,只能返回明确 blocked 错误。 - -### Meta 投影与动态字段校验 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| listMetaProjections | GET | `/app-api/muse/works/{workId}/meta-projections` | `catch_all` / `incomplete` | - | `MetaProjectionSummary[]` | Meta | -| getMetaProjection | GET | `/app-api/muse/works/{workId}/meta-projections/{projectionKey}` | `catch_all` / `incomplete` | - | `MetaProjectionDetail` | Meta | -| validateDynamicFields | POST | `/app-api/muse/works/{workId}/dynamic-fields/validate` | `catch_all` / `incomplete` | `DynamicFieldValidationRequest` | `DynamicFieldValidationResult` | Meta | - -验收要求:Content 只能做 owner guard 和调用 Meta facade;MetaSchema 规则由 Meta owner 解释。Meta owner 不可用时返回 blocked,不能在 Content 内复制一套 MetaSchema。 - -### 作品规划与候选 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| getPlanning | GET | `/app-api/muse/works/{workId}/planning` | `catch_all` / `incomplete` | - | `PlanningStructure` | - | -| savePlanningItem | PUT | `/app-api/muse/works/{workId}/planning/{sectionKey}` | `catch_all` / `incomplete` | `SavePlanningItemRequest` | `PlanningItemSaveResult` | Meta 可选 | -| listPlanningCandidates | GET | `/app-api/muse/works/{workId}/planning/candidates` | `catch_all` / `incomplete` | - | `PlanningCandidateSummary[]` | AI | -| createPlanningCandidate | POST | `/app-api/muse/works/{workId}/planning/candidates` | `catch_all` / `incomplete` | `CreatePlanningCandidateRequest` | task summary | AI | -| getPlanningCandidate | GET | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}` | `catch_all` / `incomplete` | - | `PlanningCandidateDetail` | AI | -| confirmPlanningCandidate | POST | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}/confirm` | `catch_all` / `incomplete` | `ConfirmPlanningCandidateRequest` | `PlanningCandidateDecisionResult` | AI, Meta | -| discardPlanningCandidate | POST | `/app-api/muse/works/{workId}/planning/candidates/{candidateId}/discard` | `catch_all` / `incomplete` | `DiscardPlanningCandidateRequest` | empty success | AI | -| createStyleCheck | POST | `/app-api/muse/works/{workId}/planning/style-checks` | `catch_all` / `incomplete` | `CreateStyleCheckRequest` | task summary | AI | -| getStyleCheckResult | GET | `/app-api/muse/works/{workId}/planning/style-checks/{jobId}` | `catch_all` / `incomplete` | - | `StyleCheckResult` | New-API, SSE | - -验收要求:正式 planning section 是 Content 事实;候选和 style check 运行事实由 AI owner 提供。确认候选进入正式规划时,Content 必须校验 candidate 投影状态、revision 和 sourceSnapshot。 - -### 导入与解析审阅 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| createImportTask | POST | `/app-api/muse/works/{workId}/import-tasks` | `catch_all` / `incomplete` | `CreateImportTaskRequest` | `ImportTaskDetail` | FileService | -| getImportTask | GET | `/app-api/muse/import-tasks/{taskId}` | `catch_all` / `incomplete` | - | `ImportTaskDetail` | FileService, SSE | -| createParseJob | POST | `/app-api/muse/works/{workId}/parse-jobs` | `catch_all` / `incomplete` | `CreateParseJobRequest` | `ParseJobDetail` | AI, SSE | -| getParseJob | GET | `/app-api/muse/parse-jobs/{jobId}` | `catch_all` / `incomplete` | - | `ParseJobDetail` | AI, SSE | -| retryParseJob | POST | `/app-api/muse/parse-jobs/{jobId}/retry` | `catch_all` / `incomplete` | retry body | retry result | AI, SSE | -| listParseJobChapters | GET | `/app-api/muse/parse-jobs/{jobId}/chapters` | `catch_all` / `incomplete` | - | `ChapterParseResult[]` | AI | -| confirmChapterParseResult | POST | `/app-api/muse/chapter-parse-results/{resultId}/confirm` | `catch_all` / `incomplete` | `ConfirmChapterParseResultRequest` | result | AI, Knowledge | -| rejectChapterParseResult | POST | `/app-api/muse/chapter-parse-results/{resultId}/reject` | `catch_all` / `incomplete` | `RejectChapterParseResultRequest` | empty success | AI | -| batchConfirmChapters | POST | `/app-api/muse/parse-jobs/{jobId}/chapters/batch-confirm` | `catch_all` / `incomplete` | `BatchConfirmChaptersRequest` | `BatchConfirmChaptersResult` | AI, Knowledge | - -验收要求:导入任务是 Content 事实,解析运行事实由 AI owner 提供。确认解析结果写入章节或知识草稿时,必须通过 Content / Knowledge owner 边界,不能由 catch-all 直接落 operation record。 - -### 导出与下载 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| exportWork | POST | `/app-api/muse/works/{workId}/export` | `catch_all` / `incomplete` | export body | file response 或 task | FileService | -| createExportTask | POST | `/app-api/muse/works/{workId}/export-tasks` | `catch_all` / `incomplete` | `CreateExportTaskRequest` | `ExportTaskDetail` | FileService | -| getExportTask | GET | `/app-api/muse/export-tasks/{taskId}` | `catch_all` / `incomplete` | - | `ExportTaskDetail` | FileService, SSE | -| downloadExportPackage | GET | `/app-api/muse/downloads/{credentialId}` | `catch_all` / `incomplete` | - | binary/download | FileService | - -验收要求:导出任务必须产生真实产物引用和过期下载凭证。下载必须校验 owner、credentialId、过期时间、任务状态和访问审计。 - -### 管理端治理 - -| operationId | 方法 | 路径 | P1R-0 状态 | 请求 | 响应 | 外部依赖 | -|---|---:|---|---|---|---|---| -| adminListWorks | GET | `/admin-api/muse/content/works` | `catch_all` / `incomplete` | - | `AdminWorkSummary` page | - | -| adminGetWork | GET | `/admin-api/muse/content/works/{workId}` | `catch_all` / `incomplete` | - | `AdminWorkDetail` | - | -| adminListChapters | GET | `/admin-api/muse/content/works/{workId}/chapters` | `catch_all` / `incomplete` | - | `AdminChapterSummary[]` | - | -| adminListImportTasks | GET | `/admin-api/muse/content/import-tasks` | `catch_all` / `incomplete` | - | `ImportTaskSummary` page | FileService | -| adminListExportTasks | GET | `/admin-api/muse/content/export-tasks` | `catch_all` / `incomplete` | - | `ExportTaskSummary` page | FileService | -| adminRiskAction | POST | `/admin-api/muse/content/works/{workId}/risk-actions` | `catch_all` / `incomplete` | `RiskActionRequest` | `RiskActionResult` | notify 可选 | - -验收要求:admin API 必须使用 RBAC,读模型不能暴露正文全文,高危治理动作必须有 reason、影响范围、expectedVersion、审计记录。 - -## 7. Catch-all 退役规则 - -退役顺序: - -1. 新增专用 Controller 方法。 -2. 新增或复用 Content Application Service 方法。 -3. 补齐 DTO / assembler / persistence / tests。 -4. 从 `AppMuseContentContractController` 或 `AdminMuseContentContractController` 的 catch-all path 列表移除对应路径。 -5. 运行 P1R 覆盖脚本,确认 operation 不再被识别为 `catch_all`。 - -禁止只改变扫描脚本判定规则来让 operation 看起来完成。 - -## 8. 验收门禁 - -P1R-1 完成时必须提供: - -1. 51 个 Content operation 的实现状态对账表。 -2. 36 个 catch-all operation 的退役证据。 -3. 15 个 dedicated operation 的 DTO、权限、幂等、审计、错误响应回归证据。 -4. 单元测试:Domain guard、command service、audit service、assembler。 -5. 应用服务测试:owner guard、revision 冲突、幂等重复、幂等冲突、非法状态。 -6. Controller 契约测试:`code/data/msg`、API version、参数校验、权限负例。 -7. P1R 覆盖门禁: - -```bash -python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check -``` - -8. Maven 聚焦测试: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-module-content/muse-module-content-server -am -Dsurefire.failIfNoSpecifiedTests=false -``` - -9. P1R-0 基线门禁回归: - -```bash -cd muse-cloud -JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ - mvn -o test -pl muse-server -am -Dtest=P1rApiCoverageReportTest -Dsurefire.failIfNoSpecifiedTests=false -``` - -## 9. 阻塞判定 - -以下情况不能宣称完成,只能标 `blocked`: - -1. FileService 无法生成真实文件引用或下载凭证。 -2. AI owner 无法提供 suggestion、planning candidate、parse job、style check 投影。 -3. Meta owner 无法提供 MetaProjection 或动态字段校验。 -4. Knowledge owner 无法消费章节解析结果生成知识草稿。 -5. 无法证明写命令幂等、owner guard 或审计。 - -blocked operation 必须写明: - -- 阻塞 owner。 -- 需要的 facade 方法。 -- 当前返回的错误码。 -- 后续阶段或环境验证命令。 - -## 10. 最小完成口径 - -P1R-1 的最小可验收完成口径: - -1. Content 自有事实的 operation 不再走 catch-all。 -2. 跨领域 operation 不伪造成成功;如依赖未完成,返回明确 blocked 错误并有 facade 合同。 -3. 覆盖矩阵中 Content 不再出现“看似完成但实际由 catch-all 支撑”的 operation。 -4. 文档、计划、测试和报告能够说明每个 Content operation 的真实状态。 diff --git a/docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md b/docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md deleted file mode 100644 index cda6986f..00000000 --- a/docs/superpowers/specs/2026-05-27-P1R-2-meta-real-api-design.md +++ /dev/null @@ -1,498 +0,0 @@ -# P1R-2:Meta Real API 规格 - -- 版本:v1 -- 日期:2026-05-27 -- 状态:待 review -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-meta/**`、Meta 相关测试、Meta 相关追加迁移、P1R-2 覆盖门禁 -- 非范围:实现 Content / Account / AI / Knowledge / Market owner、修改 `muse-cloud/scripts/**` 掩盖缺口、把 Meta operation 标为 `completed`、静默修改 OpenAPI - -## 1. 结论 - -P1R-2 的目标不是继续增强 `MuseContractPersistenceService`,也不是给通用持久化结果包一层 Meta 外观。 - -本阶段要把 Meta 域从“通用持久化可响应”推进到“真实 Meta 治理 API 可验收”: - -1. 16 个 Meta operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。 -2. Meta owner 只拥有 MetaSchema、ProtectionNode、FunctionChain 的治理事实和治理审计。 -3. 跨 Content / Knowledge / AI / Export / New-API / SSE 的影响只能通过 facade、投影读模型或 outbox/job 表达,不能由 Meta 伪造其他 owner 的事实。 -4. 发布、激活、灰度、回滚、废弃、功能链激活必须具备 commandId 幂等、requestHash 冲突校验、权限、版本前置条件、影响预览引用、校验引用和审计。 -5. P1R-2 通过后的覆盖状态目标统一为 `dedicated / needs_verification`,不能写成 `completed`。外部 owner 不可用时返回合同内的 `CommonResult` 业务错误,不使用 coverage 的 `blocked` 完成状态。 - -## 2. 已验证事实 - -### 2.1 执行工作面 - -用户给定的主路径 `/Users/qingse/Sync/local-git/oh-my-muse` 当前不是本阶段真实工作面。只读核验结果显示: - -- `/Users/qingse/Sync/local-git/oh-my-muse` 当前在 `feature/p1-muse-cloud-backend-20260524`,且有未跟踪文件。 -- `dev/1.0.0` 已被 worktree 占用:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 写入本规格和执行计划前,真实工作面 `dev/1.0.0` 的 `git status --short --branch` 输出为 `## dev/1.0.0...origin/dev/1.0.0`,无未提交改动;写入后只允许出现本规格和本阶段计划两个未跟踪文档。 - -本规格和执行计划应落在真实 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -### 2.2 覆盖矩阵 - -来自 `docs/superpowers/reports/p1r-api-coverage.json`: - -| 指标 | 数量 | -|---|---:| -| 全量 operation | 233 | -| completed | 0 | -| needs_verification | 51 | -| incomplete | 182 | -| generic_persistence | 180 | -| sse_placeholder | 2 | -| missing | 0 | -| blocked | 0 | -| Meta operation | 16 | -| Meta `generic_persistence / incomplete` | 16 | -| Meta `dedicated / needs_verification` | 0 | - -### 2.3 当前代码事实 - -当前 Meta 模块只有合同兜底入口: - -- `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMuseMetaContractController.java` - -该 Controller 使用宽泛路由接管: - -- `/muse/governance/meta-schemas` -- `/muse/governance/meta-schemas/**` -- `/muse/governance/protection-nodes` -- `/muse/governance/protection-nodes/**` -- `/muse/governance/function-chains` -- `/muse/governance/function-chains/**` - -并统一委托: - -- `MuseContractPersistenceService.handle(...)` - -当前缺失: - -- Meta 专用 Controller。 -- Meta Application Service。 -- OpenAPI 对应 DTO / VO。 -- DO / Mapper。 -- Meta commandId + requestHash 幂等服务。 -- Meta 发布、激活、灰度、回滚、废弃状态机。 -- Meta 领域审计。 -- ProtectionNode 不可替换 guard。 -- FunctionChain 版本、槽位、保护节点关系和影响预览模型。 - -### 2.4 当前 DDL - -`muse-cloud/sql/muse/V3__init_meta_schema.sql` 已有基础表: - -- `muse_meta_schema` -- `muse_meta_schema_version` -- `muse_meta_field` -- `muse_meta_visibility_policy` -- `muse_meta_protection_node` -- `muse_meta_function_chain` - -这些表不足以完成 P1R-2: - -- `muse_meta_schema_version.active_flag` 没有唯一 active 约束。 -- `muse_meta_schema_version.impact_preview_snapshot` 只能存快照,不等于真实影响预览记录。 -- 没有 validation result 表。 -- 没有 gray rule 表。 -- 没有 Meta 领域 command / audit 表。 -- 没有 FunctionChain version 表。 -- 没有 FunctionChain slot 表。 -- 没有 ProtectionNode 与 FunctionChain 的关系表。 -- ProtectionNode 缺少 OpenAPI 所需的 `nodeType`、`irreplaceableReason`、`permissionRequirement`、`auditRequirement`、`shadowToCanonicalBoundary`、`observabilityMetrics` 等结构化字段。 - -`muse-cloud/sql/muse/V7__add_contract_operation_audit.sql` 的 `muse_domain_operation_record` 是通用合同操作记录,不能作为 Meta 领域审计完成证据。 - -## 3. 目标 - -1. 为 16 个 Meta operation 定义真实 API 落地边界。 -2. 用专用 Controller / Service / DTO / DAL / DDL 替代通用持久化完成口径。 -3. 建立 MetaSchema 草稿、校验、影响预览、发布、激活、灰度、回滚、废弃的状态机。 -4. 建立 ProtectionNode 不可替换规则和 FunctionChain 保护节点约束。 -5. 建立 Meta 领域 commandId 幂等、requestHash 冲突校验和审计。 -6. 建立跨 owner 影响预览的 facade 边界,外部 owner 不可用时返回明确业务错误或 Meta outbox/job pending,不往 OpenAPI DTO 塞未定义字段。 -7. 建立 P1R-2 阶段门禁,防止 Meta operation 继续被 `generic_persistence` 或 `completed` 混淆。 - -## 4. 非目标 - -1. 不修改 OpenAPI 来适配当前代码。若发现合同纠偏需求,必须单独列出原因、影响范围和是否属于合同纠偏。 -2. 不实现 Content / Account / AI / Knowledge / Market 的领域 owner。 -3. 不直接写 Content、AI、Knowledge、Market、FileService 事实表。 -4. 不把 `MuseContractPersistenceService`、operation record、workflow task 原始行、空列表、`status: accepted` 作为完成证据。 -5. 不修改 `muse-cloud/scripts/**` 来掩盖真实缺口。 -6. 不把前端按钮隐藏或页面权限当作后端治理权限。 - -## 5. Operation 清单与当前状态 - -| operationId | Method | Path | 当前状态 | 真实 owner | P1R-2 目标 | -|---|---|---|---|---|---| -| `listMetaSchemas` | GET | `/admin-api/muse/governance/meta-schemas` | `generic_persistence / incomplete` | MetaSchema Query | `dedicated / needs_verification` | -| `getMetaSchema` | GET | `/admin-api/muse/governance/meta-schemas/{schemaKey}` | `generic_persistence / incomplete` | MetaSchema Query | `dedicated / needs_verification` | -| `getMetaSchemaVersion` | GET | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}` | `generic_persistence / incomplete` | MetaSchema Query | `dedicated / needs_verification` | -| `saveMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `validateMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/validate` | `generic_persistence / incomplete` | MetaSchema Validation | `dedicated / needs_verification` | -| `previewMetaSchemaDraftImpact` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/impact-preview` | `generic_persistence / incomplete` | Meta Impact Preview | `dedicated / needs_verification` | -| `publishMetaSchemaDraft` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/drafts/{draftVersion}/publish` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `activateMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/activate` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `rollbackMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/rollback` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `deprecateMetaSchemaVersion` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/deprecate` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `setMetaSchemaGrayRules` | POST | `/admin-api/muse/governance/meta-schemas/{schemaKey}/versions/{version}/gray-rules` | `generic_persistence / incomplete` | MetaSchema Command | `dedicated / needs_verification` | -| `listProtectionNodes` | GET | `/admin-api/muse/governance/protection-nodes` | `generic_persistence / incomplete` | ProtectionNode Query | `dedicated / needs_verification` | -| `getProtectionNode` | GET | `/admin-api/muse/governance/protection-nodes/{nodeKey}` | `generic_persistence / incomplete` | ProtectionNode Query | `dedicated / needs_verification` | -| `listFunctionChains` | GET | `/admin-api/muse/governance/function-chains` | `generic_persistence / incomplete` | FunctionChain Query | `dedicated / needs_verification` | -| `previewFunctionChainImpact` | POST | `/admin-api/muse/governance/function-chains/{chainKey}/impact-preview` | `generic_persistence / incomplete` | FunctionChain Impact Preview | `dedicated / needs_verification` | -| `activateFunctionChainVersion` | POST | `/admin-api/muse/governance/function-chains/{chainKey}/versions/{version}/activate` | `generic_persistence / incomplete` | FunctionChain Command | `dedicated / needs_verification` | - -## 6. 目标架构 - -```mermaid -flowchart TD - A[Admin Meta Controller] --> B[Meta Application Service] - B --> C[Meta Command Service] - B --> D[Meta Audit Service] - B --> E[Meta Domain Guards] - B --> F[Meta Query / Assembler] - B --> G[Meta Persistence] - B --> H[External Impact Facades] - G --> I[(muse_meta_* tables)] - C --> J[(muse_meta_command)] - D --> K[(muse_meta_governance_audit)] - H --> L[Content Projection Facade] - H --> M[Knowledge Projection Facade] - H --> N[AI Context Facade] - H --> O[Export Impact Facade] -``` - -### 6.1 Controller - -推荐拆分: - -| Controller | 职责 | -|---|---| -| `AdminMetaSchemaController` | MetaSchema list/detail/version/draft/validate/impact/publish/activate/rollback/deprecate/gray-rules | -| `AdminProtectionNodeController` | ProtectionNode list/detail | -| `AdminFunctionChainController` | FunctionChain list/impact/activate | - -Controller 只负责: - -- `@PreAuthorize` 权限入口。 -- `X-API-Version: 1` 校验。 -- path/query/body 参数校验。 -- DTO 进入 Application Service。 -- `CommonResult` 返回 `code/data/msg`。 - -禁止: - -- Controller 访问 Mapper。 -- Controller 拼接业务 JSON。 -- Controller 调用 `MuseContractPersistenceService` 作为完成路径。 -- Controller 静态返回 OpenAPI 样例。 - -### 6.2 Service - -推荐服务: - -| Service | 职责 | -|---|---| -| `MetaSchemaService` | MetaSchema 查询、草稿、发布、激活、回滚、废弃、灰度 | -| `MetaSchemaValidationService` | 字段类型、必填、枚举、引用、兼容性、保护节点边界校验 | -| `MetaSchemaImpactPreviewService` | 影响预览生成、跨 owner 影响计数读取、previewId 持久化 | -| `ProtectionNodeService` | 保护节点注册表、不可替换规则、审计要求读模型 | -| `FunctionChainService` | 功能链路、版本、开放槽位、保护节点影响和激活 | -| `MetaCommandService` | commandId、requestHash、resultSnapshot 幂等 | -| `MetaAuditService` | Meta 治理审计 | - -### 6.3 DTO / Assembler - -每个 OpenAPI schema 必须对应专用 VO/DTO,不能返回 `Map` 或 DO。 - -最小 DTO 覆盖: - -- `MetaSchemaSummary` -- `MetaSchemaDetail` -- `MetaSchemaVersionDetail` -- `MetaSchemaDraftRequest` -- `MetaSchemaDraftResponse` -- `ValidationResult` -- `MetaSchemaImpactPreview` -- `MetaSchemaPublishRequest` -- `MetaSchemaPublishResponse` -- `MetaSchemaActivateRequest` -- `MetaSchemaActivateResponse` -- `MetaSchemaRollbackRequest` -- `MetaSchemaRollbackResponse` -- `MetaSchemaDeprecateRequest` -- `MetaSchemaDeprecateResponse` -- `MetaSchemaGrayRulesRequest` -- `MetaSchemaGrayRulesResponse` -- `ProtectionNodeSummary` -- `ProtectionNodeDetail` -- `FunctionChainSummary` -- `FunctionChainImpactPreviewRequest` -- `FunctionChainImpactPreview` -- `FunctionChainActivateRequest` -- `FunctionChainActivateResponse` - -### 6.4 Persistence - -复用 V3 基础表: - -- `muse_meta_schema` -- `muse_meta_schema_version` -- `muse_meta_field` -- `muse_meta_visibility_policy` -- `muse_meta_protection_node` -- `muse_meta_function_chain` - -建议追加 `V10__extend_meta_real_api_schema.sql`: - -| 数据结构 | 用途 | -|---|---| -| `muse_meta_command` | Meta commandId 幂等、requestHash、resultSnapshot | -| `muse_meta_governance_audit` | Meta 发布、激活、回滚、灰度、废弃、功能链激活审计 | -| `muse_meta_validation_result` | validationResultId、错误、警告、兼容性结果 | -| `muse_meta_impact_preview` | impactPreviewId、各 owner 影响摘要、外部读取错误摘要 | -| `muse_meta_gray_rule` | 灰度规则、作用范围、状态 | -| `muse_meta_function_chain_version` | 功能链版本、状态、激活记录 | -| `muse_meta_function_chain_slot` | 开放槽位和保护节点变更影响 | -| `muse_meta_function_chain_node` | 功能链和保护节点关系 | - -必须补数据库约束: - -- 同一租户、同一 `schema_id` 最多一个 `active_flag = TRUE` 且未删除版本,不能用 `tenant_id + schema_key` 过度约束,因为 V3 允许 `(domain, scope, target_type, schema_key)` 组合唯一。 -- 新增版本约束必须优先使用 `tenant_id + schema_id + version_no`;既有 `tenant_id + schema_key + version_no` 约束如与真实数据冲突,需单独提出迁移纠偏。 -- 同一租户、同一 commandId 唯一。 -- 同一租户、同一 impactPreviewId / validationResultId 唯一。 -- 同一租户、同一 FunctionChain 只有一个 active version。 - -## 7. 状态机 - -MetaSchema version 状态: - -```mermaid -stateDiagram-v2 - [*] --> draft - draft --> validated - validated --> published - published --> active - active --> gray - gray --> active - active --> deprecated - published --> deprecated - active --> rolled_back - gray --> rolled_back - rolled_back --> active -``` - -约束: - -- `saveMetaSchemaDraft` 只能创建或更新 draft,不允许直接 active。 -- `validateMetaSchemaDraft` 只能对 draft 产生 validation result。 -- `previewMetaSchemaDraftImpact` 必须绑定 draftVersion 和 draftHash。 -- `publishMetaSchemaDraft` 必须引用同一 draft 的 `validationResultId` 和 `impactPreviewId`。 -- `activateMetaSchemaVersion` 必须引用发布态版本,且 `expectedActiveVersion` 匹配当前 active。 -- `setMetaSchemaGrayRules` 只能作用于 published / active / gray 版本。 -- `rollbackMetaSchemaVersion` 只能回到已发布且未废弃版本。 -- `deprecateMetaSchemaVersion` 必须给出 reason、影响预览和替代策略。 - -FunctionChain 状态: - -- draft / published / active / deprecated。 -- 激活前必须执行影响预览。 -- 不允许把 ProtectionNode 降级为用户可替换槽位。 -- 涉及 New-API/SSE/AI runtime 的真实副作用不在 P1R-2 直接实现;无 owner 能力时必须返回 `CommonResult` 业务错误或记录 Meta outbox/job pending,不能在响应 DTO 中增加 OpenAPI 未定义的 blocked 字段。 - -## 8. 权限与 owner guard - -所有 P1R-2 接口都是 admin 入口,必须后端强制 RBAC。 - -建议权限点: - -| 权限点 | 覆盖 | -|---|---| -| `muse:meta:query` | list/get MetaSchema、ProtectionNode、FunctionChain | -| `muse:meta:draft` | save draft、validate draft、impact preview | -| `muse:meta:publish` | publish | -| `muse:meta:activate` | activate | -| `muse:meta:gray-rule` | set gray rules | -| `muse:meta:rollback` | rollback | -| `muse:meta:deprecate` | deprecate | -| `muse:meta:function-chain` | function chain preview / activate | - -owner guard 不是用户 owner,而是领域 owner 边界: - -- Meta 只能写 Meta 表和 Meta 审计。 -- Content work、AI task、Knowledge projection、Export task 不能由 Meta 直接写。 -- ProtectionNode 是系统治理对象,不能由 FunctionChain 或用户槽位把它标记成可替换。 - -## 9. commandId / 幂等 - -所有写命令必须满足: - -1. 请求必须有 `commandId`,或合同明确的等价幂等键。 -2. 幂等 envelope 至少包含 `tenantId`、`operationId`、`commandId`、`actorUserId`、`targetType`、`targetKey`、`targetVersion`、`requestHash`。 -3. 重复 commandId + 相同 requestHash 返回首次 `resultSnapshot`。 -4. 重复 commandId + 不同 requestHash 返回幂等冲突。 -5. 写入前校验 `expectedVersion`、`expectedActiveVersion`、`validationResultId`、`impactPreviewId` 与当前目标同源。 - -适用操作: - -- `saveMetaSchemaDraft` -- `publishMetaSchemaDraft` -- `activateMetaSchemaVersion` -- `rollbackMetaSchemaVersion` -- `deprecateMetaSchemaVersion` -- `setMetaSchemaGrayRules` -- `previewFunctionChainImpact` -- `activateFunctionChainVersion` - -本阶段确认的合同取舍: - -- `validateMetaSchemaDraft` 和 `previewMetaSchemaDraftImpact` 当前 OpenAPI 无 request body,也无 `commandId`。本阶段不静默改 OpenAPI,按 `schemaKey + draftVersion + actor + draftHash` 作为等价幂等键实现;如果后续产品确认必须补 `commandId`,应单独提交合同纠偏说明和 OpenAPI 变更。 - -## 10. Audit - -Meta 治理审计最小字段: - -| 字段 | 说明 | -|---|---| -| actorUserId | 操作者 | -| side | 固定 admin | -| operationId | OpenAPI operationId | -| commandId | 幂等键 | -| targetType / targetKey / targetVersion | 目标 | -| beforeStatus / afterStatus | 状态变化 | -| beforeActiveVersion / afterActiveVersion | active 变化 | -| validationResultId | 校验引用 | -| impactPreviewId | 影响预览引用 | -| requestSummary | 请求摘要,不能记录敏感全文 | -| resultSummary | 结果摘要 | -| reason | 高危操作原因 | -| status | succeeded / failed | -| errorCode / errorMessage | 失败原因 | - -高危操作必须审计: - -- `publishMetaSchemaDraft` -- `activateMetaSchemaVersion` -- `rollbackMetaSchemaVersion` -- `deprecateMetaSchemaVersion` -- `setMetaSchemaGrayRules` -- `activateFunctionChainVersion` - -## 11. 错误响应 - -对外统一使用 Yudao `CommonResult`: - -- 成功:`code = 0`,`data = <业务 DTO>`,`msg = ""` -- 失败:`code = <业务错误码>`,`data = null`,`msg = <可读错误>` - -禁止返回: - -- `message` 替代 `msg` -- `persisted: true` 替代业务 DTO -- `status: accepted` 替代真实状态 -- operation record 原始 JSON - -Meta 错误码建议: - -| 错误 | 语义 | -|---|---| -| `META_API_VERSION_UNSUPPORTED` | `X-API-Version` 缺失或不支持 | -| `META_COMMAND_ID_REQUIRED` | 写命令缺 commandId | -| `META_COMMAND_ID_CONFLICT` | commandId 重放 hash 冲突 | -| `META_SCHEMA_NOT_FOUND` | schemaKey 不存在 | -| `META_SCHEMA_VERSION_NOT_FOUND` | version 不存在 | -| `META_SCHEMA_VERSION_CONFLICT` | expectedVersion / expectedActiveVersion 冲突 | -| `META_SCHEMA_INVALID_STATE` | 非法状态流转 | -| `META_VALIDATION_RESULT_REQUIRED` | 缺少或引用无效 validationResultId | -| `META_IMPACT_PREVIEW_REQUIRED` | 缺少或引用无效 impactPreviewId | -| `META_PROTECTION_NODE_IMMUTABLE` | 保护节点被降级或替换 | -| `META_EXTERNAL_OWNER_UNAVAILABLE` | 外部 owner 不可用,当前请求不能生成合同可信影响结果 | - -## 12. 外部边界与不可用策略 - -| 边界 | P1R-2 处理方式 | -|---|---| -| Content work / planning / projection | 只读影响 facade;不可用时返回 `META_EXTERNAL_OWNER_UNAVAILABLE`,不写 Content 表 | -| Knowledge projection | 只读影响 facade;不可用时返回 `META_EXTERNAL_OWNER_UNAVAILABLE` | -| AI context / runtime task | 只读影响 facade;不可用时返回 `META_EXTERNAL_OWNER_UNAVAILABLE` | -| Export impact | 只读影响 facade;不可用时返回 `META_EXTERNAL_OWNER_UNAVAILABLE` | -| New-API / SSE | FunctionChain 激活不得直接伪造外部副作用;需要 outbox/job pending 或 `META_EXTERNAL_OWNER_UNAVAILABLE` | -| ContentMetaFacade | P1R-2 可定义 Meta owner 能力,但默认不修改 Content 当前状态 | - -原因:`docs/api-contracts/meta/openapi.yaml` 的 `MetaSchemaImpactPreview` 和 `FunctionChainImpactPreview` 没有 blocked 字段,P1R coverage 当前也没有 blocked 分类路径。本阶段不能通过私加响应字段或手改 coverage 把外部不可用包装成完成证据。 - -## 13. 需要新增/修改的实现单元 - -### 13.1 Controller - -新增: - -- `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminMetaSchemaController.java` -- `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminProtectionNodeController.java` -- `muse-cloud/muse-module-meta/muse-module-meta-server/src/main/java/cn/iocoder/muse/module/meta/controller/admin/AdminFunctionChainController.java` - -修改: - -- `AdminMuseMetaContractController.java`:P1R-2 实现阶段必须删除或收窄 16 个 P1R-2 路径的 wildcard 覆盖,并用测试证明这些路径不会再调用 `MuseContractPersistenceService.handle(...)`。保留时只能覆盖非 P1R-2 路径,不能算完成证据。 - -### 13.2 Service / Domain - -新增: - -- `application/MetaSchemaService.java` -- `application/MetaSchemaServiceImpl.java` -- `application/MetaSchemaValidationService.java` -- `application/MetaSchemaImpactPreviewService.java` -- `application/ProtectionNodeService.java` -- `application/FunctionChainService.java` -- `application/MetaCommandService.java` -- `application/MetaAuditService.java` -- `application/facade/MetaImpactFacade.java` -- `domain/MetaApiVersionGuard.java` -- `domain/MetaVersionStateGuard.java` -- `domain/MetaProtectionNodeGuard.java` -- `domain/FunctionChainGuard.java` - -### 13.3 DTO / Convert - -新增: - -- `controller/admin/vo/*.java` -- `convert/MetaConvert.java` - -### 13.4 DAL / DDL - -新增: - -- `dal/dataobject/*.java` -- `dal/mysql/*.java` -- `muse-cloud/sql/muse/V10__extend_meta_real_api_schema.sql` - -## 14. 验收标准 - -P1R-2 spec + plan review 通过后,进入实现阶段。实现阶段完成必须满足: - -1. Meta 16 个 operation 数量不变。 -2. Meta 16 个 operation 不再是 `generic_persistence`、`catch_all`、`sse_placeholder` 或 `missing`。 -3. Meta operation 未经 P1R-7 真实端到端验收前,不允许标 `completed`。 -4. 专用 Controller、Service、DTO、DAL、DDL 和测试存在。 -5. 所有高危写命令覆盖 commandId replay、requestHash 冲突、expectedVersion 冲突、审计记录。 -6. MetaSchema active 唯一约束有数据库和服务测试证据。 -7. ProtectionNode 不可替换边界有后端测试。 -8. FunctionChain 影响预览和激活不会伪造 New-API/SSE/AI runtime 副作用。 -9. `muse-cloud/scripts/**` 无修改,除非用户单独批准脚本合同纠偏。 -10. Content 51 个 operation 保持 `dedicated / needs_verification`,不被 P1R-2 破坏。 - -## 15. 待确认项 - -1. FunctionChain 的 `New-API,SSE` 外部依赖是否是覆盖脚本粗略推断,还是产品上确实要求 P1R-2 触发外部 runtime。 -2. P1R-2 是否允许修改 Content 的 `ContentMetaFacade` 接入 Meta owner。默认策略是不改 Content,避免破坏 P1R-1 状态。 diff --git a/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md b/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md deleted file mode 100644 index a0d67e57..00000000 --- a/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md +++ /dev/null @@ -1,564 +0,0 @@ -# P1R-3:Account Real API 规格 - -- 版本:v1 -- 日期:2026-05-28 -- 状态:review 已通过;2026-05-29 实现阶段已收口到 `dedicated / needs_verification` -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-member/**` 中 Account 专用实现、Account 相关追加迁移、P1R-3 覆盖门禁 -- 非范围:AI runtime / SSE、Knowledge / RAGFlow、Market 主流程、Events SSE、修改 `muse-cloud/scripts/**` 掩盖缺口、静默修改 OpenAPI、把 Account operation 标为 `completed` - -## 1. 结论 - -P1R-3 的目标不是继续扩展 `MuseContractPersistenceService`,也不是把 Account 的通用持久化响应包装成 OpenAPI DTO。 - -本阶段要把 Account 域从“通用持久化可响应”推进到“真实账户业务 API 可验收”: - -1. Account 33 个 operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。 -2. 仓库没有 `muse-module-account`,当前 Account 合同实际挂在 `muse-module-member`;P1R-3 的 owner 应落在 `muse-module-member` 的 Account 子域,而不是新增孤立模块。 -3. Account owner 只拥有用户资料、权益、配额、用量、New-API 绑定、调用归因读模型、安全事件、个人中心导出的账户事实。 -4. 购买、授权、发布记录只能读取 Market/Content/Knowledge/AI 已产生的事实或投影,不能在 Account 阶段实现 Market 主流程。 -5. New-API binding / recheck / quota request / integration call / call attribution 必须有 commandId 幂等、外部调用记录和失败补偿边界;其中 quota request 与 call attribution 按 OpenAPI 暴露 `correlationId`,binding / recheck 只在服务端 integration call 中关联 `bindingId` / `jobId`,不向响应偷加 OpenAPI 未定义字段。未接真实 New-API runtime 前只能保持 `needs_verification`。 -6. P1R-3 通过后的覆盖状态目标统一为 `dedicated / needs_verification`,不能写成 `completed`。外部 owner 未闭合时返回合同内 `CommonResult` 业务错误或 pending job,不使用 coverage 的 `blocked` 完成状态。 - -## 2. 已验证事实 - -### 2.1 执行工作面 - -用户给定主路径 `/Users/qingse/Sync/local-git/oh-my-muse` 当前不是本阶段真实工作面。只读核验结果显示: - -- `/Users/qingse/Sync/local-git/oh-my-muse` 当前在 `feature/p1-muse-cloud-backend-20260524`,且有未跟踪文件。 -- `dev/1.0.0` 已被 worktree 占用:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`。 -- 写入本规格和执行计划前,真实工作面 `dev/1.0.0` 的 `git status --short --branch` 输出为 `## dev/1.0.0...origin/dev/1.0.0`,无未提交改动。 - -本规格和执行计划落在真实 worktree: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -### 2.2 覆盖矩阵 - -来自 `docs/superpowers/reports/p1r-api-coverage.json`: - -| 指标 | 数量 | -|---|---:| -| 全量 operation | 233 | -| completed | 0 | -| needs_verification | 67 | -| incomplete | 166 | -| generic_persistence | 164 | -| sse_placeholder | 2 | -| missing | 0 | -| blocked | 0 | -| Content `dedicated / needs_verification` | 51 | -| Meta `dedicated / needs_verification` | 16 | -| Account operation | 33 | -| Account `generic_persistence / incomplete` | 33 | -| Account `dedicated / needs_verification` | 0 | - -Account 当前没有 `catch_all`、`sse_placeholder`、`missing` 或 `dedicated`。所有 Account operation 的 `notes` 均为:由 `MuseContractPersistenceService` 通用持久化处理,不是 P1R 完成证据。 - -### 2.3 当前代码事实 - -当前没有 `muse-cloud/muse-module-account/**`。Account 合同入口在 `muse-cloud/muse-module-member/**`: - -- `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/AdminMuseAccountContractController.java` -- `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/AppMuseAccountContractController.java` - -这两个 Controller 使用宽泛路由接管: - -- Admin:`/muse/account/**` -- App:`/muse/me`、`/muse/profile`、`/muse/account/**` - -并统一委托: - -- `MuseContractPersistenceService.handle(...)` - -当前缺失: - -- Account 专用 Controller。 -- Account Application Service。 -- Account OpenAPI 对应 DTO / VO。 -- Account DO / Mapper。 -- Account commandId + requestHash 幂等服务。 -- Account 领域审计与 security event 写入。 -- App user owner guard 的统一后端强制。 -- Admin RBAC 权限点。 -- New-API binding / recheck 的真实外部调用边界。 -- integration call / correlation / call attribution 的专用事实表或投影。 -- balance snapshot、export task、download credential 的专用持久化。 -- purchase / license / publish record 的 Account 视角投影。 - -已有 Yudao 会员能力可复用但不是 OpenAPI 完成路径: - -- `AppMemberUserController` 只覆盖 `/member/user/**`。 -- `MemberUserController` 只覆盖 `/member/user/**` 管理端会员用户。 -- `MemberUserService` / `MemberUserMapper` 可作为用户资料底层能力。 - -### 2.4 当前 DDL - -`muse-cloud/sql/muse/V2__init_account_schema.sql` 已有 Account 基础表: - -- `muse_member_entitlement` -- `muse_member_entitlement_audit_log` -- `muse_member_quota` -- `muse_member_usage_record` -- `muse_member_security_event` -- `muse_member_new_api_binding` - -`muse-cloud/sql/muse/V7__add_contract_operation_audit.sql` 已有通用操作记录: - -- `muse_domain_operation_record` - -`muse-cloud/sql/muse/V8__add_contract_workflow_and_market_interaction.sql` 已有通用工作流和市场购买: - -- `muse_domain_workflow_task` -- `muse_market_purchase` - -`muse-cloud/sql/muse/V6__init_market_schema.sql` 已有 Market 侧可读来源: - -- `muse_market_asset` -- `muse_market_asset_version` -- `muse_market_installation` -- `muse_market_publish_request` -- `muse_market_appeal` -- `muse_market_handoff` - -这些表不足以完成 P1R-3: - -- 没有 Account 专用 command 表;`muse_domain_operation_record` 只有 domain 级 replay,没有 request hash 冲突判断。 -- 没有 balance snapshot 表。 -- 没有 integration call 表。 -- 没有 call attribution job 专用表和 per-call 归因结果。 -- 没有 quota request 专用状态表;当前只落 `muse_domain_workflow_task`。 -- 没有 account export task 表和 download credential 表。 -- `muse_member_security_event` 只有确认字段,缺 acknowledge action / note / riskSummary / append-only audit。 -- `muse_member_entitlement_audit_log` 缺 OpenAPI 所需 before/after 快照完整填充和审计事件关系。 -- purchase/license/publish records 没有 Account 视角读模型或明确 projection owner。 - -## 3. 目标 - -1. 为 33 个 Account operation 定义真实 API 落地边界。 -2. 用专用 Controller / Service / DTO / DAL / DDL 替代通用持久化完成口径。 -3. 建立当前用户、profile、权益、配额、余额、用量、安全事件、导出下载的账户事实模型。 -4. 建立 New-API binding / recheck / quota request / integration call / call attribution 的 correlation 边界。 -5. 建立 purchase / license / publish records 的 Account 视角投影,不接管 Market 主流程。 -6. 建立 commandId 幂等、requestHash 冲突校验、owner guard、admin RBAC、审计和错误响应。 -7. 建立 P1R-3 阶段门禁,防止 Account operation 继续被 `generic_persistence` 或 `completed` 混淆。 - -## 4. 非目标 - -1. 不修改 OpenAPI 来适配当前代码。若发现合同纠偏需求,必须单独列出原因、影响范围和是否属于合同纠偏。 -2. 不实现 AI task 真实 New-API 调用、AI runtime、Agent、Prompt、Quality、Suggestion、Job。 -3. 不实现 AI task SSE stream 或统一 Events SSE。 -4. 不实现 Knowledge / RAGFlow / projection 主流程。 -5. 不实现 Market 购买、安装、handoff、审核、召回、申诉主流程。 -6. 不把 `MuseContractPersistenceService`、`muse_domain_operation_record`、`muse_domain_workflow_task` 原始行、空列表、`status: accepted` 作为完成证据。 -7. 不修改 `muse-cloud/scripts/**` 来掩盖真实缺口。若扫描器无法识别 dedicated 实现,先说明扫描规则合同纠偏原因和影响范围。 -8. 不把前端按钮隐藏或页面权限当作后端权限。 - -## 5. Operation 清单与当前状态 - -| operationId | Method | Path | 当前状态 | 真实 owner | P1R-3 目标 | -|---|---|---|---|---|---| -| `adminListAccountUsers` | GET | `/admin-api/muse/account/users` | `generic_persistence / incomplete` | Account User Query | `dedicated / needs_verification` | -| `adminGetUserEntitlements` | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `generic_persistence / incomplete` | Account Entitlement Query | `dedicated / needs_verification` | -| `adminCreateQuotaAdjustment` | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | Account Quota Command | `dedicated / needs_verification` | -| `adminListQuotaAdjustments` | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | Account Quota Query | `dedicated / needs_verification` | -| `adminListNewApiBindings` | GET | `/admin-api/muse/account/new-api-bindings` | `generic_persistence / incomplete` | Account New-API Query | `dedicated / needs_verification` | -| `adminCreateNewApiBinding` | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `generic_persistence / incomplete` | Account New-API Command | `dedicated / needs_verification` | -| `adminCreateQuotaRequest` | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `generic_persistence / incomplete` | Account New-API Quota Command | `dedicated / needs_verification` | -| `adminGetBalanceSnapshots` | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `generic_persistence / incomplete` | Account Balance Query | `dedicated / needs_verification` | -| `adminCreateCallAttributionJob` | POST | `/admin-api/muse/account/call-attribution-jobs` | `generic_persistence / incomplete` | Account Attribution Command | `dedicated / needs_verification` | -| `adminGetCallAttributionJob` | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `generic_persistence / incomplete` | Account Attribution Query | `dedicated / needs_verification` | -| `adminGetIntegrationCallByCorrelation` | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | Account Integration Query | `dedicated / needs_verification` | -| `adminListUsageRecords` | GET | `/admin-api/muse/account/usage-records` | `generic_persistence / incomplete` | Account Usage Query | `dedicated / needs_verification` | -| `adminListPurchaseRecords` | GET | `/admin-api/muse/account/purchase-records` | `generic_persistence / incomplete` | Account Purchase Projection Query | `dedicated / needs_verification` | -| `getCurrentUser` | GET | `/app-api/muse/me` | `generic_persistence / incomplete` | Account Current User Query | `dedicated / needs_verification` | -| `getProfile` | GET | `/app-api/muse/profile` | `generic_persistence / incomplete` | Account Profile Query | `dedicated / needs_verification` | -| `updateProfile` | PATCH | `/app-api/muse/profile` | `generic_persistence / incomplete` | Account Profile Command | `dedicated / needs_verification` | -| `getAppEntitlements` | GET | `/app-api/muse/account/entitlements` | `generic_persistence / incomplete` | Account Entitlement Query | `dedicated / needs_verification` | -| `getAppUsage` | GET | `/app-api/muse/account/usage` | `generic_persistence / incomplete` | Account Usage Query | `dedicated / needs_verification` | -| `getAppNewApiBinding` | GET | `/app-api/muse/account/new-api-binding` | `generic_persistence / incomplete` | Account New-API Query | `dedicated / needs_verification` | -| `appRecheckNewApiBinding` | POST | `/app-api/muse/account/new-api-binding/recheck` | `generic_persistence / incomplete` | Account New-API Command | `dedicated / needs_verification` | -| `getAppBalanceSnapshots` | GET | `/app-api/muse/account/balance-snapshots` | `generic_persistence / incomplete` | Account Balance Query | `dedicated / needs_verification` | -| `appCreateQuotaRequest` | POST | `/app-api/muse/account/quota-requests` | `generic_persistence / incomplete` | Account Quota Request Command | `dedicated / needs_verification` | -| `appGetQuotaRequest` | GET | `/app-api/muse/account/quota-requests/{requestId}` | `generic_persistence / incomplete` | Account Quota Request Query | `dedicated / needs_verification` | -| `appGetIntegrationCallByCorrelation` | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | Account Integration Query | `dedicated / needs_verification` | -| `appListPurchases` | GET | `/app-api/muse/account/purchases` | `generic_persistence / incomplete` | Account Purchase Projection Query | `dedicated / needs_verification` | -| `appListLicenses` | GET | `/app-api/muse/account/licenses` | `generic_persistence / incomplete` | Account License Projection Query | `dedicated / needs_verification` | -| `appListPublishRecords` | GET | `/app-api/muse/account/publish-records` | `generic_persistence / incomplete` | Account Publish Projection Query | `dedicated / needs_verification` | -| `appListSecurityEvents` | GET | `/app-api/muse/account/security-events` | `generic_persistence / incomplete` | Account Security Query | `dedicated / needs_verification` | -| `appGetSecurityEvent` | GET | `/app-api/muse/account/security-events/{eventId}` | `generic_persistence / incomplete` | Account Security Query | `dedicated / needs_verification` | -| `appAcknowledgeSecurityEvent` | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `generic_persistence / incomplete` | Account Security Command | `dedicated / needs_verification` | -| `appCreateExportTask` | POST | `/app-api/muse/account/export-tasks` | `generic_persistence / incomplete` | Account Export Command | `dedicated / needs_verification` | -| `appGetExportTask` | GET | `/app-api/muse/account/export-tasks/{taskId}` | `generic_persistence / incomplete` | Account Export Query | `dedicated / needs_verification` | -| `appDownloadExport` | GET | `/app-api/muse/account/downloads/{credentialId}` | `generic_persistence / incomplete` | Account Export Download | `dedicated / needs_verification` | - -## 6. 目标架构 - -```mermaid -flowchart TD - A[Admin Account Controllers] --> B[Account Application Services] - C[App Account Controllers] --> B - B --> D[Account Command Service] - B --> E[Account Audit Service] - B --> F[Account Owner Guards] - B --> G[Account Query Assemblers] - B --> H[Account Persistence] - B --> I[External Facades] - H --> J[(member_user)] - H --> K[(muse_member_* tables)] - H --> L[(muse_account_* extension tables)] - I --> M[New-API Client Boundary] - I --> N[Market Projection Boundary] - I --> O[FileService Boundary] - I --> P[Attribution Source Boundary] -``` - -### 6.1 Controller - -推荐拆分: - -| Controller | 职责 | -|---|---| -| `AdminAccountUserController` | 用户摘要、用户权益、配额调整、余额快照 | -| `AdminAccountNewApiController` | New-API binding 列表、创建/刷新、quota request | -| `AdminAccountUsageController` | usage records、integration call、call attribution job | -| `AdminAccountPurchaseController` | purchase records 管理端查询 | -| `AppAccountProfileController` | `/app-api/muse/me`、`/app-api/muse/profile` | -| `AppAccountEntitlementController` | 权益、用量、余额、quota request | -| `AppAccountNewApiController` | binding 查询、recheck、integration call 查询 | -| `AppAccountMarketRecordController` | purchases、licenses、publish records | -| `AppAccountSecurityController` | security events、acknowledge | -| `AppAccountExportController` | export task、download credential | - -Controller 只负责: - -- `@PreAuthorize` 或 app authenticated owner 入口。 -- `X-API-Version: 1` 校验。 -- path/query/body 参数校验。 -- DTO 进入 Application Service。 -- 默认用 `CommonResult` 返回 `code/data/msg`。 -- `appDownloadExport` 是 OpenAPI 明确的例外:成功响应必须返回 `application/octet-stream` 文件流;失败响应仍沿用项目统一错误响应。 - -禁止: - -- Controller 访问 Mapper。 -- Controller 拼接业务 JSON。 -- Controller 调用 `MuseContractPersistenceService` 作为完成路径。 -- Controller 静态返回 OpenAPI 样例。 - -### 6.2 Service - -推荐服务: - -| Service | 职责 | -|---|---| -| `AccountProfileService` | 当前用户、资料查询、资料更新、乐观锁 | -| `AccountEntitlementService` | 权益、配额、余额快照查询与装配 | -| `AccountQuotaService` | 配额调整、quota request 状态机 | -| `AccountNewApiBindingService` | New-API binding、recheck、外部同步边界 | -| `AccountUsageService` | usage records、usage summary、integration call 查询 | -| `AccountAttributionService` | call attribution job 创建、状态、归因校验边界 | -| `AccountMarketRecordService` | purchase/license/publish records Account 投影查询 | -| `AccountSecurityService` | security event 查询、确认、append-only 审计 | -| `AccountExportService` | export task、download credential、FileService 边界 | -| `AccountCommandService` | commandId、requestHash、resultSnapshot 幂等 | -| `AccountAuditService` | Account 领域审计和安全事件写入 | - -### 6.3 DTO / Assembler - -每个 OpenAPI schema 必须对应专用 VO/DTO,不能返回 `Map` 或 DO。 - -最小 DTO 覆盖: - -- `AdminAccountUserSummaryRespVO` -- `AdminUserEntitlementDetailRespVO` -- `QuotaAdjustmentReqVO` -- `QuotaAdjustmentResultRespVO` -- `QuotaAdjustmentLedgerEntryRespVO` -- `NewApiBindingSummaryRespVO` -- `NewApiBindingCreateReqVO` -- `NewApiBindingResultRespVO` -- `AdminQuotaRequestCreateReqVO` -- `AdminQuotaRequestResultRespVO` -- `AppQuotaRequestResultRespVO` -- `QuotaRequestStatusRespVO` -- `AdminBalanceSnapshotEntryRespVO` -- `AppBalanceSnapshotEntryRespVO` -- `CallAttributionJobCreateReqVO` -- `CallAttributionJobResultRespVO` -- `CallAttributionJobDetailRespVO` -- `AdminIntegrationCallDetailRespVO` -- `AppIntegrationCallDetailRespVO` -- `AdminUsageRecordRespVO` -- `AdminPurchaseRecordRespVO` -- `AppCurrentUserSummaryRespVO` -- `AppProfileRespVO` -- `AppProfileUpdateReqVO` -- `AppEntitlementRespVO` -- `AppUsageSummaryRespVO` -- `AppNewApiBindingSummaryRespVO` -- `AppNewApiBindingRecheckReqVO` -- `AppNewApiBindingRecheckRespVO` -- `AppQuotaRequestCreateReqVO` -- `AppPurchaseRecordRespVO` -- `AppLicenseRecordRespVO` -- `AppPublishRecordRespVO` -- `SecurityEventSummaryRespVO` -- `SecurityEventDetailRespVO` -- `SecurityEventAcknowledgeReqVO` -- `SecurityEventAcknowledgeResultRespVO` -- `AccountExportTaskCreateReqVO` -- `AccountExportTaskResultRespVO` -- `AccountExportTaskDetailRespVO` - -Assembler 负责: - -- ID 转字符串。 -- 时间字段统一为 OpenAPI date-time。 -- 手机、邮箱、外部订单、IP、设备信息脱敏。 -- `PageResult` 与 OpenAPI `PaginatedResult.list` 对齐。 -- 不暴露 New-API token、provider authority、模型路由、Prompt/Response 原文、后台权限表。 - -## 7. 持久化设计 - -### 7.1 复用现有表 - -| 表 | P1R-3 用途 | 缺口 | -|---|---|---| -| `member_user` | 当前用户、用户摘要、账号基础状态 | 不直接承载 P1R-3 profile 乐观锁和公开笔名等扩展事实 | -| `muse_account_profile` | profile 扩展、expectedVersion、公开资料 | P1R-3 新增轻量扩展表 | -| `muse_member_entitlement` | plan、权益、额度摘要 | 需要 DTO 装配和来源语义 | -| `muse_member_quota` | quota 明细、剩余和重置周期 | 需要资源类型映射和并发调整 | -| `muse_member_entitlement_audit_log` | quota adjustment ledger | 需要 before/after 快照和 audit event 关联 | -| `muse_member_usage_record` | usage records、app usage summary | 需要 correlation、归因状态、统计聚合 | -| `muse_member_security_event` | security event 查询 | 需要 acknowledge action/note/audit | -| `muse_member_new_api_binding` | New-API binding | 需要 recheck status、失败原因、外部同步时间 | -| `muse_domain_workflow_task` | 可作为过渡读取旧任务 | 不能作为 Account dedicated 完成证据 | -| `muse_market_purchase` | purchase projection 上游来源 | Account 只通过 `muse_account_record_projection` 读取 | -| `muse_market_installation` | license projection 上游来源 | Account 只通过 `muse_account_record_projection` 读取 | -| `muse_market_publish_request` | publish records 上游来源 | Account 只通过 `muse_account_record_projection` 读取 | - -### 7.2 追加迁移 - -P1R-3 实现阶段建议新增 `V11__extend_account_real_api_schema.sql`。最小新增: - -- `muse_account_command` -- `muse_account_audit` -- `muse_account_profile` -- `muse_account_balance_snapshot` -- `muse_account_quota_request` -- `muse_account_integration_call` -- `muse_account_call_attribution_job` -- `muse_account_call_attribution_item` -- `muse_account_export_task` -- `muse_account_download_credential` -- `muse_account_security_event_ack` -- `muse_account_record_projection` - -关键约束: - -- `muse_account_command` 唯一键:`(tenant_id, command_id)`。 -- command 记录必须包含 `operation_id`、`actor_user_id`、`owner_user_id`、`target_type`、`target_id`、`request_hash`、`result_snapshot`。 -- 同一 `commandId` + 不同 `requestHash` 返回 409。 -- `muse_account_audit` 必须覆盖写操作、外部调用、导出下载和高敏安全动作,索引 `account_user_id`、`operation_id`、`correlation_id`。 -- `muse_account_profile` 必须以 `account_user_id` 唯一,包含 `profile_version` 支撑 `expectedVersion`。 -- `muse_account_record_projection` 必须包含 `record_type`,限定为 `purchase`、`license`、`publish`,并保存上游 `source_table` / `source_id` / `source_revision`。 -- app 可见表必须具备 `account_user_id` / `owner_user_id` 索引。 -- `correlation_id` 在 integration call 维度需要唯一或去重键。 -- download credential 必须具备 `credential_hash`、`account_user_id`、`task_id`、`expires_at`、`consumed_at`、`revoked_at`。 - -## 8. Owner Guard 与权限边界 - -### 8.1 App owner guard - -App 入口所有读写必须以 `getLoginUserId()` 作为唯一账户 owner: - -- 不接受请求体中的 `userId` 作为 owner。 -- `requestId`、`eventId`、`taskId`、`credentialId`、`correlationId` 都必须反查 owner。 -- 查不到或 owner 不匹配返回 403 或 404,不能返回跨用户空数据掩盖越权。 -- `appDownloadExport` 必须同时校验 credential owner、过期、撤销、source blocked 和 task 完成状态。 - -### 8.2 Admin 权限边界 - -Admin 入口必须使用后端 RBAC: - -| 操作组 | 建议权限点 | -|---|---| -| 用户摘要/权益/余额/用量/购买记录查询 | `muse:account:query` | -| quota adjustment | `muse:account:quota-adjust` | -| quota request / New-API binding | `muse:account:new-api:manage` | -| call attribution job | `muse:account:attribution:manage` | -| security event 管理端查询若后续加入 | `muse:account:security:query` | - -Admin 也必须校验目标用户存在。Admin 只能管理账户域事实,不能绕过 Market、AI、Knowledge 的 owner 状态机。 - -## 9. commandId 与幂等 - -所有写操作必须通过 `AccountCommandService`: - -| operationId | 幂等目标 | -|---|---| -| `adminCreateQuotaAdjustment` | `quotaAdjustment:userId` | -| `adminCreateNewApiBinding` | `newApiBinding:userId` | -| `adminCreateQuotaRequest` | `quotaRequest:userId` | -| `adminCreateCallAttributionJob` | `callAttribution:correlationId` | -| `updateProfile` | `profile:actorUserId` | -| `appRecheckNewApiBinding` | `newApiBindingRecheck:actorUserId` | -| `appCreateQuotaRequest` | `quotaRequest:actorUserId` | -| `appAcknowledgeSecurityEvent` | `securityEvent:eventId` | -| `appCreateExportTask` | `exportTask:actorUserId` | - -规则: - -1. `commandId` 必填的 operation 必须从请求体读取,不依赖 `X-Command-Id`。 -2. 稳定 JSON 计算 `requestHash`。 -3. 相同 `commandId` + 相同 `requestHash` 返回旧 `resultSnapshot`,并在 DTO 中按合同表达 `idempotent_hit`。 -4. 相同 `commandId` + 不同 `requestHash` 返回 409,错误码为 Account 幂等冲突。 -5. command 预占与业务写入必须在同一事务内。 -6. 异步任务只保证“任务创建幂等”,不保证外部 runtime 已完成。 - -## 10. Audit 与 Security Event - -Account 审计分两层: - -1. `muse_account_command`:所有写命令的幂等和 result snapshot。 -2. `muse_account_audit`:业务可读审计,覆盖操作者、目标用户、operation、前后状态、correlationId、requestHash、风险摘要。 - -最小审计字段: - -- `operationId` -- `actorUserId` -- `accountUserId` -- `side` -- `targetType` -- `targetId` -- `commandId` -- `requestHash` -- `correlationId` -- `beforeSnapshot` -- `afterSnapshot` -- `status` -- `errorCode` -- `errorMessage` -- `createdAt` - -Security event: - -- `appAcknowledgeSecurityEvent` 必须 append-only 记录 acknowledgement,不应覆盖历史处理动作。 -- `action=session_revoked` 不在 P1R-3 实现真实 session 失效;若 session owner 未接入,返回明确业务错误或 pending action,不伪造成功。 -- 账户导出高敏行为应生成 `sensitive_export` security event 或 audit event。 - -## 11. 错误响应 - -除 `appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream` 文件流外,其余接口使用项目现有 `CommonResult` 成功封装。所有错误响应应保持 `code/data/msg` 的项目运行态一致性,不新增 OpenAPI 未定义字段。 - -Account 最小错误码: - -| 场景 | HTTP/业务语义 | 建议业务错误 | -|---|---|---| -| X-API-Version 不支持 | 400 | `ACCOUNT_API_VERSION_UNSUPPORTED` | -| 用户不存在 | 404 | `ACCOUNT_USER_NOT_EXISTS` | -| app owner 不匹配 | 403 | `ACCOUNT_RESOURCE_FORBIDDEN` | -| commandId 缺失 | 400 | `ACCOUNT_COMMAND_ID_REQUIRED` | -| commandId 冲突 | 409 | `ACCOUNT_COMMAND_ID_CONFLICT` | -| expectedVersion 冲突 | 409 | `ACCOUNT_VERSION_CONFLICT` | -| New-API binding 不存在 | 404 | `ACCOUNT_NEW_API_BINDING_NOT_EXISTS` | -| New-API runtime 不可用 | 503 或业务错误 | `ACCOUNT_NEW_API_UNAVAILABLE` | -| correlationId 不存在 | 404 | `ACCOUNT_INTEGRATION_CALL_NOT_EXISTS` | -| 余额/权益不足 | 409 | `ACCOUNT_QUOTA_EXHAUSTED` | -| 下载凭证过期/撤销 | 403 | `ACCOUNT_DOWNLOAD_CREDENTIAL_INVALID` | -| 导出来源 blocked | 403 | `ACCOUNT_EXPORT_SOURCE_BLOCKED` | - -## 12. New-API / Correlation 边界 - -P1R-3 只定义 Account 与 New-API 的账户侧边界: - -- `adminCreateNewApiBinding` / `appRecheckNewApiBinding` 只能创建 binding 或 recheck job。 -- binding / recheck 必须写入 `muse_account_integration_call`,并通过 `bindingId` / `jobId` 与内部 `correlationId` 关联;现有 OpenAPI 响应没有 `correlationId` 字段,P1R-3 不修改响应合同。 -- quota request 和 call attribution 已在 OpenAPI 中暴露 `correlationId`,必须用该值支持 `adminGetIntegrationCallByCorrelation` / `appGetIntegrationCallByCorrelation`。 -- 真实 New-API 用户创建、额度配置和余额查询若 runtime 未接入,必须以合同内业务错误或内部 pending/failed integration call 记录表达;不能为了“可追踪”向响应添加 OpenAPI 未定义字段,也不能返回 OpenAPI 未声明的状态值。 -- `muse_account_integration_call` 是外部调用去重、重试、错误和 attribution 的账户侧事实。 -- `adminGetIntegrationCallByCorrelation` 可看全局调用详情。 -- `appGetIntegrationCallByCorrelation` 只能查看当前用户 owner 的调用。 - -Call attribution: - -- `adminCreateCallAttributionJob` 只能基于已存在的 integration call / New-API callIds。 -- 请求体不能直接指定可信 user/work/agent/asset/license 归因事实。 -- 服务端必须从调用审计记录、业务任务、作品、资产、授权关系校验后写 attribution item。 -- AI runtime 真实调用不在 P1R-3 实现;Account 只消费调用记录。 - -## 13. 数据来源 - -| 数据域 | P1R-3 数据来源 | -|---|---| -| user/profile/me | `member_user` + `muse_account_profile` + entitlement/security projection | -| entitlements | `muse_member_entitlement` | -| quota | `muse_member_quota` + `muse_member_entitlement_audit_log` | -| quota requests | `muse_account_quota_request` + integration call | -| balance snapshots | `muse_account_balance_snapshot` | -| usage records/app usage | `muse_member_usage_record` + attribution fields | -| purchase records | `muse_account_record_projection(record_type=purchase)`,上游来自 `muse_market_purchase` | -| license records | `muse_account_record_projection(record_type=license)`,上游来自 `muse_market_installation` | -| publish records | `muse_account_record_projection(record_type=publish)`,上游来自 `muse_market_publish_request` | -| New-API binding | `muse_member_new_api_binding` + recheck job | -| integration calls | `muse_account_integration_call` | -| call attribution jobs | `muse_account_call_attribution_job` / item | -| security events | `muse_member_security_event` + ack extension | -| export tasks/downloads | `muse_account_export_task` + `muse_account_download_credential` + FileService boundary;下载成功响应为文件流 | - -## 14. 外部阶段边界 - -| 边界 | P1R-3 处理方式 | -|---|---| -| AI task 真实 New-API 调用 | P1R-4;P1R-3 不创建 AI task、不调模型 | -| AI runtime / agent / prompt / quality / suggestions / jobs | P1R-4;Account 只读已归因用量 | -| AI task SSE stream | P1R-4;P1R-3 export/call attribution job 不实现 SSE | -| Knowledge / RAGFlow / projection | P1R-5;Account 只读授权或用量投影 | -| Market | P1R-6;Account 只读购买/授权/发布投影,不处理购买/安装/审核 | -| Events SSE / 端到端验收 | P1R-7;P1R-3 不标 completed | -| FileService | P1R-3 定义导出下载边界;真实存储未接入时保持 needs_verification | - -## 15. 实现单元 - -P1R-3 实现阶段建议按以下单元推进: - -1. Preflight 与覆盖基线冻结。 -2. Account 基础设施与追加 DDL。 -3. DTO / DAL / Mapper 骨架。 -4. App `me` / `profile`。 -5. 权益、配额、余额、用量。 -6. New-API binding、recheck、quota request、integration call。 -7. call attribution job。 -8. purchase / license / publish records 投影。 -9. security events。 -10. export task / download credential。 -11. Account catch-all 路由退役。 -12. P1R-3 覆盖门禁与报告对账。 - -## 16. 验收标准 - -P1R-3 spec + plan review 通过后,进入实现阶段。实现阶段完成必须满足: - -1. Account 33 个 operation 有专用 Controller / Service / DTO。 -2. Account 33 个 operation 不再是 `generic_persistence`、`catch_all`、`sse_placeholder` 或 `missing`。 -3. Account operation 未经 P1R-7 真实端到端验收前,不允许标 `completed`。 -4. Account 全部保持 `dedicated / needs_verification`。 -5. Content 51 个 operation 保持 `dedicated / needs_verification`。 -6. Meta 16 个 operation 保持 `dedicated / needs_verification`。 -7. `completedOperations=0`。 -8. `muse-cloud/scripts/**` 未被用来掩盖缺口。 -9. Account 模块聚焦测试通过。 -10. `P1rAccountMigrationSqlTest` 通过,证明 V11 迁移包含 Account 审计、profile、projection、integration、export/download 等必要约束。 -11. P1R 门禁测试通过。 -12. 若未运行真实 PostgreSQL / Flyway / New-API runtime / FileService,必须在留痕中保留 `needs_verification` 风险。 - -## 17. 待确认项 - -1. P1R-3 是否允许在 `muse-module-member` 内新增 `account` 包作为长期 owner。默认推荐此方案,因为仓库没有 `muse-module-account`,且用户资料属于 member 模块。 -2. New-API runtime 在 P1R-3 是否有可用测试环境。若没有,binding/recheck/quota request 只能做到合同内业务错误或内部 pending/failed 可追踪,保持 `needs_verification`。 -3. FileService 是否有可用测试环境。若没有,export task / download credential 只能验证凭证与状态机,不能验证真实文件流交付。 diff --git a/docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md b/docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md deleted file mode 100644 index 1d0c68e7..00000000 --- a/docs/superpowers/specs/2026-05-30-P1R-4-ai-real-api-design.md +++ /dev/null @@ -1,519 +0,0 @@ -# P1R-4:AI Real API 规格 - -- 版本:v1 -- 日期:2026-05-30 -- 状态:review gate 已通过 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-ai/**` 中 AI 专用实现、AI 相关追加迁移、P1R-4 覆盖门禁、AI task SSE stream -- 非范围:P1R-5 Knowledge / RAGFlow 主流程、P1R-6 Market 主流程、P1R-7 统一 Events SSE、修改 `docs/api-contracts/ai/openapi.yaml`、修改 `muse-cloud/scripts/**` 掩盖缺口、把 AI operation 标为 `completed` - -## 1. 结论 - -P1R-4 的目标不是继续使用 `MuseContractPersistenceService` 保存通用 operation record,也不是把当前 `streamAiTask` 的一次性 `done` 占位事件包装成真实 SSE。 - -本阶段要把 AI 域从“通用持久化可响应”推进到“真实 AI 业务 API 可验收”: - -1. AI 41 个 operation 当前为 40 个 `generic_persistence / incomplete` 和 1 个 `sse_placeholder / incomplete`,必须退出通用持久化和占位 SSE 完成口径。 -2. AI owner 落在 `muse-module-ai`,负责 Prompt、Agent、Tool Grant 消费边界、Quality Policy、AI task、AI job、AI source status、Suggestion、AI task SSE、API access log projection 和 business audit projection。 -3. AI runtime 必须通过服务端生成的 source snapshot 和 runtime permission envelope 执行;客户端只能提交 OpenAPI 声明的意图、引用和 commandId,不能提交正文上下文、权限包或授权快照来绕过后端校验。 -4. New-API runtime 不可用、超时、认证失败、限流、模型错误或返回不可解析时,必须进入真实失败/可重试状态和审计,不能伪造成 `completed`、`done` 或空成功。 -5. `streamAiTask` 属于 P1R-4,但只服务单个 AI task;统一 `/app-api/muse/events` 属于 P1R-7,不在本阶段实现。 -6. Knowledge / RAGFlow、Market、Content、Meta、Account 外部 owner 只通过 facade、读模型或 unavailable 边界消费;P1R-4 不实现 P1R-5 / P1R-6 主流程,也不破坏 P1R-1 / P1R-2 / P1R-3 已有 `dedicated / needs_verification`。 -7. P1R-4 通过后的覆盖状态目标统一为 `dedicated / needs_verification`,不能写成 `completed`。 - -## 2. 已验证事实 - -### 2.1 执行工作面 - -本阶段真实工作面为: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -已执行: - -```bash -git status --short --branch -git pull --ff-only origin dev/1.0.0 -``` - -结果: - -```text -## dev/1.0.0...origin/dev/1.0.0 -Already up to date. -HEAD 7f5553d -``` - -### 2.2 覆盖矩阵 - -来自 `docs/superpowers/reports/p1r-api-coverage.json`: - -| 指标 | 数量 | -|---|---:| -| 全量 operation | 233 | -| completed | 0 | -| needs_verification | 100 | -| incomplete | 133 | -| generic_persistence | 131 | -| sse_placeholder | 2 | -| missing | 0 | -| blocked | 0 | -| Content `dedicated / needs_verification` | 51 | -| Meta `dedicated / needs_verification` | 16 | -| Account `dedicated / needs_verification` | 33 | -| AI operation | 41 | -| AI `generic_persistence / incomplete` | 40 | -| AI `sse_placeholder / incomplete` | 1 | - -P1R-4 基线复核命令: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-4 AI Real API" or .domain == "ai")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出: - -```text -generic_persistence/incomplete 40 -sse_placeholder/incomplete 1 -``` - -### 2.3 当前代码事实 - -当前 AI 合同入口仍在 `muse-module-ai-contract-server`: - -- `muse-cloud/muse-module-ai/muse-module-ai-contract-server/src/main/java/cn/iocoder/muse/module/ai/controller/admin/AdminMuseAiContractController.java` -- `muse-cloud/muse-module-ai/muse-module-ai-contract-server/src/main/java/cn/iocoder/muse/module/ai/controller/app/AppMuseAiContractController.java` - -Admin 合同入口使用宽泛路由覆盖 Prompt、Agent、Tool Grant、AI task、Quality Policy、Evaluation Run、Jobs、Source Events 和 Audit,并统一委托: - -```text -MuseContractPersistenceService.handle(...) -``` - -App 合同入口中除 `streamAiTask` 之外,也统一委托: - -```text -MuseContractPersistenceService.handle(...) -``` - -`streamAiTask` 当前只创建 `SseEmitter`,发送一次 `done` 事件,事件 data 包含固定摘要 `muse ai task stream ready`,随后立即 `complete()`。scanner 因此将其识别为 `sse_placeholder / incomplete`。 - -当前 `muse-module-ai-server` 已有 Yudao AI 基础能力,包括 chat、image、knowledge、mindmap、model、music、workflow、write、Spring AI model factory 和若干 provider test。这些能力不是 P1R-4 合同完成证据,因为它们没有持有 `docs/api-contracts/ai/openapi.yaml` 中 41 个 Muse AI operation 的 dedicated controller、DTO、状态机、覆盖门禁和运行态验收。 - -当前 `muse-cloud/muse-server/pom.xml` 只依赖 `muse-module-ai-contract-server`,注释明确为“不拉起完整 AI Runtime”。这与 P1R-4 真实 AI API 的目标冲突:如果 dedicated Controller 写入 `muse-module-ai-server` 但 `muse-server` 不引入该模块,主服务仍只会加载合同兜底入口,coverage / route ownership gate 无法证明真实入口。 - -### 2.4 当前 DDL - -`muse-cloud/sql/muse/V4__init_ai_schema.sql` 已有 AI 基础表: - -- `muse_ai_generation` -- `muse_ai_suggestion` -- `muse_agent` -- `muse_agent_version` -- `muse_agent_slot_binding` -- `muse_prompt` -- `muse_prompt_version` -- `muse_quality_policy` -- `muse_quality_policy_version` -- `muse_tool_grant` - -`muse-cloud/sql/muse/V7__add_contract_operation_audit.sql` 已有: - -- `muse_domain_operation_record` - -`muse-cloud/sql/muse/V8__add_contract_workflow_and_market_interaction.sql` 已有: - -- `muse_domain_workflow_task` - -这些表不足以完成 P1R-4: - -- 没有 AI 专用 command/requestHash/replay 生命周期。 -- `muse_ai_generation` 可作为 task 事实基础,但缺少 `command_id`、`request_hash`、`job_id`、`correlation_id`、`runtime_permission_envelope_id`、`source_snapshot_id`、New-API 调用状态和 SSE replay 事件关系。 -- 没有 New-API runtime call 记录表,无法证明超时、失败、重试、成本、脱敏摘要和 correlation。 -- 没有 AI job 专用读模型,`muse_domain_workflow_task` 原始行不能直接作为 OpenAPI `JobSummary` / `JobDetail` 完成证据。 -- 没有 AI source event / source status projection 表,无法支撑 source-events 和 source-status API。 -- 没有 AI task SSE event 存储或 replay 序列,当前 SSE 无法断线恢复、无法校验事件序列。 -- 没有 evaluation run 专用状态和样本结果。 -- 没有 Muse API access log / business audit 的合同读模型;Infra `infra_api_access_log` 和 System `operate_log` 只能作为输入来源,不能直接泄露原始 header、token、请求体或响应体。 - -### 2.5 OpenAPI 事实 - -`docs/api-contracts/ai/openapi.yaml` 声明 41 个 operation,核心合同约束包括: - -- 所有 Admin/App 接口使用 `X-API-Version`。 -- 写命令多处声明 `commandId`,scanner 当前只把 schema.required 或 required header 识别为必填;实现阶段仍应按业务写命令强制幂等,不能因为 OpenAPI 未声明 required 就绕过。 -- `createAiTask` 请求只允许提交 `intent`、work/chapter/block 引用、`agentSlotKey` 或 `agentOverrideRef`;服务端生成 `sourceSnapshotId` 和 `runtimePermissionEnvelopeId`。 -- `AiTaskDetail.status` 允许:`queued`、`running`、`streaming`、`completed`、`failed`、`cancelled`。 -- `JobSummary` / `JobDetail` / `UserJobDetail` 状态允许:`queued`、`running`、`completed`、`failed`、`cancelled`。 -- `SourceStatusResult.sourceStatus` 允许:`active`、`stale`、`revoked`、`recalled`、`delisted`、`blocked`、`owner_missing`、`unauthorized`。 -- `SourceStatusResult.actionPolicy` 允许:`allowed`、`read_only`、`blocked`、`needs_recheck`。 -- `streamAiTask` 只能返回 OpenAPI 声明的 SSE event:`chunk`、`quality_check`、`done`、`error`。 -- 未声明的 response status 不得泄露;未声明字段不得加入响应 data。 - -### 2.6 P1R-3 迁移门禁事实 - -P1R-3 已在同一 PostgreSQL 实例的 `muse_local_test` 数据库跑通 Flyway V1-V11: - -```text -flyway_success=true -migrations_executed=11 -target_schema_version=11 -flyway_latest=11:extend account real api schema -v11_tables=12 -``` - -因此 P1R-4 如新增或修改 DDL,阶段收口前必须继续在同实例 `_test` 数据库跑真实 Flyway,不能只靠 SQL grep 或静态测试。 - -## 3. 目标 - -1. 为 AI 41 个 operation 定义真实 API 落地边界。 -2. 用专用 Controller / Application Service / DTO / DAL / DDL / 测试替代通用持久化和占位 SSE。 -3. 建立 Prompt、Quality Policy、Agent、Tool Grant 的配置治理事实和版本规则。 -4. 建立 AI task、AI job、New-API runtime call、Suggestion、SSE event 的状态机。 -5. 建立 source status / source event 在 AI owner 内的消费与投影边界。 -6. 建立 Admin/App job 查询和取消/重试边界,区分 admin 全局治理与 app owner 可见范围。 -7. 建立 API access log / business audit 的脱敏读模型,敏感详情读取本身必须写审计。 -8. 建立 P1R-4 阶段门禁,防止 AI operation 继续被 `generic_persistence`、`sse_placeholder` 或 `completed` 混淆。 - -## 4. 非目标 - -1. 不修改 OpenAPI 来适配当前代码。 -2. 不修改 scanner 来掩盖 AI 仍未 dedicated 的事实。 -3. 不实现 Knowledge / RAGFlow 文档入库、索引、检索、GraphRAG 或知识草稿主流程。 -4. 不实现 Market 购买、安装、handoff、发布、审核、申诉主流程。 -5. 不实现统一 `/app-api/muse/events`。 -6. 不把 `MuseContractPersistenceService`、`muse_domain_operation_record`、`muse_domain_workflow_task` 原始行、空列表、固定样例、占位 `done` SSE 作为完成证据。 -7. 不允许 AI runtime 自授权、绕过 Security facade 或自行放大工具权限。 -8. 不把 New-API unavailable 伪造成 success。 -9. 不把 Content / Meta / Account 已有 `dedicated / needs_verification` 改成 `completed` 或退回非真实状态。 - -## 5. Operation 清单与当前状态 - -| operationId | Method | Path | 当前状态 | 真实 owner | P1R-4 目标 | -|---|---|---|---|---|---| -| `adminListAgents` | GET | `/admin-api/muse/ai/agents` | `generic_persistence / incomplete` | AI Agent Query | `dedicated / needs_verification` | -| `adminCreateAgent` | POST | `/admin-api/muse/ai/agents` | `generic_persistence / incomplete` | AI Agent Command | `dedicated / needs_verification` | -| `adminCreateAgentVersion` | POST | `/admin-api/muse/ai/agents/{agentId}/versions` | `generic_persistence / incomplete` | AI Agent Version Command | `dedicated / needs_verification` | -| `adminStartEvaluationRun` | POST | `/admin-api/muse/ai/evaluation-runs` | `generic_persistence / incomplete` | AI Quality Evaluation Command | `dedicated / needs_verification` | -| `adminGetEvaluationRun` | GET | `/admin-api/muse/ai/evaluation-runs/{runId}` | `generic_persistence / incomplete` | AI Quality Evaluation Query | `dedicated / needs_verification` | -| `adminListPrompts` | GET | `/admin-api/muse/ai/prompts` | `generic_persistence / incomplete` | AI Prompt Query | `dedicated / needs_verification` | -| `adminCreatePromptVersion` | POST | `/admin-api/muse/ai/prompts/{promptKey}/versions` | `generic_persistence / incomplete` | AI Prompt Version Command | `dedicated / needs_verification` | -| `adminActivatePromptVersion` | POST | `/admin-api/muse/ai/prompts/{promptKey}/versions/{version}/activate` | `generic_persistence / incomplete` | AI Prompt Activation Command | `dedicated / needs_verification` | -| `adminListQualityPolicies` | GET | `/admin-api/muse/ai/quality-policies` | `generic_persistence / incomplete` | AI Quality Policy Query | `dedicated / needs_verification` | -| `adminCreateQualityPolicyVersion` | POST | `/admin-api/muse/ai/quality-policies/{policyKey}/versions` | `generic_persistence / incomplete` | AI Quality Policy Version Command | `dedicated / needs_verification` | -| `adminListAiTasks` | GET | `/admin-api/muse/ai/tasks` | `generic_persistence / incomplete` | AI Task Admin Query | `dedicated / needs_verification` | -| `adminListToolGrants` | GET | `/admin-api/muse/ai/tool-grants` | `generic_persistence / incomplete` | AI Tool Grant Query | `dedicated / needs_verification` | -| `adminCreateOrAdjustToolGrant` | POST | `/admin-api/muse/ai/tool-grants` | `generic_persistence / incomplete` | AI Tool Grant Command | `dedicated / needs_verification` | -| `adminListApiAccessLogs` | GET | `/admin-api/muse/audit/api-logs` | `generic_persistence / incomplete` | Muse Audit Query | `dedicated / needs_verification` | -| `adminGetApiAccessLog` | GET | `/admin-api/muse/audit/api-logs/{logId}` | `generic_persistence / incomplete` | Muse Audit Detail Query | `dedicated / needs_verification` | -| `adminListBusinessAuditEvents` | GET | `/admin-api/muse/audit/business-events` | `generic_persistence / incomplete` | Muse Business Audit Query | `dedicated / needs_verification` | -| `adminGetBusinessAuditEvent` | GET | `/admin-api/muse/audit/business-events/{eventId}` | `generic_persistence / incomplete` | Muse Business Audit Detail Query | `dedicated / needs_verification` | -| `adminListJobs` | GET | `/admin-api/muse/jobs` | `generic_persistence / incomplete` | Muse Job Admin Query | `dedicated / needs_verification` | -| `adminGetJob` | GET | `/admin-api/muse/jobs/{jobId}` | `generic_persistence / incomplete` | Muse Job Admin Query | `dedicated / needs_verification` | -| `adminCancelJob` | POST | `/admin-api/muse/jobs/{jobId}/cancel` | `generic_persistence / incomplete` | Muse Job Admin Command | `dedicated / needs_verification` | -| `adminRetryJob` | POST | `/admin-api/muse/jobs/{jobId}/retry` | `generic_persistence / incomplete` | Muse Job Admin Command | `dedicated / needs_verification` | -| `adminListSourceEvents` | GET | `/admin-api/muse/source-events` | `generic_persistence / incomplete` | AI Source Event Query | `dedicated / needs_verification` | -| `adminGetSourceEvent` | GET | `/admin-api/muse/source-events/{eventId}` | `generic_persistence / incomplete` | AI Source Event Detail Query | `dedicated / needs_verification` | -| `adminRetrySourceEvent` | POST | `/admin-api/muse/source-events/{eventId}/retry` | `generic_persistence / incomplete` | AI Source Event Command | `dedicated / needs_verification` | -| `listUserAgents` | GET | `/app-api/muse/agents` | `generic_persistence / incomplete` | AI Agent App Query | `dedicated / needs_verification` | -| `createUserAgent` | POST | `/app-api/muse/agents` | `generic_persistence / incomplete` | AI Agent App Command | `dedicated / needs_verification` | -| `testAgent` | POST | `/app-api/muse/agents/{agentId}/test` | `generic_persistence / incomplete` | AI Agent Runtime Command | `dedicated / needs_verification` | -| `createAgentVersion` | POST | `/app-api/muse/agents/{agentId}/versions` | `generic_persistence / incomplete` | AI Agent Version App Command | `dedicated / needs_verification` | -| `createAiTask` | POST | `/app-api/muse/ai/tasks` | `generic_persistence / incomplete` | AI Task Command | `dedicated / needs_verification` | -| `getAiTask` | GET | `/app-api/muse/ai/tasks/{taskId}` | `generic_persistence / incomplete` | AI Task Query | `dedicated / needs_verification` | -| `streamAiTask` | GET | `/app-api/muse/ai/tasks/{taskId}/stream` | `sse_placeholder / incomplete` | AI Task SSE | `dedicated / needs_verification` | -| `getUserJob` | GET | `/app-api/muse/jobs/{jobId}` | `generic_persistence / incomplete` | Muse Job App Query | `dedicated / needs_verification` | -| `cancelUserJob` | POST | `/app-api/muse/jobs/{jobId}/cancel` | `generic_persistence / incomplete` | Muse Job App Command | `dedicated / needs_verification` | -| `querySourceStatus` | POST | `/app-api/muse/source-status/query` | `generic_persistence / incomplete` | AI Source Status Query | `dedicated / needs_verification` | -| `recheckSourceStatus` | POST | `/app-api/muse/source-status/recheck` | `generic_persistence / incomplete` | AI Source Status Command | `dedicated / needs_verification` | -| `getSuggestion` | GET | `/app-api/muse/suggestions/{suggestionId}` | `generic_persistence / incomplete` | AI Suggestion Query | `dedicated / needs_verification` | -| `rejectSuggestion` | POST | `/app-api/muse/suggestions/{suggestionId}/reject` | `generic_persistence / incomplete` | AI Suggestion Command | `dedicated / needs_verification` | -| `listWorkAgentSlots` | GET | `/app-api/muse/works/{workId}/agent-slots` | `generic_persistence / incomplete` | AI Agent Slot Query | `dedicated / needs_verification` | -| `bindAgentSlot` | POST | `/app-api/muse/works/{workId}/agent-slots/{slotKey}/bind` | `generic_persistence / incomplete` | AI Agent Slot Command | `dedicated / needs_verification` | -| `precheckAgentSlot` | POST | `/app-api/muse/works/{workId}/agent-slots/{slotKey}/prechecks` | `generic_persistence / incomplete` | AI Agent Slot Precheck Command | `dedicated / needs_verification` | -| `listWorkSuggestions` | GET | `/app-api/muse/works/{workId}/suggestions` | `generic_persistence / incomplete` | AI Suggestion Query | `dedicated / needs_verification` | - -## 6. 目标架构 - -```mermaid -flowchart TD - AA[Admin AI Controllers] --> AS[AI Application Services] - AP[App AI Controllers] --> AS - AS --> CMD[AI Command Service] - AS --> AUD[AI Audit Service] - AS --> PERM[Runtime Permission Service] - AS --> TASK[AI Task Orchestrator] - AS --> QRY[AI Query Assemblers] - AS --> JOB[AI Job Service] - AS --> SRC[AI Source Status Service] - AS --> SSE[AI Task SSE Service] - TASK --> RUNTIME[New-API Runtime Adapter] - TASK --> QUAL[Quality Evaluation Service] - TASK --> SUG[Suggestion Service] - SSE --> EVT[(muse_ai_task_event)] - RUNTIME --> CALL[(muse_ai_runtime_call)] - AS --> DB[(muse_ai_* tables)] - AS --> EXT[External Owner Facades] - EXT --> CONTENT[Content Facade] - EXT --> META[Meta Facade] - EXT --> ACCOUNT[Account Usage Facade] - EXT --> SECURITY[Security Tool Grant Facade] - EXT --> KNOWLEDGE[Knowledge Unavailable Boundary] - EXT --> MARKET[Market Unavailable Boundary] -``` - -### 6.1 运行时装配决策 - -P1R-4 推荐路线是显式把 `muse-server` 的 AI 依赖从合同入口推进到完整 AI runtime: - -1. `muse-server/pom.xml` 必须引入 `muse-module-ai-server`。 -2. 旧 `muse-module-ai-contract-server` 只能在过渡期保留非 P1R-4 路由保护网;P1R-4 41 个路由必须由 `muse-module-ai-server` 的 dedicated Controller 持有。 -3. 若实现阶段选择替换依赖,应删除或注释 `muse-module-ai-contract-server` 依赖,避免同一路由被兜底 Controller 抢占。 -4. 若实现阶段必须短期双依赖,`AdminMuseAiContractController` / `AppMuseAiContractController` 必须删除或精确避让 41 个 P1R-4 路由,并由 route ownership test 证明不再调用 `MuseContractPersistenceService`。 - -不推荐把真实 AI runtime 塞进 `muse-module-ai-contract-server`:该模块的现有职责就是避免聚合服务强依赖完整 AI Runtime;继续在 contract-server 内扩展 runtime 会形成职责反转,也会让后续 P1R-5 / P1R-6 难以判断真实 owner。 - -运行时装配的验证要求: - -- `mvn -o test -pl muse-server -am -Dtest=P1rAiRouteOwnershipTest,P1rAiRealApiGateTest -Dsurefire.failIfNoSpecifiedTests=false` 能证明主服务依赖图下 dedicated routes 生效。 -- `git diff` 必须显式显示 `muse-server/pom.xml` 的 AI dependency 变更。 -- 回滚时恢复 `muse-module-ai-contract-server` 依赖,并确保 AI 41 个 operation 回到未完成口径,不能保留半截 dedicated 状态。 - -### 6.2 Controller - -推荐在 `muse-module-ai-server` 下新增 Muse 合同专用 Controller,避免混入 Yudao 原有 AI admin 控制器: - -| Controller | 职责 | -|---|---| -| `AdminMusePromptController` | Prompt 列表、版本创建、激活 | -| `AdminMuseAgentController` | system Agent 列表、创建、版本 | -| `AdminMuseToolGrantController` | Security facade 已批准授权的查询与登记 | -| `AdminMuseQualityController` | Quality Policy、Evaluation Run | -| `AdminMuseAiTaskController` | Admin AI task 列表 | -| `AdminMuseJobController` | Admin job 查询、取消、重试 | -| `AdminMuseSourceEventController` | Source event 查询、详情、重试 | -| `AdminMuseAuditController` | API access log、business audit 查询 | -| `AppMuseAiTaskController` | create/get AI task、task SSE | -| `AppMuseSuggestionController` | suggestion 列表、详情、拒绝 | -| `AppMuseAgentController` | user/system agent 查询、创建、版本、test | -| `AppMuseAgentSlotController` | work agent slot 列表、precheck、bind | -| `AppMuseJobController` | user job 查询、取消 | -| `AppMuseSourceStatusController` | source status 查询、recheck | - -Controller 只负责: - -- Admin/App 认证入口和 RBAC/owner guard。 -- `X-API-Version: 1` 校验。 -- path/query/body 参数校验。 -- OpenAPI VO 到 Application Service 请求转换。 -- `CommonResult` 返回 `code/data/msg`。 -- SSE endpoint 返回 `text/event-stream`,事件类型和 data 字段只能使用 OpenAPI 声明 schema。 - -禁止: - -- Controller 访问 Mapper。 -- Controller 拼接业务 JSON。 -- Controller 调用 `MuseContractPersistenceService` 作为完成路径。 -- Controller 静态返回样例或空列表。 -- Controller 在响应中添加 OpenAPI 未声明字段。 - -### 6.3 Application Service - -推荐服务: - -| Service | 职责 | -|---|---| -| `MuseAiCommandService` | commandId、requestHash、replay、冲突判断 | -| `MuseAiAuditService` | 脱敏业务审计、敏感读取审计、结果摘要 | -| `MusePromptService` | Prompt version、activate、active version 规则 | -| `MuseQualityPolicyService` | Quality policy version、active version、evaluation run | -| `MuseAgentService` | system/user Agent、版本、owner/scope、归档状态 | -| `MuseToolGrantService` | 消费 Security facade 批准结果,禁止 AI 自授权 | -| `MuseAiTaskService` | create/get task、状态机、New-API runtime 编排、Suggestion 生成 | -| `MuseAiTaskStreamService` | SSE event append、replay、订阅、心跳和终态事件 | -| `MuseSuggestionService` | suggestion query/reject、source revision、质量结果 | -| `MuseAgentSlotService` | slot list、precheck、bind、revision 冲突 | -| `MuseJobService` | AI job 创建、查询、取消、重试、admin/app 权限 | -| `MuseSourceStatusService` | source status query、recheck、source event 投影 | -| `MuseApiAccessLogQueryService` | API access log 脱敏查询 | -| `MuseBusinessAuditQueryService` | business audit 脱敏查询 | - -### 6.4 状态机 - -AI task 状态: - -```mermaid -stateDiagram-v2 - [*] --> queued - queued --> running - running --> streaming - streaming --> completed - running --> completed - queued --> cancelled - running --> cancelled - streaming --> cancelled - queued --> failed - running --> failed - streaming --> failed - failed --> queued: retry -``` - -规则: - -- `createAiTask` 首次命令创建 `queued` task 和 job。 -- runtime 开始前必须生成 `sourceSnapshotId` 与 `runtimePermissionEnvelopeId`。 -- New-API 调用开始后必须写 `muse_ai_runtime_call`,并绑定 `correlationId`。 -- 流式输出时 task 可进入 `streaming`;每个 SSE event 必须有递增 sequence。 -- `completed` 必须有真实 runtime result、Suggestion 或 OpenAPI 声明的 outputTarget 结果。 -- 外部 runtime unavailable 只能进入 `failed` 或可重试 `queued`,不能进入 `completed`。 -- `cancelled` 后不能再写 chunk/done;重复 cancel 返回同一终态。 - -Job 状态: - -```mermaid -stateDiagram-v2 - [*] --> queued - queued --> running - running --> completed - running --> failed - queued --> cancelled - running --> cancelled - failed --> queued: retry -``` - -Source event 状态: - -- 来源事件只表达 AI owner 消费来源时的可用性投影。 -- 对 Content / Meta / Account 可调用 dedicated facade 读取 owner 摘要。 -- 对 Knowledge / Market 主流程不可用时,`sourceStatus` 只能使用 OpenAPI 声明的 `owner_missing`、`unauthorized`、`blocked` 等来源状态,`needs_recheck` 只能落在 `actionPolicy`,不得把 `needs_recheck` 写入 `sourceStatus`,也不得伪造 RAGFlow/Market 成功。 - -### 6.5 数据模型 - -P1R-4 推荐新增 `muse-cloud/sql/muse/V12__extend_ai_real_api_schema.sql`,基于 V4 现有 AI 表扩展,避免丢弃已有基础表。 - -新增或扩展的核心事实: - -| 表或变更 | 用途 | -|---|---| -| `muse_ai_command` | AI 写命令幂等、requestHash、replay、冲突 | -| `ALTER TABLE muse_ai_generation ...` | 将现有 generation 扩展为 OpenAPI AI task 事实,补 `command_id`、`request_hash`、`job_id`、`correlation_id`、`source_snapshot_id`、`runtime_permission_envelope_id`、`owner_user_id`、`runtime_status` | -| `muse_ai_runtime_call` | New-API 调用记录、超时、失败分类、重试、token/cost 脱敏摘要、correlation | -| `muse_ai_task_event` | SSE `chunk` / `quality_check` / `done` / `error` 事件序列和 replay | -| `muse_ai_job` | AI job 专用读模型,承载 evaluation、runtime、source recheck、agent test | -| `muse_ai_source_event` | AI 消费来源状态变化事件 | -| `muse_ai_source_status_projection` | `querySourceStatus` 读模型 | -| `muse_ai_suggestion_decision` | suggestion reject 幂等与审计;接受正文属于 Content owner,不在本阶段新增接受接口 | -| `muse_ai_evaluation_run` | Quality policy 离线评估运行 | -| `muse_ai_evaluation_sample_result` | 评估样本结果摘要,不存用户正文全文 | -| `muse_api_access_log_projection` | 面向 OpenAPI 的脱敏接口访问日志读模型 | -| `muse_business_audit_event` | 面向 OpenAPI 的 append-only 业务审计读模型 | - -关键约束: - -- 所有写命令必须唯一约束 `(tenant_id, command_id)` 或在业务表中以 `(tenant_id, operation_id, command_id)` 唯一。 -- task/job/source/status/suggestion/app 可见事实必须索引 `tenant_id`、owner user、workId、status、create_time。 -- runtime call 必须唯一约束 `correlation_id`,并记录 `request_summary` / `response_summary`,不得存 Prompt/Response 全文或 token。 -- SSE event 必须唯一约束 `(tenant_id, task_id, sequence_no)`。 -- Prompt、Quality Policy active version 必须有唯一 active 约束,不能出现同一 key 多个 active。 -- Tool Grant 只能记录 Security facade 批准的结果;AI 不能把 pending/rejected 当 approved。 -- Audit projection 只存脱敏摘要;敏感 detail 查看必须追加 business audit event。 - -### 6.6 外部边界 - -| 边界 | P1R-4 处理方式 | -|---|---| -| New-API | 通过 `MuseAiRuntimeClient` 调用,显式 timeout、失败分类、重试预算、correlation、脱敏日志;不可用时 fail closed | -| Account | 只通过 `AccountAiUsageFacade` 写用量/成本归因摘要或返回 unavailable;不改 Account 已有状态 | -| Content | 只通过 `ContentAiContextFacade` 读取 work/chapter/block 摘要、revision 和 owner;不写正文 | -| Meta | 只读取保护节点/功能链/MetaSchema 摘要;保护节点不可被 slot bind 覆盖 | -| Security | Tool Grant approval 和 runtime permission envelope 的权威;AI runtime 只能消费 | -| Knowledge | P1R-4 不做 RAGFlow 主流程;不可用时 source status 使用合同内阻断状态 | -| Market | P1R-4 不做 Market 主流程;market asset 来源不可用时使用合同内阻断状态 | -| SSE | 只实现 `/app-api/muse/ai/tasks/{taskId}/stream`;统一 Events SSE 留给 P1R-7 | - -### 6.7 New-API runtime 最小契约 - -实现阶段必须先定义稳定的 `MuseAiRuntimeClient` 契约,再接入真实 provider 或 unavailable adapter。最小契约如下: - -| 对象 | 必需字段 | 说明 | -|---|---|---| -| `RuntimeCommand` | `commandId`、`taskId`、`jobId`、`correlationId`、`attemptNo`、`modelKey`、`promptTemplateVersion`、`inputSummary`、`sourceSnapshotId`、`runtimePermissionEnvelopeId`、`timeoutPolicy`、`retryPolicy` | 只允许保存脱敏摘要;正文上下文由服务端在执行时读取和裁剪,不进入长期日志 | -| `RuntimeResult` | `correlationId`、`status`、`outputSummary`、`tokenUsage`、`costCents`、`providerRequestId`、`finishReason`、`completedAt` | `status=success` 时才允许推进 task `completed` 并创建 suggestion / done event | -| `RuntimeFailure` | `correlationId`、`failureType`、`providerStatusCode`、`errorCode`、`sanitizedMessage`、`retryable`、`nextRetryAt`、`failedAt` | 不保存 provider 原始错误体,不保存 token,不保存 Prompt/Response 全文 | - -默认 timeout / retry 规则: - -| 场景 | 默认规则 | -|---|---| -| connect timeout | 5s | -| first byte timeout | 15s | -| non-stream read timeout | 60s | -| stream idle timeout | 30s | -| total task runtime timeout | 180s,超过后 task/job 进入 `failed`,`retryable` 按失败类型判断 | -| retry budget | 最多 3 次,指数退避 1s / 2s / 4s,必须记录每次 `attemptNo` | -| 可重试失败 | network timeout、连接失败、HTTP 408、HTTP 429、HTTP 5xx、provider transient overload | -| 不可重试失败 | 认证失败、权限失败、模型不存在、请求参数错误、内容安全阻断、runtime permission denied | - -调用记录和事务边界: - -1. `createAiTask` 的业务事务只创建 command、task、job、source snapshot 和 runtime permission envelope,不在同一事务中阻塞等待外部 New-API。 -2. job executor 每次调用 New-API 前先写入 `muse_ai_runtime_call(status=requested, attemptNo=...)` 并提交。 -3. 外部调用完成后更新 runtime call 为 `succeeded` 或 `failed`,再推进 task/job/SSE event。 -4. 可重试失败只能通过新的 attempt 追加 runtime call,不能覆盖旧 attempt。 -5. 不可用 adapter 也必须写 `RuntimeFailure(failureType=runtime_unavailable, retryable=false)`,不能返回 `RuntimeResult(success)`。 - -失败到对外状态映射: - -| failureType | task status | job status | SSE event | 备注 | -|---|---|---|---|---| -| `runtime_unavailable` | `failed` | `failed` | `error` | 本地未配置或 New-API 不可访问,不能 fake success | -| `timeout_retryable` | `queued` 或 `failed` | `queued` 或 `failed` | `error` 或无新事件 | 未耗尽 retry budget 时回到 queued;耗尽后 failed | -| `rate_limited` | `queued` 或 `failed` | `queued` 或 `failed` | `error` 或无新事件 | 必须写 `nextRetryAt` | -| `auth_failed` | `failed` | `failed` | `error` | 不可重试 | -| `permission_denied` | `failed` | `failed` | `error` | runtime permission envelope 或 Tool Grant 不允许 | -| `provider_bad_request` | `failed` | `failed` | `error` | 不可重试,错误消息脱敏 | - -## 7. 合同与错误口径 - -1. HTTP status 只能使用 OpenAPI 声明的响应状态;业务错误通过 `CommonResult` 的 `code/data/msg` 表达。 -2. `streamAiTask` 的 SSE error event 可使用 `message` 字段,因为该字段由 OpenAPI `SSEErrorEvent.data.message` 明确声明;其他 JSON API 仍使用 `msg`。 -3. 未声明字段不得加入响应,例如不能给 `createAiTask` 偷加 `runtimeStatus` 或 `providerStatus`。 -4. New-API runtime unavailable 不返回 success;对应 task/job 必须进入 `failed` 或可重试状态,并记录 `errorCode`、脱敏 `errorMessage` 和 `retryable`。 -5. Admin 读接口不得泄露 token、secret、完整 header、完整请求体、完整响应体、Prompt/Response 全文、用户私有正文。 - -## 8. 完成口径 - -P1R-4 spec + plan 阶段完成条件: - -- 本规格、执行计划和 memory 文档已产出。 -- P1R-4 41 个 operation 清单和当前状态已冻结。 -- 两轮 review gate 通过:spec compliance review、quality / feasibility review。 -- 未进入代码实现,未修改 OpenAPI、scanner、Java、SQL。 - -P1R-4 实现阶段的 coverage 目标: - -- AI 41 个 operation 均退出 `generic_persistence` / `sse_placeholder`。 -- AI 41 个 operation 均进入 `dedicated / needs_verification`。 -- Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`。 -- completed operations 仍为 0,除非后续阶段有真实端到端验收证据并单独批准。 - -P1R-4 不能宣称 completed 的情况: - -- New-API 未真实跑通。 -- SSE 只发占位 done。 -- 只创建 accepted job,没有执行器和终态。 -- 只落通用 operation record。 -- 只返回空列表或固定样例。 -- 未跑真实 PostgreSQL / Flyway `_test` 数据库迁移链。 -- 未跑 route ownership 和 coverage gate。 - -## 9. 待确认项 - -1. 实现阶段是否允许新增 `V12__extend_ai_real_api_schema.sql`。本规格推荐新增;若用户不批准 DDL,P1R-4 只能做较弱的 dedicated shell,不能形成可靠 job/runtime/SSE/审计状态机。 -2. New-API runtime 凭据和 base URL 是否已经在本地/远端配置可用。未确认前只能设计 fail-closed adapter,不能承诺真实 runtime completed。 -3. Security facade 是否已有可调用审批结果 API。若没有,Tool Grant command 只能登记已存在的批准投影或返回合同内业务错误,不能由 AI 自批准。 -4. API access log 是否优先投影 Infra `infra_api_access_log`,还是由 Muse 专用 filter 写 `muse_api_access_log_projection`。本规格推荐后者用于 Muse 合同脱敏读模型。 diff --git a/docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md b/docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md deleted file mode 100644 index 34d79044..00000000 --- a/docs/superpowers/specs/2026-06-01-P1R-5-knowledge-ragflow-real-api-design.md +++ /dev/null @@ -1,363 +0,0 @@ -# P1R-5:Knowledge / RAGFlow Real API 规格 - -- 版本:v1 -- 日期:2026-06-01 -- 状态:review gate 待执行 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-knowledge/**` 中 Knowledge 专用实现、Knowledge 相关追加迁移、RAGFlow adapter、P1R-5 覆盖门禁 -- 非范围:P1R-6 Market 主流程、P1R-7 统一 Events SSE、修改 `docs/api-contracts/knowledge/openapi.yaml`、修改 `docs/api-contracts/ai/openapi.yaml`、修改 `muse-cloud/scripts/**` 掩盖缺口、把 Knowledge operation 标为 `completed` - -## 1. 结论 - -P1R-5 的目标不是把 `MuseContractPersistenceService` 的通用持久化响应包装成知识库结果,也不是复用 `muse-module-ai` 里旧 Yudao AI 知识库管理页来抢 Knowledge owner。 - -本阶段要把 Knowledge 域从“合同兜底可响应”推进到“专用 Knowledge API 可验证”: - -1. Knowledge 59 个 operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。 -2. Knowledge owner 落在 `muse-module-knowledge`,负责全局知识库、用户知识库、资料版本、处理任务、RAGFlow 外部映射、实体、关系、图谱、知识草稿、绑定预检、安装知识库读模型、发布源侧材料和来源事件。 -3. RAGFlow 是 P1R-5 的外部知识引擎,负责文档入库、解析、切片、索引、检索和 GraphRAG/知识图谱能力;不可用时必须形成真实失败、可重试任务、source status 或 action policy,不能 fake success。 -4. New-API 负责 LLM 调用、模型路由、token/用量和调用归因;P1R-5 不能把 New-API 当作知识库,也不能把 RAGFlow chat completion 当作 Muse 的主 LLM runtime。 -5. `muse-module-ai` 下既有 `/ai/knowledge/**` 是 Yudao AI 管理入口和 Spring AI VectorStore 参考实现,不属于 `docs/api-contracts/knowledge/openapi.yaml` 的 owner;最多只读参考切片、rerank、向量写入思路。 -6. `Knowledge Draft` 确认必须重新校验 draft revision、source snapshot、authorization snapshot、risk acknowledgement 和 stale/source hash;解析完成不等于 canonical 知识已经正式入库。 -7. P1R-5 通过后的覆盖目标只能是 `dedicated / needs_verification`,没有真实端到端验收证据前不能写成 `completed`。 - -## 2. 已验证事实 - -### 2.1 执行工作面 - -真实工作面: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -已执行同步: - -```bash -git push origin dev/1.0.0 -git pull --ff-only origin dev/1.0.0 -git status --short --branch -git log --oneline -3 -``` - -结果事实: - -```text -## dev/1.0.0...origin/dev/1.0.0 -4f38bf0 feat(p1r): 实现 AI 真实 API dedicated gate -7f5553d docs(p1r): 补记 Account 数据库迁移验收 -7c92a49 docs(p1r): 记录 Account 真实 API 收口 -``` - -上述状态是本阶段文档产出前的同步快照。本规格、计划和 memory 产出期间,工作树会出现三份 P1R-5 文档未跟踪;进入实现 Task 1 前必须重新检查 `git status --short --branch`,要么已提交这些 review 文档,要么只允许这三份 review 文档作为未跟踪项并显式纳入阶段交付。 - -### 2.2 覆盖矩阵 - -P1R-5 preflight 命令: - -```bash -jq -r '[.operations[] | select(.targetStage == "P1R-5 Knowledge / RAGFlow Real API" or .domain == "knowledge")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出: - -```text -generic_persistence/incomplete 59 -``` - -`targetStage` 实际名称为 `P1R-5 Knowledge Real API`,不是 `P1R-5 Knowledge / RAGFlow Real API`。domain fallback 已覆盖全部 59 个 Knowledge operation。 - -全局 summary: - -```text -totalOperations = 233 -completedOperations = 0 -needsVerificationOperations = 141 -incompleteOperations = 92 -genericPersistenceOperations = 91 -ssePlaceholderOperations = 1 -missingOperations = 0 -``` - -P1R-4 当前必须保持的事实: - -```text -AI 41 dedicated / needs_verification -Content 51 dedicated / needs_verification -Meta 16 dedicated / needs_verification -Account 33 dedicated / needs_verification -completedOperations = 0 -唯一剩余 sse_placeholder = events / streamEvents / P1R-7 -``` - -### 2.3 OpenAPI 事实 - -`docs/api-contracts/knowledge/openapi.yaml` 存在,声明 59 个 Knowledge operation: - -| 分组 | 数量 | 主要能力 | -|---|---:|---| -| Admin global KB / policy / document / source | 23 | 全局知识库、资料、版本、授权策略、影响预览、处理任务、来源绑定、治理摘要 | -| App user KB / documents / processing | 18 | 用户知识库、资料版本、导出、重建索引、发布预检、发布快照、Market 交接材料 | -| Installed KB | 4 | 已安装知识库列表、停用、删除、恢复 | -| Entity / Relation / Graph / Local KB | 7 | 作品实体、实体详情、关系、图谱、Local KB | -| Knowledge Draft | 4 | 草稿列表、确认、忽略、重验 | -| Binding | 3 | 绑定预检、绑定、解绑 | - -关键合同约束: - -1. 所有接口使用 `X-API-Version`。 -2. 写命令使用 `commandId` 或等价幂等键。 -3. 上传资料支持 file、entryContent、linkUrl;linkUrl 必须做 SSRF 防护、类型校验和恶意文件扫描接入点。 -4. 处理状态至少覆盖 `pending_scan`、`scan_blocked`、`pending_process`、`processing`、`searchable`、`generative`、`failed`、`deleting`。 -5. 处理任务状态至少覆盖 `queued`、`scanning`、`cleaning`、`parsing`、`indexing`、`completed`、`failed`、`cancelling`。 -6. `confirmKnowledgeDraft` 必填 `commandId`、`expectedDraftRevision`、`sourceSnapshotId`、`authorizationSnapshotId`、`confirmMode`、`decisionType`、`riskAcknowledgement`。 -7. `createKnowledgeBindingPrecheck` 消费来源状态、授权摘要、授权快照和 handoff token;后续 `createKnowledgeBinding` 只能消费 `kbBindPrecheckId`、`commandId`、`expectedWorkRevision`。 -8. `createKBMarketPublishReadiness` / `createKBPublishSnapshot` 是源侧材料,不等于 Market 发布主流程。 - -### 2.4 当前代码事实 - -当前 Knowledge 合同入口: - -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/controller/admin/AdminMuseKnowledgeContractController.java` -- `muse-cloud/muse-module-knowledge/muse-module-knowledge-server/src/main/java/cn/iocoder/muse/module/knowledge/controller/app/AppMuseKnowledgeContractController.java` - -两个 Controller 都统一委托: - -```text -MuseContractPersistenceService.handle(...) -``` - -当前 `muse-module-knowledge-server` 源码只有合同 Controller 和 `package-info.java`,没有 Knowledge Application Service、专用 DTO/VO、DO、Mapper、RAGFlow adapter、状态机或 P1R-5 gate test。 - -`muse-server/pom.xml` 已装配: - -```text -muse-module-knowledge-server -muse-module-ai-server -``` - -因此 P1R-5 实现应继续在 `muse-module-knowledge-server` 内落 owner,不需要把 Knowledge 真实 API 写入 AI 模块。 - -### 2.5 当前 DDL - -`muse-cloud/sql/muse/V5__init_knowledge_schema.sql` 已有初始表: - -- `muse_knowledge_base` -- `muse_knowledge_document` -- `muse_knowledge_document_version` -- `muse_knowledge_entity` -- `muse_knowledge_relation` -- `muse_knowledge_draft` -- `muse_knowledge_binding` - -这些表不足以完成 P1R-5: - -1. 没有 Knowledge 专用 command/requestHash/replay 表。 -2. 没有 RAGFlow dataset/document/chunk/job 外部映射表。 -3. 没有处理任务、扫描任务、索引任务、GraphRAG 任务的可查询状态表。 -4. 没有绑定预检快照、发布快照、发布 readiness、授权策略版本和影响预览的独立事实。 -5. 没有 source event、source binding projection、projection task 的完整读模型。 -6. `muse_knowledge_draft` 有 snapshot 字段,但缺少确认决策、stale/source hash 校验记录和 canonical 写入事件。 -7. 没有 RAGFlow 调用日志、失败分类、重试预算、超时和脱敏 request/response summary。 - -P1R-4 已新增 V12、V13;P1R-5 如新增迁移,推荐使用 `V14__extend_knowledge_ragflow_real_api_schema.sql`。 - -### 2.6 RAGFlow 外部事实 - -只读查看 `/Users/qingse/Sync/local-git/ragflow` 得到: - -1. RAGFlow HTTP API 需要 `Authorization: Bearer `;文档和 smoke 记录只能写“凭据存在/缺失/需配置”,不能写出完整 key。 -2. 公开集成面应走 HTTP reference 的 `/api/v1/**`,例如 dataset、document、chunk、retrieval、GraphRAG 和 knowledge graph;不要把 UI 内部 `/v1/kb`、`/v1/document`、`/v1/chunk` 入口作为 Muse 后端合同。 -3. HTTP reference 声明 dataset API:`POST /api/v1/datasets`、`GET /api/v1/datasets`、`PUT /api/v1/datasets/{dataset_id}`、`DELETE /api/v1/datasets`。 -4. HTTP reference 声明 document API:`POST /api/v1/datasets/{dataset_id}/documents`、`GET /api/v1/datasets/{dataset_id}/documents`、`PUT /api/v1/datasets/{dataset_id}/documents/{document_id}`、`DELETE /api/v1/datasets/{dataset_id}/documents`。 -5. HTTP reference 声明 parse/chunk API:`POST /api/v1/datasets/{dataset_id}/chunks`、`DELETE /api/v1/datasets/{dataset_id}/chunks`、`POST /api/v1/datasets/{dataset_id}/documents/{document_id}/chunks`、`GET /api/v1/datasets/{dataset_id}/documents/{document_id}/chunks`。 -6. HTTP reference 声明 retrieval API:`POST /api/v1/retrieval`。 -7. HTTP reference 声明 GraphRAG API:`POST /api/v1/datasets/{dataset_id}/run_graphrag`、`GET /api/v1/datasets/{dataset_id}/trace_graphrag`、`GET /api/v1/datasets/{dataset_id}/knowledge_graph`、`DELETE /api/v1/datasets/{dataset_id}/knowledge_graph`。 -8. HTTP reference 声明健康检查:`GET /v1/system/healthz`。 -9. RAGFlow 文档明确:检索测试前提是文件已上传并成功解析;启用知识图谱要求知识图谱已构建;GraphRAG 会增加耗时。 -10. 2026-06-01 live health 已验证:`http://100.64.0.8:9380/v1/system/healthz` 返回 200,body 中 `db/doc_engine/redis/storage/status` 均为 `ok`。 -11. 2026-06-01 live health 已验证:`http://100.64.0.8/v1/system/healthz` 返回 200,body 中 `db/doc_engine/redis/storage/status` 均为 `ok`。 -12. 2026-06-01 live 探针显示:`http://100.64.0.11:9380/v1/system/healthz` 在 5 秒内超时,不能作为当前 P1R-5 默认 RAGFlow 地址。 - -未验证事实: - -1. 当前仅验证 health,尚未确认可用于 Muse P1R-5 smoke 的 RAGFlow API key。 -2. 当前未执行 RAGFlow dataset/document/retrieval/GraphRAG smoke。 -3. 当前不能把 RAGFlow 端到端能力写成 completed。 - -### 2.7 New-API 边界事实 - -只读查看 `/Users/qingse/Sync/local-git/new-api` 得到: - -1. `GET /api/status` 是 New-API health 入口。 -2. `GET /v1/models`、`POST /v1/chat/completions` 是 OpenAI-compatible 入口,使用 token auth。 -3. `POST /api/internal/muse/users/sync`、`POST /api/internal/muse/users/:id/disable`、`POST /api/internal/muse/tokens/:id/revoke`、`GET /api/internal/muse/users/:id/status` 使用 `X-Muse-Service-Secret`。 -4. 2026-06-01 live health 已验证:`http://100.64.0.8:3000/api/status` 返回 200 且 `success=true`。 -5. P1R-4 留痕显示 `GET /v1/models` 曾用 Muse token 返回 200;真实 `POST /v1/chat/completions` smoke 曾超时,因此 AI/New-API 不能写 completed。 - -P1R-5 边界: - -| 组件 | 负责 | 不负责 | -|---|---|---| -| Knowledge owner | Muse 知识库事实、权限、绑定、draft、canonical、RAGFlow 映射、状态机 | LLM provider 路由和 token 计费 | -| RAGFlow | dataset/document/parse/chunk/retrieval/GraphRAG/knowledge graph | Muse tenant/owner 权限最终判定、Market 安装授权、New-API 计费 | -| New-API | LLM 调用、模型路由、用量、调用归因 | 知识库存储、RAGFlow 索引、Knowledge Draft 确认 | -| AI module | AI task、Suggestion、source status 消费、New-API runtime | Knowledge API owner、RAGFlow 主流程 | -| Market | 市场资产、授权摘要、handoff token、安装来源 | 目标作品绑定事实、Knowledge canonical 写入 | - -## 3. 目标 - -1. 将 Knowledge 59 个 operation 从 `generic_persistence / incomplete` 推进到 `dedicated / needs_verification`。 -2. 建立 `muse-module-knowledge` 专用 Admin/App Controller、Application Service、DTO/VO、DO、Mapper、Assembler 和 domain guard。 -3. 建立 Knowledge command 幂等、API version guard、owner/tenant guard、source snapshot/authorization snapshot 校验。 -4. 建立用户知识库、全局知识库、已安装知识库、资料版本、处理任务、访问策略和发布材料的真实读写模型。 -5. 建立 RAGFlow adapter,覆盖 dataset、document、parse、retrieval、GraphRAG、health、failure、timeout、retry、correlation 和脱敏日志。 -6. 建立 Knowledge Draft 确认、忽略、重验状态机,确认时真实写入 canonical `muse_knowledge_entity` / `muse_knowledge_relation`。 -7. 建立 source binding、source event、projection task 和 binding precheck,保证 Market handoff 只能作为来源授权摘要,目标事实由 Knowledge owner 写入。 -8. 建立 P1R-5 gate,防止 Knowledge operation 继续被合同兜底、通用持久化或 completed 混淆。 - -## 4. 非目标 - -1. 不修改 Knowledge OpenAPI 来适配当前代码。 -2. 不修改 AI OpenAPI、P1R scanner 或 coverage report 逻辑来掩盖缺口。 -3. 不实现 P1R-6 Market 购买、安装、发布审核、申诉、资产治理主流程。 -4. 不实现 P1R-7 统一 `/app-api/muse/events`。 -5. 不把 RAGFlow chat completion 作为 Muse AI/New-API runtime 的替代。 -6. 不把 `ai_knowledge`、`ai_knowledge_document`、`ai_knowledge_segment` 旧表或旧 Controller 当作 P1R-5 completion 证据。 -7. 不把空列表、operation record、workflow task 原始行、内存 Map、固定样例、跳过 RAGFlow 的假成功作为真实 API 证据。 -8. 不把 `completedOperations` 从 0 改大,除非后续有真实端到端验收证据并单独批准。 - -## 5. 目标架构 - -```mermaid -flowchart TD - AC[Admin Knowledge Controllers] --> AS[Knowledge Application Services] - PC[App Knowledge Controllers] --> AS - AS --> CMD[Knowledge Command Service] - AS --> AUTH[Owner / Tenant / Version Guards] - AS --> KB[Knowledge Base Service] - AS --> DOC[Document Processing Service] - AS --> DRAFT[Knowledge Draft Service] - AS --> BIND[Binding / Install Service] - AS --> QUERY[Query Assemblers] - DOC --> RAG[RAGFlow Adapter] - QUERY --> RAG - RAG --> RD[(RAGFlow Dataset / Document / Chunk / Graph)] - DOC --> TASK[(muse_knowledge_processing_task)] - DRAFT --> CANON[(muse_knowledge_entity / relation)] - BIND --> SNAP[(precheck / auth / publish snapshots)] - AS --> SRC[(source events / projections)] - AS --> LOG[(ragflow call log / audit)] - AS --> EXT[External Owner Facades] - EXT --> MARKET[Market authorization / handoff facade] - EXT --> ACCOUNT[Account entitlement / quota facade] - EXT --> AI[AI source status consumer] - EXT --> NEWAPI[New-API LLM boundary] -``` - -### 5.1 Knowledge owner - -`muse-module-knowledge` 是 P1R-5 唯一 owner。Controller 只做入口转换、鉴权、`X-API-Version` 校验、参数校验、DTO/VO 装配;Application Service 持有事务、幂等、状态机、跨模块 facade、任务创建、RAGFlow adapter 调度和失败归一。 - -### 5.2 RAGFlow adapter - -RAGFlow adapter 的最小合同: - -| 类型 | 输入 | 输出 | 失败语义 | -|---|---|---|---| -| `CreateDatasetCommand` | tenant、kbId、name、embedding/chunk 配置 | ragflowDatasetId | timeout/auth_error/conflict/validation_error | -| `UploadDocumentCommand` | ragflowDatasetId、documentVersionId、已材料化 fileRef | ragflowDocumentId、processingStatus | file_rejected/ssrf_blocked/auth_error/ragflow_unavailable | -| `ParseDocumentsCommand` | ragflowDatasetId、ragflowDocumentIds、museProcessingTaskId、correlationId | parse accepted、museProcessingTaskId | queued/running/failed/cancelled | -| `PollDocumentStatusCommand` | ragflowDatasetId、ragflowDocumentIds、museProcessingTaskId | run/progress/progressMsg 映射后的 processingStatus | not_found/response_schema_changed/unavailable/timeout | -| `RetrieveChunksCommand` | datasetIds、documentIds、question、topK、threshold、metadata filter | chunk refs、scores、doc refs | no_chunk/index_not_ready/auth_error/timeout | -| `RunGraphRagCommand` | datasetId、museProcessingTaskId、correlationId | graph accepted 或 `GRAPH_NOT_SUPPORTED` | no_documents/already_running/not_supported/attribution_not_configured | -| `TraceGraphRagCommand` | datasetId | progress、progressMsg、task status | not_found/unavailable/timeout | -| `GetKnowledgeGraphCommand` | datasetId | nodes、edges、mindMap | empty/not_ready/unavailable | - -Adapter 必须记录 correlation、requestHash、timeout、attempt、failureClass、脱敏 request/response summary。不得记录完整 API key、Authorization、文件正文、用户私有正文、完整 prompt 或 provider 原始错误体。 - -RAGFlow parse 的公开接口成功响应不提供外部 task id;Muse 必须先创建内部 `muse_knowledge_processing_task` 和 correlation,调用 parse 后通过 RAGFlow document list/detail 中的 `run`、`progress`、`progress_msg` 等字段轮询,并归一成 Muse `processingStatus`。不得把不存在的 RAGFlow task id 写入合同。 - -上传入口必须先通过 `KnowledgeFileFacade` 或等价材料化边界把 `file`、`entryContent`、`linkUrl` 统一转成可审计 fileRef,再上传给 RAGFlow multipart file API。`entryContent` 需要生成临时/持久文本文件并记录 hash、size、contentType;`linkUrl` 需要先做 SSRF/DNS rebinding/类型/大小策略检查,再抓取、扫描、存储并转 fileRef。文件服务或对象存储不可用时,文档版本和 processing task 必须落 `failed` 或 `blocked/needs_verification`,不能绕过材料化直接写 RAGFlow 成功。 - -GraphRAG 在 P1R-5 不是无条件必达外部成功。由于 RAGFlow GraphRAG 构图依赖 RAGFlow 默认 chat model,并会产生 LLM/token 成本,只有确认 RAGFlow 默认模型经 New-API 且可完成 Muse 归因、审计和预算控制后,才允许真实 `run_graphrag`。未确认前,P1R-5 只要求实现 `GRAPH_NOT_SUPPORTED` / `ATTRIBUTION_NOT_CONFIGURED` / `needs_verification` 的真实边界、任务记录和图谱读取降级,不得伪造 GraphRAG 构图成功。 - -### 5.3 状态映射 - -Muse 状态不直接泄露 RAGFlow 原始字段,应通过 projection 归一: - -| Muse processingStatus | 语义 | 可能来源 | -|---|---|---| -| `pending_scan` | 已登记,等待扫描 | Muse 文件安全扫描 | -| `scan_blocked` | 扫描或 SSRF/类型策略阻断 | Muse 安全策略 | -| `pending_process` | 可交给 RAGFlow,尚未处理 | Muse task queue | -| `processing` | RAGFlow 正在解析/切片/索引 | RAGFlow parse/chunk task | -| `searchable` | 可检索 | RAGFlow document/chunk ready | -| `generative` | 可用于生成上下文 | Muse 授权策略 + RAGFlow ready | -| `partial_failed` | 部分文档或 chunk 失败 | RAGFlow partial failure | -| `failed` | 处理失败且需人工/重试 | RAGFlow/Muse failure | -| `disabled` | 被 owner 停用 | Muse state | -| `authorization_expired` | 授权过期 | Market/Account facade | -| `delisted` | 来源下架 | Market facade | -| `deleting` | 删除或清理中 | Muse task | - -## 6. 数据模型要求 - -P1R-5 推荐新增 `V14__extend_knowledge_ragflow_real_api_schema.sql`,在 V5 基础上补齐: - -1. `muse_knowledge_command`:幂等、request hash、replay result、operation、actor、target。 -2. `muse_knowledge_processing_task`:材料化、扫描、解析、状态轮询、索引、reindex、GraphRAG、export、recheck 等任务。 -3. `muse_knowledge_ragflow_binding`:Muse KB/document/version 与 RAGFlow dataset/document/chunk/task ID 映射。 -4. `muse_knowledge_ragflow_call`:RAGFlow 调用记录、correlation、attempt、failure class、脱敏摘要。 -5. `muse_knowledge_source_event`:source status/action policy 传播事件。 -6. `muse_knowledge_source_binding_projection`:admin source binding 查询读模型。 -7. `muse_knowledge_projection_task`:投影和索引任务读模型。 -8. `muse_knowledge_access_policy` / `muse_knowledge_access_policy_draft`:全局 KB 授权策略版本。 -9. `muse_knowledge_impact_preview`:影响预览快照。 -10. `muse_knowledge_bind_precheck`:绑定预检快照。 -11. `muse_knowledge_publish_snapshot` / `muse_knowledge_publish_readiness`:源侧发布材料。 -12. `muse_knowledge_draft_decision`:confirm/ignore/recheck 决策和 canonical 写入证据。 - -关键约束: - -1. 所有表包含 tenant 约束和 soft delete 字段。 -2. command 唯一键 `(tenant_id, command_id)`。 -3. RAGFlow dataset 映射唯一键 `(tenant_id, kb_id, active_version)` 或等价 active partial unique index。 -4. document version 映射唯一键 `(tenant_id, document_version_id)`。 -5. task 索引覆盖 `(tenant_id, owner_user_id, status, create_time)`、`(tenant_id, kb_id, status)`。 -6. draft confirm 必须有 expected revision 和 source/authorization snapshot 记录。 -7. materialized fileRef 必须记录 hash、size、contentType、scanStatus、sourceType 和 source summary,不保存未脱敏正文。 -8. 不允许 SQL 中出现明文 token、secret、bearer、api_key 字段名;凭据只通过配置和加密字段处理。 - -## 7. 安全和失败路径 - -1. 后端强制 tenant、owner、admin RBAC、work owner、kb owner、source authorization、expected revision。 -2. 上传 file/link/entry 必须先走材料化和安全扫描接入点;linkUrl 必须防 SSRF、DNS rebinding、环回/私网/链路本地地址和超大文件。 -3. RAGFlow 不可用、认证失败、超时、限流、返回格式变化、索引未就绪、GraphRAG 不支持、GraphRAG 归因未配置、任务已运行,必须进入明确 failureClass。 -4. retry 必须有 retry budget、backoff、幂等 correlation;同一 commandId 不能重复创建外部 dataset/document。 -5. delete/disable/reindex/source event 必须更新绑定和 source projection,不能只改 KB 自身状态。 -6. 敏感详情读取本身必须写审计;审计只存脱敏摘要。 -7. Market handoff token 只能由 precheck 消费并转成服务端快照;bind 请求体不能重新信任 sourceType/sourceVersion/authorizationSnapshot。 - -## 8. 验收门禁 - -P1R-5 dedicated gate 通过条件: - -1. `python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check` 通过。 -2. Knowledge 59 个 operation 全部为 `dedicated / needs_verification`。 -3. `completedOperations = 0`,除非后续单独批准端到端 completed 口径。 -4. AI 41、Content 51、Meta 16、Account 33 保持 `dedicated / needs_verification`。 -5. 唯一剩余 `sse_placeholder` 仍只能是 P1R-7 `events / streamEvents`。 -6. `docs/api-contracts/knowledge/openapi.yaml`、`docs/api-contracts/ai/openapi.yaml` 和 `muse-cloud/scripts/p1r-audit-api-coverage.py` 无业务适配性修改。 -7. 新增/修改 DDL 时,真实 PostgreSQL/Flyway `_test` 数据库跑通 V1-V14 全量迁移。 -8. Maven gate 覆盖 P1R-5 coverage、route ownership、migration SQL、RAGFlow adapter failure mapping、Knowledge service 状态机。 -9. 如缺少 RAGFlow 服务地址/API key,相关外部 smoke 只能标记 blocked/needs_verification,不能写 completed。 - -## 9. 待确认项 - -1. RAGFlow 内网服务地址、API key 来源和最小测试 dataset 是否已准备。 -2. RAGFlow dataset 命名规范:按 tenant/work/kb/version 组合,还是独立外部 ID 映射。 -3. 文件服务/对象存储在 P1R-5 内是否只做接入点,还是必须跑真实上传二进制。 -4. GraphRAG 真实构图 smoke 是否已有经 New-API 的模型路由、token 预算和调用归因;未确认前只实现 not_supported/needs_verification 的真实失败边界。 -5. Market installed KB 的来源授权摘要由 P1R-6 完整实现前,P1R-5 是否只消费 facade/unavailable 边界。 diff --git a/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md b/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md deleted file mode 100644 index e10d6113..00000000 --- a/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md +++ /dev/null @@ -1,365 +0,0 @@ -# P1R-6:Market Real API 规格 - -- 版本:v1 -- 日期:2026-06-03 -- 状态:review gate 待执行 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 范围:`muse-cloud/muse-module-market/**` 中 Market 专用实现、Market 相关追加迁移、授权/安装/handoff/发布/治理/申诉状态机、P1R-6 覆盖门禁 -- 非范围:P1R-7 统一 Events SSE、目标 owner 绑定确认、AI / Knowledge / Content canonical 写入、修改 `docs/api-contracts/market/openapi.yaml`、把 Market operation 标为 `completed` - -## 1. 结论 - -P1R-6 的目标不是继续用 `MuseContractPersistenceService` 记录市场合同请求,也不是把 Market 设计成能直接替目标 owner 写入作品、智能体槽位或知识库绑定事实的“中间缝合层”。 - -本阶段要把 Market 域从“通用持久化可响应”推进到“专用 Market API 可验证”: - -1. Market 32 个 operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。 -2. Market owner 落在 `muse-module-market`,负责市场资产、版本、分类/推荐曝光、收藏、购买授权、安装、来源授权摘要、handoff、发布草稿、发布检查、发布申请、管理端审核、下架、召回和申诉。 -3. Market 只生成来源侧授权摘要和 handoff token。目标 owner 的预检、确认和事实写入仍由 AI、Knowledge 或 Content 自己完成。 -4. 购买只产生授权记录,不等于安装;安装只把资产放入账户可用列表,不等于绑定作品或写目标事实。 -5. 作品资产高阶模式默认关闭。未完成 ADR、Schema/API、lineage 和授权快照前,只允许 `read_only_favorite_license`。 -6. 下架/召回必须形成治理影响预览、状态传播和审计事实,不能只改 `listing_status`。 -7. P1R-6 实现通过后的覆盖目标只能是 `dedicated / needs_verification`。没有 P1R-7 真实跨域端到端验收前,Market 不能写成 `completed`。 - -## 2. 已验证事实 - -### 2.1 执行工作面 - -真实工作面: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -当前分支: - -```text -dev/1.0.0 -``` - -当前 worktree 已存在 P1R-4 / P1R-5 coverage 状态推进相关未提交 diff。进入 P1R-6 实现前,必须先提交或明确保留这些已验证改动,避免新阶段实现和上一阶段 coverage 收口混在不可追踪状态里。 - -### 2.2 覆盖矩阵 - -当前全局 summary: - -```text -completedOperations 100 -needsVerificationOperations 100 -incompleteOperations 33 -genericPersistenceOperations 32 -ssePlaceholderOperations 1 -``` - -按域状态: - -```text -account 33 dedicated / needs_verification -ai 41 dedicated / completed -content 51 dedicated / needs_verification -events 1 sse_placeholder / incomplete -knowledge 59 dedicated / completed -market 32 generic_persistence / incomplete -meta 16 dedicated / needs_verification -``` - -P1R-6 preflight 命令: - -```bash -jq -r '.operations[] | select(.domain == "market") | [.operationId,.method,.path,.targetStage,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json -``` - -输出事实:32 行,全部为 `P1R-6 Market Real API / generic_persistence / incomplete`。 - -### 2.3 OpenAPI 事实 - -`docs/api-contracts/market/openapi.yaml` 声明 32 个 Market operation: - -| 分组 | 数量 | operation | -|---|---:|---| -| 用户端发现 | 4 | `listMarketplaceAssets`、`listMarketplaceCategories`、`listMarketplaceRecommendations`、`getMarketplaceAsset` | -| 收藏 | 2 | `favoriteAsset`、`unfavoriteAsset` | -| 授权与安装 | 2 | `purchaseAsset`、`installMarketplaceAsset` | -| Handoff | 4 | `createBindPrecheck`、`createMarketplaceHandoff`、`getHandoffStatus`、`cancelHandoff` | -| 发布 | 5 | `savePublishDraft`、`runPublishCheck`、`submitPublishRequest`、`withdrawPublishRequest`、`listMyPublishRecords` | -| 用户端治理与申诉 | 4 | `getGovernanceImpact`、`submitAppeal`、`supplementAppeal`、`withdrawAppeal` | -| 管理端资产治理 | 5 | `adminListMarketAssets`、`adminGetMarketAsset`、`adminPreviewGovernanceImpact`、`adminDelistAsset`、`adminRecallAsset` | -| 管理端发布审核 | 3 | `adminListPublishRequests`、`adminApprovePublishRequest`、`adminRejectPublishRequest` | -| 管理端申诉 | 3 | `adminListAppeals`、`adminGetAppeal`、`adminResolveAppeal` | - -关键合同约束: - -1. 所有接口使用 `X-API-Version`。 -2. 写命令使用 `commandId` 并结合 `expectedStatus` 或检查快照防止盲写。 -3. `purchaseAsset` 只获取授权,不安装、不绑定、不写目标 owner。 -4. `installMarketplaceAsset` 只写安装事实,不写作品事实。 -5. `createBindPrecheck` 只生成来源侧授权摘要和 handoff 准备信息,不生成目标 owner 预检 ID。 -6. `createMarketplaceHandoff` 只生成一次性 token 和目标页,不替换绑定、不解绑、不降权、不改作品事实。 -7. `runPublishCheck` 只证明草稿可提交审核,不直接上架。 -8. `submitPublishRequest` 必须原子消费未过期且匹配草稿 hash 的发布检查结果。 -9. `adminDelistAsset` / `adminRecallAsset` 必须绑定治理影响预览,不能盲下架或盲召回。 -10. 申诉处理只能读取申诉材料和发布资产,不读取用户私有副本。 - -### 2.4 当前代码事实 - -当前 Market 合同入口: - -- `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/admin/AdminMuseMarketContractController.java` -- `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/app/AppMuseMarketContractController.java` - -两个 Controller 都统一委托: - -```text -MuseContractPersistenceService.handle(...) -``` - -当前 `muse-module-market-server` 只有合同 Controller 和 `package-info.java`,没有 Market Application Service、专用 VO/DTO、DO、Mapper、状态机、owner facade 或 P1R-6 gate test。 - -`muse-server/pom.xml` 已装配: - -```text -muse-module-market-server -``` - -因此 P1R-6 实现应继续在 `muse-module-market-server` 内落 owner,不需要新增合同模块或把 Market 逻辑写入 Account、AI、Knowledge 或 Content。 - -### 2.5 当前 DDL - -`muse-cloud/sql/muse/V6__init_market_schema.sql` 已有: - -- `muse_market_asset` -- `muse_market_asset_version` -- `muse_market_installation` -- `muse_market_publish_request` -- `muse_market_appeal` -- `muse_market_handoff` - -`muse-cloud/sql/muse/V8__add_contract_workflow_and_market_interaction.sql` 已有: - -- `muse_domain_workflow_task` -- `muse_market_favorite` -- `muse_market_purchase` - -这些表不足以完成 P1R-6: - -1. 没有 Market 专用 command/requestHash/replay 表。 -2. 没有来源侧授权摘要、授权快照、兼容性结果和 handoff 消费状态的独立事实。 -3. 没有发布草稿、发布检查、发布检查快照、发布材料 hash、审核校验结果和审核证据记录。 -4. 没有治理影响预览、治理动作、治理传播、受影响授权/安装/绑定/任务/候选的可查询结果。 -5. 没有申诉补充材料、申诉处理历史、申诉证据脱敏摘要。 -6. 没有分类/推荐/曝光读模型或可降级策略。 -7. 没有 Account 投影同步状态,无法证明购买、安装、授权能被 Account 查询消费。 - -P1R-5 已使用 V14;P1R-6 如新增迁移,推荐使用: - -```text -muse-cloud/sql/muse/V15__extend_market_real_api_schema.sql -``` - -## 3. 目标 - -1. 将 Market 32 个 operation 从 `generic_persistence / incomplete` 推进到 `dedicated / needs_verification`。 -2. 建立 `muse-module-market` 专用 Admin/App Controller、Application Service、VO/DTO、DO、Mapper、Assembler 和 domain guard。 -3. 建立 Market command 幂等、API version guard、tenant/user/owner guard、expectedStatus guard。 -4. 建立资产、版本、分类、推荐、详情和收藏真实读写模型。 -5. 建立购买授权、安装、Account projection 同步状态和授权快照。 -6. 建立来源侧 bind precheck、authorization summary、authorization snapshot 和 handoff token 生命周期。 -7. 建立发布草稿、发布检查、发布申请、撤回和我的发布记录状态机。 -8. 建立管理端审核、下架、召回、治理影响预览和申诉处理状态机。 -9. 建立 owner facades,明确 Account、AI、Knowledge、Content 不可用时的 fail-closed 边界。 -10. 建立 P1R-6 coverage gate,防止 Market operation 继续被合同兜底、通用持久化或 `completed` 混淆。 - -## 4. 非目标 - -1. 不修改 Market OpenAPI 来适配当前代码。 -2. 不修改 scanner 掩盖 Market 仍未 dedicated 的事实。 -3. 不把 Market operation 标为 `completed`。 -4. 不实现 P1R-7 `/app-api/muse/events`。 -5. 不让 Market 直接写 AI agent slot、Knowledge binding、Content work/block/canonical 事实。 -6. 不在 P1R-6 开放作品资产 `full`、`reference`、`context_use` 高阶模式。 -7. 不把 `MuseContractPersistenceService`、空列表、固定样例或 `muse_domain_workflow_task` 原始行作为完成证据。 -8. 不把 Account、AI、Knowledge、Content owner unavailable 伪造成 success。 - -## 5. Operation 清单与目标 owner - -| operationId | Method | Path | 当前状态 | P1R-6 owner | P1R-6 目标 | -|---|---|---|---|---|---| -| `listMarketplaceAssets` | GET | `/app-api/muse/marketplace/assets` | `generic_persistence / incomplete` | Market Asset Query | `dedicated / needs_verification` | -| `listMarketplaceCategories` | GET | `/app-api/muse/marketplace/categories` | `generic_persistence / incomplete` | Market Discovery Query | `dedicated / needs_verification` | -| `listMarketplaceRecommendations` | GET | `/app-api/muse/marketplace/recommendations` | `generic_persistence / incomplete` | Market Discovery Query | `dedicated / needs_verification` | -| `getMarketplaceAsset` | GET | `/app-api/muse/marketplace/assets/{assetId}` | `generic_persistence / incomplete` | Market Asset Query | `dedicated / needs_verification` | -| `favoriteAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `generic_persistence / incomplete` | Market Favorite Command | `dedicated / needs_verification` | -| `unfavoriteAsset` | DELETE | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `generic_persistence / incomplete` | Market Favorite Command | `dedicated / needs_verification` | -| `purchaseAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/purchase` | `generic_persistence / incomplete` | Market License Command | `dedicated / needs_verification` | -| `installMarketplaceAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/install` | `generic_persistence / incomplete` | Market Install Command | `dedicated / needs_verification` | -| `createBindPrecheck` | POST | `/app-api/muse/marketplace/assets/{assetId}/bind-precheck` | `generic_persistence / incomplete` | Market Handoff Source Command | `dedicated / needs_verification` | -| `createMarketplaceHandoff` | POST | `/app-api/muse/marketplace/handoffs` | `generic_persistence / incomplete` | Market Handoff Command | `dedicated / needs_verification` | -| `getHandoffStatus` | GET | `/app-api/muse/marketplace/handoffs/{handoffToken}` | `generic_persistence / incomplete` | Market Handoff Query | `dedicated / needs_verification` | -| `cancelHandoff` | POST | `/app-api/muse/marketplace/handoffs/{handoffToken}/cancel` | `generic_persistence / incomplete` | Market Handoff Command | `dedicated / needs_verification` | -| `savePublishDraft` | POST | `/app-api/muse/marketplace/publish-drafts` | `generic_persistence / incomplete` | Market Publish Draft Command | `dedicated / needs_verification` | -| `runPublishCheck` | POST | `/app-api/muse/marketplace/publish-drafts/{draftId}/checks` | `generic_persistence / incomplete` | Market Publish Check Command | `dedicated / needs_verification` | -| `submitPublishRequest` | POST | `/app-api/muse/marketplace/publish-requests` | `generic_persistence / incomplete` | Market Publish Request Command | `dedicated / needs_verification` | -| `withdrawPublishRequest` | POST | `/app-api/muse/marketplace/publish-requests/{requestId}/withdraw` | `generic_persistence / incomplete` | Market Publish Request Command | `dedicated / needs_verification` | -| `listMyPublishRecords` | GET | `/app-api/muse/marketplace/my-publish-records` | `generic_persistence / incomplete` | Market Publish Query | `dedicated / needs_verification` | -| `getGovernanceImpact` | GET | `/app-api/muse/marketplace/assets/{assetId}/governance-impact` | `generic_persistence / incomplete` | Market Governance Query | `dedicated / needs_verification` | -| `submitAppeal` | POST | `/app-api/muse/marketplace/appeals` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | -| `supplementAppeal` | POST | `/app-api/muse/marketplace/appeals/{appealId}/supplements` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | -| `withdrawAppeal` | POST | `/app-api/muse/marketplace/appeals/{appealId}/withdraw` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | -| `adminListMarketAssets` | GET | `/admin-api/muse/market/assets` | `generic_persistence / incomplete` | Admin Market Asset Query | `dedicated / needs_verification` | -| `adminGetMarketAsset` | GET | `/admin-api/muse/market/assets/{assetId}` | `generic_persistence / incomplete` | Admin Market Asset Query | `dedicated / needs_verification` | -| `adminPreviewGovernanceImpact` | POST | `/admin-api/muse/market/assets/{assetId}/governance-impact` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | -| `adminDelistAsset` | POST | `/admin-api/muse/market/assets/{assetId}/delist` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | -| `adminRecallAsset` | POST | `/admin-api/muse/market/assets/{assetId}/recall` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | -| `adminListPublishRequests` | GET | `/admin-api/muse/market/publish-requests` | `generic_persistence / incomplete` | Admin Publish Review Query | `dedicated / needs_verification` | -| `adminApprovePublishRequest` | POST | `/admin-api/muse/market/publish-requests/{requestId}/approve` | `generic_persistence / incomplete` | Admin Publish Review Command | `dedicated / needs_verification` | -| `adminRejectPublishRequest` | POST | `/admin-api/muse/market/publish-requests/{requestId}/reject` | `generic_persistence / incomplete` | Admin Publish Review Command | `dedicated / needs_verification` | -| `adminListAppeals` | GET | `/admin-api/muse/market/appeals` | `generic_persistence / incomplete` | Admin Appeal Query | `dedicated / needs_verification` | -| `adminGetAppeal` | GET | `/admin-api/muse/market/appeals/{appealId}` | `generic_persistence / incomplete` | Admin Appeal Query | `dedicated / needs_verification` | -| `adminResolveAppeal` | POST | `/admin-api/muse/market/appeals/{appealId}/resolve` | `generic_persistence / incomplete` | Admin Appeal Command | `dedicated / needs_verification` | - -## 6. 目标架构 - -```mermaid -flowchart TD - APP[App Market Controllers] --> SVC[Market Application Services] - ADM[Admin Market Controllers] --> SVC - SVC --> CMD[Market Command Service] - SVC --> ASSET[Asset and Discovery Service] - SVC --> LIC[License and Install Service] - SVC --> HND[Handoff Service] - SVC --> PUB[Publish Service] - SVC --> GOV[Governance Service] - SVC --> APL[Appeal Service] - CMD --> DB[(Market Tables)] - ASSET --> DB - LIC --> DB - HND --> DB - PUB --> DB - GOV --> DB - APL --> DB - LIC --> ACC[Account Projection Facade] - HND --> AI[AI Owner Facade] - HND --> KNO[Knowledge Owner Facade] - HND --> CON[Content Owner Facade] - GOV --> SRC[Source Status Propagation Facades] -``` - -## 7. 关键设计 - -### 7.1 Market 是来源授权 owner,不是目标事实 owner - -Market 只能输出: - -- 授权记录。 -- 安装记录。 -- 来源授权摘要。 -- 授权快照。 -- handoff token。 -- 治理影响和来源状态。 - -Market 不能直接输出: - -- AI agent slot binding。 -- Knowledge binding。 -- Content work/block/canonical 写入。 -- 目标 owner precheck ID。 - -### 7.2 写命令必须幂等且可审计 - -所有写命令至少包含: - -- `commandId` -- `operationId` -- `requestHash` -- `actorUserId` -- `ownerUserId` -- `targetType` -- `targetId` 或 `targetKey` -- `correlationId` -- `status` -- `resultSnapshot` - -相同 `commandId` + 相同 `requestHash` 返回同一业务结果;相同 `commandId` + 不同 `requestHash` 必须冲突。 - -### 7.3 授权、安装和 handoff 不可互相替代 - -`purchaseAsset`: - -- 校验资产可获取、许可可用、治理策略允许新获取。 -- 写 `muse_market_purchase` 和授权快照。 -- 投影给 Account。 -- 不安装、不绑定。 - -`installMarketplaceAsset`: - -- 消费有效授权。 -- 写 `muse_market_installation`。 -- 不写目标 owner。 -- Account 投影采用既有 member owner 契约:Market 实现只写 V11 已有 `muse_account_record_projection` 读模型,并提供 member 侧 `MarketAccountProjectionFacade` 的真实 provider;Market 侧追加表只允许表达投影同步/outbox 状态,不能另建第二套 Account 查询读模型。 - -`createBindPrecheck` / `createMarketplaceHandoff`: - -- 消费有效安装和来源状态。 -- 生成来源侧授权摘要、授权快照和 token。 -- 不创建目标 owner 的预检或绑定事实。 - -### 7.4 发布审核是硬门槛 - -`savePublishDraft` 只保存草稿。 - -`runPublishCheck` 只产出有有效期的检查结果。 - -`submitPublishRequest` 必须消费未过期且匹配草稿版本/材料 hash 的检查结果。 - -`adminApprovePublishRequest` 才能让资产版本进入上架可见状态。 - -### 7.5 治理动作必须有影响预览和传播边界 - -下架和召回前必须生成 `impactPreviewId`,并记录受影响: - -- 授权。 -- 安装。 -- handoff。 -- 目标 owner 绑定摘要。 -- 运行中任务。 -- Shadow 候选。 -- Knowledge Draft。 -- 导出限制。 - -传播给 AI、Knowledge、Content、Account 时只能通过 facade 或 projection;目标 owner 不可用时必须记录 `blocked`,不能伪造成已传播。需要重验时只能写入 action policy / recheck reasons,不能把 `needs_recheck` 写成 Market `SourceStatus`,因为合同内 `SourceStatus` 只表达来源可用性。 - -### 7.6 申诉只能处理治理事实 - -申诉处理读取申诉材料、资产、治理动作和发布申请,不读取用户私有副本正文。补充材料必须脱敏,禁止记录密钥或无关正文全文。 - -## 8. 验收标准 - -P1R-6 实现阶段完成后,必须满足: - -1. Market 32 个 operation 全部为 `dedicated / needs_verification`。 -2. AI 41 和 Knowledge 59 继续保持 `dedicated / completed`。 -3. Content 51、Meta 16、Account 33 继续保持 `dedicated / needs_verification`。 -4. Events 仍只有 P1R-7 `streamEvents` 为 `sse_placeholder / incomplete`。 -5. `completedOperations` 仍为 100,除非后续有真实端到端验收证据并获得单独批准。 -6. `docs/api-contracts/market/openapi.yaml` 无 diff。 -7. `muse-cloud/scripts/p1r-audit-api-coverage.py` 不为 Market 特判 `completed`。 -8. 若新增 V15,必须通过真实 PostgreSQL / Flyway `_test` 迁移验收。 - -## 9. Review gate 待检查项 - -Spec review 应重点检查: - -1. Market 是否没有越权写 AI / Knowledge / Content 目标事实。 -2. 购买、安装、handoff 是否分层清楚。 -3. 发布检查、审核和上架是否没有被压扁成一个开关。 -4. 下架/召回是否包含影响预览、传播和审计。 -5. 申诉是否没有读取私有副本。 - -Quality review 应重点检查: - -1. 是否能按小任务拆实现。 -2. 是否有足够的 gate test 和 focused tests。 -3. 是否避免大而全抽象。 -4. 是否保持 OpenAPI、coverage 和实现三者一致。 -5. 是否明确失败、重试、幂等和回滚边界。 diff --git a/docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md b/docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md deleted file mode 100644 index 7d4cda34..00000000 --- a/docs/superpowers/specs/2026-06-05-P1R-7-events-sse-review.md +++ /dev/null @@ -1,318 +0,0 @@ -# P1R-7a:Events SSE 前置子任务审阅版 - -- 版本:v1 -- 日期:2026-06-05 -- 状态:待审阅 -- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` -- 目标读者:用户 / 主代理 / 架构评审 / 后端执行代理 -- 范围:统一 `/app-api/muse/events` SSE 入口的真实事件流设计、owner 边界、跨 owner 投影入口、回放语义、失败路径和验收口径 -- 非范围:修改 OpenAPI 合同、修改 scanner 掩盖缺口、把 Market 或 Events 标为 completed、重做 AI task 专用 SSE、把本前置子任务等同总 P1R-7 End-to-End Acceptance - -## 1. 结论 - -P1R-7a 是总 P1R-7 End-to-End Acceptance 的前置子任务,不是全量 P1R-7 completed approval。总 P1R-7 仍要求在真实环境证明作品、AI、Knowledge、Market、Account 和治理链路的端到端闭环;本文件只审阅统一 Events SSE 如何退出占位实现。 - -P1R-7a 不应该把 `/app-api/muse/events` 继续做成发送固定 `notification` 后立刻结束的占位连接,也不应该把 AI、Knowledge、Market、Account 各自的事件表在 Controller 里临时拼接。 - -推荐方案是新增独立 Events owner:新增 `muse-module-events-api/server` 或等价的独立 owner 模块,接管统一 `/app-api/muse/events` 路由和 `muse_unified_event` 投影表;各 source owner 只通过 Events API / outbox 契约幂等发布可公开事件,Events 不反向依赖 AI、Knowledge、Market、Account 或 Member server。阶段目标只允许把 `streamEvents` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`,不能标为 `completed`。 - -```mermaid -flowchart TD - Browser["muse-studio fetch SSE client"] --> Controller["Events module controller"] - Controller --> Stream["EventsStreamService"] - Stream --> Cursor["lastEventId cursor parser"] - Stream --> Query["Unified event query"] - Query --> Store[("muse_unified_event")] - AI["AI owner outbox/API"] --> Projector["Events ingest/projector"] - Knowledge["Knowledge owner outbox/API"] --> Projector - Market["Market owner outbox/API"] --> Projector - Account["Account owner outbox/API"] --> Projector - Projector --> Store - Stream --> SSE["SSE id + event + JSON data"] - Stream --> Heartbeat["SSE comment heartbeat"] -``` - -## 2. 已验证事实 - -1. 正确工作区已核实为: - -```text -/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 -``` - -2. `git pull --ff-only origin dev/1.0.0` 输出 `Already up to date`。 -3. 最近提交为: - -```text -3deb0de test(p1r): 收口 Market 真实 API 门禁 -621d45a feat(p1r): 落地 Market 真实 API -f02defd docs(p1r): 补齐 Market 真实 API 规格计划 -``` - -4. coverage summary 当前为: - -```text -completedOperations 100 -needsVerificationOperations 132 -incompleteOperations 1 -genericPersistenceOperations 0 -ssePlaceholderOperations 1 -``` - -5. Events 当前只有 1 个 operation: - -```text -domain: events -operationId: streamEvents -method: GET -path: /app-api/muse/events -targetStage: P1R-7 End-to-End Acceptance -implementationStatus: sse_placeholder -completionStatus: incomplete -controllerFiles: - muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java -``` - -6. `docs/api-contracts/events/openapi.yaml` 已声明 `GET /app-api/muse/events`,支持 `lastEventId`,响应为 `text/event-stream`,事件 schema 只包含 `chunk`、`quality_check`、`done`、`error`、`notification`。 -7. 当前 `AppMuseEventsController` 创建 `SseEmitter` 后发送固定 `notification`,其中 message 为 `muse event stream ready`,随后立即 `complete()`。 -8. scanner 的占位判定包含 `muse event stream ready`、`SseEmitter`、`.name("done")`、`emitter.complete()` 等占位特征;P1R-7a 必须通过真实实现自然退出占位识别,不得修改 scanner 放宽口径。 -9. AI task 专用 SSE 已有 `MuseAiTaskStreamService`,支持持久化事件 replay、SSE id、keepalive、终态 done/error、owner/tenant 可见性校验和错误脱敏;但它只服务 `/app-api/muse/ai/tasks/{taskId}/stream`,不等于统一 `/app-api/muse/events`。 -10. 总 P1R 规格中的 P1R-7 是 End-to-End Acceptance,要求在真实环境证明全量 P1R 业务闭环;本文件只覆盖 Events SSE 前置子任务。 -11. `muse-module-content-server` 当前没有依赖 AI、Knowledge、Market 或 Member server,而 AI、Knowledge 已反向依赖 content server;因此不能让 content server 直接依赖这些 source owner server 来做统一事件聚合。 -12. `docs/api-contracts/events/openapi.yaml` 对 `/app-api/muse/events` 只声明 `200 text/event-stream` 和 `401`,没有声明 `400` 或版本错误响应。 -13. `muse-studio/src/lib/sse.ts` 当前用 `new EventSource(url)` 建立全局事件流,只拼接 `lastEventId` query;标准浏览器 `EventSource` 不能发送 `Authorization` 或 `X-API-Version` 自定义 header。 -14. 在 `docs/memorys`、`docs/superpowers/specs`、`docs/superpowers/plans` 中未找到完整 P1R-7a Events SSE 阶段 spec / plan。 - -## 3. 目标 - -1. 将 `streamEvents` 从 `sse_placeholder / incomplete` 推进到 `dedicated / needs_verification`。 -2. 建立独立统一 Events owner,负责统一事件流入口、事件游标、事件投影读模型、SSE 生命周期、心跳、回放和可观测日志。 -3. 保持各业务事实 owner 不变:AI、Knowledge、Market、Account 仍写自己的领域事实,并通过 Events 发布契约幂等投影可公开事件;Events 不直接扫描 source owner 业务表。 -4. `lastEventId` 必须有明确语义:不传表示从当前连接时刻之后推送;传入表示从该事件之后恢复。 -5. SSE 事件只能发送 OpenAPI 已声明事件类型;没有新事件时只能发送 SSE comment 心跳,不能伪造 `done` 或固定 `notification`。 -6. 所有事件必须按 tenant、ownerUserId 和可见资源过滤,后端强制校验,不能依赖前端过滤。 -7. 失败路径必须可追踪:DB 查询失败、游标非法、后台轮询失败、客户端断开、线程池拒绝、事件 payload 不合法都要有安全日志和明确收敛方式;在不修改 OpenAPI 的前提下,非 401 业务错误进入 SSE 后统一收敛为已声明 `error` event。 -8. 阶段收口必须包含 coverage scanner、focused tests、muse-server P1R gate、reactor build 和必要 Flyway `_test` 证据。 - -## 4. 非目标 - -1. 不修改 `docs/api-contracts/events/openapi.yaml` 适配当前实现;若合同本身要调整,必须单独提出合同变更。 -2. 不修改 `docs/api-contracts/market/openapi.yaml`、`docs/api-contracts/ai/openapi.yaml`、`docs/api-contracts/knowledge/openapi.yaml`。 -3. 不修改 `muse-cloud/scripts/p1r-audit-api-coverage.py` 掩盖占位实现。 -4. 不把 `streamEvents` 标为 `completed`。 -5. 不把 Market 32 个 `dedicated / needs_verification` 标为 `completed`。 -6. 不把 P1R-7a 等同于全量 P1R 完成;真实 HTTP、权限、幂等、失败路径、数据库落库、投影 outbox、审计日志和跨 owner 传播证据仍需要后续 completion approval。 -7. 不在统一 Events Controller 中直接跨模块查多张业务表拼 JSON。 -8. 不让 AI / Knowledge / Market / Account 直接拥有统一 SSE 路由。 - -## 5. 推荐方案 - -### 5.1 Owner 与模块边界 - -推荐新增独立 Events owner,而不是继续把统一事件流落在 Content 模块内。最小可执行形态是: - -1. 新增 `muse-module-events-api`:只暴露 source owner 发布统一事件所需的 facade / DTO 契约。 -2. 新增 `muse-module-events-server`:持有 `/app-api/muse/events`、`muse_unified_event`、游标、SSE 生命周期和投影幂等消费。 -3. 从 `muse-module-content-server` 移走或避让旧 `AppMuseEventsController`,避免同一路由双 owner。 -4. AI、Knowledge、Market、Member/Account 只依赖 `events-api` 或发布 outbox 消息,不允许 `events-server` 反向依赖这些 server。 - -推荐边界: - -| 层级 | 职责 | -|---|---| -| Controller | 校验 `X-API-Version`、登录用户、`lastEventId` 参数,返回 `SseEmitter` | -| Application Service | 建立连接、回放事件、轮询增量、发送心跳、处理断开和超时 | -| Domain / Cursor | 解析和校验 `lastEventId`,维护事件顺序和事件类型约束 | -| DAL / Projection | 读取统一事件表,不直接读取跨域业务事实表 | -| Ingest / Projector | 通过 source owner 发布契约幂等写入统一事件投影 | - -### 5.2 跨 owner 发布契约 - -Events owner 必须提供完整入口,避免 `muse_unified_event` 变成孤儿表: - -```text -EventsPublishFacade.publish(EventsPublishCommand) -``` - -命令字段建议: - -```text -commandId -tenantId -ownerUserId -sourceOwner -sourceType -sourceId -sourceRevision -eventType -resourceType -resourceId -payloadSummary -emittedAt -``` - -发布规则: - -1. `commandId` 或 `(sourceOwner, sourceType, sourceId, sourceRevision, eventType)` 必须幂等。 -2. Events owner 只校验统一事件合同、可见性摘要和 payload 脱敏,不回写 source owner 业务事实。 -3. source owner 失败或未接入时,Events stream 不伪造事件;执行版应把未接入 source 标成 `needs_verification` 风险,不得据此标 completed。 -4. 重复发布返回同一 `eventId` / `sequenceNo`,不得重复推送。 -5. payload 不符合 OpenAPI event schema 时,记录 rejected 状态或丢弃,并输出安全日志;不能把非法字段透给浏览器。 -6. source owner 可同步调用 facade,也可先写本域 outbox 再由 worker 调用 facade;执行版必须二选一并写清 retry / dead-letter / replay 策略。 - -### 5.3 数据模型 - -推荐新增统一事件投影表,而不是让 SSE 入口直接扫描多域事件表: - -```text -muse_unified_event -- id -- tenant_id -- event_id -- sequence_no -- event_type -- owner_user_id -- source_owner -- source_type -- source_id -- resource_type -- resource_id -- payload_summary -- publish_status -- publish_error_code -- visible_from -- emitted_at -- create_time / update_time / deleted -``` - -关键约束: - -1. `(tenant_id, sequence_no)` 唯一,作为统一 `lastEventId` 的恢复顺序。 -2. `(tenant_id, event_id)` 唯一,防止 projector 重复写入。 -3. 查询索引覆盖 `(tenant_id, owner_user_id, sequence_no)`。 -4. `payload_summary` 必须只保存 OpenAPI 允许透出的字段摘要,不能保存 provider raw body、token、授权头或私有资源全文。 -5. `publish_status` 至少区分 `accepted`、`rejected`、`blocked`,便于排查 source owner 发布失败,不把失败吞成静默成功。 - -### 5.4 事件类型映射 - -统一 Events 只能发送 OpenAPI 已声明事件: - -| OpenAPI event | 来源建议 | 说明 | -|---|---|---| -| `chunk` | AI task event | 可回放 AI task 文本片段;必须 owner 可见 | -| `quality_check` | AI task event | 可回放 AI 质量检查结果;必须 owner 可见 | -| `done` | AI task terminal event | 只表达 AI task 终态;不得用于系统通知 | -| `error` | AI task terminal error | 错误消息必须脱敏 | -| `notification` | AI / Knowledge / Market / Account source event | 用于 source status、knowledge projection、governance action、quota alert | - -如果某个业务事件无法映射到上述 schema,就不能在 P1R-7a 中发送;应先保留为内部领域事件,后续单独做合同变更。 - -### 5.5 `lastEventId` 语义 - -推荐采用统一 `sequence_no` 作为 SSE id: - -```text -event id = muse: -``` - -规则: - -1. `lastEventId` 为空:从当前连接建立时间之后的新事件开始,先发心跳,不 replay 历史事件。 -2. `lastEventId=muse:123`:只 replay `sequence_no > 123` 且当前用户可见的事件。 -3. `lastEventId` 格式非法:在不修改 OpenAPI 的前提下,建立 SSE 后发送已声明的 `error` event 并结束连接;如果未来要连接前返回 400,必须先单独变更 Events OpenAPI。 -4. 指向不存在或已归档的旧序号:从可查询窗口内第一条大于该序号的事件恢复,并记录 warning 日志。 - -### 5.6 鉴权与前端连接 - -当前 `muse-studio` 使用标准 `EventSource`,无法发送 `Authorization` 和 `X-API-Version` header。P1R-7a 推荐执行版改为 fetch 流式 SSE 客户端: - -```text -fetch("/app-api/muse/events?lastEventId=...", { - headers: { - "Authorization": "Bearer ", - "X-API-Version": "1" - } -}) -``` - -取舍: - -1. 不推荐 query token;URL 会进入日志、浏览器历史和代理链路,除非另起安全设计并做严格脱敏。 -2. 如果继续用 cookie/session,必须证明 app-api 当前认证链路支持浏览器自动带 cookie,且 CSRF / sameSite 边界清楚。 -3. 执行版必须把 `muse-studio/src/lib/sse.ts` 的 EventSource 适配列为前置验证项;否则后端 header 校验与前端能力冲突。 - -### 5.7 失败路径 - -| 场景 | 处理方式 | -|---|---| -| 未登录 / token 无效 | 进入 SSE 前由安全框架返回 401 | -| API version 不支持 | 建立 SSE 后发送 `error` event 并结束连接;若要返回 HTTP 400,先变更 OpenAPI | -| `lastEventId` 非法 | 建立 SSE 后发送 `error` event 并结束连接;若要返回 HTTP 400,先变更 OpenAPI | -| 无可见事件 | 只发送 SSE comment heartbeat | -| 客户端断开 | 取消后台轮询,记录连接关闭日志 | -| 线程池拒绝 | fail-closed,结束连接并记录 warning | -| DB 查询失败 | 结束连接,记录 task-less events stream 错误类型 | -| payload 含敏感字段 | 丢弃敏感字段或丢弃事件,记录脱敏日志 | - -## 6. 关键取舍 - -1. **统一事件投影优先于 Controller 拼接。** 这样能明确 owner 边界、权限过滤和 replay 顺序,避免临时缝合。 -2. **先 `dedicated / needs_verification`,不追求 `completed`。** 当前缺真实浏览器 fetch SSE 验证、跨 owner outbox 传播和线上级验收证据,不能宣称 completed。 -3. **先定义发布契约,再消费已有事件事实。** AI task event、AI source event、Knowledge source event、Market source/governance event、Account security event 已有不同程度的事实表;P1R-7a 应先建立 source owner 发布契约、统一投影和可见性规则,不重做各域状态机,也不让 Events 反向依赖 source owner server。 -4. **不扩 OpenAPI event 类型。** 如果当前合同事件类型不够表达全部业务事件,先做最小真实映射;合同扩展另起任务。 -5. **不改 scanner。** scanner 当前正确暴露了占位实现;P1R-7a 的职责是让代码满足真实标准。 -6. **fetch SSE 优先于 EventSource。** 当前合同要求 `X-API-Version` 和 app bearer auth;标准 `EventSource` 无法发送自定义 header,继续使用会造成前后端契约断裂。 - -## 7. 影响范围 - -预期实现会触及: - -```text -muse-cloud/muse-module-events/** -muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/controller/app/AppMuseEventsController.java -muse-cloud/sql/muse/V16__extend_events_sse_schema.sql -muse-cloud/muse-module-events/muse-module-events-server/src/test/java/** -muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/** -muse-studio/src/lib/sse.ts -docs/superpowers/reports/p1r-api-coverage.json -docs/superpowers/reports/p1r-api-coverage.md -``` - -是否采用 `V16__extend_events_sse_schema.sql` 需要执行版计划确认当前 Flyway 版本序列;截至本审阅版,V15 已用于 Market。 - -## 8. 风险与兼容性 - -1. **模块引入风险:** 新增 Events 模块会增加 Maven 装配面;执行版必须验证 reactor 依赖树,避免 content / ai / knowledge 循环依赖。 -2. **合同表达不足:** OpenAPI 的 `notification.type` 只有四类,无法表达所有领域事件;不得为了覆盖更多事件而偷改合同。 -3. **事件顺序风险:** 多来源事件必须进入统一 sequence,否则 `lastEventId` 无法可靠恢复。 -4. **权限泄露风险:** 统一事件流比单接口更容易误推跨用户事件,必须以后端 ownerUserId / tenant 过滤为硬门槛。 -5. **资源占用风险:** SSE 长连接必须有超时、心跳、线程池和断开清理,不能阻塞请求线程。 -6. **完成口径风险:** coverage gate PASS 只代表退出占位实现,不代表真实端到端 completed。 -7. **前端连接风险:** 从 `EventSource` 切到 fetch 流式解析会改变客户端实现;必须保留事件分发语义、断线重连和关闭能力。 - -## 9. 验收标准 - -审阅版通过后,执行版计划至少要覆盖以下验收: - -1. coverage 中 Events 只有 `streamEvents`,状态从 `sse_placeholder / incomplete` 变为 `dedicated / needs_verification`。 -2. scanner 仍不修改,`ssePlaceholderOperations` 从 1 变为 0。 -3. `/app-api/muse/events` 不再发送 `muse event stream ready` 固定占位消息。 -4. 无事件时只发送 SSE comment heartbeat,不发送假业务 event。 -5. 有可见历史事件时,`lastEventId` 能按统一 sequence replay。 -6. 跨 tenant / 跨 owner 事件不会被 replay 或推送。 -7. payload 不泄露 token、authorization、provider raw body 或私有资源全文。 -8. 客户端断开、超时、线程池拒绝、DB 异常都有测试或可观测日志。 -9. source owner 发布契约有幂等、重复投影、payload rejected、source unavailable 测试或门禁。 -10. `muse-studio` 全局事件流能发送 app bearer auth 和 `X-API-Version`,并保留 `lastEventId` 重连。 -11. P1R gate、focused Events tests、muse-server build 和 Flyway `_test` 验证通过。 -12. `docs/api-contracts/**/openapi.yaml` 和 `muse-cloud/scripts/p1r-audit-api-coverage.py` 未被修改。 - -## 10. 待确认项 - -1. 是否接受新增 `muse-module-events-api/server` 作为独立 Events owner,并从 content 旧 controller 迁出统一 SSE 路由? -2. 是否批准新增 `V16__extend_events_sse_schema.sql` 作为统一事件投影表? -3. P1R-7a 的目标是否限定为 `streamEvents dedicated / needs_verification`,不包含总 P1R-7 End-to-End Acceptance completed approval? -4. `lastEventId` 是否采用 `muse:`,还是必须兼容纯数字游标? -5. 首批 source owner 发布契约是否只覆盖 AI task event + source/knowledge/market/account notification 的最小集合,避免一次性扩大到所有业务事件? -6. 前端全局事件流是否接受从 `EventSource` 切到 fetch 流式 SSE,以满足 bearer auth 和 `X-API-Version`? diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000..89313554 --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +java = "21" diff --git a/muse-cloud/.github/workflows/maven.yml b/muse-cloud/.github/workflows/maven.yml index 7c765927..5d3c1315 100644 --- a/muse-cloud/.github/workflows/maven.yml +++ b/muse-cloud/.github/workflows/maven.yml @@ -6,8 +6,8 @@ name: Java CI with Maven on: push: branches: [ master ] - # pull_request: - # branches: [ master ] + pull_request: + branches: [ master ] jobs: build: @@ -16,15 +16,17 @@ jobs: strategy: matrix: - java: [ '8', '11', '17' ] + java: [ '21' ] steps: - uses: actions/checkout@v2 - - name: Set up JDK ${{ matrix.Java }} + - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v2 with: java-version: ${{ matrix.java }} distribution: 'temurin' cache: maven + # P0 止血(2026-06-13):移除 -Dmaven.test.skip=true,让 CI 真正编译并运行测试。 + # 此前跳过全部测试 = 假绿;单测走 surefire(yudao H2/内嵌 Redis 基类,无需外部 DB)。 - name: Build with Maven - run: mvn -B package --file pom.xml -Dmaven.test.skip=true + run: mvn -B package --file pom.xml diff --git a/muse-cloud/muse-module-ai/.agent b/muse-cloud/muse-module-ai/.agent new file mode 100644 index 00000000..e95c2cd7 --- /dev/null +++ b/muse-cloud/muse-module-ai/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-ai(AI 编排 BC) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §5.1](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:链路编排 / 上下文组装 / 生成 / 分析 / 全书解析 / 质量门控 / AI 候选(Suggestion)/ 规划候选 / 风险标记 / Quality Result / 运行记录;承载 Protection Node(grant)与 Quality Policy。 +- **边界(不可违反)**:**只产 Shadow,绝不直写 Canonical**;只消费服务端签发的 Runtime Permission Envelope,不能自授权;grant 与 runtime **包级隔离**;守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md);历史违例 `ContentMuseWorkOwnerFacade` **已整改消除**——改消费 content `MuseContentWorkOwnerApi`,门禁已收紧、无豁免)。 +- **out-of-scope**:Accept Suggestion(归 content BC)、正文写入。 +- **现状**:只读评估 72%;后端真实较高质量(无 stub),`RealNewApiMuseAiRuntimeClient` 真打 New-API,SSE 已脱占位。 +- **关键风险 / TODO**:前端全 MSW;SSE 疑漂移;真实 New-API 未端到端验收;`muse-module-ai-contract-server` 空壳孤儿 submodule(待清理/合并)。 diff --git a/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacade.java b/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacade.java new file mode 100644 index 00000000..a361f8d1 --- /dev/null +++ b/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacade.java @@ -0,0 +1,162 @@ +package cn.iocoder.muse.module.ai.application.muse.facade; + +import cn.iocoder.muse.framework.common.util.json.JsonUtils; +import cn.iocoder.muse.module.ai.dal.dataobject.muse.MuseAiSuggestionDO; +import cn.iocoder.muse.module.ai.dal.mysql.muse.MuseAiSuggestionMapper; +import cn.iocoder.muse.module.content.application.facade.ContentAiSuggestionFacade; +import cn.iocoder.muse.module.content.controller.app.vo.ContentSourceSnapshotVO; +import jakarta.annotation.Resource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.Map; +import java.util.Objects; + +/** + * AI 侧实现 Content 的 {@link ContentAiSuggestionFacade} 接缝 adapter(P1 增量 2 / 缺口 A)。 + * + *

从 AI 自有 suggestion 事实表读取“可合并投影”,供 Content 的 mergeBlockSuggestion 消费, + * 让“AI 先审后入”链路的候选源真实接通(此前只有 UnavailableContentAiSuggestionFacade 桩,整条跑不动)。

+ * + *

跨 BC 接缝契约:一个“可合并 suggestion”必须携带以下事实,否则视为尚未通过“先审后入”的 + * “审”,一律返回 {@code unavailable}(绝不伪造,缺字段硬接 = 把未知质量写成正式 Content):

+ *
    + *
  • {@code status=pending}、{@code sourceStatus=active}、数值 {@code authorizationSnapshotId}、正文;
  • + *
  • “审”结果:{@code outputComplianceResultId}(输出合规)、{@code staticCheckResultId}(静态检查)、 + * {@code licenseRestrictionSnapshot}(许可限制快照);
  • + *
  • 结构化来源快照:{@code sourceType}/{@code sourceId}。
  • + *
+ * + *

边界:本 adapter 只读 AI 自有 {@link MuseAiSuggestionMapper},不触碰 Content 的 DAL + * (规避对抗复盘 A4 的跨 BC DAL 直连);Content 的 mergeBlockSuggestion 仍是最终校验权威。

+ * + *

遗留与后续:V4 的 muse_ai_suggestion 用 BIGINT 历史列,字符串 ID 沿用既有约定放在 + * {@code diffSummary} JSON 摘要(与 MuseSuggestionServiceImpl#toDetail 读 sourceSnapshotId/ + * runtimePermissionEnvelopeId 同一约定)。把“审”字段升为独立列、并让 AI 生成链路真实产出这些 + * “审”结果(合规扫描 / 静态检查 / 许可解析),是后续子增量;在那之前未携带“审”的 suggestion 仍 unavailable。

+ */ +@Component +@Primary +@ConditionalOnBean(MuseAiSuggestionMapper.class) +public class AiSuggestionMergeProjectionFacade implements ContentAiSuggestionFacade { + + /** AI suggestion 在 Content 来源归因中的规范来源类型。 */ + private static final String SOURCE_TYPE_AI_SUGGESTION = "ai_suggestion"; + + @Resource + private MuseAiSuggestionMapper suggestionMapper; + + @Override + public SuggestionLookupResult getSuggestion(Long userId, Long workId, Long blockId, Long suggestionId) { + if (suggestionId == null) { + return SuggestionLookupResult.unavailable(); + } + MuseAiSuggestionDO suggestion = suggestionMapper.selectById(suggestionId); + // suggestion 必须确实属于本次 merge 的 work + block,杜绝借 suggestionId 跨作品/跨块合并; + // 调用方(Content)已先校验过 work/block 归属,这里只做作用域一致性核对。 + if (suggestion == null + || !Objects.equals(suggestion.getWorkId(), workId) + || !Objects.equals(suggestion.getBlockId(), blockId)) { + return SuggestionLookupResult.unavailable(); + } + // 列上事实:授权快照(数值)、来源状态、正文必须齐。 + if (suggestion.getAuthorizationSnapshotId() == null + || !StringUtils.hasText(suggestion.getSourceStatus())) { + return SuggestionLookupResult.unavailable(); + } + String content = textField(suggestion.getContentSnapshot(), "content", "text"); + if (!StringUtils.hasText(content)) { + return SuggestionLookupResult.unavailable(); + } + // “审”结果 + 结构化来源:沿用 diffSummary JSON 摘要约定承载字符串 ID。 + Map envelope = parseJsonMap(suggestion.getDiffSummary()); + String outputComplianceResultId = text(envelope.get("outputComplianceResultId")); + String staticCheckResultId = text(envelope.get("staticCheckResultId")); + String licenseRestrictionSnapshot = jsonText(envelope.get("licenseRestrictionSnapshot")); + // “审”未齐 = 尚未通过“先审后入”的审,诚实返回 unavailable,不伪造可合并投影。 + if (!StringUtils.hasText(outputComplianceResultId) + || !StringUtils.hasText(staticCheckResultId) + || !StringUtils.hasText(licenseRestrictionSnapshot)) { + return SuggestionLookupResult.unavailable(); + } + String authorizationSnapshot = String.valueOf(suggestion.getAuthorizationSnapshotId()); + ContentSourceSnapshotVO sourceSnapshot = new ContentSourceSnapshotVO(); + sourceSnapshot.setSourceType(defaultText(text(envelope.get("sourceType")), SOURCE_TYPE_AI_SUGGESTION)); + sourceSnapshot.setSourceId(defaultText(text(envelope.get("sourceId")), String.valueOf(suggestion.getId()))); + sourceSnapshot.setSourceVersion(suggestion.getSourceRevision()); + sourceSnapshot.setAuthorizationSnapshotId(authorizationSnapshot); + + SuggestionProjection projection = new SuggestionProjection( + suggestion.getId(), + normalizeStatus(suggestion.getStatus()), + suggestion.getSourceRevision(), + content, + sourceSnapshot, + authorizationSnapshot, + suggestion.getSourceStatus(), + outputComplianceResultId, + staticCheckResultId, + licenseRestrictionSnapshot); + return SuggestionLookupResult.available(projection); + } + + /** suggestion 状态规范化:只透出 Content 合同认得的三态,脏状态按最保守的 pending 处理。 */ + private String normalizeStatus(String status) { + String normalized = StringUtils.hasText(status) ? status : "pending"; + return switch (normalized) { + case "pending", "accepted", "rejected" -> normalized; + default -> "pending"; + }; + } + + /** 从 JSON 摘要中按 keys 顺序取第一个非空文本值。 */ + private String textField(String json, String... keys) { + Map map = parseJsonMap(json); + for (String key : keys) { + String value = text(map.get(key)); + if (StringUtils.hasText(value)) { + return value; + } + } + return null; + } + + private Map parseJsonMap(String json) { + if (!StringUtils.hasText(json)) { + return Map.of(); + } + try { + @SuppressWarnings("unchecked") + Map parsed = JsonUtils.parseObject(json, Map.class); + return parsed == null ? Map.of() : parsed; + } catch (RuntimeException ex) { + // 摘要 JSON 只能保证语法合法,不能保证结构;解析失败按缺字段处理,后续 unavailable 兜底。 + return Map.of(); + } + } + + private String text(Object value) { + return value == null ? null : String.valueOf(value); + } + + /** + * 许可限制快照需作为 JSON 字符串交给 Content(其 requireLicenseRestrictionSnapshot 用 JSON 解析校验): + * 若摘要里已是字符串则原样透传;若是嵌套数组/对象则重新序列化为 JSON 文本,避免 List.toString 产出非法 JSON。 + */ + private String jsonText(Object value) { + if (value == null) { + return null; + } + if (value instanceof String string) { + return string; + } + return JsonUtils.toJsonString(value); + } + + private String defaultText(String value, String fallback) { + return StringUtils.hasText(value) ? value : fallback; + } + +} diff --git a/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacade.java b/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacade.java index 1eb1dcd8..1a2fc88f 100644 --- a/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacade.java +++ b/muse-cloud/muse-module-ai/muse-module-ai-server/src/main/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacade.java @@ -2,13 +2,9 @@ package cn.iocoder.muse.module.ai.application.muse.facade; import cn.iocoder.muse.framework.common.exception.ServiceException; import cn.iocoder.muse.framework.common.util.json.JsonUtils; -import cn.iocoder.muse.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.muse.module.content.dal.dataobject.BlockDO; -import cn.iocoder.muse.module.content.dal.dataobject.ChapterDO; -import cn.iocoder.muse.module.content.dal.dataobject.WorkDO; -import cn.iocoder.muse.module.content.dal.mysql.BlockMapper; -import cn.iocoder.muse.module.content.dal.mysql.ChapterMapper; -import cn.iocoder.muse.module.content.dal.mysql.WorkMapper; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi.BlockRef; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi.ChapterRef; import jakarta.annotation.Resource; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Primary; @@ -19,27 +15,29 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + import static cn.iocoder.muse.module.ai.enums.ErrorCodeConstants.AI_RESOURCE_FORBIDDEN; import static cn.iocoder.muse.module.ai.enums.ErrorCodeConstants.AI_SOURCE_RECHECK_REQUIRED; /** - * 基于 Content DAL 的真实 Content facade adapter。 + * 基于 Content **对外 API** 的 work-owner adapter(BC 合规)。 + * + *

整改记录(2026-06-14):此前直连 content DAL(WorkMapper/ChapterMapper/BlockMapper + DO),破坏"模块化单体 + 清晰 BC 边界"。 + * 现改为消费 content 对外契约 {@link MuseContentWorkOwnerApi},只取脱敏的归属/层级/revision 元数据; + * AI 侧业务规则(归属校验、source snapshot 构建、revision 新鲜度)保持不变。整改后 ArchUnit 已移除对本类的豁免, + * 门禁随之收紧(见 .agents/rules/bc-boundaries.md)。

*/ @Component @Primary -@ConditionalOnBean({WorkMapper.class, ChapterMapper.class, BlockMapper.class}) +@ConditionalOnBean(MuseContentWorkOwnerApi.class) public class ContentMuseWorkOwnerFacade implements MuseContentWorkOwnerFacade { @Resource - private WorkMapper workMapper; - @Resource - private ChapterMapper chapterMapper; - @Resource - private BlockMapper blockMapper; + private MuseContentWorkOwnerApi contentWorkOwnerApi; @Override public void requireWorkOwner(Long workId, Long ownerUserId) { - requireOwnedWork(workId, ownerUserId); + requireOwnedWorkRevision(workId, ownerUserId); } @Override @@ -48,11 +46,11 @@ public class ContentMuseWorkOwnerFacade implements MuseContentWorkOwnerFacade { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } switch (sourceType.trim().toLowerCase()) { - case "work" -> requireOwnedWork(sourceId, ownerUserId); - case "chapter" -> requireOwnedWork(requireChapter(sourceId).getWorkId(), ownerUserId); - case "block" -> requireOwnedWork(requireBlock(sourceId).getWorkId(), ownerUserId); + case "work" -> requireOwnedWorkRevision(sourceId, ownerUserId); + case "chapter" -> requireOwnedWorkRevision(requireChapterRef(sourceId).workId(), ownerUserId); + case "block" -> requireOwnedWorkRevision(requireBlockRef(sourceId).workId(), ownerUserId); default -> { - // Content source recheck 只承认已实现真实归属路径的来源类型;未知类型必须失败关闭,不能排队 job。 + // Content source recheck 只承认已实现真实归属路径的来源类型;未知类型必须失败关闭,不能排队 job。 throw new ServiceException(AI_RESOURCE_FORBIDDEN); } } @@ -63,20 +61,20 @@ public class ContentMuseWorkOwnerFacade implements MuseContentWorkOwnerFacade { if (request == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } - WorkDO work = requireOwnedWork(request.workId(), request.ownerUserId()); + Integer workRevision = requireOwnedWorkRevision(request.workId(), request.ownerUserId()); List refSummaries = new ArrayList<>(); if (request.chapterId() != null) { - ChapterDO chapter = requireChapterBelongsToWork(request.chapterId(), request.workId()); - refSummaries.add(new RefSummary("chapter", chapter.getId(), chapter.getRevision(), null)); + ChapterRef chapter = requireChapterBelongsToWork(request.chapterId(), request.workId()); + refSummaries.add(new RefSummary("chapter", chapter.chapterId(), chapter.revision(), null)); } if (request.blockId() != null) { - BlockDO block = requireBlockBelongsToWork(request.blockId(), request.workId()); + BlockRef block = requireBlockBelongsToWork(request.blockId(), request.workId()); requireBlockBelongsToRequestedChapter(block, request.chapterId()); - refSummaries.add(new RefSummary("block", block.getId(), block.getRevision(), null)); + refSummaries.add(new RefSummary("block", block.blockId(), block.revision(), null)); } if (request.additionalRefs() != null) { for (ContextRef ref : request.additionalRefs()) { - RefSummary refSummary = resolveAdditionalRef(request.workId(), work.getRevision(), ref); + RefSummary refSummary = resolveAdditionalRef(request.workId(), workRevision, ref); requireFreshRevision(refSummary.actualRevision(), refSummary.expectedRevision()); refSummaries.add(refSummary); } @@ -87,70 +85,69 @@ public class ContentMuseWorkOwnerFacade implements MuseContentWorkOwnerFacade { summary.put("workId", request.workId()); summary.put("chapterId", request.chapterId()); summary.put("blockId", request.blockId()); - summary.put("sourceRevision", work.getRevision()); + summary.put("sourceRevision", workRevision); summary.put("contextScope", StringUtils.hasText(request.contextScope()) ? request.contextScope() : "work"); summary.put("additionalContextRefCount", request.additionalRefs() == null ? 0 : request.additionalRefs().size()); summary.put("refs", refSummaries.stream().map(RefSummary::toSummaryMap).toList()); - // summary 只包含 ID 与 revision;这里强制 JSON 可序列化,避免 adapter 误把 Content DO 或正文塞入快照。 + // summary 只包含 ID 与 revision;这里强制 JSON 可序列化,避免 adapter 误把内容塞入快照。 JsonUtils.toJsonString(summary); return new SourceSnapshot(sourceSnapshotId, request.workId(), request.chapterId(), request.blockId(), - work.getRevision(), String.valueOf(summary.get("contextScope")), refSummaries, summary); + workRevision, String.valueOf(summary.get("contextScope")), refSummaries, summary); } - private WorkDO requireOwnedWork(Long workId, Long ownerUserId) { + /** 校验作品归属并返回其 revision;入参为空或非当前用户拥有时失败关闭。 */ + private Integer requireOwnedWorkRevision(Long workId, Long ownerUserId) { if (workId == null || ownerUserId == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } - Long tenantId = TenantContextHolder.getRequiredTenantId(); - // 只查询当前租户下未删除且 owner_user_id 匹配的作品;查不到统一按无权访问处理,避免泄露作品存在性。 - WorkDO work = workMapper.selectActiveByIdAndOwner(tenantId, workId, ownerUserId); - if (work == null) { + Integer revision = contentWorkOwnerApi.getActiveOwnedWorkRevision(workId, ownerUserId); + if (revision == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } - return work; + return revision; } - private ChapterDO requireChapterBelongsToWork(Long chapterId, Long workId) { - ChapterDO chapter = requireChapter(chapterId); - if (!java.util.Objects.equals(chapter.getWorkId(), workId)) { + private ChapterRef requireChapterBelongsToWork(Long chapterId, Long workId) { + ChapterRef chapter = requireChapterRef(chapterId); + if (!java.util.Objects.equals(chapter.workId(), workId)) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } return chapter; } - private BlockDO requireBlockBelongsToWork(Long blockId, Long workId) { - BlockDO block = requireBlock(blockId); - if (!java.util.Objects.equals(block.getWorkId(), workId)) { + private BlockRef requireBlockBelongsToWork(Long blockId, Long workId) { + BlockRef block = requireBlockRef(blockId); + if (!java.util.Objects.equals(block.workId(), workId)) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } return block; } - private ChapterDO requireChapter(Long chapterId) { + private ChapterRef requireChapterRef(Long chapterId) { if (chapterId == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } - ChapterDO chapter = chapterMapper.selectById(chapterId); - if (chapter == null || chapter.getWorkId() == null) { + ChapterRef chapter = contentWorkOwnerApi.getChapterRef(chapterId); + if (chapter == null || chapter.workId() == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } return chapter; } - private BlockDO requireBlock(Long blockId) { + private BlockRef requireBlockRef(Long blockId) { if (blockId == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } - BlockDO block = blockMapper.selectById(blockId); - if (block == null || block.getWorkId() == null) { + BlockRef block = contentWorkOwnerApi.getBlockRef(blockId); + if (block == null || block.workId() == null) { throw new ServiceException(AI_RESOURCE_FORBIDDEN); } return block; } - private void requireBlockBelongsToRequestedChapter(BlockDO block, Long chapterId) { - if (chapterId != null && (block == null || !java.util.Objects.equals(block.getChapterId(), chapterId))) { - // 同一作品下跨章节 block 不能被 request.chapterId 偷带进来,否则 source snapshot 会把两个不同章节拼成伪上下文。 + private void requireBlockBelongsToRequestedChapter(BlockRef block, Long chapterId) { + if (chapterId != null && (block == null || !java.util.Objects.equals(block.chapterId(), chapterId))) { + // 同一作品下跨章节 block 不能被 request.chapterId 偷带进来,否则 source snapshot 会把两个不同章节拼成伪上下文。 throw new ServiceException(AI_RESOURCE_FORBIDDEN); } } @@ -166,12 +163,12 @@ public class ContentMuseWorkOwnerFacade implements MuseContentWorkOwnerFacade { return new RefSummary(ref.refType(), ref.refId(), workRevision, ref.expectedRevision()); } if ("chapter".equals(ref.refType())) { - ChapterDO chapter = requireChapterBelongsToWork(ref.refId(), workId); - return new RefSummary(ref.refType(), ref.refId(), chapter.getRevision(), ref.expectedRevision()); + ChapterRef chapter = requireChapterBelongsToWork(ref.refId(), workId); + return new RefSummary(ref.refType(), ref.refId(), chapter.revision(), ref.expectedRevision()); } if ("block".equals(ref.refType())) { - BlockDO block = requireBlockBelongsToWork(ref.refId(), workId); - return new RefSummary(ref.refType(), ref.refId(), block.getRevision(), ref.expectedRevision()); + BlockRef block = requireBlockBelongsToWork(ref.refId(), workId); + return new RefSummary(ref.refType(), ref.refId(), block.revision(), ref.expectedRevision()); } throw new ServiceException(AI_RESOURCE_FORBIDDEN); } diff --git a/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacadeTest.java b/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacadeTest.java new file mode 100644 index 00000000..5b4c6073 --- /dev/null +++ b/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/AiSuggestionMergeProjectionFacadeTest.java @@ -0,0 +1,115 @@ +package cn.iocoder.muse.module.ai.application.muse.facade; + +import cn.iocoder.muse.framework.common.util.json.JsonUtils; +import cn.iocoder.muse.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.muse.module.ai.dal.dataobject.muse.MuseAiSuggestionDO; +import cn.iocoder.muse.module.ai.dal.mysql.muse.MuseAiSuggestionMapper; +import cn.iocoder.muse.module.content.application.facade.ContentAiSuggestionFacade.SuggestionLookupResult; +import cn.iocoder.muse.module.content.application.facade.ContentAiSuggestionFacade.SuggestionProjection; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +/** + * AI→Content suggestion 接缝 adapter 种子测试(P1 增量 2 / 缺口 A)。 + * + *

验证“立接缝契约 + 种子测证”:喂一个字段齐全的可合并 suggestion → adapter 产出有效投影且字段映射正确; + * 任一“审”字段缺失或 work/block 作用域不符 → 诚实返回 unavailable,绝不伪造。

+ */ +class AiSuggestionMergeProjectionFacadeTest extends BaseMockitoUnitTest { + + @InjectMocks + private AiSuggestionMergeProjectionFacade facade; + + @Mock + private MuseAiSuggestionMapper suggestionMapper; + + @Test + void should_returnAvailableProjection_when_suggestionCarriesFullMergeContract() { + when(suggestionMapper.selectById(501L)).thenReturn(completeSuggestion()); + + SuggestionLookupResult result = facade.getSuggestion(1001L, 101L, 301L, 501L); + + assertTrue(result.available()); + SuggestionProjection projection = result.suggestion(); + assertEquals(501L, projection.suggestionId()); + assertEquals("pending", projection.status()); + assertEquals(7, projection.revision()); + assertEquals("AI 正文", projection.content()); + assertEquals("active", projection.sourceStatus()); + assertEquals("88", projection.authorizationSnapshot()); + assertEquals("compliance-new", projection.outputComplianceResultId()); + assertEquals("static-new", projection.staticCheckResultId()); + // 许可快照必须是合法 JSON 字符串(Content 端会 JSON 解析校验)。 + assertEquals("[\"non_commercial\"]", projection.licenseRestrictionSnapshot()); + assertEquals("ai_suggestion", projection.sourceSnapshot().getSourceType()); + assertEquals("501", projection.sourceSnapshot().getSourceId()); + assertEquals(7, projection.sourceSnapshot().getSourceVersion()); + assertEquals("88", projection.sourceSnapshot().getAuthorizationSnapshotId()); + } + + @Test + void should_returnUnavailable_when_auditFieldMissing() { + MuseAiSuggestionDO suggestion = completeSuggestion(); + Map envelope = new LinkedHashMap<>(); + envelope.put("sourceType", "ai_suggestion"); + envelope.put("sourceId", "501"); + envelope.put("outputComplianceResultId", "compliance-new"); + // 故意缺 staticCheckResultId / licenseRestrictionSnapshot —— 未通过“审”。 + suggestion.setDiffSummary(JsonUtils.toJsonString(envelope)); + when(suggestionMapper.selectById(501L)).thenReturn(suggestion); + + SuggestionLookupResult result = facade.getSuggestion(1001L, 101L, 301L, 501L); + + assertFalse(result.available()); + } + + @Test + void should_returnUnavailable_when_suggestionBelongsToAnotherBlock() { + when(suggestionMapper.selectById(501L)).thenReturn(completeSuggestion()); + + // blockId 不匹配(请求 999,suggestion 属于 301)→ 不可跨块合并。 + SuggestionLookupResult result = facade.getSuggestion(1001L, 101L, 999L, 501L); + + assertFalse(result.available()); + } + + @Test + void should_returnUnavailable_when_suggestionMissing() { + when(suggestionMapper.selectById(501L)).thenReturn(null); + + assertFalse(facade.getSuggestion(1001L, 101L, 301L, 501L).available()); + assertFalse(facade.getSuggestion(1001L, 101L, 301L, null).available()); + } + + /** 构造一个字段齐全、满足接缝契约的可合并 suggestion(diffSummary 承载“审”结果 + 结构化来源)。 */ + private MuseAiSuggestionDO completeSuggestion() { + MuseAiSuggestionDO suggestion = new MuseAiSuggestionDO(); + suggestion.setId(501L); + suggestion.setWorkId(101L); + suggestion.setBlockId(301L); + suggestion.setStatus("pending"); + suggestion.setSourceRevision(7); + suggestion.setSourceStatus("active"); + suggestion.setAuthorizationSnapshotId(88L); + suggestion.setContentSnapshot("{\"content\":\"AI 正文\"}"); + Map envelope = new LinkedHashMap<>(); + envelope.put("sourceType", "ai_suggestion"); + envelope.put("sourceId", "501"); + envelope.put("outputComplianceResultId", "compliance-new"); + envelope.put("staticCheckResultId", "static-new"); + envelope.put("licenseRestrictionSnapshot", List.of("non_commercial")); + suggestion.setDiffSummary(JsonUtils.toJsonString(envelope)); + return suggestion; + } + +} diff --git a/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacadeTest.java b/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacadeTest.java index 1b1c7cb2..6eca3605 100644 --- a/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacadeTest.java +++ b/muse-cloud/muse-module-ai/muse-module-ai-server/src/test/java/cn/iocoder/muse/module/ai/application/muse/facade/ContentMuseWorkOwnerFacadeTest.java @@ -2,15 +2,10 @@ package cn.iocoder.muse.module.ai.application.muse.facade; import cn.iocoder.muse.framework.common.exception.ServiceException; import cn.iocoder.muse.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.muse.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.muse.module.content.dal.dataobject.BlockDO; -import cn.iocoder.muse.module.content.dal.dataobject.ChapterDO; -import cn.iocoder.muse.module.content.dal.dataobject.WorkDO; -import cn.iocoder.muse.module.content.dal.mysql.BlockMapper; -import cn.iocoder.muse.module.content.dal.mysql.ChapterMapper; -import cn.iocoder.muse.module.content.dal.mysql.WorkMapper; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi.BlockRef; +import cn.iocoder.muse.module.content.api.MuseContentWorkOwnerApi.ChapterRef; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -22,80 +17,66 @@ import static cn.iocoder.muse.module.ai.enums.ErrorCodeConstants.AI_SOURCE_RECHE import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; /** * Content 作品归属 Facade 测试。 + * + *

整改后(2026-06-14):适配器改消费 content 对外契约 {@link MuseContentWorkOwnerApi},不再直连 content.dal; + * 本测试相应改为 mock 该端口,验证 AI 侧归属校验/快照构建/revision 新鲜度等业务规则保持不变。

*/ class ContentMuseWorkOwnerFacadeTest extends BaseMockitoUnitTest { @InjectMocks private ContentMuseWorkOwnerFacade workOwnerFacade; @Mock - private WorkMapper workMapper; - @Mock - private ChapterMapper chapterMapper; - @Mock - private BlockMapper blockMapper; - - @AfterEach - void clearTenantContext() { - TenantContextHolder.clear(); - } + private MuseContentWorkOwnerApi contentWorkOwnerApi; @Test - void should_allowOwnerWhenWorkExistsInCurrentTenant() { - TenantContextHolder.setTenantId(100L); - when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10001L)).thenReturn(work(9001L, 10001L)); + void should_allowOwnerWhenWorkOwned() { + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9001L, 10001L)).thenReturn(5); workOwnerFacade.requireWorkOwner(9001L, 10001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9001L, 10001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9001L, 10001L); } @Test void should_rejectWhenWorkBelongsToOtherOwnerOrMissing() { - TenantContextHolder.setTenantId(100L); - when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10002L)).thenReturn(null); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9001L, 10002L)).thenReturn(null); ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.requireWorkOwner(9001L, 10002L)); assertEquals(AI_RESOURCE_FORBIDDEN.getCode(), exception.getCode()); - verify(workMapper).selectActiveByIdAndOwner(100L, 9001L, 10002L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9001L, 10002L); } @Test void should_rejectBlankIdentityBeforeQuery() { - TenantContextHolder.setTenantId(100L); - ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.requireWorkOwner(null, 10001L)); assertEquals(AI_RESOURCE_FORBIDDEN.getCode(), exception.getCode()); - verify(workMapper, never()).selectActiveByIdAndOwner(100L, null, 10001L); + // 空入参必须在调用 content 契约前失败关闭,绝不触达跨 BC 端口。 + verifyNoInteractions(contentWorkOwnerApi); } @Test - void should_registerRealFacadeOnlyWhenContentWorkMapperExists() { + void should_registerRealFacadeOnlyWhenContentApiExists() { ConditionalOnBean condition = ContentMuseWorkOwnerFacade.class.getAnnotation(ConditionalOnBean.class); assertNotNull(condition); - assertEquals(WorkMapper.class, condition.value()[0]); - assertEquals(ChapterMapper.class, condition.value()[1]); - assertEquals(BlockMapper.class, condition.value()[2]); + assertEquals(MuseContentWorkOwnerApi.class, condition.value()[0]); } @Test void should_buildRedactedSourceSnapshotWithRevisionFacts() { - TenantContextHolder.setTenantId(100L); - WorkDO work = work(9001L, 10001L); - work.setRevision(8); - when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10001L)).thenReturn(work); - when(chapterMapper.selectById(8001L)).thenReturn(chapter(8001L, 9001L, 3)); - when(blockMapper.selectById(7001L)).thenReturn(block(7001L, 9001L, 8001L, 4)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9001L, 10001L)).thenReturn(8); + when(contentWorkOwnerApi.getChapterRef(8001L)).thenReturn(new ChapterRef(8001L, 9001L, 3)); + when(contentWorkOwnerApi.getBlockRef(7001L)).thenReturn(new BlockRef(7001L, 9001L, 8001L, 4)); MuseContentWorkOwnerFacade.SourceSnapshot snapshot = workOwnerFacade.buildSourceSnapshot( new MuseContentWorkOwnerFacade.SourceSnapshotRequest(9001L, 8001L, 7001L, "work", @@ -112,12 +93,9 @@ class ContentMuseWorkOwnerFacadeTest extends BaseMockitoUnitTest { @Test void should_rejectWhenMainBlockDoesNotBelongToRequestedChapter() { - TenantContextHolder.setTenantId(100L); - WorkDO work = work(9001L, 10001L); - work.setRevision(8); - when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10001L)).thenReturn(work); - when(chapterMapper.selectById(8001L)).thenReturn(chapter(8001L, 9001L, 3)); - when(blockMapper.selectById(7001L)).thenReturn(block(7001L, 9001L, 8002L, 4)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9001L, 10001L)).thenReturn(8); + when(contentWorkOwnerApi.getChapterRef(8001L)).thenReturn(new ChapterRef(8001L, 9001L, 3)); + when(contentWorkOwnerApi.getBlockRef(7001L)).thenReturn(new BlockRef(7001L, 9001L, 8002L, 4)); ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.buildSourceSnapshot( new MuseContentWorkOwnerFacade.SourceSnapshotRequest(9001L, 8001L, 7001L, "work", @@ -128,11 +106,8 @@ class ContentMuseWorkOwnerFacadeTest extends BaseMockitoUnitTest { @Test void should_rejectStaleAdditionalRefRevision() { - TenantContextHolder.setTenantId(100L); - WorkDO work = work(9001L, 10001L); - work.setRevision(8); - when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10001L)).thenReturn(work); - when(blockMapper.selectById(7001L)).thenReturn(block(7001L, 9001L, 8001L, 6)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9001L, 10001L)).thenReturn(8); + when(contentWorkOwnerApi.getBlockRef(7001L)).thenReturn(new BlockRef(7001L, 9001L, 8001L, 6)); ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.buildSourceSnapshot( new MuseContentWorkOwnerFacade.SourceSnapshotRequest(9001L, null, null, "work", @@ -143,101 +118,69 @@ class ContentMuseWorkOwnerFacadeTest extends BaseMockitoUnitTest { @Test void should_allowWorkSourceWhenSourceWorkIsOwnedByCurrentUser() { - TenantContextHolder.setTenantId(100L); - when(workMapper.selectActiveByIdAndOwner(100L, 9002L, 10001L)).thenReturn(work(9002L, 10001L)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9002L, 10001L)).thenReturn(5); workOwnerFacade.requireSourceOwner("work", 9002L, 10001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9002L, 10001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9002L, 10001L); } @Test void should_allowChapterSourceOnlyWhenChapterWorkIsOwnedByCurrentUser() { - TenantContextHolder.setTenantId(100L); - when(chapterMapper.selectById(8001L)).thenReturn(chapter(8001L, 9002L, 3)); - when(workMapper.selectActiveByIdAndOwner(100L, 9002L, 10001L)).thenReturn(work(9002L, 10001L)); + when(contentWorkOwnerApi.getChapterRef(8001L)).thenReturn(new ChapterRef(8001L, 9002L, 3)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9002L, 10001L)).thenReturn(5); workOwnerFacade.requireSourceOwner("chapter", 8001L, 10001L); - verify(chapterMapper).selectById(8001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9002L, 10001L); + verify(contentWorkOwnerApi).getChapterRef(8001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9002L, 10001L); } @Test void should_rejectChapterSourceWhenChapterWorkIsNotOwnedByCurrentUser() { - TenantContextHolder.setTenantId(100L); - when(chapterMapper.selectById(8001L)).thenReturn(chapter(8001L, 9002L, 3)); - when(workMapper.selectActiveByIdAndOwner(100L, 9002L, 10001L)).thenReturn(null); + when(contentWorkOwnerApi.getChapterRef(8001L)).thenReturn(new ChapterRef(8001L, 9002L, 3)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9002L, 10001L)).thenReturn(null); ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.requireSourceOwner("chapter", 8001L, 10001L)); assertEquals(AI_RESOURCE_FORBIDDEN.getCode(), exception.getCode()); - verify(chapterMapper).selectById(8001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9002L, 10001L); + verify(contentWorkOwnerApi).getChapterRef(8001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9002L, 10001L); } @Test void should_allowBlockSourceOnlyWhenBlockWorkIsOwnedByCurrentUser() { - TenantContextHolder.setTenantId(100L); - when(blockMapper.selectById(7001L)).thenReturn(block(7001L, 9002L, 8001L, 4)); - when(workMapper.selectActiveByIdAndOwner(100L, 9002L, 10001L)).thenReturn(work(9002L, 10001L)); + when(contentWorkOwnerApi.getBlockRef(7001L)).thenReturn(new BlockRef(7001L, 9002L, 8001L, 4)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9002L, 10001L)).thenReturn(5); workOwnerFacade.requireSourceOwner("block", 7001L, 10001L); - verify(blockMapper).selectById(7001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9002L, 10001L); + verify(contentWorkOwnerApi).getBlockRef(7001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9002L, 10001L); } @Test void should_rejectBlockSourceWhenBlockWorkIsNotOwnedByCurrentUser() { - TenantContextHolder.setTenantId(100L); - when(blockMapper.selectById(7001L)).thenReturn(block(7001L, 9002L, 8001L, 4)); - when(workMapper.selectActiveByIdAndOwner(100L, 9002L, 10001L)).thenReturn(null); + when(contentWorkOwnerApi.getBlockRef(7001L)).thenReturn(new BlockRef(7001L, 9002L, 8001L, 4)); + when(contentWorkOwnerApi.getActiveOwnedWorkRevision(9002L, 10001L)).thenReturn(null); ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.requireSourceOwner("block", 7001L, 10001L)); assertEquals(AI_RESOURCE_FORBIDDEN.getCode(), exception.getCode()); - verify(blockMapper).selectById(7001L); - verify(workMapper).selectActiveByIdAndOwner(100L, 9002L, 10001L); + verify(contentWorkOwnerApi).getBlockRef(7001L); + verify(contentWorkOwnerApi).getActiveOwnedWorkRevision(9002L, 10001L); } @Test void should_failClosedForUnsupportedContentSourceType() { - TenantContextHolder.setTenantId(100L); - ServiceException exception = assertThrows(ServiceException.class, () -> workOwnerFacade.requireSourceOwner("planning_section", 6001L, 10001L)); assertEquals(AI_RESOURCE_FORBIDDEN.getCode(), exception.getCode()); - verify(workMapper, never()).selectActiveByIdAndOwner(100L, 6001L, 10001L); - verify(chapterMapper, never()).selectById(6001L); - verify(blockMapper, never()).selectById(6001L); - } - - private static WorkDO work(Long id, Long ownerUserId) { - WorkDO work = new WorkDO(); - work.setId(id); - work.setOwnerUserId(ownerUserId); - return work; - } - - private static ChapterDO chapter(Long id, Long workId, Integer revision) { - ChapterDO chapter = new ChapterDO(); - chapter.setId(id); - chapter.setWorkId(workId); - chapter.setRevision(revision); - return chapter; - } - - private static BlockDO block(Long id, Long workId, Long chapterId, Integer revision) { - BlockDO block = new BlockDO(); - block.setId(id); - block.setWorkId(workId); - block.setChapterId(chapterId); - block.setRevision(revision); - return block; + // 不支持的来源类型必须在触达 content 契约前失败关闭。 + verifyNoInteractions(contentWorkOwnerApi); } } diff --git a/muse-cloud/muse-module-content/.agent b/muse-cloud/muse-module-content/.agent new file mode 100644 index 00000000..34836557 --- /dev/null +++ b/muse-cloud/muse-module-content/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-content(作品/编辑器 BC) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §5.2](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:Work / Chapter / Block / 正文版本 / Block Source Attribution / Import-Export / Planning Canonical / Narrative State。 +- **边界(不可违反)**:**正文 Canonical 唯一写入方**;Block 写入须带 expectedRevision 且 revision 单调递增;**Accept Suggestion 是候选进正文的唯一合法入口**——只写正文 + 候选归档 + 来源归因,**不写 Local KB**;守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:知识入库(归 knowledge BC)、AI 生成(归 ai BC)。 +- **现状**:只读评估 82%;后端齐全,契约高度一致。 +- **关键风险 / TODO**:**前端 studio 未接 `suggestion-merges`**,AI 候选进正文在用户端断链不可用(objective“先审后入”落点)。 diff --git a/muse-cloud/muse-module-content/muse-module-content-api/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApi.java b/muse-cloud/muse-module-content/muse-module-content-api/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApi.java new file mode 100644 index 00000000..22501f40 --- /dev/null +++ b/muse-cloud/muse-module-content/muse-module-content-api/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApi.java @@ -0,0 +1,48 @@ +package cn.iocoder.muse.module.content.api; + +/** + * Content 作品归属与层级元数据的对外只读 API(BC 对外契约)。 + * + *

供他域(如 AI)在**不直连 content DAL** 的前提下,获取脱敏的归属/层级/版本元数据: + * 作品归属校验所需的 revision、章节与块的 {@code {workId, chapterId, revision}}。 + * 绝不返回正文/标题等内容字段,只返回 ID 与 revision,守住 Shadow→Canonical 与 BC 边界。

+ * + *

由 content-server 的 {@code MuseContentWorkOwnerApiImpl} 实现(读 content 自有 DAL,本域读本域合规); + * 消费方依赖本 -api 契约,不得依赖 {@code content.dal}(由 ArchUnit BcBoundaryArchTest 机械约束)。

+ */ +public interface MuseContentWorkOwnerApi { + + /** + * 返回当前租户下、由 ownerUserId 拥有且未删除的作品的 revision。 + * + * @param workId 作品 ID + * @param ownerUserId 期望的拥有者用户 ID + * @return 作品 revision;若作品不存在 / 非该用户拥有 / 已删除 / 入参为空,返回 {@code null}(调用方据此按无权处理) + */ + Integer getActiveOwnedWorkRevision(Long workId, Long ownerUserId); + + /** + * 返回章节的归属与版本元数据。 + * + * @param chapterId 章节 ID + * @return {@link ChapterRef};章节不存在 / 无归属作品 / 入参为空时返回 {@code null} + */ + ChapterRef getChapterRef(Long chapterId); + + /** + * 返回块的归属与版本元数据。 + * + * @param blockId 块 ID + * @return {@link BlockRef};块不存在 / 无归属作品 / 入参为空时返回 {@code null} + */ + BlockRef getBlockRef(Long blockId); + + /** 章节脱敏元数据:仅 ID、归属作品、版本(无正文)。 */ + record ChapterRef(Long chapterId, Long workId, Integer revision) { + } + + /** 块脱敏元数据:仅 ID、归属作品、归属章节、版本(无正文)。 */ + record BlockRef(Long blockId, Long workId, Long chapterId, Integer revision) { + } + +} diff --git a/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImpl.java b/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImpl.java new file mode 100644 index 00000000..b8355823 --- /dev/null +++ b/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImpl.java @@ -0,0 +1,64 @@ +package cn.iocoder.muse.module.content.api; + +import cn.iocoder.muse.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.muse.module.content.dal.dataobject.BlockDO; +import cn.iocoder.muse.module.content.dal.dataobject.ChapterDO; +import cn.iocoder.muse.module.content.dal.dataobject.WorkDO; +import cn.iocoder.muse.module.content.dal.mysql.BlockMapper; +import cn.iocoder.muse.module.content.dal.mysql.ChapterMapper; +import cn.iocoder.muse.module.content.dal.mysql.WorkMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * {@link MuseContentWorkOwnerApi} 的 content 实现。 + * + *

读 content **自有** DAL(本域读本域,合规),只对外暴露脱敏的归属/层级/版本元数据,绝不外泄正文。 + * 这是把"AI 直连 content.dal"的历史违例收口到对外契约背后的落点(见 .agents/rules/bc-boundaries.md)。

+ */ +@Service +public class MuseContentWorkOwnerApiImpl implements MuseContentWorkOwnerApi { + + @Resource + private WorkMapper workMapper; + @Resource + private ChapterMapper chapterMapper; + @Resource + private BlockMapper blockMapper; + + @Override + public Integer getActiveOwnedWorkRevision(Long workId, Long ownerUserId) { + if (workId == null || ownerUserId == null) { + return null; + } + Long tenantId = TenantContextHolder.getRequiredTenantId(); + // 只认当前租户下未删除且 owner_user_id 匹配的作品;查不到一律返回 null,避免泄露作品存在性。 + WorkDO work = workMapper.selectActiveByIdAndOwner(tenantId, workId, ownerUserId); + return work == null ? null : work.getRevision(); + } + + @Override + public ChapterRef getChapterRef(Long chapterId) { + if (chapterId == null) { + return null; + } + ChapterDO chapter = chapterMapper.selectById(chapterId); + if (chapter == null || chapter.getWorkId() == null) { + return null; + } + return new ChapterRef(chapter.getId(), chapter.getWorkId(), chapter.getRevision()); + } + + @Override + public BlockRef getBlockRef(Long blockId) { + if (blockId == null) { + return null; + } + BlockDO block = blockMapper.selectById(blockId); + if (block == null || block.getWorkId() == null) { + return null; + } + return new BlockRef(block.getId(), block.getWorkId(), block.getChapterId(), block.getRevision()); + } + +} diff --git a/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentSourceServiceImpl.java b/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentSourceServiceImpl.java index d2bbf75e..d2ba6ffd 100644 --- a/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentSourceServiceImpl.java +++ b/muse-cloud/muse-module-content/muse-module-content-server/src/main/java/cn/iocoder/muse/module/content/application/ContentSourceServiceImpl.java @@ -61,6 +61,8 @@ public class ContentSourceServiceImpl implements ContentSourceService { private ContentAuditService auditService; @Resource private ContentAiSuggestionFacade suggestionFacade; + @Resource + private ContentEventPublishOutboxService eventPublishOutboxService; @Override public SourceAttributionRespVO getBlockSourceAttribution(Long userId, Long workId, Long blockId) { @@ -99,6 +101,11 @@ public class ContentSourceServiceImpl implements ContentSourceService { BlockSourceAttributionDO sourceAttribution = buildSuggestionSourceAttribution(workId, blockId, nextRevision, reqVO, suggestion); sourceAttributionMapper.insert(sourceAttribution); + // 与 saveBlock(ContentAppServiceImpl)一致:merge 成功写入 active 来源归因后,在同一事务内写本域 outbox, + // 由 ContentEventPublishWorker 异步投递 source_status_change 到统一 Events,驱动前端 SSE 回流。 + // 此前缺这一步 → 合并落库成功但用户收不到 SSE 通知(对抗复盘"缺口 B")。 + eventPublishOutboxService.createForBlockSourceAttribution(userId, workId, blockId, nextRevision, + reqVO.getCommandId(), sourceAttribution); MergeBlockSuggestionRespVO respVO = buildMergeSuggestionResp(blockId, reqVO, nextRevision, sourceAttribution, sourceAttribution.getCreateTime()); diff --git a/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImplTest.java b/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImplTest.java new file mode 100644 index 00000000..90201ec4 --- /dev/null +++ b/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/api/MuseContentWorkOwnerApiImplTest.java @@ -0,0 +1,113 @@ +package cn.iocoder.muse.module.content.api; + +import cn.iocoder.muse.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.muse.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.muse.module.content.dal.dataobject.BlockDO; +import cn.iocoder.muse.module.content.dal.dataobject.ChapterDO; +import cn.iocoder.muse.module.content.dal.dataobject.WorkDO; +import cn.iocoder.muse.module.content.dal.mysql.BlockMapper; +import cn.iocoder.muse.module.content.dal.mysql.ChapterMapper; +import cn.iocoder.muse.module.content.dal.mysql.WorkMapper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +/** + * {@link MuseContentWorkOwnerApiImpl} 单测:验证只读元数据的归属/版本映射与 fail-soft(查不到返回 null、空入参不查询)。 + */ +class MuseContentWorkOwnerApiImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private MuseContentWorkOwnerApiImpl api; + @Mock + private WorkMapper workMapper; + @Mock + private ChapterMapper chapterMapper; + @Mock + private BlockMapper blockMapper; + + @AfterEach + void clearTenant() { + TenantContextHolder.clear(); + } + + @Test + void should_returnRevisionWhenWorkOwnedInTenant() { + TenantContextHolder.setTenantId(100L); + WorkDO work = new WorkDO(); + work.setId(9001L); + work.setRevision(7); + when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10001L)).thenReturn(work); + + assertEquals(7, api.getActiveOwnedWorkRevision(9001L, 10001L)); + } + + @Test + void should_returnNullWhenWorkNotOwnedOrMissing() { + TenantContextHolder.setTenantId(100L); + when(workMapper.selectActiveByIdAndOwner(100L, 9001L, 10002L)).thenReturn(null); + + assertNull(api.getActiveOwnedWorkRevision(9001L, 10002L)); + } + + @Test + void should_notQueryWhenWorkIdentityBlank() { + // 空入参必须在查询前返回 null,绝不触达 mapper(与原 AI 适配器"先校验后查询"语义一致)。 + assertNull(api.getActiveOwnedWorkRevision(null, 10001L)); + assertNull(api.getActiveOwnedWorkRevision(9001L, null)); + verifyNoInteractions(workMapper); + } + + @Test + void should_mapChapterRefWithRevisionFacts() { + ChapterDO chapter = new ChapterDO(); + chapter.setId(8001L); + chapter.setWorkId(9001L); + chapter.setRevision(3); + when(chapterMapper.selectById(8001L)).thenReturn(chapter); + + MuseContentWorkOwnerApi.ChapterRef ref = api.getChapterRef(8001L); + + assertEquals(8001L, ref.chapterId()); + assertEquals(9001L, ref.workId()); + assertEquals(3, ref.revision()); + } + + @Test + void should_returnNullChapterWhenMissingOrBlank() { + when(chapterMapper.selectById(8001L)).thenReturn(null); + assertNull(api.getChapterRef(8001L)); + assertNull(api.getChapterRef(null)); + } + + @Test + void should_mapBlockRefWithRevisionFacts() { + BlockDO block = new BlockDO(); + block.setId(7001L); + block.setWorkId(9001L); + block.setChapterId(8001L); + block.setRevision(4); + when(blockMapper.selectById(7001L)).thenReturn(block); + + MuseContentWorkOwnerApi.BlockRef ref = api.getBlockRef(7001L); + + assertEquals(7001L, ref.blockId()); + assertEquals(9001L, ref.workId()); + assertEquals(8001L, ref.chapterId()); + assertEquals(4, ref.revision()); + } + + @Test + void should_returnNullBlockWhenMissingOrBlank() { + when(blockMapper.selectById(7001L)).thenReturn(null); + assertNull(api.getBlockRef(7001L)); + assertNull(api.getBlockRef(null)); + } + +} diff --git a/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java b/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java index 5fe4dc79..e442559f 100644 --- a/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java +++ b/muse-cloud/muse-module-content/muse-module-content-server/src/test/java/cn/iocoder/muse/module/content/application/ContentSourceServiceTest.java @@ -48,6 +48,8 @@ class ContentSourceServiceTest extends BaseMockitoUnitTest { private ContentAuditService auditService; @Mock private ContentAiSuggestionFacade suggestionFacade; + @Mock + private ContentEventPublishOutboxService eventPublishOutboxService; @Test void should_throwForbidden_when_sourceAttributionWorkOwnerMismatch() { @@ -470,6 +472,9 @@ class ContentSourceServiceTest extends BaseMockitoUnitTest { argThat(summary -> summary.contains("\"operationId\":\"mergeBlockSuggestion\"") && summary.contains("\"suggestionId\":501")), eq("suggestion_merged"), eq("用户接受"), eq("succeeded")); + // 缺口 B 回归:merge 成功后必须写本域 outbox,驱动 source_status_change 经 SSE 回流。 + verify(eventPublishOutboxService).createForBlockSourceAttribution(eq(1001L), eq(101L), eq(301L), + eq(2), eq("cmd-merge-suggestion"), any(BlockSourceAttributionDO.class)); } @Test @@ -495,6 +500,8 @@ class ContentSourceServiceTest extends BaseMockitoUnitTest { verify(suggestionFacade, never()).getSuggestion(anyLong(), anyLong(), anyLong(), anyLong()); verify(blockMapper, never()).update(any(), any()); verify(sourceAttributionMapper, never()).insert(any(BlockSourceAttributionDO.class)); + // 幂等 replay 不得重复发事件,避免 SSE 回流重复通知。 + verify(eventPublishOutboxService, never()).createForBlockSourceAttribution(any(), any(), any(), any(), any(), any()); } private MergeBlockSuggestionReqVO mergeReq(String commandId, Integer expectedRevision, Long suggestionId, diff --git a/muse-cloud/muse-module-events/.agent b/muse-cloud/muse-module-events/.agent new file mode 100644 index 00000000..335f382b --- /dev/null +++ b/muse-cloud/muse-module-events/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-events(事件/SSE · Source/Authorization 横切) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §四/§五](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:Source/Authorization 横切上下文(事件驱动):Source Snapshot / Authorization Snapshot / Source Status Event / Source Propagation Target;统一事件 SSE 与内部 `EventsPublishApi.publish`(双重幂等 + schema 校验)。 +- **边界(不可违反)**:**非独立 source 模块**(ADR-017),来源对象 owner + outbox 协作;各消费模块自治监听并直接决策 active/disabled;传播失败 fail-default 禁用新使用,**不自动回滚已确认 Canonical**;守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:业务事实写入(各 owner 自负)。 +- **现状**:只读评估 88%;后端产线级,跨模块集成广泛(ai/knowledge/market/member/content 各有 EventPublishWorker + outbox,88 处引用)。 +- **关键风险 / TODO**:**前端 `connectAIStream` 契约漂移**(被 mock 掩盖,真实联调会失败);统一事件客户端为孤儿,无 UI 消费。 diff --git a/muse-cloud/muse-module-knowledge/.agent b/muse-cloud/muse-module-knowledge/.agent new file mode 100644 index 00000000..60205965 --- /dev/null +++ b/muse-cloud/muse-module-knowledge/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-knowledge(知识库 BC) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §5.3](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:Local KB / User KB / 全局知识处理 / Knowledge Draft / Knowledge Source Binding / 投影与索引状态。 +- **边界(不可违反)**:**进 Local KB 唯一入口=用户显式确认草稿或手动修正**;全书解析章节确认只产/推 Draft,不写正式知识;来源优先级 Local KB > 用户绑定 > 市场/全局;依赖 RAGFlow GraphRAG(不可用时 Unavailable 优雅降级);守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:正文写入(归 content)。 +- **现状**:只读评估 72%;后端最完整模块之一(59 端点对齐契约),RAGFlow 真 HTTP + SSRF 防护。 +- **关键风险 / TODO**:前端仅约 1/3 面(草稿确认/绑定/发布/图谱缺 hook);**Task4 跨 BC owner 计数未实现**(占位 'not implemented');recheck/worker 默认未生效。 diff --git a/muse-cloud/muse-module-market/.agent b/muse-cloud/muse-module-market/.agent new file mode 100644 index 00000000..b68c8112 --- /dev/null +++ b/muse-cloud/muse-module-market/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-market(市场/资产 BC) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §5.4](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:市场资产(Work/Agent/KB Asset)/ Publish Draft / Publish Check Snapshot / Review / Listing / License / Purchase / Install / Handoff Token / 授权摘要 / 跳转审计 / Governance Action / Source Status Event。 +- **边界(不可违反)**:**授权 ≠ 所有权转移**;市场**非**源事实 owner,目标事实回目标 owner;Work 资产仅只读且排除出 install(`INSTALLABLE_ASSET_TYPES=Set.of(agent,knowledge_base)`);守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:完整电商支付结算 DRM(项目非目标);模板化/参考写入/AI 上下文受 Feature Gate 默认关闭。 +- **现状**:只读评估 82%;核心不变式已被代码强制。 +- **关键风险 / TODO**:studio 仅消费侧(浏览/授权/安装);**生产侧发布/上架/申诉无 UI**,资产飞轮在 UI 层断裂。 diff --git a/muse-cloud/muse-module-member/.agent b/muse-cloud/muse-module-member/.agent new file mode 100644 index 00000000..89be8318 --- /dev/null +++ b/muse-cloud/muse-module-member/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-member(账户/个人中心 · Account BC 物理承载) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §四/§五](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:Account/Usage/Audit BC——Profile / Entitlement-Quota / Usage Record / Personal Center Summary / 记录总览 / Security Event / New-API Binding / Audit Log / Download Credential。**member = Account BC 物理承载,不新增 account 模块**。 +- **边界(不可违反)**:是用户可见权益的聚合**读模型**与跳转入口,**不是消费账本事实源,不能反写**作品/知识/智能体/市场事实;高危审计 append-only + WORM/哈希链;Entitlement/Quota/Usage/Security/Binding 唯一写入 authority;守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:他域事实写入。 +- **现状**:只读评估 68%。 +- **关键风险 / TODO**:**跨 BC facade 全 Unavailable → 约 21/33 端面运行期 `*_UNAVAILABLE`**;前端仅约 21% 面且跑 MSW mock。 diff --git a/muse-cloud/muse-module-meta/.agent b/muse-cloud/muse-module-meta/.agent new file mode 100644 index 00000000..86946d7e --- /dev/null +++ b/muse-cloud/muse-module-meta/.agent @@ -0,0 +1,9 @@ +# .agent — muse-module-meta(元治理/MetaSchema BC) + +> 模块跨会话记忆。真实现状权威见 [现状基线 §四/§五](../../docs/agent-specs/2026-06-13-项目目标与模块现状基线.md);进度见 [总账](../../docs/mvp/进度总账.md)。本文件只记目标/边界/out-of-scope/现状指针/TODO,过时即更。 + +- **目标(owner 职责)**:元结构定义——字段类型/校验/枚举/引用/领域/范围/目标类型/可见性/AI 上下文/导出语义;控制 uiVisible / aiContext / userEditable / userSearchable / exportable。 +- **边界(不可违反)**:admin 写全局定义,用户作品级覆盖扩展字段;业务模块经 **facade-api 只读消费投影**;ADR-016 已拆为独立模块;守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。 +- **out-of-scope**:业务数据本身。 +- **现状**:只读评估 82%;契约高度一致。 +- **关键风险 / TODO**:**impact-preview facade 仅 Unavailable → 写链路运行期端到端阻断**;facade-api 缺失,跨 BC 投影消费契约未建立;`后端-03 §3` 残留旧表述待统一([contract-first](../../.agents/rules/contract-first.md))。 diff --git a/muse-cloud/muse-server/pom.xml b/muse-cloud/muse-server/pom.xml index 4119a04d..267e4e98 100644 --- a/muse-cloud/muse-server/pom.xml +++ b/muse-cloud/muse-server/pom.xml @@ -145,6 +145,13 @@ muse-spring-boot-starter-test test + + + com.tngtech.archunit + archunit-junit5 + 1.3.0 + test + diff --git a/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java index 42975820..cc96ce49 100644 --- a/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java +++ b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java @@ -284,7 +284,13 @@ class P1rApiCoverageReportTest { completed++; } } - assertEquals(147, completed, "Account Security Events read completed approval 后 completed 总数只能从 145 增至 147"); + // P0 止血(2026-06-13):废弃硬编码 assertEquals(147, ...)。此前每次"收口"需人工把该常量与 + // JSON 互相对着拨大,形成自证回路(verification theater)。改为校验"operations 实算的 completed 数 + // == summary 自身声明的 completedOperations",防止只改其一造成台账自相矛盾,以实际 operations 为单一可信来源。 + int declaredCompleted = objectMapper.readTree(findReportPath().toFile()) + .path("summary").path("completedOperations").asInt(-1); + assertEquals(declaredCompleted, completed, + "summary.completedOperations 必须与 operations 实算的 completed 数一致(防手工拨动台账)"); assertOperationStatus("events", "streamEvents", "dedicated", "completed"); for (String operationId : APPROVED_META_SCHEMA_COMPLETED_OPERATIONS) { @@ -334,6 +340,38 @@ class P1rApiCoverageReportTest { assertFalse(report.contains("\"message\""), "覆盖报告不能使用 message 作为响应完成证据字段"); } + /** + * P0 止血(2026-06-13):completed 的 operation 必须引用磁盘上真实存在的 controller 与 service 文件, + * 杜绝“仅在 JSON 里自填标签即算 completed”的假绿。 + * + *

说明:本检查只保证“引用的源码文件存在”,不证明端到端可用——真实可用性由 P1 自动化用户旅程测试承担 + * (见 docs/agent-specs/2026-06-13-P0-止血冻结令.md)。

+ * + * @throws IOException 读取覆盖报告失败时抛出 + */ + @Test + void should_back_completed_operations_with_existing_source_files() throws IOException { + Path repoRoot = findRepoRoot(); + for (JsonNode operation : readOperations()) { + if (!"completed".equals(operation.path("completionStatus").asText())) { + continue; + } + String operationId = operation.path("operationId").asText(""); + assertTrue(operation.path("controllerFiles").size() > 0, + operationId + " 标记 completed 必须至少引用一个 controllerFiles"); + assertTrue(operation.path("serviceFiles").size() > 0, + operationId + " 标记 completed 必须至少引用一个 serviceFiles"); + for (JsonNode file : operation.path("controllerFiles")) { + assertTrue(Files.exists(repoRoot.resolve(file.asText())), + operationId + " 引用的 controller 文件不存在: " + file.asText()); + } + for (JsonNode file : operation.path("serviceFiles")) { + assertTrue(Files.exists(repoRoot.resolve(file.asText())), + operationId + " 引用的 service 文件不存在: " + file.asText()); + } + } + } + private JsonNode readOperations() throws IOException { return objectMapper.readTree(findReportPath().toFile()).path("operations"); } @@ -401,4 +439,18 @@ class P1rApiCoverageReportTest { return current.resolve("docs/superpowers/reports/p1r-api-coverage.json"); } + /** + * 定位仓库根目录(含 docs/superpowers/reports/p1r-api-coverage.json 的目录), + * 用于把 operation 的 controllerFiles/serviceFiles 相对路径(形如 muse-cloud/...)解析为绝对路径。 + */ + private static Path findRepoRoot() { + Path current = Path.of("").toAbsolutePath(); + for (Path candidate = current; candidate != null; candidate = candidate.getParent()) { + if (Files.exists(candidate.resolve("docs/superpowers/reports/p1r-api-coverage.json"))) { + return candidate; + } + } + return current; + } + } diff --git a/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java new file mode 100644 index 00000000..54169fbd --- /dev/null +++ b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java @@ -0,0 +1,47 @@ +package cn.iocoder.muse.server.framework.arch; + +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.lang.ArchRule; +import org.junit.jupiter.api.Test; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +/** + * BC 边界机械门禁(agent 基建 / 机械门禁优先)。 + * + *

规则:一个业务域(BC)不得直连他域的 DAL(dataobject/mysql Mapper);跨 BC 只能走对外 API / facade-api 契约。 + * 这是把“边界靠自觉”升级为“机械阻断”——对抗复盘 A4 已实证:软约束下 AI 模块直连了 content 的 DAL。

+ * + *

规则与已知违例登记见 {@code .agents/rules/bc-boundaries.md};本测试随 CI(P0 已打开测试门)在 PR/push 阻断新违例。

+ */ +class BcBoundaryArchTest { + + /** 只分析本项目业务模块的 main 字节码(排除测试类),覆盖 muse-server 聚合的全部 BC。 */ + private final JavaClasses museModuleClasses = new ClassFileImporter() + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .importPackages("cn.iocoder.muse.module"); + + /** + * AI 域不得直连他域 DAL。 + * + *

跨 BC 只能走对外 API/facade-api 契约;任何 AI 类对他域 DAL(DO/Mapper)的依赖都会让本测试变红、阻断合入。 + * 历史违例 {@code ContentMuseWorkOwnerFacade} 已整改(改消费 content 对外 API {@code MuseContentWorkOwnerApi}), + * 故此处**不再设任何豁免**——门禁已收紧(整改记录见 .agents/rules/bc-boundaries.md §三)。

+ */ + @Test + void ai_bc_must_not_depend_on_other_bc_dal() { + ArchRule rule = noClasses() + .that().resideInAPackage("..muse.module.ai..") + .should().dependOnClassesThat().resideInAnyPackage( + "..muse.module.content.dal..", + "..muse.module.knowledge.dal..", + "..muse.module.market.dal..", + "..muse.module.meta.dal..", + "..muse.module.member.dal..") + .because("BC 边界:AI 不得直连他域 DAL(DO/Mapper),跨 BC 只能走对外 API/facade-api 契约;" + + "历史违例 ContentMuseWorkOwnerFacade 已整改消除,门禁不再豁免任何 AI 类"); + rule.check(museModuleClasses); + } +} diff --git a/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java new file mode 100644 index 00000000..1e91f406 --- /dev/null +++ b/muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/contract/ContractFirstGateTest.java @@ -0,0 +1,158 @@ +package cn.iocoder.muse.server.framework.contract; + +import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * 契约先行(contract-first)机械门禁。 + * + *

本项目失控根因之一是“假绿”——契约与 DB schema 靠自觉维护、无机械校验,易漂移、易被删改而不被发现。 + * 本测试把“契约先行”从文档纪律升级为 CI 可阻断的机械门禁,覆盖两类契约: + *

    + *
  1. DB schema 契约:Flyway 迁移(SSOT = {@code muse-cloud/sql/muse/V*.sql};application.yaml 的 + * {@code spring.flyway.locations} 含 {@code filesystem:sql/muse},运行时真实读取)。无需起库,纯静态校验版本与命名。
  2. + *
  3. API 契约:OpenAPI 规格(SSOT = {@code docs/api-contracts/<域>/openapi.yaml} + {@code openapi-base.yaml})。 + * 校验存在性与结构合法,防止被误删/写坏。
  4. + *
+ * 规则与扩展见 {@code .agents/rules/contract-first.md};本测试随 CI(P0 已打开测试门、JDK21)在 PR/push 阻断。

+ */ +class ContractFirstGateTest { + + /** Flyway 迁移文件名规范:V<版本号>__<下划线描述>.sql(与 Flyway versioned migration 默认命名一致)。 */ + private static final Pattern FLYWAY_NAME = Pattern.compile("^V(\\d+)__[A-Za-z0-9_]+\\.sql$"); + + /** 必须存在的业务域 API 契约(= 当前 BC 对外契约集;新增 BC 须在此登记其 OpenAPI 契约,见 bc-boundaries.md)。 */ + private static final Set REQUIRED_API_DOMAINS = Set.of( + "account", "ai", "content", "events", "knowledge", "market", "meta"); + + /** OpenAPI 规格必须具备的顶层键。 */ + private static final Set REQUIRED_OPENAPI_KEYS = Set.of("openapi", "info", "paths"); + + /** + * DB schema 契约门禁:Flyway 迁移版本必须唯一、连续(1..N 无缺口)、命名规范。 + * + *

为何:重复版本号会让 Flyway 启动直接报错;缺口/乱名通常意味迁移被误删或改名——属破坏性变更, + * 必须在合入前机械拦截,而非等部署时炸。本检查不起库,只做静态校验,适配无 DB 的单测 CI。

+ * + * @throws IOException 读取迁移目录失败时抛出 + */ + @Test + void flyway_migrations_must_have_unique_contiguous_versions_and_valid_names() throws IOException { + Path migrationDir = findRepoRoot().resolve("muse-cloud/sql/muse"); + assertTrue(Files.isDirectory(migrationDir), "Flyway 迁移 SSOT 目录必须存在: " + migrationDir); + + // 收集全部 .sql 迁移并按文件名规范解析出版本号 + List versions = new ArrayList<>(); + try (Stream files = Files.list(migrationDir)) { + List sqlFiles = files + .filter(p -> p.getFileName().toString().endsWith(".sql")) + .sorted() + .toList(); + assertFalse(sqlFiles.isEmpty(), "迁移目录必须至少包含一个 .sql 迁移: " + migrationDir); + for (Path file : sqlFiles) { + String name = file.getFileName().toString(); + Matcher matcher = FLYWAY_NAME.matcher(name); + assertTrue(matcher.matches(), "迁移文件名不符合 V<版本>__<描述>.sql 规范: " + name); + versions.add(Integer.parseInt(matcher.group(1))); + } + } + + // 唯一性:重复版本号会导致 Flyway 启动报错 + Set unique = new TreeSet<>(versions); + assertEquals(versions.size(), unique.size(), "Flyway 迁移版本号必须唯一(出现重复): " + versions); + + // 连续性:1..max 无缺口,缺口通常意味迁移被误删/改名(破坏性变更) + int max = unique.stream().max(Integer::compareTo).orElse(0); + for (int v = 1; v <= max; v++) { + assertTrue(unique.contains(v), + "Flyway 迁移版本号必须从 1 连续到 " + max + ",缺失 V" + v + + "(若为有意跳号,请在本测试与 contract-first.md 登记说明)"); + } + } + + /** + * API 契约门禁:每个业务域的 OpenAPI 规格必须存在且结构合法(可解析 + 含 openapi/info/paths 顶层键)。 + * + *

为何:契约是前后端与跨 BC 协作的 SSOT,被误删/写坏却无人察觉会让“契约先行”形同虚设。 + * 本检查保证契约文件在、且是合法 OAS3 骨架;破坏性变更检测(openapi-diff)作为 CI 级加固另行启用, + * 见 contract-first.md。

+ * + * @throws IOException 读取契约目录失败时抛出 + */ + @Test + void api_contracts_must_exist_and_be_structurally_valid() throws IOException { + Path contractsDir = findRepoRoot().resolve("docs/api-contracts"); + assertTrue(Files.isDirectory(contractsDir), "API 契约 SSOT 目录必须存在: " + contractsDir); + + // 基础契约必须在 + assertValidOpenApi(contractsDir.resolve("openapi-base.yaml")); + + // 遍历实际发现的业务域(排除 generated 产物目录),逐一校验其 openapi.yaml + Set foundDomains = new TreeSet<>(); + try (Stream dirs = Files.list(contractsDir)) { + for (Path dir : dirs.filter(Files::isDirectory).toList()) { + String domain = dir.getFileName().toString(); + if ("generated".equals(domain)) { + continue; // openapi-generator 产物,非手写契约 SSOT + } + Path spec = dir.resolve("openapi.yaml"); + assertTrue(Files.exists(spec), "业务域缺少 OpenAPI 契约: " + spec); + assertValidOpenApi(spec); + foundDomains.add(domain); + } + } + + // 必备业务域不得整体缺失(防整域契约被删) + assertTrue(foundDomains.containsAll(REQUIRED_API_DOMAINS), + "缺少必备业务域 API 契约,期望包含 " + REQUIRED_API_DOMAINS + ",实际 " + foundDomains); + } + + /** 解析一份 OpenAPI 规格并校验其为含必备顶层键的 OAS3 文档(解析失败会抛异常 → 测试红,即“写坏即拦截”)。 */ + private static void assertValidOpenApi(Path spec) throws IOException { + assertTrue(Files.exists(spec), "OpenAPI 契约文件必须存在: " + spec); + Object root; + try (InputStream in = Files.newInputStream(spec)) { + root = new Yaml().load(in); + } + assertTrue(root instanceof Map, spec + " 必须是合法 YAML 映射"); + @SuppressWarnings("unchecked") + Map map = (Map) root; + for (String key : REQUIRED_OPENAPI_KEYS) { + assertTrue(map.containsKey(key), spec + " 缺少 OpenAPI 顶层键: " + key); + } + assertTrue(String.valueOf(map.get("openapi")).startsWith("3."), spec + " 必须是 OpenAPI 3.x"); + } + + /** + * 从 surefire 执行目录逐级上溯,定位同时含 {@code muse-cloud/sql/muse} 与 {@code docs/api-contracts} 的仓库根, + * 适配 surefire 在不同模块目录执行时的相对路径差异。 + */ + private static Path findRepoRoot() { + Path current = Path.of("").toAbsolutePath(); + for (Path candidate = current; candidate != null; candidate = candidate.getParent()) { + if (Files.isDirectory(candidate.resolve("muse-cloud/sql/muse")) + && Files.isDirectory(candidate.resolve("docs/api-contracts"))) { + return candidate; + } + } + return current; + } + +}