Files
sub2api-cn-relay-manager/docs/EXECUTION_BOARD.md
2026-05-29 11:43:11 +08:00

66 KiB
Raw Blame History

sub2api-cn-relay-manager 执行板

日期2026-05-22 当前 GateAPPROVED代码门禁已通过并且 2026-05-21 已继续收掉 account probe、gateway probe 认证语义和 latest-head self_service fresh-host 复验的剩余问题。最新 MiniMax 53hk fresh-host 验收 artifacts/real-host-acceptance/20260521_191418_remote43_minimax_key_import/21-summary.json、DeepSeek 2166 subscription fresh-host 验收 artifacts/real-host-acceptance/20260521_201509_remote43_deepseek_key_import/21-summary.json、以及 latest-head self_service 标准 fresh-host 验收 artifacts/real-host-acceptance/20260521_210403/05-import.json / 07-access-status.json 已共同证明:subscriptionself_service 主链路都能在真实 fresh host 上闭环到 readyhost /v1/models/v1/chat/completions 也都真实返回 HTTP 200。当前仍存在的 reconcile=drifted 只反映共享 fresh-host 环境里的历史残留资源,不阻塞 PRD 首版放行) 目标:实现独立控制面、零侵入宿主、可导入国产模型并具备可运维的导入/回滚/访问闭环。

