# P1R Account Stage A New-API / Quota / Integration 解锁执行版 日期:2026-06-14 ## 结论 本执行版只用于 Stage A 的 implementation plan 审阅,不是 implementation 授权,也不是 completed approval 授权。 Stage A 覆盖 Account 剩余 21 个 operation 中的 9 个 New-API / quota / integration operation。当前仓库已经具备 dedicated Controller、Service、Mapper 和部分单元测试,但仍缺两类关键闭环: 1. `NewApiAccountFacade` 的默认 fail-closed Bean 仍以 `@Component` 注册,真实 facade 与 unavailable facade 并存时存在静默选错或启动歧义风险。 2. 已验证的 New-API 外部证据只覆盖 OpenAI-compatible chat runtime,不覆盖 Account 绑定、重验、额度请求管理端点;不能凭空写 HTTP adapter 并把它当 completed 证据。 推荐路线: ```text A0 先做 facade 装配硬化和本地 HTTP+DB 闭环准备 A1 查证 New-API Account 管理端点合同 A2/A3 在合同查证后实现 binding runtime 与 quota submit worker A4 用 Stage A HTTP+DB + opt-in live acceptance 形成 unlock evidence 之后另起 operation-level completed approval ``` ## 当前事实基线 工作区: ```text /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 ``` 本轮 final review 起点基线: ```text main...origin/main HEAD=862fe9e docs(p1r): 收敛 Account Stage A 解锁计划 origin/main=862fe9e origin/dev/1.0.0=862fe9e ``` 当前 coverage summary: ```text total=233 completed=147 needsVerification=86 incomplete=0 genericPersistence=0 ssePlaceholder=0 ``` 当前 Account 状态: ```text total=33 completed=12 needsVerification=21 ``` ## Stage A 范围 Stage A 只覆盖以下 9 个 operation: | operation | method | path | commandId | |---|---|---|---| | `account:adminListNewApiBindings` | GET | `/admin-api/muse/account/new-api-bindings` | no | | `account:adminCreateNewApiBinding` | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | yes | | `account:getAppNewApiBinding` | GET | `/app-api/muse/account/new-api-binding` | no | | `account:appRecheckNewApiBinding` | POST | `/app-api/muse/account/new-api-binding/recheck` | yes | | `account:adminCreateQuotaRequest` | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | yes | | `account:appCreateQuotaRequest` | POST | `/app-api/muse/account/quota-requests` | yes | | `account:appGetQuotaRequest` | GET | `/app-api/muse/account/quota-requests/{requestId}` | no | | `account:adminGetIntegrationCallByCorrelation` | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | no | | `account:appGetIntegrationCallByCorrelation` | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | no | 本执行版不覆盖: - Account FileService / export / download 3 个 operation。 - Account usage / attribution 4 个 operation。 - Market projection 4 个 operation。 - security ack / session revoke 1 个 operation。 - Account domain-level completed。 - scanner、coverage report、OpenAPI 合同变更。 ## 已验证代码事实 当前 Stage A 相关主文件: - `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingServiceImpl.java` - `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java` - `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceImpl.java` - `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/NewApiAccountFacade.java` - `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableNewApiAccountFacade.java` 当前行为事实: - `AccountNewApiBindingServiceImpl.adminCreateNewApiBinding` 已通过 `NewApiAccountFacade.createOrRefreshBinding` 写入 `MemberNewApiBindingDO` 和 `AccountIntegrationCallDO`;外部不可用时通过 `REQUIRES_NEW` 写入 failed/unavailable integration call 后抛错。 - `AccountNewApiBindingServiceImpl.appRecheckNewApiBinding` 已通过 `NewApiAccountFacade.createRecheckJob` 创建 recheck job,并要求 job status 只能是 `queued` 或 `processing`。 - `UnavailableNewApiAccountFacade` 当前有 `@Component`,会作为默认 Bean 进入容器;这与已有 `AccountFileServiceFacadeAutoConfiguration`、`MarketAccountProjectionFacadeAutoConfiguration` 的 `@ConditionalOnMissingBean` 模式不一致。 - `NewApiAccountFacade` 当前只有 binding sync 与 recheck job 两组方法,没有 quota submit 方法。 - `AccountQuotaServiceImpl.adminCreateQuotaRequest` 与 `appCreateQuotaRequest` 当前只创建本地 `AccountIntegrationCallDO(status=pending_attribution)` 和 `AccountQuotaRequestDO(status=queued)`,没有提交真实 New-API runtime。 - `AccountUsageServiceImpl` 当前直接读取 `AccountIntegrationCallDO`,admin/detail 不做 owner guard,app/detail 做 owner guard;状态映射包含 `pending -> pending_attribution`、`unavailable -> failed`、未知状态归一为 `failed`。 - OpenAPI 中 `QuotaRequestResult.status` 只允许 `queued`、`processing`、`idempotent_hit`;`QuotaRequestStatus.status` 允许 `queued`、`processing`、`completed`、`failed`、`idempotent_hit`。 - 现有外部验收留痕只证明 New-API `/v1/chat/completions` live acceptance,不证明 Account binding / quota management endpoint 可用。 ## 架构边界 Stage A 必须保持以下边界: ```mermaid flowchart LR Admin["Admin/App Account API"] --> Account["Account Service
command / audit / local facts"] Account --> Binding["muse_member_new_api_binding"] Account --> Quota["muse_account_quota_request"] Account --> Call["muse_account_integration_call"] Account --> Facade["NewApiAccountFacade"] Facade --> Runtime["Verified New-API Account Runtime"] Runtime -. "callback or worker result" .-> Account ``` 规则: - Account 子域持有 Account API、本地 command、audit、binding、quota request、integration call 事实。 - New-API 只能通过 `NewApiAccountFacade` 或其明确拆分后的同 owner facade 接入,不能由 Account service 直接散落 HTTP 调用。 - 没有查证的 New-API Account 管理端点不得被写进真实 adapter。 - unavailable facade 必须 fail closed,不能返回伪造成功。 - quota request 推荐采用 Account 接收、审计、入队,再由 worker/submitter 提交 New-API;不要在 HTTP 请求事务中直接等待外部调用完成。 - Stage A 默认不新增 Events outbox;command、audit、quota request、integration call 是本阶段审计边界。 ## 执行切片 ### A0:facade 装配硬化 目标:先消除 `UnavailableNewApiAccountFacade @Component` 带来的真实 facade 装配风险。 允许在用户批准 implementation 后修改: - Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableNewApiAccountFacade.java` - Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/autoconfigure/member/account/NewApiAccountFacadeAutoConfiguration.java` - Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` - Create: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/facade/NewApiAccountFacadeConfigurationTest.java` 必须实现的测试事实: - 无真实 `NewApiAccountFacade` Bean 时,容器只有 1 个 facade,类型为 `UnavailableNewApiAccountFacade`。 - 有真实 `NewApiAccountFacade` Bean 时,unavailable fallback 不注册,`context.getBean(NewApiAccountFacade.class)` 返回真实 Bean。 - `AutoConfiguration.imports` 包含 `NewApiAccountFacadeAutoConfiguration`。 - fallback 调用 `createOrRefreshBinding`、`createRecheckJob` 时均 fail closed;若 A3 扩展 quota 方法,quota fallback 也必须 fail closed。 ### A1:New-API Account 管理端点合同查证 目标:在实现真实 HTTP adapter 前,查证 Account 绑定、重验、额度请求的真实 New-API 管理端点。 查证来源优先级: 1. 本机或远端 `new-api` 代码、OpenAPI、README、路由表。 2. 当前部署 `http://100.64.0.8:3000` 的只读管理接口或健康接口。 3. 用户提供的系统管理令牌与端点说明。 必须形成的证据: - binding create/refresh 端点:method、path、auth、request、response、错误码。 - binding recheck 端点:method、path、auth、request、response、job status 枚举、错误码。 - quota submit 端点:method、path、auth、request、response、request/job id、状态枚举、错误码。 - timeout、5xx、401/403、409、业务错误的失败分类。 - 哪些字段允许写入 `requestSummary` / `responseSummary`,哪些必须脱敏。 如果 A1 无法查证上述端点,Stage A implementation 只能完成 A0 和本地闭环测试准备,不能实现 `HttpNewApiAccountFacade`,不能把 9 个 operation 推进 completed。 ### A2:binding runtime adapter 只有 A1 查证通过后,才允许实现真实 binding adapter。 候选文件: - Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountNewApiProperties.java` - Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountNewApiConfiguration.java` - Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/HttpNewApiAccountFacade.java` - Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/NewApiAccountFacade.java` 属性命名建议: ```text muse.account.new-api.enabled muse.account.new-api.base-url muse.account.new-api.token muse.account.new-api.connect-timeout-seconds muse.account.new-api.read-timeout-seconds muse.account.new-api.max-attempts muse.account.new-api.retry-backoff-seconds ``` 合同要求: - 关闭 `enabled` 时不注册真实 facade,回落 A0 fallback。 - token 不写日志、不写测试报告、不进入 `requestSummary` / `responseSummary`。 - adapter 必须带 correlationId、commandId、accountUserId、bindingId。 - `createOrRefreshBinding` 只有在 New-API 返回非空 `newApiUserId` 后才能写本地 `bound`。 - `createRecheckJob` 只有在 New-API 返回非空 `jobId` 且 status 属于 `queued` / `processing` 后才能成功。 - timeout、认证失败、不可用、未知 response 均映射为 fail closed,不伪造 job 或 binding。 ### A3:quota submit worker 目标:补齐 `adminCreateQuotaRequest`、`appCreateQuotaRequest`、`appGetQuotaRequest` 和 integration detail 的 runtime 闭环。 推荐设计: - `AccountQuotaServiceImpl` 继续负责 HTTP create:校验 user、requestType、command 幂等,创建 `AccountQuotaRequestDO(status=queued)` 和 `AccountIntegrationCallDO(status=pending_attribution)`。 - 新增 `AccountQuotaRequestWorker` 或 `AccountQuotaRequestSubmitter` 负责把 queued request 提交给 `NewApiAccountFacade`。 - `NewApiAccountFacade` 增加 quota submit 合同,而不是在 `AccountQuotaServiceImpl` 内直接写 HTTP。 候选接口扩展: ```text NewApiAccountFacade.submitQuotaRequest(QuotaSubmitCommand) NewApiAccountFacade.QuotaSubmitResult ``` `QuotaSubmitCommand` 必须包含: - `accountUserId` - `requestId` - `commandId` - `correlationId` - `requestType` - `requestedAmount` - `side` `QuotaSubmitResult` 必须包含: - `externalCallId` - `status` - `responseSummary` - `failureCode` - `failureMessage` 状态规则: - create API 初始只返回 OpenAPI 允许的 `queued` 或 `processing`,不能返回 `completed`。 - worker claim 后可把 quota request 更新为 `processing`。 - New-API 接受成功但异步执行时,`appGetQuotaRequest` 返回 `processing`。 - New-API 明确终态成功时,`appGetQuotaRequest` 返回 `completed`。 - New-API 明确失败或不可恢复错误时,`appGetQuotaRequest` 返回 `failed`,并写 `failureReason`。 - integration call 对应更新 `status`、`externalCallId`、`responseSummary`、`errorCode`、`errorMessage`、`finishedAt`。 并发与幂等要求: - 不新增 SQL migration 时,worker 必须复用现有字段承载重试:`muse_account_integration_call.retry_count`、`next_retry_at`、`status`、`errorCode`、`errorMessage`。 - worker claim 必须使用 tenant 范围内的条件更新,并冻结为单一路径:初次 claim 只允许 `quota_request.status=queued`;retry / lease 恢复 claim 只允许 `quota_request.status=processing` 且对应 `integration_call.next_retry_at <= now` 且 integration call 未 terminal。claim 成功后 quota request 统一保持或进入 `processing`,并同步把 integration call 改为 `retrying` 或等价运行态。 - lease 超时恢复必须由 `integration_call.next_retry_at` 表达;crash 后只扫描 `next_retry_at <= now` 且未 terminal 的记录。 - retry budget 必须由 stage implementation 固定,默认 `maxAttempts=3`;达到上限后 quota request 必须进入 `failed`,integration call 必须进入 `failed` 并写失败原因。 - tenant context 必须在 claim、submit、terminal update 前显式设置,推荐使用 `TenantContextHolder.setTenantId(...)` 并在 finally 中 clear。 - terminal update 必须带状态条件,只允许 `queued/processing` 进入 `completed/failed`,不得覆盖已 terminal 的 request。 - command replay 不能重复创建 quota request 或 integration call。 - 同 commandId 不同 request hash 必须冲突并 no-write。 - worker 重试不得覆盖已 terminal 的 request。 - 如果上述状态机需要新增字段或索引,必须先退出当前 Stage A unlock,另起 SQL migration 审批并把 V22 schema、mapper、rollback 写入新的 allowed diff。 worker 事务边界必须冻结为短事务模型: - `claimNext` 独立事务:用 tenant + request/call status + `next_retry_at` 条件原子 claim 一条 quota request;初次 claim 匹配 `queued`,重试和 crash 恢复只匹配 `processing + next_retry_at <= now`,并同步把 quota request 置为 `processing`、integration call 置为运行态,同时递增或记录本轮 attempt。该事务提交后才能调用外部 New-API。 - 外部 New-API submit 不得包在数据库事务内;调用前后必须保留可审计日志,日志只能包含 requestId/correlationId/tenantId/attempt/status/errorCode,不得写 token、secret 或完整外部响应。 - `finalizeSuccess` 独立事务:仅在 quota request 仍为 `processing` 且 integration call 仍为本轮运行态时写 `completed`、`externalCallId`、`responseSummary`、`finishedAt`,并清空 `next_retry_at`;若条件更新为 0,必须记录 conflict 日志并停止覆盖。 - `finalizeFailure` 独立事务:不可恢复失败或 maxAttempts 用状态条件写 quota request `failed`、integration call `failed`、`errorCode`、`errorMessage`、`finishedAt`,并清空 `next_retry_at`。 - `finalizeRetryable` 独立事务:runtime unavailable、timeout 或外部 5xx 等可重试错误只更新 integration call `errorCode/errorMessage/retry_count/next_retry_at`,quota request 必须保持 `processing`,不得回写 `queued`;下一轮 claim 必须由 `processing + next_retry_at <= now` 驱动;达到 `maxAttempts=3` 后必须走 `finalizeFailure`。 - 外部 submit 成功但 finalize 失败时,不得二次提交同一外部请求;后续恢复只能先查 integration call / externalCallId / correlationId 的已有结果,再决定 finalize 或 fail closed。 - Stage A HTTP+DB / focused tests 必须按 `queued -> processing -> completed/failed` 唯一路径覆盖:double-claim 只有一个 worker 成功、runtime unavailable 只产生 allowed-delta 且 request 保持 `processing`、finalize conflict 不覆盖 terminal fact、`processing + next_retry_at <= now` 可恢复、maxAttempts 后 terminal failed。 ### A4:HTTP+DB unlock gate Stage A implementation 必须新增 HTTP+DB `_test` 主证据,但该证据只证明 Muse Account 侧真实 HTTP 入口、真实服务、真实 mapper、真实 PostgreSQL 和 facade 合同可闭合;如果使用 test-local facade,不得把它写成外部 New-API completed 证据。 推荐新增: - Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountStageANewApiQuotaIntegrationUnlockIT.java` 必须覆盖: - V1-V21 clean migrate,数据库名包含 `_test`。 - `adminCreateNewApiBinding` success、replay、command conflict、New-API unavailable failed integration call。 - `adminListNewApiBindings` 能读取真实 `member_user` + `muse_member_new_api_binding`。 - `getAppNewApiBinding` owner summary,不暴露 token、provider authority 或 supplier route。 - `appRecheckNewApiBinding` success、invalid job status fail closed、missing binding、replay、command conflict。 - `adminCreateQuotaRequest` 和 `appCreateQuotaRequest` success、replay、command conflict、invalid requestType、missing user。 - quota worker success / failure / unavailable,验证 quota request 与 integration call 状态转移。 - `appGetQuotaRequest` owner guard、missing、terminal completed/failed、createdAt/completedAt。 - `adminGetIntegrationCallByCorrelation` 和 `appGetIntegrationCallByCorrelation` no-write、owner guard、敏感字段 allowlist、status/requestType/retryGroup 映射。 - 参数校验、幂等冲突、越权、cross tenant、missing resource 必须使用全表 no-write snapshot,至少覆盖 `muse_member_new_api_binding`、`muse_account_quota_request`、`muse_account_integration_call`、`muse_account_command`、`muse_account_audit`、`muse_account_event_publish_outbox`。 - runtime unavailable 不属于全表 no-write:只允许新增或更新一条 `failed/unavailable` integration call;必须用 allowed-delta snapshot 证明 binding/quota request、command succeeded fact、audit succeeded fact 和 outbox 均未产生成功事实。 可选 live acceptance: - Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountStageANewApiLiveAcceptanceIT.java` - 默认跳过,仅在显式环境变量开启并提供真实 New-API Account 管理端点配置时运行。 - 只有该类使用已查证的外部 New-API Account runtime;它不能依赖 test-local facade。 ## 验证命令 Maven root: ```bash cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud ``` A0 facade wiring: ```bash JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ mvn -pl muse-module-member/muse-module-member-server -am -Dtest=NewApiAccountFacadeConfigurationTest \ -Dsurefire.failIfNoSpecifiedTests=false test ``` Stage A focused member tests: ```bash JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ mvn -pl muse-module-member/muse-module-member-server -am \ -Dtest=AccountNewApiBindingServiceTest,AccountQuotaServiceTest,AccountUsageServiceTest \ -Dsurefire.failIfNoSpecifiedTests=false test ``` Stage A HTTP+DB `_test`: ```bash cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0/muse-cloud export P1R_ACCOUNT_STAGE_A_DB_NAME="muse_p1r_account_stage_a_test" export P1R_ACCOUNT_STAGE_A_JDBC_URL="jdbc:postgresql://127.0.0.1:5432/${P1R_ACCOUNT_STAGE_A_DB_NAME}" export P1R_ACCOUNT_STAGE_A_DB_USER="${P1R_ACCOUNT_STAGE_A_DB_USER:?set test db user}" export P1R_ACCOUNT_STAGE_A_DB_PASSWORD="${P1R_ACCOUNT_STAGE_A_DB_PASSWORD:?set test db password}" export P1R_FLYWAY_PASSWORD="${P1R_FLYWAY_PASSWORD:-${P1R_ACCOUNT_STAGE_A_DB_PASSWORD}}" JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ mvn -pl muse-server -am -Dtest=P1rAccountStageANewApiQuotaIntegrationUnlockIT \ -Dsurefire.failIfNoSpecifiedTests=false test \ -Dp1r.flyway.locations=filesystem:sql/muse \ -Dp1r.flyway.url="${P1R_ACCOUNT_STAGE_A_JDBC_URL}" \ -Dp1r.flyway.user="${P1R_ACCOUNT_STAGE_A_DB_USER}" \ -Dp1r.account.stage-a.jdbc-url="${P1R_ACCOUNT_STAGE_A_JDBC_URL}" \ -Dp1r.account.stage-a.jdbc-user="${P1R_ACCOUNT_STAGE_A_DB_USER}" ``` Stage A HTTP+DB IT 必须只从环境变量读取密码,禁止读取或传递 `p1r.*password` JVM system property。IT 内必须包含: - `assertNoPasswordSystemProperties()`:拒绝 `p1r.flyway.password`、`p1r.account.stage-a.jdbc-password` 等 password system property。 - `assertNoCredentialQuery()`:拒绝 JDBC URL query、日志或 evidence summary 中出现 password。 - `_test` URL 断言:数据库名必须包含 `_test`。 P1R mixed gates 仍不得推进 completed: ```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=P1rApiCoverageReportTest,P1rAccountRealApiGateTest,P1rEventsRealApiGateTest,P1rAiRealApiGateTest,P1rKnowledgeRealApiGateTest,P1rMarketRealApiGateTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest \ -Dsurefire.failIfNoSpecifiedTests=false test ``` coverage check 仍应保持当前值,并且必须在 repo root 运行: ```bash cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 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 ``` 期望仍为: ```text 233 147 86 0 0 0 ``` ## XML 防空跑 Stage A implementation 后必须检查这些 XML,具体文件名以实际类名为准: ```text muse-cloud/muse-module-member/muse-module-member-server/target/surefire-reports/TEST-cn.iocoder.muse.module.member.application.account.facade.NewApiAccountFacadeConfigurationTest.xml muse-cloud/muse-module-member/muse-module-member-server/target/surefire-reports/TEST-cn.iocoder.muse.module.member.application.account.AccountNewApiBindingServiceTest.xml muse-cloud/muse-module-member/muse-module-member-server/target/surefire-reports/TEST-cn.iocoder.muse.module.member.application.account.AccountQuotaServiceTest.xml muse-cloud/muse-module-member/muse-module-member-server/target/surefire-reports/TEST-cn.iocoder.muse.module.member.application.account.AccountUsageServiceTest.xml muse-cloud/muse-server/target/surefire-reports/TEST-cn.iocoder.muse.server.framework.api.P1rAccountStageANewApiQuotaIntegrationUnlockIT.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.P1rApiCoverageReportTest.xml ``` 所有目标 XML 必须满足: ```text tests > 0 failures = 0 errors = 0 skipped = 0 ``` ## allowed diff 当前文档阶段只允许 Account Remaining21 与 Stage A 文档变更: ```text docs/agent-specs/.agent docs/agent-specs/2026-06-14-P1RAccountRemaining21解锁审阅版.md docs/agent-specs/2026-06-14-P1RAccountRemaining21解锁执行版.md docs/agent-specs/2026-06-14-P1RAccountStageA-NewAPIQuotaIntegration解锁执行版.md ``` 后续用户明确批准 Stage A implementation 后,默认只允许以下类别: ```text muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/autoconfigure/member/account/NewApiAccountFacadeAutoConfiguration.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/** muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountNewApi*.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaRequestWorker.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaRequestSubmitter.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountQuotaRequestMapper.java muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java muse-cloud/muse-module-member/muse-module-member-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/** muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountStageANewApiQuotaIntegrationUnlockIT.java docs/agent-specs/.agent docs/memorys/** ``` 始终禁止: ```text docs/api-contracts/** muse-cloud/scripts/p1r-audit-api-coverage.py docs/superpowers/reports/** 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 ``` 如 implementation 必须修改禁止路径,必须停止 Stage A unlock,另起 OpenAPI 合同变更审批或 completed approval 审批。 Stage A diff gate 必须覆盖 tracked、cached 和 untracked 文件,不能只用 `git diff --name-only`: ```bash cd /Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 { 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 } | sort -u > /tmp/p1r-account-stage-a.diff-files python3 - <<'PY' from pathlib import Path allowed = ( "docs/agent-specs/", "docs/memorys/", "muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/autoconfigure/member/account/NewApiAccountFacadeAutoConfiguration.java", "muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/", "muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/framework/config/MuseAccountNewApi", "muse-cloud/muse-module-member/muse-module-member-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports", "muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/", "muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountStageANewApiQuotaIntegrationUnlockIT.java", ) protected = ( "docs/api-contracts/", "muse-cloud/scripts/p1r-audit-api-coverage.py", "docs/superpowers/reports/", "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", ) files = [line.strip() for line in Path("/tmp/p1r-account-stage-a.diff-files").read_text().splitlines() if line.strip()] violations = [path for path in files if path.startswith(protected) or not path.startswith(allowed)] if violations: raise SystemExit("diff gate violation:\n" + "\n".join(violations)) print("diff_gate=pass") PY ``` ## 回滚策略 - A0 可通过恢复 `NewApiAccountFacadeAutoConfiguration` / `AutoConfiguration.imports` / `UnavailableNewApiAccountFacade` 的最小 diff 回滚。 - A2/A3 必须保留 `muse.account.new-api.enabled=false` runtime switch;关闭后回到 fail-closed 或 queued-only。 - 已写入的 command、quota request、integration call 不做物理删除;通过状态和失败原因表达补偿。 - scanner/report 回滚只属于后续 completed approval,不属于 Stage A unlock。 ## 当前完成条件 本 Stage A 执行版自身完成条件: - 文档写入本文件。 - `.agent` 记录 Stage A 计划状态。 - 不修改 OpenAPI、scanner、coverage report、Account 业务实现、SQL migration 或测试代码。 - `git diff --check` 通过。 - protected diff 为空。 - fresh spec/scope review PASS。 - fresh quality/feasibility/testing review PASS。 通过后的下一步: 1. 用户明确批准 Stage A implementation。 2. implementation 先做 A0 RED/GREEN,再进入 A1 合同查证。 3. A1 查证失败则停止在 A0,不实现伪造 HTTP adapter。 4. A1 查证成功后继续 A2/A3/A4。 5. Stage A implementation fresh 双 review PASS 后,再决定是否另起 9 个 operation 的 completed approval。