Files
sub2api-cn-relay-manager/docs/2026-05-18-PRODUCTION_READINESS_REVIEW.md

409 lines
19 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.
# Sub2API CN Relay Manager 上线审查报告
日期2026-05-18
审查范围:`/home/long/project/sub2api-cn-relay-manager`
审查目标:评估当前实现是否与规划设计对齐,是否达到生产上线要求,并明确阻塞项、非阻塞项和建议整改路径。
> 状态更新2026-05-18 晚些时候):本报告识别出的 4 个系统性阻塞项已进入代码修复并已落地到当前分支;对应执行任务见 `docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md`。本报告保留为“发现问题时的审查快照”,最新门禁结论以整改板与执行板为准。
>
> 再次状态更新2026-05-18 最新真实宿主复验后):已在最新代码上重新生成两套真实宿主 artifact
> - `artifacts/real-host-acceptance/20260518_self_service_reaccept_v6`
> - `artifacts/real-host-acceptance/20260518_subscription_reaccept_v6`
>
> 结果:两条链路都未形成最终通过 artifact因此当前仍不能把项目从 `CONDITIONAL_APPROVED` 推进到最终放行。
>
> 最终状态更新2026-05-18 fresh redeploy 复验后):`artifacts/real-host-acceptance/20260518_redeploy_matrix` 已在全新 redeploy 宿主上确认两条真实普通用户访问链路都可打通:
> - `self_service`:普通用户 key 绑定标准 group 且用户具备可用余额后,`/v1/models -> 200`
> - `subscription`subscription 类型 group + 普通用户订阅分配 + key/group 绑定后,`/v1/models -> 200`
>
> 进一步状态更新2026-05-18 reconcile host-scope 复验后):已在最新代码上补充两套 host-scoped acceptance artifact
> - `artifacts/real-host-acceptance/20260518_reconcile_hostscope_self_service`
> - `artifacts/real-host-acceptance/20260518_reconcile_hostscope_subscription`
>
> 这两套新 artifact 补齐了 `status / resources / reconcile / batch detail / rollback` 的 host-scoped 证据链,进一步证明 `reconcile_runs` 带上 `host_id + batch_id` 后batch detail 的 reconcile 视图已不再按 provider 粗暴聚合。
>
> 因此本报告中的 `REJECT / CONDITIONAL_APPROVED` 结论已成为历史快照;当前最新真相以 `docs/EXECUTION_BOARD.md`、`docs/PRODUCTION_CLOSURE_BOARD.md`、`20260518_redeploy_matrix` 与 `20260518_reconcile_hostscope_*` artifact 为准。
## 一、审查结论
本节为“首次审查时的历史结论快照”,不再代表当前最新 gate
- 当时判断:代码层质量门禁整体通过,但真实宿主最终放行证据不足。
- 因此当时结论为“代码层 `CONDITIONAL_APPROVED`,真实宿主最终放行未完成”。
当前最新上线判定请以:
- `docs/EXECUTION_BOARD.md`
- `docs/PRODUCTION_CLOSURE_BOARD.md`
- `artifacts/real-host-acceptance/20260518_redeploy_matrix`
- `artifacts/real-host-acceptance/20260518_reconcile_hostscope_self_service`
- `artifacts/real-host-acceptance/20260518_reconcile_hostscope_subscription`
为准。
## 二、审查方法与证据
本次审查结合以下证据来源:
1. 设计与规划文档对齐
- [PRD.md](/home/long/project/sub2api-cn-relay-manager/docs/PRD.md:1)
- [TDD_PLAN.md](/home/long/project/sub2api-cn-relay-manager/docs/TDD_PLAN.md:1)
- [implementation-plan.md](/home/long/project/sub2api-cn-relay-manager/docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md:1)
- [EXECUTION_BOARD.md](/home/long/project/sub2api-cn-relay-manager/docs/EXECUTION_BOARD.md:1)
- [PRODUCTION_CLOSURE_BOARD.md](/home/long/project/sub2api-cn-relay-manager/docs/PRODUCTION_CLOSURE_BOARD.md:1)
2. 代码审查重点
- 控制面 API[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:1)
- 宿主适配器:[client.go](/home/long/project/sub2api-cn-relay-manager/internal/host/sub2api/client.go:1)
- 能力探测:[capability_probe.go](/home/long/project/sub2api-cn-relay-manager/internal/host/sub2api/capability_probe.go:1)
- 导入运行时:[runtime_import_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/runtime_import_service.go:1)
- 回滚:[rollback_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/rollback_service.go:1)
- 对账:[service.go](/home/long/project/sub2api-cn-relay-manager/internal/reconcile/service.go:1)
- batch detail[batch_detail_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/batch_detail_service.go:1)
- 状态库:[db.go](/home/long/project/sub2api-cn-relay-manager/internal/store/sqlite/db.go:1)
- 资源记录:[managed_resources_repo.go](/home/long/project/sub2api-cn-relay-manager/internal/store/sqlite/managed_resources_repo.go:1)
3. 本地质量门禁复核
- `gofmt -l .`:空输出
- `go vet ./...`:通过
- `go test ./...`:通过
- `go test -race ./...`:通过
- `go test -cover ./internal/...`:通过
- `internal/access``77.3%`
- `internal/pack``72.7%`
- `internal/provision``76.9%`
- `internal/store/sqlite``68.2%`
4. 真实宿主 artifact 复核
- 历史 `self_service` 成功样例(旧证据,现仅作历史对照):
- [05-import.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_openai_platform_fix_retest/05-import.json:1)
- [06-access-preview.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_openai_platform_fix_retest/06-access-preview.json:1)
- [07-access-status.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_openai_platform_fix_retest/07-access-status.json:1)
- 最新 `self_service` 复验(当前真相):
- [05-import.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_self_service_reaccept_v6/05-import.json:1)
- [06-access-preview.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_self_service_reaccept_v6/06-access-preview.json:1)
- [09-reconcile.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_self_service_reaccept_v6/09-reconcile.json:1)
- 最新 `subscription` 复验(当前真相):
- [05-import.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_subscription_reaccept_v6/05-import.json:1)
- [06-access-preview.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_subscription_reaccept_v6/06-access-preview.json:1)
- [09-reconcile.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260518_subscription_reaccept_v6/09-reconcile.json:1)
## 三、设计对齐判断
### 已对齐部分
- 满足“零宿主代码改动”的核心约束。
- 当前所有宿主交互都通过 `internal/host/sub2api` 适配层完成,未发现直接写宿主数据库或注入宿主目录的实现。
- 满足 MVP 的主链路目标。
- `pack` 装载、provider 解析、导入编排、账号探测、网关 `GET /v1/models` 检查、状态库存证据链都已具备。
- 控制面 API 基本覆盖当前计划要求。
- 对照 implementation plan 中列出的当前 API仓库已实现主要端点。
- 测试覆盖与门禁实现基本符合仓库自述。
### 未完全对齐部分
- implementation plan 中的“宿主管理对象”与运行时导入路径没有形成统一身份模型。
- implementation plan 默认呈现为“可管理宿主对象 + 持久化资源状态”的控制面,但当前状态模型仍偏向“单次导入任务持久化”,不足以支撑稳定的多次运维动作。
- PRD 把多宿主管理列为首版非目标,但仓库已经暴露 `hosts` 管理 API这意味着系统外观上已像多宿主管理器但状态语义并未真正收口。
## 四、阻塞项
以下问题阻塞“整体生产无条件上线”。
### 阻塞项 1宿主身份模型不统一`/api/hosts` 与导入/对账/回滚没有形成同一条资产链
严重级别:`High`
证据:
- `POST /api/hosts` 保存的是用户提供的 `name -> host_id`[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:53) [http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:1077)
- `POST /api/providers/{providerID}/import` 请求体没有 `host_id` 字段,只有 `host_base_url` 和临时认证信息,[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:168)
- `RuntimeImportService.Import()``HostID` 为空时直接退回到 `HostBaseURL` 作为宿主身份,[runtime_import_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/runtime_import_service.go:45)
影响:
- 先注册的宿主记录和后续真实导入批次可能落在两条不同的 `hosts` 记录上。
- `GET /api/hosts/{hostID}`、批次查询、provider 状态、回滚定位不会共享同一条宿主身份链。
- 这会直接削弱控制面的可运维性和可审计性。
结论:
- 当前宿主对象模型只具备“登记能力”,不具备稳定的“生命周期主键能力”。
### 阻塞项 2`managed_resources` 没有宿主维度,状态库存在跨宿主资源串扰风险
严重级别:`High`
证据:
- `managed_resources` 的唯一键是 `(resource_type, host_resource_id)`[0002_operational_runtime.sql](/home/long/project/sub2api-cn-relay-manager/internal/store/migrations/0002_operational_runtime.sql:17)
- repo 的资源身份查询也只按这两个字段判断,[managed_resources_repo.go](/home/long/project/sub2api-cn-relay-manager/internal/store/sqlite/managed_resources_repo.go:53)
- 运行时持久化遇到“资源已存在”就直接跳过,[runtime_import_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/runtime_import_service.go:238)
影响:
- 两个不同宿主只要资源 ID 恰好相同,就会被控制面当成同一条资源。
- rollback/reconcile 的依据会被污染。
- 该风险和宿主 ID 通常为自增整数的现实形态高度相容,不能假设不会发生。
结论:
- 这不是边界体验问题,而是状态建模缺陷。
### 阻塞项 3宿主能力探测存在副作用风险违背“零侵入宿主”目标的工程保守性
严重级别:`High`
证据:
- `ProbeCapabilities()` 直接对真实创建接口发空 `POST`
- `/api/v1/admin/groups`
- `/api/v1/admin/channels`
- `/api/v1/admin/payment/plans`
- `/api/v1/admin/accounts`
- `/api/v1/admin/subscriptions/assign`
- 见 [capability_probe.go](/home/long/project/sub2api-cn-relay-manager/internal/host/sub2api/capability_probe.go:10)
影响:
- 该实现假设宿主会把空请求稳定地当作“无副作用校验失败”处理。
- 一旦宿主版本行为变化、参数默认值变化或某接口宽松接受空载荷,探测可能制造脏资源。
- 这和 PRD 的“零侵入”承诺不冲突于字面,但明显冲突于生产工程保守性。
结论:
- 在真实生产宿主上,这类探测方式不可视为安全。
### 阻塞项 4`rollback-provider` 仍按同名资源扫描删除,不是按批次记录的真实资源集删除
严重级别:`High`
证据:
- `rollback-provider` 入口虽然先找到了 pack/provider/latest batch[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:981)
- 但实际执行仍走 `Rollback(ctx, RollbackRequest{Provider: providerManifest})`[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:1005)
- `Rollback()` 的实现会按名字重新枚举宿主资源再删除,[rollback_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/rollback_service.go:40)
- 更安全的 `RollbackStoredResources()` 已存在,但未被该路径采用,[rollback_service.go](/home/long/project/sub2api-cn-relay-manager/internal/provision/rollback_service.go:58)
影响:
- 在脏现场、残留现场、同名 provider 现场,会有误删风险。
- 当前真实 artifact 已经反复体现 reconcile 仍可能看到 `extra_count`,此时按名字删尤其不稳。
结论:
- 当前回滚策略还不满足“生产可放心执行”的标准。
## 五、非阻塞项
以下问题不一定阻塞受限范围上线,但会显著影响运维成熟度与文档可信度。
### 非阻塞项 1部署文档承诺高于实际实现
证据:
- `DEPLOYMENT.md``/metrics`、限流、监控接入写进了生产清单,[DEPLOYMENT.md](/home/long/project/sub2api-cn-relay-manager/docs/DEPLOYMENT.md:90)
- 实际路由只看到 `/healthz` 和控制面 API[http_api.go](/home/long/project/sub2api-cn-relay-manager/internal/app/http_api.go:193)
影响:
- 运维人员容易误以为仓库已内置观测与生产保护机制。
- 文档可信度低于代码可信度。
结论:
- 应补功能,或先下调文档承诺。
### 非阻塞项 2计划结构与物理目录仍有明显漂移
证据:
- 该评审形成时implementation plan 里期望的 `internal/reconcile/*``access/planner.go``worker/scheduler.go` 等结构仍未落地,[implementation-plan.md](/home/long/project/sub2api-cn-relay-manager/docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md:69)
- 截至 2026-05-22这些结构项已分别落到 `internal/reconcile/*``internal/access/{planner,subscription,self_service,validation}.go``internal/worker/*`
影响:
- 目前更像可运行 MVP而不是已经完成结构收敛的生产后端。
- 长期维护和职责边界清晰度会受影响。
结论:
- 属于已知结构债务,不阻塞受限范围上线,但不应被忽略。
### 非阻塞项 3`subscription` 模式缺少现成通过的真实闭环 artifact
证据:
- 当前看到的 `subscription` 真实宿主样例仍是:
- `batch_status=partially_succeeded`
- `provider_status=degraded`
- `access_status=broken`
- 见 [05-import.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_104007_subscription_after_fix/05-import.json:1)
- 访问预检也仍显示不可用,[06-access-preview.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_104007_subscription_after_fix/06-access-preview.json:1)
影响:
- `subscription` 不能被纳入当前上线放行范围。
结论:
- 该项虽已在执行板中被承认为剩余风险,但从上线审查角度仍需明确隔离。
### 非阻塞项 4`reconcile` 结果在真实宿主上仍有漂移
证据:
- `self_service` 成功样例中,`09-reconcile.json` 仍是 `status=drifted``extra_count=11`[09-reconcile.json](/home/long/project/sub2api-cn-relay-manager/artifacts/real-host-acceptance/20260517_openai_platform_fix_retest/09-reconcile.json:1)
影响:
- 说明系统主链路成功并不等于现场状态完全收敛。
- 当前更适合“可上线但需现场治理”,不适合“上线即稳定自治”。
结论:
- 该问题单独不阻塞 `self_service` 条件性放行,但阻塞“成熟运维能力”结论。
## 六、建议整改 PR 列表
以下 PR 列表按优先级排序。
### PR-1统一宿主身份模型导入/对账/回滚全面切换到 `host_id`
目标:
- 把宿主从“可登记对象”升级为“真实生命周期主对象”。
建议内容:
- 为导入、reconcile、rollback、assign-subscriptions 等请求增加显式 `host_id` 输入。
- 通过 `host_id` 查宿主记录并派生 `base_url`、认证策略。
- 禁止运行时再用 `HostBaseURL` 作为默认宿主主键。
- 梳理 `hosts``import_batches`、provider 状态接口的主键一致性。
预期收益:
- 彻底消除宿主对象分裂。
- 为后续多宿主治理和审计打基础。
### PR-2为 `managed_resources` 增加宿主维度并修复唯一约束
目标:
- 把资源身份从“宿主外的资源 ID”升级成“宿主内资源身份”。
建议内容:
-`managed_resources` 中增加 `host_id` 或等价外键。
- 唯一约束改为 `host_id + resource_type + host_resource_id`
- `GetByResourceIdentity()``ListByProviderID()`、持久化逻辑同步改造。
- 增加迁移和回归测试,覆盖“两个宿主同 ID 资源不串扰”场景。
预期收益:
- 消除跨宿主资源串写与误跳过问题。
### PR-3重写 `ProbeCapabilities`,改成无副作用探测
目标:
- 让能力探测满足生产保守性要求。
建议内容:
- 优先使用只读接口、标准版本接口或显式 capability endpoint。
- 无只读接口时,退化为“版本白名单 + 已知能力矩阵”。
- 至少不要再通过真实创建接口发空 `POST`
- 给真实宿主兼容矩阵补测试与文档。
预期收益:
- 避免宿主现场因 probe 被污染。
### PR-4让 `rollback-provider` 走批次资源集回滚,不再按名字重新扫描
目标:
- 让回滚动作真正基于控制面状态库,而不是宿主现场猜测。
建议内容:
- `rollback-provider` 先定位 latest batch再读取该 batch 的 managed resources。
- 统一调用 `RollbackStoredResources()`
- 名字扫描保留为显式“人工应急模式”,不要做默认路径。
- 增加“存在同名残留资源时不误删”的测试。
预期收益:
- 明显降低误删风险。
### PR-5补齐最小生产运维能力或下调部署文档口径
目标:
- 让文档承诺和代码现实一致。
建议内容:
- 二选一:
- 实装 `/metrics`、基础限流、最小审计日志
- 或把 `DEPLOYMENT.md` 中的生产清单改成“建议外挂能力”,不暗示已内建
预期收益:
- 避免上线预期与实际能力错配。
### PR-6补 subscription 真实闭环验收,并固化 artifact
目标:
-`subscription` 模式从“理论支持”转为“真实可放行”。
建议内容:
- 使用有效凭据复跑真实宿主验证。
- 固化 import / access-preview / access-status / reconcile / rollback artifact。
- 失败时把原因收敛到代码问题还是宿主现场问题。
预期收益:
- 决定 `subscription` 是否可以进入下一个放行窗口。
## 七、上线建议
### 可以上线的范围
- 单宿主
- OpenAI-compatible provider
- `self_service` 主链路
- 接受以下前提:
- reconcile 仍需现场治理
- 回滚不作为默认高频自动运维动作
- 不把 `/api/hosts` 视为稳定宿主管理源
### 不建议上线的范围
- `subscription` 模式对外承诺
- 多宿主统一治理
- 高信任自动回滚
- 把控制面当成已具备完整生产运维能力的平台使用
## 八、最终判定
综合设计对齐度、代码门禁、真实 artifact 和运维语义,本次审查给出如下正式判定:
1. 代码质量:`PASS`
2. MVP 主链路完整性:`PASS`
3. 生产运维闭环完整性:`FAIL`
4. 全量生产放行:`REJECT`
5. 单宿主 self-service 条件性放行:`CONDITIONAL_APPROVED`
## 九、附注
以下事项需要避免被错误解读:
- `go test``race``vet` 全通过,不等于已经具备完整生产运维语义。
- `self_service` 成功,不等于 `subscription` 已成功。
- 当前存在 `hosts` API不等于宿主对象模型已经真正完成。
- 当前有 rollback 能力,不等于 rollback 已达到低误删风险的生产标准。