oh-my-muse/docs/agent-specs/2026-06-11-P1RMarketCompletedApproval执行版.md
zizi 00327749a2 test(p1r): 收口 Market 第一批 completed approval 门禁
只推进 Market 4 个 operation-level completed approval,保持 market domain-level allowlist 关闭,并保留剩余 28 个 Market operation 为 needs_verification。
2026-06-11 18:56:54 +08:00

739 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<br/>32 dedicated / needs_verification"] --> Exec["执行版<br/>锁定第一批 4 个 operation"]
Exec --> Review["fresh execution spec/scope review<br/>fresh execution quality/feasibility review"]
Review --> Approval{"用户明确批准<br/>4 ops / operation-level / OpenAPI commandId / allowed diff"}
Approval -->|否| Stay["保持当前 coverage<br/>233/127/106"]
Approval -->|是| Red["TDD RED<br/>先改 Market/Coverage/legacy gates<br/>旧 report 必须失败"]
Red --> Green["TDD GREEN<br/>Market OpenAPI commandId + scanner operation allowlist + regenerate report"]
Green --> Verify["focused + P1R mixed + Flyway _test<br/>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` 当前未声明该 headercoverage 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 completed28 个继续 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 1TDD 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 2OpenAPI 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 3TDD 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 4Market 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 evidenceP1R gate 必须继续断言它们是 `needs_verification`
如测试数量因补断言增加XML 检查的最低 tests 可高于上表,但不能低于上表。
### Task 5Market 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 7P1R 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 9memory 与 `.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 10fresh implementation review
实现、验证和文档留痕后,必须派发两类 fresh reviewer
- spec/correctness reviewer检查 4 个 operation 是否严格匹配本执行版28 个 operation 是否仍 needs_verificationMarket 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。
实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。