# 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。
实现完成条件必须等用户批准后另行满足,不由本执行版写入自动成立。