2026-05-22 当前真相

  • 当前主目录 artifacts/real-host-acceptance/ 已只保留最终证据;历史调试样本已迁到 artifacts/real-host-acceptance-archive/
  • access ready 语义已经收口为:/v1/models 命中 smoke_test_model,且最小 POST /v1/chat/completions smoke 成功;不会再出现 models-only 假 ready
  • subscription 主链路已通过 latest fresh-host 复验:
    • MiniMax 53hkartifacts/real-host-acceptance/20260521_191418_remote43_minimax_key_import/21-summary.json
    • DeepSeek 2166artifacts/real-host-acceptance/20260521_201509_remote43_deepseek_key_import/21-summary.json
    • Kimi A7Mlocal host v0.1.129artifacts/real-host-acceptance/20260522_122706_local_v0129_kimi_a7m_subscription_freshhost/21-summary.json
  • self_service 主链路已通过 latest-head 标准 fresh-host 复验:
    • artifacts/real-host-acceptance/20260521_210403/05-import.json
    • artifacts/real-host-acceptance/20260521_210403/07-access-status.json
  • 2026-05-27 已把公网用户入口从 kimi-portal 收口为通用多模型 portal
    • 新正式地址:https://sub.tksea.top/portal/
    • 旧地址 https://sub.tksea.top/kimi-portal/ 当前保留为 302 跳转,避免历史分享链接失效
    • 站点资产与 Nginx 路由不再只存在 /tmp 临时文件,已收口进仓库:
      • deploy/tksea-portal/index.html
      • deploy/tksea-portal/admin-batch-import.html
      • deploy/tksea-portal/nginx.sub.tksea.top.conf.example
      • scripts/deploy/deploy_tksea_portal.sh
    • 新页面已补齐登录态、用户信息、可绑定分组、活跃订阅、历史 key 列表,以及“新创建 key 对应分组/模型”的即时展示
    • 同轮已补最小 batch-import 管理页:
      • 地址:/portal/admin-batch-import.html
      • 直接消费 POST /api/batch-import/runs
      • 直接消费 GET /api/batch-import/runs/{run_id}
      • 直接消费 GET /api/batch-import/runs/{run_id}/items
      • 用于验证 matched_account_state / account_resolution / provision_reused
    • 2026-05-27 已继续把管理入口收成统一 /portal/admin/ 体系:
      • https://sub.tksea.top/portal/admin/:管理首页
      • https://sub.tksea.top/portal/admin/providers.htmlprovider 目录 / preview-import / import / manifest 草稿页
      • https://sub.tksea.top/portal/admin/batch-import.html:结构化 batch-import 入口,当前跳转到 legacy admin-batch-import.html
      • Nginx 示例与 deploy 脚本已补同域 CRM 反代 https://sub.tksea.top/portal-admin-api/
      • 目的不是绕过鉴权,而是让浏览器可直接操作 remote43 CRM当前已继续补成“管理员用户名 / 密码登录 + HttpOnly session cookie”同时保留 Bearer admin token 兼容脚本与紧急兜底
    • 2026-05-27 已继续把 provider manifest 草稿从“只存在浏览器”补成真正的服务端能力:
      • 新增 POST /api/provider-drafts
      • 新增 GET /api/provider-drafts
      • 新增 GET /api/provider-drafts/{draft_id}
      • 新增 PUT /api/provider-drafts/{draft_id}
      • 新增 DELETE /api/provider-drafts/{draft_id}
      • 数据当前落到 CRM SQLite provider_drafts
      • providers.html 已可直接“保存到服务端”、回看历史草稿、以及更新 / 删除已保存草稿
    • 2026-05-28 已把“草稿一键生成 pack/provider 文件并提交到仓库”的发布链路补齐:
      • 新增 POST /api/provider-drafts/{draft_id}/publish
      • 发布动作会把草稿 canonicalize 成完整 pack.ProviderManifest
      • 服务端会原子执行:写 providers/<provider_id>.json、bump pack.json patch 版本、更新 checksums.txt、重跑整包校验、git add + git commit
      • 运行前提新增:SUB2API_CRM_REPO_ROOT 必须指向真实 Git 仓库
      • remote43 原本的 /home/ubuntu/sub2api-cn-relay-manager 只是普通目录,不带 .git
      • 2026-05-28 已继续把这条路径收成正式部署约定:
        • CRM 现在应统一指向 /home/ubuntu/sub2api-cn-relay-manager-git-current
        • scripts/deploy/setup_remote43_patched_stack.sh 会自动生成并刷新该固定 checkout
        • 这样 provider 草稿发布链不再依赖任何一次性的时间戳 repo 目录
      • 公网 providers.html 已新增“发布到仓库”按钮与 commit message 输入框
      • remote43 公网真验已通过:
        • draft_id=draft_remote43_publish_smoke_1779924243
        • provider_id=smoke-publish-1779924243
        • provider_path=packs/openai-cn-pack/providers/smoke-publish-1779924243.json
        • pack_version=1.1.5 -> 1.1.6
        • publish_mode=created
        • commit_sha=d8d647e
        • 远端 repo HEAD 与 API 返回 commit_sha 一致,说明 create -> publish -> git commit 已完整闭环
    • 线上无副作用验收已确认:
      • GET /portal/ 返回 200
      • GET /kimi-portal/ 返回 302 -> /portal/
      • GET /portal-proxy/api/v1/keys 在无效 token 下已命中宿主真实 INVALID_TOKEN,说明新的同域代理已生效
    • 2026-05-28 已继续把管理态“每次手贴 Bearer token”收口为正式登录流
      • 新增 GET /api/admin/session
      • 新增 POST /api/admin/session/login
      • 新增 POST /api/admin/session/logout
      • 管理态受保护接口现已同时接受:
        • Authorization: Bearer <SUB2API_CRM_ADMIN_TOKEN>
        • 或同域管理员 session cookie
      • providers.htmladmin-batch-import.html 现已优先走 sessiontoken 输入框仅保留为兜底
      • 当前部署环境可通过以下变量显式配置管理员账号:
        • SUB2API_CRM_ADMIN_USERNAME
        • SUB2API_CRM_ADMIN_PASSWORD
        • SUB2API_CRM_ADMIN_SESSION_TTL
    • 2026-05-28 已继续把 providers.html 的 manifest 草稿表单收口成“按最近成功模板起步”的录入流:
      • 草稿区首次打开且字段为空时,会优先回填最近一次成功发布的模板;没有历史时,回退到当前 pack/provider 目录里的现有 provider最后才使用静态样例
      • Provider ID 现已按 display_name / base_url / supported_models 自动生成,并在与现有 provider / draft 冲突时自动补后缀避重
      • 当新填的 supported_models 已在现有 provider 或草稿里出现时,页面会直接提示“同模型已存在”,并优先建议复用已有 provider_id,避免因为“官方 / 中转”重复新增同一模型定义
      • GET /api/packs/{pack_id}/providers 现已补充返回 base_url / smoke_test_model / supported_models,用于前端做模板参考和模型冲突提示
      • 2026-05-28 继续把这条规则下沉到服务端:POST /api/provider-draftsPUT /api/provider-drafts/{draft_id}POST /api/provider-drafts/{draft_id}/publish 现在都会做 pack 级模型冲突校验;同模型若已被其他 provider / draft 占用,会直接返回 409 provider_model_conflict
    • 2026-05-28 已新增独立实验 pack packs/openai-cn-pack-route-lab/,用于验证 “同一个 group 下挂多条 GPT 路线” 的方案 B 骨架:
      • 当前实验 provider 为 gpt-asxs-route-labgpt-codex2api-route-lab
      • 两者共用 group_template.name=GPT Shared 路由实验plan_template.name=GPT Shared 路由实验套餐
      • 两者使用不同 channel_template.name,并先以不同公开 alias 对外:
        • gpt-5.4-asxs / gpt-5.4-mini-asxs
        • gpt-5.4-codex2api / gpt-5.4-mini-codex2api
      • 这轮只解决 “同组多线路” 的第一阶段验证,不代表当前系统已经支持 “同公开模型名双线路 + 显式 route policy”
      • codex2api 当前先按 https://www.codex2api.com/v1 作为 API 根地址假设,若 preview/import 失败,需先校正真实 API base URL
      • 2026-05-28 remote43 真验结论已落地:
        • gpt-asxs-route-lab 可成功导入artifactartifacts/real-host-acceptance/20260528_142205_remote43_gpt-asxs-route-lab_key_import/21-summary.json
        • 导入时创建资源:group_id=8channel_id=7plan_id=7account_id=9
        • upstream asxs/models/chat/completions200;但该轮 managed chat 仍返回 503,原因是脚本首轮使用了 canonical model gpt-5.4 探测,而当前 route-lab 对外 alias 实际是 gpt-5.4-asxs
        • gpt-codex2api-route-lab 在尝试复用同一 group 时被宿主直接拒绝artifactartifacts/real-host-acceptance/20260528_142320_remote43_gpt-codex2api-route-lab_key_import/03-import.body.json
        • 宿主返回 409 GROUP_ALREADY_IN_CHANNEL,错误为:one or more groups already belong to another channel
        • 因此当前真实结论不是“同组多 channel 可继续验证路由策略”,而是 stock / patched sub2api 当前结构上不允许同一个 group 绑定到第二个 channel
        • 2026-05-28 已进一步把宿主侧最小改造方案固化到 docs/HOST_MULTI_CHANNEL_MINIMAL_RETROFIT.md
          • 真实最小改造不只是移除 channel_groups(group_id) 唯一索引
          • 还必须给宿主 account_groups 引入 channel_id,并让 gateway / scheduler / sticky session / account stats pricing 全部从 group -> single channel 升级到 group + channel 维度
          • 否则就算数据库允许同一 group 绑定多个 channel运行时账号池仍会被按 group 混跑,结构上仍不成立
        • 2026-05-28 已明确 fallback 方案:不修改宿主源码,改由 relay-manager 插件层维护 logical_group -> route -> shadow_group 三层抽象,详见 docs/PLUGIN_ROUTE_STICKY_DESIGN.md
          • 前端只看到一个逻辑分组
          • 插件层先做 route 级 sticky再把请求稳定转发到某个宿主 shadow group
          • 宿主继续只做单线路 group 内的 account sticky / 调度
        • 2026-05-28 已新增插件整体需求盘点 docs/PLUGIN_REQUIREMENTS_OVERVIEW_2026-05-28.md
          • 已把“增加模型、维护逻辑分组、智能路由、供应商帐号导入与停启用、普通用户前端”五大功能域统一收口
          • 并明确区分 已完成 / 待优化 / 待完成 / 未来规划
        • 2026-05-28 已继续细化闭环实施规划 docs/PLUGIN_CLOSED_LOOP_IMPLEMENTATION_PLAN_2026-05-28.md
          • 明确当前插件数据库仍为 SQLiteSUB2API_CRM_SQLITE_DSN
          • 明确后续继续以 SQLite 作为主状态库Redis 作为智能路由运行态缓存
          • 明确智能路由日志必须结构化落入插件 SQLite而不是只放 Redis 或 stdout
        • 2026-05-28 已新增 Phase 1 可开工任务单 docs/plans/2026-05-28-phase1-logical-routing-foundation-plan.md
          • 已把 SQLite migration / logical_group-route repo+API / 路由日志写入器 / Redis sticky 抽象 拆成可执行任务
          • 已继续细化到任务级 入场条件 / 产出清单 / 远端验证步骤 / 证据要求 / 回滚原则
          • 并明确要求:每个闭环功能完成后,都必须提交、推送、部署到 remote43 再验证,不能只停留在本地测试
          • 当前 Phase 1 的统一真相是:
            • 主状态库继续使用 SQLite
            • 路由运行态使用 Redis 或 memory backend 抽象
            • 智能路由日志必须最终结构化写回插件 SQLite
        • 2026-05-28 已完成 Phase 1 / P1-T1 SQLite schema foundation
          • 提交:7f75d8a6 feat(routing): add logical group schema foundation
          • 新 migrationinternal/store/migrations/0010_logical_groups_and_routes.sql
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 7f75d8a
          • http://127.0.0.1:18173/healthz 返回 ok
          • remote43 实例 SQLite /home/ubuntu/sub2api-kimi-patched-auto2-20260525_18169/sub2api-cn-relay-manager.db 已确认包含:
            • logical_groups
            • logical_group_models
            • logical_group_routes
            • logical_group_route_models
          • 这轮远端验证还顺手暴露并修正了一个部署细节:
            • 若只在 /home/ubuntu 下直接拉起 CRM新进程会回退到默认相对 SQLite 路径 /home/ubuntu/sub2api-cn-relay-manager.db
            • 当前已改为显式 cd 到实例目录并 source .env.crm 后再启动,确保 migration 生效在实例库而不是错误的默认库
        • 2026-05-28 已完成 Phase 1 / P1-T2 logical_group / route repo + admin API
          • 提交:28188922 feat(routing): add logical group admin api
          • 新增 SQLite repo
            • logical_groups
            • logical_group_models
            • logical_group_routes
            • logical_group_route_models
          • 新增管理 API
            • POST /api/logical-groups
            • GET /api/logical-groups
            • GET /api/logical-groups/{group_id}
            • PUT /api/logical-groups/{group_id}
            • DELETE /api/logical-groups/{group_id}
            • POST /api/logical-groups/{group_id}/models
            • GET /api/logical-groups/{group_id}/models
            • DELETE /api/logical-groups/{group_id}/models/{model}
            • POST /api/logical-groups/{group_id}/routes
            • GET /api/logical-groups/{group_id}/routes
            • PUT /api/logical-groups/{group_id}/routes/{route_id}
            • DELETE /api/logical-groups/{group_id}/routes/{route_id}
            • POST /api/logical-groups/{group_id}/routes/{route_id}/models
            • GET /api/logical-groups/{group_id}/routes/{route_id}/models
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 2818892
          • http://127.0.0.1:18173/healthz 返回 ok
          • remote43 真实 API 验证已通过:
            • POST /api/logical-groups 创建 logical_group_id=p1t2-gpt-shared-1779971040
            • GET /api/logical-groups 返回列表,当前计数 2
            • GET /api/logical-groups/p1t2-gpt-shared-1779971040 返回 display_name=P1T2 GPT Shared,建 route 前 routes_count=0
            • POST /api/logical-groups/p1t2-gpt-shared-1779971040/routes 创建 route_id=asxs-1779971040shadow_group_id=p1t2-gpt-shared-1779971040__asxs
        • 2026-05-28 已完成 Phase 1 / P1-T3 route logging repo + async writer
          • 提交:6e0bd59e feat(routing): add route log writer and admin api
          • 新增 migrationinternal/store/migrations/0011_route_logging.sql
          • 新增 SQLite repo
            • route_decision_logs
            • route_failover_events
            • route_sticky_audit
          • 新增异步写入器:
            • internal/routing/logwriter.go
            • 默认采用内存 channel + 定时/显式 Flush() 批量落 SQLite
            • 队列满时退化为同步写入,避免热路径静默丢日志
          • 新增管理 API
            • POST /api/routing/logs/decisions
            • GET /api/routing/logs/decisions
            • POST /api/routing/logs/failovers
            • GET /api/routing/logs/failovers
            • POST /api/routing/logs/sticky-audit
            • GET /api/routing/logs/sticky-audit
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 6e0bd59
          • http://127.0.0.1:18173/healthz 返回 ok
          • remote43 真实公网 API 验证已通过:
            • POST /api/routing/logs/decisions 创建 request_id=req-p1t3-decision-1779976705,返回 selected_route_id=asxs-1779971040
            • GET /api/routing/logs/decisions?request_id=req-p1t3-decision-1779976705 回读到同一条 decision log
            • POST /api/routing/logs/failovers 创建 request_id=req-p1t3-failover-1779976748,返回 to_route_id=asxs-1779971040-fallback
            • GET /api/routing/logs/failovers?request_id=req-p1t3-failover-1779976748 回读到同一条 failover event
            • POST /api/routing/logs/sticky-audit 创建 sticky_key=sticky-p1t3-1779976750,返回 action=bind
            • GET /api/routing/logs/sticky-audit?sticky_key=sticky-p1t3-1779976750 回读到同一条 sticky audit
        • 2026-05-29 已完成 Phase 1 / P1-T4 Redis sticky store abstraction
          • 提交:98bd619e feat(routing): add sticky runtime backends
          • 新增运行时抽象:
            • internal/routing/sticky.go
            • internal/routing/sticky_memory.go
            • internal/routing/sticky_redis.go
          • 新增统一 key 规则:
            • sticky:{scope}:{logical_group_id}:{public_model}:{subject_id}
            • routefail:{route_id}
            • routecool:{route_id}
          • 新增启动配置:
            • SUB2API_CRM_ROUTE_RUNTIME_BACKEND
            • SUB2API_CRM_REDIS_ADDR
            • SUB2API_CRM_REDIS_PASSWORD
            • SUB2API_CRM_REDIS_DB
          • 新增管理 API
            • POST /api/routing/sticky/bindings
            • GET /api/routing/sticky/bindings
            • POST /api/routing/sticky/route-failures
            • GET /api/routing/sticky/route-failures
            • POST /api/routing/sticky/cooldowns
            • GET /api/routing/sticky/cooldowns
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 98bd619
          • http://127.0.0.1:18173/healthzmemory / redis 两种运行模式下均返回 ok
          • remote43 redis 运行时目标已确认使用容器地址 172.24.0.3:6379127.0.0.1:6379 不可用
          • remote43 真实公网 API 验证已通过:
            • memory 模式:
              • POST /api/routing/sticky/bindings 创建 subject_id=conv-p1t4-memory-1780011984,返回 backend=memory
              • GET /api/routing/sticky/bindings 回读到同一条绑定,route_id=asxs-1779971040,返回 backend=memory
              • POST /api/routing/sticky/route-failures 创建 route_id=route-p1t4-memory-1780011984,返回 failure_count=2backend=memory
              • GET /api/routing/sticky/route-failures 回读到同一条故障计数,返回 backend=memory
              • POST /api/routing/sticky/cooldowns 创建 route_id=route-p1t4-memory-1780011984,返回 reason=degradedbackend=memory
              • GET /api/routing/sticky/cooldowns 回读到同一条 cooldown返回 backend=memory
            • redis 模式:
              • POST /api/routing/sticky/bindings 创建 subject_id=conv-p1t4-redis-1780012047,返回 backend=redis
              • GET /api/routing/sticky/bindings 回读到同一条绑定,route_id=asxs-1779971040,返回 backend=redis
              • POST /api/routing/sticky/route-failures 创建 route_id=route-p1t4-redis-1780012047,返回 failure_count=3backend=redis
              • GET /api/routing/sticky/route-failures 回读到同一条故障计数,返回 backend=redis
              • POST /api/routing/sticky/cooldowns 创建 route_id=route-p1t4-redis-1780012047,返回 reason=cooldown-activebackend=redis
              • GET /api/routing/sticky/cooldowns 回读到同一条 cooldown返回 backend=redis
        • 2026-05-29 已完成基础设施闭环补充 / route resolve + sticky hit
          • 提交:66ad319c feat(routing): add sticky-backed route resolver
          • 新增管理 API
            • POST /api/routing/resolve
          • 行为收口:
            • 首次 resolve 按 logical_group_routes.priority 选择可用 route
            • 选择结果会同步写入 sticky store
            • 同步写入 route_decision_logsroute_sticky_audit
            • 后续同 scope + logical_group_id + public_model + subject_id 请求会优先命中 sticky
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 66ad319
          • http://127.0.0.1:18173/healthz 返回 ok
          • 远端实例二进制校验:
            • 活跃实例目录:/home/ubuntu/sub2api-kimi-patched-auto2-20260525_18169
            • 本地构建与远端实例二进制 sha256 已对齐为 f7b8334cd992e0a3e65d3f129163f0a01f06a1c746071b67f6e8d1f6fe38ad99
            • 修正了一次“按绝对路径 pkill 未命中 ./sub2api-cn-relay-manager-server”导致旧进程仍在跑的问题,之后已定向 kill 活跃 PID 并重启
          • remote43 真实公网 API 验证已通过(redis 运行时):
            • 创建临时逻辑分组 logical_group_id=p1t5-gpt-shared-1780019458
            • 创建两条 route
              • asxs-1780019458priority=20
              • codex2api-1780019458priority=10
            • 第一次 POST /api/routing/resolve
              • request_id=req-p1t5-first-1780019458
              • subject_id=conv-p1t5-1780019458
              • 返回 backend=redis
              • 返回 route_id=codex2api-1780019458
              • 返回 sticky_hit=false
              • 返回 sticky_action=bind
            • 第二次 POST /api/routing/resolve(同 subject
              • request_id=req-p1t5-second-1780019458
              • 返回 backend=redis
              • 返回 route_id=codex2api-1780019458
              • 返回 sticky_hit=true
              • 返回 sticky_action=hit
            • GET /api/routing/logs/decisions?sticky_key=lg:p1t5-gpt-shared-1780019458:m:gpt-5.4:conv:conv-p1t5-1780019458
              • 回读到两条 decision log
              • 最新一条 request_id=req-p1t5-second-1780019458sticky_hit=true
              • 较早一条 request_id=req-p1t5-first-1780019458sticky_hit=false
            • GET /api/routing/logs/sticky-audit?sticky_key=lg:p1t5-gpt-shared-1780019458:m:gpt-5.4:conv:conv-p1t5-1780019458
              • 回读到两条 sticky audit
              • 最新一条 action=hit
              • 较早一条 action=bind
        • 2026-05-29 已完成基础设施闭环补充 / route failure threshold failover
          • 提交:eb2242ca feat(routing): add resolver failover fallback
          • 行为收口:
            • resolve 现在会在选路时读取 route-failurescooldowns
            • 当高优先级 route 的 failure_count >= logical_group.failover_threshold 时,会自动跳过并选择下一条可用 route
            • 首次 fallback 会把 route_decision_logs.fallback_used 置为 true
            • 同时写入 route_failover_events
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = eb2242c
          • http://127.0.0.1:18173/healthz 返回 ok
          • 远端实例二进制已更新为 sha256=cc177700541d9ab85a638f768e6fba045d1e864c347e6dfd895ea9e05f27c571
          • remote43 真实公网 API 验证已通过(redis 运行时):
            • 创建临时逻辑分组 logical_group_id=p1t5-failover-1780020305
            • 创建两条 route
              • codex2api-1780020305priority=10
              • asxs-1780020305priority=20
            • POST /api/routing/sticky/route-failures
              • codex2api-1780020305 写入 failure_count=2
              • last_error_class=timeout
              • 返回 backend=redis
            • 第一次 POST /api/routing/resolve
              • request_id=req-p1t5-failover-first-1780020305
              • 返回 route_id=asxs-1780020305
              • 返回 sticky_hit=false
              • 返回 sticky_action=bind
              • 说明高优先级 codex2api-1780020305 已因超阈值被自动跳过
            • 第二次 POST /api/routing/resolve(同 subject
              • request_id=req-p1t5-failover-second-1780020305
              • 返回 route_id=asxs-1780020305
              • 返回 sticky_hit=true
            • GET /api/routing/logs/failovers?request_id=req-p1t5-failover-first-1780020305
              • 回读到一条 failover event
              • from_route_id=codex2api-1780020305
              • to_route_id=asxs-1780020305
              • reason=failure_threshold_exceeded:timeout
              • failure_count=2
            • GET /api/routing/logs/decisions?sticky_key=lg:p1t5-failover-1780020305:m:gpt-5.4:conv:conv-p1t5-failover-1780020305
              • 第一条 resolve 对应记录 fallback_used=truesticky_hit=false
              • 第二条 resolve 对应记录 fallback_used=falsesticky_hit=true
        • 2026-05-29 已完成基础设施闭环补充 / cooldown rebinding regression matrix
          • 代码基线:沿用 eb2242ca feat(routing): add resolver failover fallback
          • 验证目标:
            • 人工设置 cooldown
            • resolve 自动跳过处于 cooldown 的高优先级 route
            • 写入 route_failover_events
            • 验证旧 sticky 失效后的重新绑定,以及新 sticky 的后续命中
          • remote43 真实公网 API 验证已通过(redis 运行时):
            • 创建临时逻辑分组 logical_group_id=p1t5-cooldown-1780020999
            • 创建两条 route
              • codex2api-1780020999priority=10
              • asxs-1780020999priority=20
            • 第一次 POST /api/routing/resolve
              • request_id=req-p1t5-cooldown-first-1780020999
              • 正常命中主 route codex2api-1780020999
              • 返回 sticky_hit=false
              • 返回 sticky_action=bind
            • POST /api/routing/sticky/cooldowns
              • codex2api-1780020999 写入 reason=degraded
              • 返回 backend=redis
            • 第二次 POST /api/routing/resolve(同 subject
              • request_id=req-p1t5-cooldown-second-1780020999
              • 旧 sticky 指向的主 route 因 cooldown 被自动判定失效
              • 返回 route_id=asxs-1780020999
              • 返回 sticky_hit=false
              • 返回 sticky_action=bind
              • 说明已完成 sticky 重绑定到 fallback route
            • 第三次 POST /api/routing/resolve(同 subject
              • request_id=req-p1t5-cooldown-third-1780020999
              • 返回 route_id=asxs-1780020999
              • 返回 sticky_hit=true
              • 说明新的 sticky 已稳定命中 fallback route
            • GET /api/routing/logs/failovers?request_id=req-p1t5-cooldown-second-1780020999
              • 回读到一条 failover event
              • from_route_id=codex2api-1780020999
              • to_route_id=asxs-1780020999
              • reason=active_cooldown:degraded
              • failure_count=0
            • GET /api/routing/logs/decisions?sticky_key=lg:p1t5-cooldown-1780020999:m:gpt-5.4:conv:conv-p1t5-cooldown-1780020999
              • 第一条 resolveselected_route_id=codex2api-1780020999fallback_used=false
              • 第二条 resolveselected_route_id=asxs-1780020999fallback_used=truesticky_hit=false
              • 第三条 resolveselected_route_id=asxs-1780020999fallback_used=falsesticky_hit=true
            • GET /api/routing/logs/sticky-audit?sticky_key=lg:p1t5-cooldown-1780020999:m:gpt-5.4:conv:conv-p1t5-cooldown-1780020999
              • 依次可回读到三条 sticky audit
                • action=bindroute_id=codex2api-1780020999
                • action=bindroute_id=asxs-1780020999
                • action=hitroute_id=asxs-1780020999
        • 2026-05-29 已完成基础设施闭环补充 / resolve + minimal chat proxy bridge
          • 提交:9b1c6f43 feat(routing): add minimal chat proxy bridge
          • 新增管理 API
            • POST /api/routing/proxy/chat/completions
          • 行为收口:
            • 先复用 POST /api/routing/resolve 选出 route_id / shadow_host_id / shadow_group_id / shadow_model
            • 再从插件 SQLite hosts 表读取 shadow_host_id -> host.base_url
            • 用调用方显式提供的 gateway_api_key 代理转发最小 POST /v1/chat/completions
            • 默认在未提供 messages 时发送最小 ping 消息
            • 转发结果会以同一 request_id 追加写回 route_decision_logs,补齐 upstream_status / latency_ms / error_class
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = 9b1c6f43
          • http://127.0.0.1:18173/healthz 返回 ok
          • remote43 真实服务器 API 验证已通过:
            • 为避免依赖现成 managed gateway key这轮在 remote43 本机临时起了 loopback stub shadow host
              • host_id=proxy-shadow-host-1780022254
              • base_url=http://127.0.0.1:18095
              • 只暴露最小 POST /v1/chat/completions
              • 要求 Authorization: Bearer gateway-key
            • 通过真实 CRM API 创建临时逻辑分组与路由:
              • logical_group_id=p1t6-proxy-1780022254
              • route_id=asxs-1780022254
              • shadow_group_id=p1t6-proxy-1780022254__asxs
              • shadow_model=gpt-5.4-asxs
            • 调用 POST /api/routing/proxy/chat/completions
              • request_id=req-p1t6-proxy-1780022254
              • subject_id=conv-p1t6-proxy-1780022254
              • 返回 route_id=asxs-1780022254
              • 返回 shadow_host_id=proxy-shadow-host-1780022254
              • 返回 shadow_model=gpt-5.4-asxs
              • 返回 forward.ok=true
              • 返回 forward.upstream_status=200
              • 返回 forward.response.choices[0].message.content=pong-from-stub
            • stub 侧回读确认:
              • 实际收到 Authorization: Bearer gateway-key
              • 实际收到 model=gpt-5.4-asxs
            • GET /api/routing/logs/decisions?request_id=req-p1t6-proxy-1780022254&limit=5
              • 共回读到 2 条 decision log
              • 最新一条 upstream_status=200
              • 说明控制面 resolve 与数据面最小转发已被同一 request_id 串成闭环
        • 2026-05-29 已完成真实宿主 managed key 供应路径补充 / route proxy -> managed subscription -> real host chat
          • 提交:cffe3332 feat(routing): auto-supply managed proxy keys
          • POST /api/routing/proxy/chat/completions 新增:
            • 当请求未显式提供 gateway_api_key 且带 subscription_user_id 时,会自动调用目标宿主的 EnsureSubscriptionAccess(...)
            • 通过 shadow_group_id + shadow_host_id 解析真实宿主 group再以新签发的 managed subscription key 转发到真实 host /v1/chat/completions
            • 转发结果继续以同一 request_id 追加写回 route_decision_logs
          • 本地门禁已通过:
            • gofmt -l .
            • go vet ./...
            • go test -cover ./internal/...
            • go test ./tests/integration/... -count=1
          • remote43 已原位升级到 repo HEAD = cffe3332
          • http://127.0.0.1:18173/healthz 返回 ok
          • remote43 真实服务器 API 验证结果:
            • 通过 remote43 本机 host admin login 拿到真实宿主管理员 access token
            • 已确认 route-lab 真实 shadow group
              • name=GPT Shared 路由实验-subscription
              • host_group_id=8
            • 通过真实 CRM API 创建临时 host / logical group / route
              • host_id=proxy-real-host-1780026133
              • logical_group_id=p1t7-real-1780026133
              • route_id=asxs-real-1780026133
              • shadow_group_id=8
              • shadow_model=gpt-5.4-asxs
            • 调用 POST /api/routing/proxy/chat/completions
              • request_id=req-p1t7-real-1780026133
              • subscription_user_id=proxy-managed-1780026133
              • 返回 effective_gateway_key_source=managed_subscription
              • 返回 managed_user_id=33
              • 返回 effective_gateway_key_fingerprint=sha256:f0fa3dec6e94348945431c9470c1faa8258c50fcee1adcb1904dac0fa42a15d6
              • 返回 forward.ok=false
              • 返回 forward.upstream_status=503
              • 返回 forward.response.error.message=Service temporarily unavailable
            • GET /api/routing/logs/decisions?request_id=req-p1t7-real-1780026133&limit=5
              • 共回读到 2 条 decision log
              • 最新一条 upstream_status=503
            • 当前结论:
              • 插件控制面 resolve -> shadow_host/shadow_group/shadow_model
              • managed subscription key 自动供给
              • 真实宿主 /v1/chat/completions 转发
              • 插件侧 decision log 回写
              • 以上链路均已真实命中
              • 但该次 upstream 仍返回 503,因此当前收口应表述为“真实 managed key 供应路径已打通,真实上游可用性仍待继续压实”
  • 2026-05-26 已把“最终用户 -> 公网域名 -> OpenClaw”这一跳补进正式验证口径
    • 公网根地址当前统一为 https://sub.tksea.top
    • OpenClaw 本地 MiniMax 运行时故障已定位为 pi-ai/openai-node 未继承系统 HTTP(S)_PROXY,不是 allowlist 或模型名大小写问题
    • 操作者本机已新增升级后自检与提醒链路:
      • ~/.openclaw/bin/apply-openclaw-minimax-proxy-fix.sh
      • ~/.openclaw/bin/openclaw-minimax-post-upgrade-check.sh
      • ~/.openclaw/bin/openclaw-minimax-proxy-reminder.sh
      • ~/.openclaw/bin/install-openclaw-minimax-reminder-cron.sh
    • 当前 OpenClaw 真实验证基线已收口为:
      • tksea-gpt/gpt-5.4PASS
      • tksea-gpt/gpt-5.4-miniPASS
      • tksea-gpt/gpt-5.5:当前 upstream 503
      • tksea-minimax/MiniMax-M2.5-highspeedPASS
      • tksea-minimax/MiniMax-M2.7-highspeedPASS
      • deepseek-official/deepseek-chatPASS2026-05-27 已补齐本机 auth profileone-shot 返回 OK
    • 这部分测试用例与执行过程已沉淀到 docs/OPENCLAW_EXTERNAL_VALIDATION.md
  • 2026-05-26 remote43 patched host 最新 provider 扩展验收:
    • openai-zhongzhuanartifacts/real-host-acceptance/20260526_155548_remote43_openai_zhongzhuan_multi_model_rootprep/21-summary.json 已确认 batch_status=succeededprovider_status_from_import=activedirect_chat_status=200
    • minimax-53hkartifacts/real-host-acceptance/20260526_155705_remote43_minimax53hk_multi_model_rootprep/21-summary.json 已确认 batch_status=succeededprovider_status_from_import=activedirect_chat_status=200upstream_chat_status=200
    • deepseek-chat-official
      • 旧 artifact artifacts/real-host-acceptance/20260526_155810_remote43_deepseek_chat_official_multi_model_rootprep/21-summary.json 停在 partially_succeeded/degraded
      • 2026-05-27 rerun artifacts/real-host-acceptance/20260527_051655_remote43_deepseek-chat-official_key_import/21-summary.json 已确认本机经 remote43 patched host 的真实数据面恢复:direct_models_http200=truedirect_models=["deepseek-chat"]direct_chat_status=200latest_access_status=subscription_readyupstream_chat_status=200
      • 剩余 partially_succeeded/degraded 的唯一原因已定位为宿主 account probe 返回裸 API returned 404:,而后续 gateway /v1/models + /v1/chat/completions 实际都已通过HEAD 现已把该 404 视为 advisory不再阻塞最终状态收敛
      • 同轮还补上 remote43 scripted stack 的真实脚本缺陷:.env.crm 里的 SQLite DSN 含 &_busy_timeout=5000 时,旧版未做 shell escapesource .env.crm 会吞掉 SUB2API_CRM_SQLITE_DSN,导致 remote CRM 实际退回默认 DB 路径;scripts/deploy/remote43_patched_stack_lib.sh 已修复并有回归测试覆盖
  • latest-head relay-manager 已新增宿主 capability 自愈:
    • 当第三方 OpenAI-compatible upstream 因宿主把 openai_responses_supported 误判成 true 而导致 host /v1/chat/completions 返回 502 upstream_erroraccess closure 与后台 reconcile 会自动把相关 account 修正到 raw /chat/completions 路径后再重试
    • 但这条控制面自愈当前仍不足以单独收敛本地 stock weishaw/sub2api:0.1.129 + kimi-a7m 场景;artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_scheme_c_stockhost_rerun/21-summary.json 已再次证明在不改宿主源码的前提下managed /v1/models 虽然命中 kimi-k2.6/v1/chat/completions 仍会落到 502 upstream_error,所以该 case 仍需宿主运行时兼容补丁或 shim
  • 2026-05-23 remote43 线上验收脚本已继续收口:
    • scripts/acceptance/import_remote43_provider.sh 现已明确拆分 CRM_HOST_BASEREMOTE_HOST_BASE
    • 远端 Postgres / Redis 容器已改成按目标宿主端口自动解析,不再硬编码落到 sub2api-relaymgr-pg/redis
    • 远端 managed probe /v1/models/v1/chat/completions 已改成只走 REMOTE_HOST_BASE
    • provider status / access status / access preview 末尾查询已补 host_id,避免本地 CRM 有多宿主历史时被 provider exists on multiple hosts 截断
  • 2026-05-25 已把 Hermes 里可复用的 a7m-kimi 正式并入主 pack
    • 新增 packs/openai-cn-pack/providers/kimi-a7m.json
    • openai-cn-pack 版本现为 1.1.3
    • 当前主仓不再需要依赖历史临时 pack openai-cn-pack-kimi-a7m
    • kimi-a7m provider manifest 现在也开始承载 host_overlays 元数据;本地已把 sub2api v0.1.129 的 Kimi A7M runtime overlay 说明与 .patch 资产纳入 packs/openai-cn-pack/overlays/
    • 新增 go run ./cmd/cli apply-host-overlay 最小执行器;当前 pack 内命中的 overlay 已可直接生成 patched 宿主构建目录,不再只是 preview/import 阶段的提示信息
  • 2026-05-25 已继续把路线 A 推进到运行态层面:
    • /tmp/sub2api-clean 的 clean worktree HEAD 导出 stock 源码,再用 go run ./cmd/cli apply-host-overlay --provider-id kimi-a7m --host-version 0.1.129 生成全新 patched 源码树
    • 基于该 patched 源码树重建 localhost/sub2api:patched-overlay-20260525-clean,并在独立 Podman 网络里启动新的 Postgres / Redis / App fresh-host
    • artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_patched_overlay_image_freshhost_clean/21-summary.json 已确认:import_batch_status=succeededprovider_status=activelatest_access_status=subscription_readycompletion_ok=truecompletion_status=200
    • 同目录 07-access-status.json 与 patched host 运行日志已共同证明 managed subscription key 真实打通 /v1/modelsPOST /v1/chat/completions
    • 注意:该 fresh-host 使用的镜像基底仍是 weishaw/sub2api:0.1.129,但宿主管理 API 当前自报 host_version=0.1.126;后续读 artifact 时应以日期和证据链为准,不要只依赖版本字段
    • 2026-05-25 已把同一条 patched overlay 路线放到 remote43 做线上验收:
      • remote43 侧单独拉起了 sub2api-kimi-patched-20260525-{app,pg,redis}patched host 暴露 127.0.0.1:18139
      • 临时 CRM 也切到 remote43 本机运行,再通过 SSH 隧道映射回本地 127.0.0.1:18143,避免“本地 CRM 透过隧道探远端 host”导致的 get host version 超时噪音
      • artifacts/real-host-acceptance/20260525_remote43_kimi_a7m_patched_overlay_freshhost_remotecrm/21-summary.json 已确认:batch_status=succeededaccess_status_from_import=subscription_readyprovider_status_from_import=activedirect_models_http200=truedirect_chat_http200=trueupstream_chat_status=200
      • 同目录 14-access-status.json 已确认 effective_probe_key_source=managed_subscriptioncompletion_status=200
      • remote43 宿主日志也已落到真实 GET /v1/models = 200POST /v1/chat/completions = 200,说明这条 patched overlay 路线不只在本地 fresh-host 成功,也已在远端真实环境收敛到 ready
      • 这轮还顺手修掉了 scripts/acceptance/import_remote43_provider.sh 的一个真实脚本缺陷:查找未分配 relay-sub-* 用户时,NOT EXISTS 子查询错误引用了无 alias 的 users.id,在 PostgreSQL 上会报 invalid reference to FROM-clause entry for table "users"
    • 2026-05-25 继续把这套 remote43 patched-host / remote CRM 的启动流程脚本化:
      • 新增 scripts/deploy/setup_remote43_patched_stack.sh,把 pack 镜像、二进制上传、remote43 上的 PG/Redis/patched host/临时 CRM 拉起、以及本地 operator env / SSH 隧道提示收口为一个固定入口
      • 新增 scripts/deploy/remote43_patched_stack_lib.sh,把远端 host env / CRM env / bootstrap script 渲染逻辑抽成可测试 helper
      • scripts/test/test_real_host_scripts.sh 已新增对应回归,避免以后再回退到手工 /tmp/*.sh 拼装
      • 脚本首轮真实演练暴露出一个运行态细节patched sub2api 二进制实际监听 8080,不能沿用旧临时脚本里的 127.0.0.1:$HOST_PORT:3000 端口映射;当前 setup_remote43_patched_stack.sh 已新增 HOST_CONTAINER_PORT=8080 默认值并完成 remote43 二次实跑验证
      • 用修复后的固定脚本在 remote43 新起的 sub2api-kimi-patched-auto2-20260525 栈上,kimi-a7m 再次完成真实导入主链路:artifacts/real-host-acceptance/20260525_remote43_kimi_a7m_patched_overlay_scripted_stack/03-import.body.json 已确认 batch_status=succeededaccess_status=subscription_readyprovider_status=active,同目录 10-models.body.json / 12-chat.body.json / 18-upstream-models.body.json / 20-upstream-chat.body.txt 也已再次证明 managed 与 upstream 双侧都回到 HTTP 200
      • 2026-05-26 继续把 scripted stack 的末尾状态查询收口为稳定契约:scripts/acceptance/import_remote43_provider.sh 末尾不再只传 host_id,而是显式拼上 pack_id=openai-cn-pack&host_id=<create-host 返回值>;修复原因是 remote43 上同一个 provider 可能存在多个 pack 版本,缺 pack_id/api/providers/{providerID}/status 会返回 400 provider exists in multiple packs; pack_id is required
      • 修复后,artifacts/real-host-acceptance/20260526_remote43_kimi_a7m_patched_overlay_scripted_stack_rerun2/13-provider-status.json14-access-status.json21-summary.json 已全部自动补齐;其中 21-summary.json 已再次确认 batch_status=succeededprovider_status_from_import=activelatest_access_status=subscription_readydirect_chat_status=200upstream_chat_status=200
  • artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_from_hermes/21-summary.json 已证明:
    • Hermes 本机 A7M_KIMI_API_KEY 直探 upstream /v1/models/v1/chat/completions 均为 200
    • latest-head relay-manager + 本地 weishaw/sub2api:0.1.129 fresh-host 下import-time gateway /v1/models 命中 kimi-k2.6
    • 但 completion 仍落到 502 upstream_error,手工 managed key 再探 /v1/chat/completions 也返回 503
    • 管理员 account 视角 /api/v1/admin/accounts/1/models 正确,但手工 managed key /v1/models 仍会回到 GPT 默认集合,当前应继续归类为宿主运行时 gap / drift而不是 Hermes key 失效
  • artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_from_hermes/22-patched-host-validation.json 已证明:
    • 问题根因是宿主把 Kimi A7M 这类 custom upstream 误走到 Responses 路径,而不是 Hermes key 或 relay-manager pack 失效
    • /tmp/sub2api-clean 的宿主补丁下,旁路容器 sub2api-patched 已恢复 managed key /v1/models=200managed key /v1/chat/completions=200admin accounts/:id/test=true
    • fallback 日志与账号 extra.openai_responses_supported=false 持久化已同时出现,说明这条链路已经从 stock host 的 partially_succeeded / broken 收敛到 patched host 的 ready
  • artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_scheme_c_stockhost_rerun/21-summary.json 已证明:
    • 仅启用 relay-manager 侧方案 C预先 force_disable_openai_responses_api + import/access/reconcile capability repair但保持宿主仍是未打补丁的 stock weishaw/sub2api:0.1.129
    • import-time gateway /v1/models 仍能命中 kimi-k2.6
    • 但 import-time gateway /v1/chat/completions 依旧返回 502 upstream_erroraccess_status 仍是 brokenprovider_status_latest 仍是 partially_succeeded
    • 因此当前最新真相不是“方案 C 已替代宿主补丁”,而是“方案 C 缩小了控制面误判范围,但这条 Kimi A7M / stock v0.1.129 链路仍需要宿主运行时兼容修复”
  • 2026-05-25 已继续补齐方案 C控制面侧 capability repair
    • internal/host/sub2api 新增 ClearTempUnschedulable
    • access / reconcile 的 capability repair 现在会同时写 extra.openai_responses_supported=false 并清理账号 temp_unschedulable
    • packs/openai-cn-pack/providers/kimi-a7m.json 新增 force_disable_openai_responses_api=true,导入后会在 gateway closure 前预先把该账号切到 raw /v1/chat/completions
  • artifacts/real-host-acceptance/20260523_144937_remote43_kimi-a7m_key_import 已证明:
    • 这轮线上 kimi-a7m 不再复现“错库取 key 导致统一 401”或“模型列表串成 GPT 默认集合”
    • import 已返回 gateway.status_code=200models=["kimi-k2.6"]has_expected_model=true
    • upstream /models/chat/completions 都是 200
    • 未改宿主的真实阻塞已收缩为 host /v1/chat/completions 仍返回 503/502,不再是插件脚本的数据面问题
  • artifacts/real-host-acceptance/20260523_145531_remote43_kimi-a7m_key_import 说明另一类运行时噪音:
    • 当本地 SSH 隧道端口存活但链路已失活时,POST /api/hosts 阶段会在 get host version 处超时
    • 这类现象应优先解释为 tunnel/runtime 故障,而不是 provider 导入逻辑回退
  • 官方 provider 验证矩阵当前仍保留一条非阻塞事实:
    • artifacts/real-host-acceptance/20260521_222212_remote43_minimax-m2-7-official_key_import/21-summary.json 已证明 official MiniMax 模板链路是通的,但该验证 key 当前命中 upstream 429
  • reconcile=drifted 仍可能在 shared fresh-host 上出现,但当前解释是“历史残留资源噪音”,不阻塞 PRD 首版放行
  • 调通细节与诊断经验已沉淀到:
    • docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md
    • docs/REAL_HOST_ARTIFACT_RETENTION.md
  • 2026-05-24 本地代码门禁修复已继续收口三类非回归点:
    • go test -race ./... -count=1 现已再次真实通过;根因不是业务逻辑,而是多个测试包并行 sqlite.Open() 时与 modernc.org/sqlite 初始化路径的 race 噪音。当前已把 internal/appinternal/provisioninternal/reconcile 的测试 SQLite 打开路径收口到串行 helper关闭这类假红灯同时保持 sqlite 包内测试不引入导入环。
    • DELETE /api/hosts/{hostID} 不再默认放行危险级联删除;hosts repo 现在会先统计 import_batches / managed_resources / reconcile_runs 三类运行态依赖,有残留时返回 409 host_in_use,避免误删状态库里的回滚/对账真相。
    • 控制面 JSON 请求体现在统一受 MaxBytesReader 限制;超限请求会明确返回 413 request_too_large,不再允许无界 body 直接进入解码路径。

本轮已完成

  1. 宿主身份模型统一
    • host 注册时持久化 auth_type/auth_token
    • import / reconcile / rollback-provider / access 运行时链路切换为 host_id 主键
    • provider status / resources / access status / import-batches 支持 host_id 查询维度
  2. managed_resources 宿主维度收口
    • 新增迁移 0004_host_identity_and_managed_resources.sql
    • managed_resources 唯一键提升为 (host_id, resource_type, host_resource_id)
    • 仓储与服务查询切换为 host-scoped 语义
  3. reconcile run 结果按批次收口
    • 新增迁移 0006_reconcile_runs_batch_scope.sql
    • reconcile_runs 补充 batch_idbatch detail 仅返回本批次 reconcile 记录
  4. capability probe 收敛为无副作用探测
    • 不再对真实创建接口发送空 POST
  5. rollback-provider 风险收敛
    • 改为优先按已记录批次资源 RollbackStoredResources() 回滚
    • 缺少已记录资源时拒绝危险删除
  6. 文档真相同步
    • 新增 docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md
    • 下调 DEPLOYMENT.md 中未实现的 /metrics / 限流 / 监控承诺
  7. current-code remote43 导入链路已补齐 tunnel-aware 验证能力
    • scripts/acceptance/import_remote43_provider.sh 新增 CRM_HOST_BASE允许把“operator 访问 host 地址”和“CRM 进程访问 host 地址”分离
    • 历史 live model-mapping 关键证据保留在:artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation
  8. current-code remote43 access gate 根因修正已落地
    • subscription access 改为宿主侧闭环CRM 不再依赖外部预先给定的宿主普通用户 key而是按 subscription_users selector 在宿主创建/查找托管普通用户、登录创建托管 key、回写 allowed_groups / balance、再执行订阅分配
    • account 创建请求现在同步写入 credentials.model_mapping,修正 /v1/models 读取 account model whitelist 时回退到 GPT 默认集合的问题
    • 新增/更新测试覆盖:internal/accessinternal/provisioninternal/host/sub2api
  9. current-code access ready 语义已提升到 completion 层
    • /v1/models 不再单独决定 subscription_ready/self_service_ready
    • 只有 /v1/models 命中 smoke_test_model/v1/chat/completions smoke 成功,控制面才会把 access 状态记成 ready
    • access closure / import runtime artifact / reconcile rerun payload 都会持久化 completion_ok/completion_status/completion_type/completion_preview
  10. current-code remote43 验收脚本已补 upstream API 证据层
  • scripts/acceptance/import_remote43_provider.sh 会直探 provider base_url 对应的 upstream /models/chat/completions
  • 新增 21-summary.json,用于把 completion 失败自动分流成 host_compatibility_gapupstream_key_quota_issue
  • 2026-05-27 已把 V2 batch-import reuse runtime 真正接到 live action
    • internal/app/batch_runtime.go 现已接入 InspectReuse
    • runtime reuse 查询优先命中既有 import_run_items,再回退到 legacy import_batches / import_batch_items / managed_resources / providers
    • 兼容 V2 短指纹与 legacy 完整 sha256 指纹
    • live run 现在可真实产出 matched_account_state / account_resolution / provision_reused
  • 2026-05-27 继续用 /portal/admin-batch-import.html 做真实页面操作验证,抓到了一个 live reuse 兼容缺口并已在本地修正:
    • real remote43 样本 https://api.53hk.cn/v1 + sk-4175...d776 + host=remote43-kimi-patched-auto2-18169 首轮返回 TOKEN_EXPIRED,根因是 CRM 中持久化的宿主 bearer 已过期;刷新 host auth 后item 已能恢复到 access_status=active
    • 旧版 runtime 仍把同一条历史账号判成 matched_account_state=none / account_resolution=created,根因是 live runtime 的 normalized provider_id(如 api-53hk-42797c06)与 legacy pack provider idminimax-53hk不一致时legacy reuse fallback 只按 provider_id 精确匹配
    • 当前已补 base_url fallback + ProviderMatched 策略信号legacy lookup 会补查相同 base_url 的 provider且“同 base_url + 同 key + family covered”现在可以真实收敛到 reused/reactivated
    • 定向回归已通过:go test ./internal/app -run 'TestBatchImportHTTP/(create run action reuses matched legacy account|create run action reuses legacy account when pack provider id differs from normalized runtime id)$' -count=1go test ./internal/batch -run TestDecideReuse -count=1go test ./internal/store/sqlite -run 'TestProvidersRepoListBy(BaseURL|BaseURLEmpty)$' -count=1
    • remote43 二次复验现已补证:更新后的 CRM 二进制已替换到 18173 控制面,真实 rerun run_1779882868037300268 已确认 item 从 account_resolution=created 收敛为 account_resolution=reused,并且 provision_reused=trueaccess_status=active
    • 当前剩余的细节是:该 rerun item 的 matched_account_state 仍为 none说明“reuse 命中后是否补出 active/disabled/deprecated state badge”仍可继续优化但这不影响本轮要验证的 created -> reused 结果成立
  1. patched CRM external validation 已完成
  • patched CRM 实例下DeepSeek 与 MiniMax 都已验证“completion smoke 通过时能落成 succeeded/active失败时不会误记成 ready”
  • 20260521_191418_remote43_minimax_key_import20260521_201509_remote43_deepseek_key_import 已同时证明当前 subscription provider 链路可真实闭环
  • 20260521_210403 已证明 latest-head self_service 标准 fresh-host 验收也可闭环到 self_service_ready / fully_ready
  1. artifact 保留策略已收口
  • 主目录 artifacts/real-host-acceptance/ 当前只保留最终证据
  • 历史失败/半成功/试错样本已迁到 artifacts/real-host-acceptance-archive/
  • 分类规则见:docs/REAL_HOST_ARTIFACT_RETENTION.md
  1. relay-manager latest-head 已收口 Kimi A7M 两段竞态
  • account test 首次 403 Forbidden 已降级为 advisory warning只要 /models 已命中 smoke_test_model,不会再把 batch 误判为 blocking failure
  • access closure 对导入后瞬时 503 / no available accounts 增加短暂 completion retry避免宿主异步 probe / account warm-up 窗口把真实可用链路误记成 broken
  • 20260522_122706_local_v0129_kimi_a7m_subscription_freshhost 已证明:在修复后的 relay-manager + patched host 组合下,kimi-a7m / kimi-k2.6 可落到 batch_status=succeededprovider_status=activelatest_access_status=subscription_ready
  1. relay-manager latest-head 已补宿主升级后的 capability 自愈
  • API returned 403: Forbidden 这类 /responses 误判 advisory控制面现在会在 access closure 与 reconcile rerun 中把目标 account 的 openai_responses_supported 修正为 false,随后重试 gateway /v1/chat/completions
  • 这样即使宿主升级或异步 probe 把 capability 标记覆写错,控制面也能在“安装后确认”与“后台持续对账”两个环节重新拉回可用状态
  1. 2026-05-24 本地质量门禁补丁已完成
  • 新增 repo 级删除保护:internal/store/sqlite/hosts_repo.go 引入 RuntimeDependencyCountsByHostIDHostDeleteBlocker
  • 新增回归测试:TestHostsRepoDeleteByHostIDBlocksWhenRuntimeStateExistsTestBatchImportRejectsOversizedJSONBodyTestDecodeJSON/rejects oversized request body
  • internal/app/http_api.go 现已统一限制 JSON request body 大小,并把 host 删除占用态映射为 host_in_use
  • internal/app / internal/provision / internal/reconcile 测试 SQLite 打开路径已改为串行 helper当前 go test -race ./... -count=1 重新恢复为绿

已验证门禁

  • gofmt -l . 空输出
  • go vet ./...
  • go test ./...
  • go test -race ./...
  • go test -cover ./internal/...
    • internal/access: 80.5%
    • internal/host/sub2api: 78.1%
    • internal/pack: 73.9%
    • internal/provision: 79.4%
    • internal/store/sqlite: 77.9%
  • go test ./tests/integration/... -count=1
  • bash ./scripts/test/test_real_host_scripts.sh

当前保留的最终证据

  1. artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation

    • 证明 account credentials.model_mapping 与 live runtime 对齐
  2. artifacts/real-host-acceptance/20260521_142211_crm18100_deepseek_completion_split

    • 证明 host completion 失败与 upstream completion 成功可以分离
    • 是 completion 分流逻辑的关键根因证据
  3. artifacts/real-host-acceptance/20260521_191418_remote43_minimax_key_import

    • MiniMax 53hk subscription 最终成功样本
    • 21-summary.json 已到 batch_status=succeededprovider_status=active
  4. artifacts/real-host-acceptance/20260521_201509_remote43_deepseek_key_import

    • DeepSeek 2166 subscription 最终成功样本
    • 21-summary.json 已到 batch_status=succeededprovider_status=active
  5. artifacts/real-host-acceptance/20260521_210403

    • latest-head self_service 标准 fresh-host 验收最终成功样本
    • 05-import.json = succeeded/self_service_ready/active
    • 07-access-status.json = latest_access_status=fully_ready
  6. artifacts/real-host-acceptance/20260521_222212_remote43_minimax-m2-7-official_key_import

    • official MiniMax 模板 live 样本
    • 模板链路打通,但当前验证 key 命中 upstream 429
  7. artifacts/real-host-acceptance/20260522_122706_local_v0129_kimi_a7m_subscription_freshhost

    • latest-head relay-manager 对 patched host v0.1.129 的 Kimi A7M subscription 最终成功样本
    • 21-summary.json 已到 batch_status=succeededprovider_status=active
    • account_probe_summary 明确记录 probe_advisory=truevalidation_status=warning,证明 403 probe race 已被 relay-manager 正确降级
  8. artifacts/real-host-acceptance/20260523_144937_remote43_kimi-a7m_key_import

    • remote43 未改宿主 + 修正后的 latest-head 验收脚本样本
    • 已证明脚本层的“错库取 key / 错地址 / 多 host 历史查询”问题被收掉
    • 仍保留的真实阻塞是宿主 completion 路径 502/503
  9. artifacts/real-host-acceptance/20260525_local_v0129_kimi_a7m_from_hermes

    • 当前主 pack 1.1.1 正式纳入 kimi-a7m 后的本地 fresh-host 验收样本
    • 21-summary.json 保留了 stock host v0.1.129 的原始失败快照,证明 Hermes A7M upstream key 当前在线可用,阻塞不在 key 本身
    • 22-patched-host-validation.json23-sub2api-host-patch-notes.md 已补齐 patched host 的真实收敛证据:问题是宿主 runtime 的 Responses -> raw chat 兼容缺口,补丁后链路已回到 ready

剩余项P2 / 运营前置,不阻塞按 PRD 首版范围上线)

  1. 运营前置

    • 真实宿主初始化不会自动创建普通用户;上线前必须显式创建普通用户并留存可复用凭据
    • self_service 需要普通用户 key 绑定目标标准 group且通常还需要可用余额
    • subscription 需要 subscription 类型 group + 普通用户订阅分配 + key/group 绑定
    • 若启用持续后台 reconcileSQLite 状态库将持久化最新 access probe 元数据,部署时必须按 secret 级别保护数据库文件
  2. 部署与环境限制

    • 标准多阶段 Dockerfile 在受限网络环境下仍不稳
    • 当前推荐 scripts/deploy/build_local_image.sh + Dockerfile.local
  3. official provider 验证矩阵

    • official MiniMax 当前 live 样本已证明模板链路可用,但验证 key 命中 upstream 429
    • Qwen / GLM / Kimi / Step 等官方 provider 是否通过 live 验收,仍取决于后续官方 key 与 quota

当前最短后续路径

  1. 若继续扩大 provider 覆盖面,优先按 docs/PROVIDER_VALIDATION_MATRIX.md 补官方 key再做 official live 验收
  2. 若继续优化 shared fresh-host 信噪比,对历史残留资源做一次环境清理,降低 reconcile=drifted 噪音
  3. 若继续产品化,优先扩大 official provider live 验收覆盖面,并基于新 create-run 入口补充真实宿主 acceptance artifact

v2 规划Batch Auto-ImportURL + Key

当前阶段 已按基线计划恢复实现T1~T13 已落地create-run entry wiring 已补齐,最新全量验证通过)

文档docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md(需求规格) TDD 计划docs/2026-05-21-BATCH_AUTO_IMPORT_TDD_PLAN.md(实现路径,已确认开放问题) 技术架构docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md运行态状态库、结果页、API、页面字段布局 Migration 草案docs/2026-05-22-BATCH_AUTO_IMPORT_V2_MIGRATION_DRAFT.mdSQLite 新表、索引、lease/retry 字段、legacy link API Schema 细稿docs/2026-05-22-BATCH_AUTO_IMPORT_V2_API_SCHEMAS.mdrun/item 响应结构、筛选参数、badge 文案、错误语义)

本轮设计收敛

  • 已把真实验收中的三类高频问题写入 v2 方案:
    • 添加模型时的模型名归一化与纠错
    • 第三方国产模型的兼容能力画像(/responses/chat/completions、Anthropic compatible、stream/tools
    • 添加账号后的异步确认窗口(首次 403 probe race、首次 503 no available accounts warm-up
  • 已补充两类产品化能力到 v2
    • run / item 状态持久化、retry 轨迹、控制面重启后的历史结果查看
    • 批次列表页 / 批次详情页用于查看模型纠错结果、账号状态、provider warning 与最终 access 状态
  • 当前 v2 的目标已从“同步导入成功”升级为“导入 + 异步确认 + 最终闭环验真”
  • 已按 review 收口最关键的 4 个冲突:
    • 统一 canonical contractrun_id/item_id/provider_id/run.state/confirmation_status/access_status
    • 补齐 subscription / self_service 的输入契约
    • 明确 V2 唯一状态源为 import_runs/import_run_items/import_run_item_events
    • 明确 ConfirmationWorker + lease + next_retry_at 的异步确认执行机制
  • 其余 review 问题也已同步收口:
    • capability 从 upstream 总画像升级为 transport + model profiles
    • 结果页字段、状态库存储字段、retry/event trail 已统一
    • run 级请求上下文已持久化到 import_runs,控制面重启后 validate 能继续使用 host_id / subscription_users / subscription_days / probe_api_key
    • OpenAPI 已补齐 /api/batch-import/runs*legacy /api/import-batches/* 降级为 v1/legacy
    • run/item 列表 API 已补齐 cursor/next_cursorrun 列表 q 可命中 run_id / provider_id / base_url
    • 已补充重复导入自动复用策略:按 provider_id + api_key_fingerprint + canonical_model_family 判断 reused / patch_only / replace
    • 已补充同模型别名归一化契约:例如 kimi 2.6 / kimi-2.6 / kimi-k2.6 可归并到同一模型家族并快速复用
    • 已补充多账号重复导入与弃用账号再启用策略active 账号提示“重复已启用”disabled/deprecated 账号显示原状态并走 reactivated 快速启用路径
    • 已修正 access closure artifact 的 probe key 语义漂移:subscription 现在持久化 requested_probe_api_keyeffective_probe_key_sourceeffective_probe_key_fingerprint,不再把外部 raw key 伪装成 probe_api_keyprobe_api_key 仅继续用于 self_service 向后兼容
    • 最新干净本地 fresh-host 验收 artifacts/real-host-acceptance/20260523_local_clean_minimax_subscription_probe_semantics 已验证:
      • subscription closure 会正确区分 requested_probe_api_keymanaged_subscription 实际 probe 来源
      • 同一轮 raw key 直打宿主仍返回 403 not assigned to any group
      • provider completion 仍受 MiniMax 官方 upstream 429 rate_limit_error 影响,但这已不再会被 artifact 误读成 raw key 可用
    • 同一 fresh-host 上继续补的 MiniMax M2.5 缩圈验证已证明:
      • artifacts/real-host-acceptance/20260523_local_clean_minimax_m25_only_probe:单独只打 M2.5 时,宿主会选中真实账号并命中 upstream 429
      • artifacts/real-host-acceptance/20260523_local_clean_minimax_m25_repeated_probe:连续第 1 次 M2.5429,后续第 2/3 次退化成 503 Service temporarily unavailable
      • 对应宿主日志中,第一次有 account_id=1upstream_status=429,后续只有 account_select_failed error=\"no available accounts\"
      • 当前 MiniMax live 阻断要按两层解释:第一次是 upstream quota/rate-limit后续 503 是唯一账号进入临时不可调度窗口后的宿主侧结果

本轮实现状态T1 ~ T13

  • internal/batch canonical types / reuse policy / service / confirmation / validation / projection 已落地
  • internal/probe models / alias / capability / smoke completion 已落地
  • internal/store/sqlite run/item/event runtime state repo 与 migration 已落地
  • /api/batch-import/runs* 路由、projection 读取、CLI batch-import 命令、集成测试与设计还原审计已落地
  • go test ./... -count=1
  • go test ./tests/integration/... -count=1
  • go test -cover ./internal/... -count=1
  • go vet ./...
  • gofmt -l .

T13 审计结论

  • docs/2026-05-22-BATCH_AUTO_IMPORT_V2_RESTORATION_CHECKLIST.md 已完成
  • latest-head 已补齐 internal/app/http_batch_import.go -> internal/app/batch_runtime.go 的 create-run 入口 wiring
  • API 与 CLI create-run 现在都会真实驱动 BatchImportService + ConfirmationWorker + ValidationService
  • 控制面 server 启动后会自动运行 batch-import background schedulerrunning run 在重启后可继续推进
  • 最新一轮验证结果保持全绿:go test ./... -count=1go test ./tests/integration/... -count=1go test -cover ./internal/... -count=1go vet ./...gofmt -l .

真实 Gate 文档、状态机、投影、测试、审计与 create-run 入口已经对齐,V2 设计已按基线计划交付


禁止错误结论

  • 历史失败 artifact ≠ 当前 latest-head 仍失败
  • capability probe 无副作用 ≠ 所有宿主版本都已真实兼容
  • rollback-provider 已改安全路径 ≠ 历史脏资源自动消失
  • HTTP 200 ≠ 宿主初始化会自动准备普通用户/订阅/余额;这些仍是显式运营前置