19 KiB
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_v6artifacts/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 -> 200subscription: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_serviceartifacts/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.mddocs/PRODUCTION_CLOSURE_BOARD.mdartifacts/real-host-acceptance/20260518_redeploy_matrixartifacts/real-host-acceptance/20260518_reconcile_hostscope_self_serviceartifacts/real-host-acceptance/20260518_reconcile_hostscope_subscription
为准。
二、审查方法与证据
本次审查结合以下证据来源:
-
设计与规划文档对齐
-
代码审查重点
- 控制面 API:http_api.go
- 宿主适配器:client.go
- 能力探测:capability_probe.go
- 导入运行时:runtime_import_service.go
- 回滚:rollback_service.go
- 对账:service.go
- batch detail:batch_detail_service.go
- 状态库:db.go
- 资源记录:managed_resources_repo.go
-
本地质量门禁复核
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%
-
真实宿主 artifact 复核
- 历史
self_service成功样例(旧证据,现仅作历史对照): - 最新
self_service复验(当前真相): - 最新
subscription复验(当前真相):
- 历史
三、设计对齐判断
已对齐部分
- 满足“零宿主代码改动”的核心约束。
- 当前所有宿主交互都通过
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 http_api.goPOST /api/providers/{providerID}/import请求体没有host_id字段,只有host_base_url和临时认证信息,http_api.goRuntimeImportService.Import()在HostID为空时直接退回到HostBaseURL作为宿主身份,runtime_import_service.go
影响:
- 先注册的宿主记录和后续真实导入批次可能落在两条不同的
hosts记录上。 GET /api/hosts/{hostID}、批次查询、provider 状态、回滚定位不会共享同一条宿主身份链。- 这会直接削弱控制面的可运维性和可审计性。
结论:
- 当前宿主对象模型只具备“登记能力”,不具备稳定的“生命周期主键能力”。
阻塞项 2:managed_resources 没有宿主维度,状态库存在跨宿主资源串扰风险
严重级别:High
证据:
managed_resources的唯一键是(resource_type, host_resource_id),0002_operational_runtime.sql- repo 的资源身份查询也只按这两个字段判断,managed_resources_repo.go
- 运行时持久化遇到“资源已存在”就直接跳过,runtime_import_service.go
影响:
- 两个不同宿主只要资源 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
影响:
- 该实现假设宿主会把空请求稳定地当作“无副作用校验失败”处理。
- 一旦宿主版本行为变化、参数默认值变化或某接口宽松接受空载荷,探测可能制造脏资源。
- 这和 PRD 的“零侵入”承诺不冲突于字面,但明显冲突于生产工程保守性。
结论:
- 在真实生产宿主上,这类探测方式不可视为安全。
阻塞项 4:rollback-provider 仍按同名资源扫描删除,不是按批次记录的真实资源集删除
严重级别:High
证据:
rollback-provider入口虽然先找到了 pack/provider/latest batch,http_api.go- 但实际执行仍走
Rollback(ctx, RollbackRequest{Provider: providerManifest}),http_api.go Rollback()的实现会按名字重新枚举宿主资源再删除,rollback_service.go- 更安全的
RollbackStoredResources()已存在,但未被该路径采用,rollback_service.go
影响:
- 在脏现场、残留现场、同名 provider 现场,会有误删风险。
- 当前真实 artifact 已经反复体现 reconcile 仍可能看到
extra_count,此时按名字删尤其不稳。
结论:
- 当前回滚策略还不满足“生产可放心执行”的标准。
五、非阻塞项
以下问题不一定阻塞受限范围上线,但会显著影响运维成熟度与文档可信度。
非阻塞项 1:部署文档承诺高于实际实现
证据:
DEPLOYMENT.md把/metrics、限流、监控接入写进了生产清单,DEPLOYMENT.md- 实际路由只看到
/healthz和控制面 API,http_api.go
影响:
- 运维人员容易误以为仓库已内置观测与生产保护机制。
- 文档可信度低于代码可信度。
结论:
- 应补功能,或先下调文档承诺。
非阻塞项 2:计划结构与物理目录仍有明显漂移
证据:
- 该评审形成时,implementation plan 里期望的
internal/reconcile/*、access/planner.go、worker/scheduler.go等结构仍未落地,implementation-plan.md - 截至 2026-05-22,这些结构项已分别落到
internal/reconcile/*、internal/access/{planner,subscription,self_service,validation}.go与internal/worker/*。
影响:
- 目前更像可运行 MVP,而不是已经完成结构收敛的生产后端。
- 长期维护和职责边界清晰度会受影响。
结论:
- 属于已知结构债务,不阻塞受限范围上线,但不应被忽略。
非阻塞项 3:subscription 模式缺少现成通过的真实闭环 artifact
证据:
- 当前看到的
subscription真实宿主样例仍是:batch_status=partially_succeededprovider_status=degradedaccess_status=broken- 见 05-import.json
- 访问预检也仍显示不可用,06-access-preview.json
影响:
subscription不能被纳入当前上线放行范围。
结论:
- 该项虽已在执行板中被承认为剩余风险,但从上线审查角度仍需明确隔离。
非阻塞项 4:reconcile 结果在真实宿主上仍有漂移
证据:
self_service成功样例中,09-reconcile.json仍是status=drifted且extra_count=11,09-reconcile.json
影响:
- 说明系统主链路成功并不等于现场状态完全收敛。
- 当前更适合“可上线但需现场治理”,不适合“上线即稳定自治”。
结论:
- 该问题单独不阻塞
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 和运维语义,本次审查给出如下正式判定:
- 代码质量:
PASS - MVP 主链路完整性:
PASS - 生产运维闭环完整性:
FAIL - 全量生产放行:
REJECT - 单宿主 self-service 条件性放行:
CONDITIONAL_APPROVED
九、附注
以下事项需要避免被错误解读:
go test、race、vet全通过,不等于已经具备完整生产运维语义。self_service成功,不等于subscription已成功。- 当前存在
hostsAPI,不等于宿主对象模型已经真正完成。 - 当前有 rollback 能力,不等于 rollback 已达到低误删风险的生产标准。