AMR 生产发布影响报告

生成时间:2026-10-12(观察时刻 2026-10-12T02:02:39Z);分析对象:从 origin/main(HEAD 5b655e207)发布 Web、API、Link、Admin、DB migration 与 Model Routing Catalog 的影响分析。四个服务以 EKS Pod 实际运行镜像为生产发布点,DB migration 与 Catalog 以对应 Forgejo workflow 最近一次成功运行的 commit_sha 为生产发布点;六个维度分别计算变更范围。

当前生产发布点(EKS 实态 + Forgejo workflow 记录)

Web、API、Link、Admin 的发布点来自 Grafana / Prometheus 观察到的 Ready EKS Pod 镜像(namespace nexu,datasource prometheus,由 kube-state-metrics 采集,样本新鲜度均在 13 秒内);DB migration 与 Catalog 的发布点来自对应 Forgejo workflow 最近一次成功运行。四个服务均已校验:同一服务所有 Ready Pod 的 commit 与 digest 一致、副本数 spec = updated = available、无滚动中或回滚中的混合状态。 feature 表里只允许列出发布点之后的变更;发布点之前的内容属于已生效,归入「已在生产生效」一节。目标 commit 均为 5b655e207(fix(link): clamp output budgets with Admin-configured backend limits (#170),2026-10-10 11:18,mrcfps)。

Web · EKS Pod 实态
Workload:nexu/amr-web · container web
观察时间:2026-10-12T02:02:39Z(Grafana / Prometheus kube_pod_container_info,image 过滤 amr-web:prod-[0-9a-f]{40} 且仅取 Ready Pod)
Ready Pods:2 / 2(updated 2,available 2)
实际 commit:f1b0d7643
镜像 digest:sha256:d726100bfc0a3885cdbe85b28b381e4947571ffae20f44e42feb815650e7761b
Pod:amr-web-6bdb94c8b6-kpljj, amr-web-6bdb94c8b6-sn8r8
Workflow 审计(仅供参考):deploy-web-eks-prod.yml 最近成功 run #4299 @ f1b0d7643;与 Pod:一致(workflow 最近成功运行的 commit_sha 与 Pod 实态相同)
本次新增(f1b0d7643...5b655e207):2 commits,其中 0 个触及该服务目录的变更
发布点之后 0 个触及 apps/web/ 的 commit(范围 f1b0d7643...5b655e207 只有 #234、#170 两个提交),Web 无净新增内容。 Web 已于 2026-10-10T10:09:54Z 由 run #4299 滚动到该 commit,Pod 集合为 amr-web-6bdb94c8b6-*。
API · EKS Pod 实态
Workload:nexu/amr-api · container api
观察时间:2026-10-12T02:02:39Z(Grafana / Prometheus kube_pod_container_info,image 过滤 amr-api:prod-[0-9a-f]{40} 且仅取 Ready Pod)
Ready Pods:2 / 2(updated 2,available 2)
实际 commit:3b3ea92c1
镜像 digest:sha256:6599559c5f91bdad4c73298152779034a2938f27d32c74bc8808dee470f0a002
Pod:amr-api-cb7b56b95-bz9ln, amr-api-cb7b56b95-dw5q7
Workflow 审计(仅供参考):deploy-api-eks-prod.yml 最近成功 run #3477 @ 3b3ea92c1;与 Pod:一致(workflow 最近成功运行的 commit_sha 与 Pod 实态相同)
本次新增(3b3ea92c1...5b655e207):15 commits,其中 0 个触及该服务目录的变更
发布点之后 15 个 commit 均不触及 services/api/,API 无净新增内容。 API 发布点仍是 6 个维度中最旧的之一(2026-09-30),本轮重发不会带来运行时变化。
Link · EKS Pod 实态
Workload:nexu/amr-link · container link
观察时间:2026-10-12T02:02:39Z(Grafana / Prometheus kube_pod_container_info,image 过滤 amr-link:prod-[0-9a-f]{40} 且仅取 Ready Pod)
Ready Pods:4 / 4(updated 4,available 4)
实际 commit:f1b0d7643
镜像 digest:sha256:797c97be0b63dabbe560c3cd518df7192a7e46ef810ffd461b6cbe7cff2acbab
Pod:amr-link-56d55fcf86-r6sxw, amr-link-56d55fcf86-fcrrp, amr-link-56d55fcf86-vnzvx, amr-link-56d55fcf86-s7fs6
Workflow 审计(仅供参考):deploy-link-eks-prod.yml 最近成功 run #4290 @ f1b0d7643;与 Pod:一致(workflow 最近成功运行的 commit_sha 与 Pod 实态相同)
本次新增(f1b0d7643...5b655e207):2 commits,其中 5b655e207 fix(link): clamp output budgets with Admin-configured backend limits (#170)
发布点之后 2 个 commit 中 1 个触及 services/link/:5b655e207(#170 输出预算 clamp),即本报告 feature 表第 1 行。 Link 副本为 4(spec 4 = updated 4 = available 4)。
Admin · EKS Pod 实态
Workload:nexu/amr-admin · container admin
观察时间:2026-10-12T02:02:39Z(Grafana / Prometheus kube_pod_container_info,image 过滤 amr-admin:prod-[0-9a-f]{40} 且仅取 Ready Pod)
Ready Pods:2 / 2(updated 2,available 2)
实际 commit:f1b0d7643
镜像 digest:sha256:92dd3463c3c5f18c078cd2afe6c85ca28c003143f26095525cd8dd84d1b2f443
Pod:amr-admin-7d458f7fbd-d7tsv, amr-admin-7d458f7fbd-6vx5b
Workflow 审计(仅供参考):deploy-admin-eks-prod.yml 最近成功 run #4291 @ f1b0d7643;与 Pod:一致(workflow 最近成功运行的 commit_sha 与 Pod 实态相同)
本次新增(f1b0d7643...5b655e207):2 commits,其中 69ffad4b0 fix(admin): restore CLI SSO consent and loopback redirect (#234)、5b655e207 fix(link): clamp output budgets with Admin-configured backend limits (#170)
发布点之后 2 个 commit 中 2 个触及 apps/admin/:69ffad4b0(#234 CLI SSO 授权修复)与 5b655e207(#170 后端输出上限字段),即本报告 feature 表第 1、2 行。 上一轮 Admin 还曾把 0dc3fc2fb(#127)列入待发布,该变更已由 ed2fdcd97(#193)回退并在 10-10 随 f1b0d7643 生效。
DB migration · workflow 记录
最近成功:run #4289 @ 2026-10-10T09:48:00Z
发布点 commit:f1b0d7643
本次新增(f1b0d7643...5b655e207):2 commits,其中触及 db/migrations/ + db/schema/ 的变更 0 个
数据源为 Forgejo run 列表(tea api /repos/core/amr/actions/runs),不使用 GitHub Actions。 发布点之后 0 个 migration / schema 变更,无需重新运行该 workflow。仓库最新一份 migration 20261010060924_admin_cli_credentials.sql(af748c660,#229)已随本基线 apply。
Model Routing Catalog · workflow 记录
最近成功:run #3923 @ 2026-10-08T06:37:30Z
发布点 commit:f3ab9fccd
本次新增(f3ab9fccd...5b655e207):10 commits,其中触及 data/json/ + db/seeds/ 的变更 2 个
数据源为 Forgejo run 列表(tea api /repos/core/amr/actions/runs),不使用 GitHub Actions。 发布点之后 2 个 commit 触及 catalog 源:cb6e014f4(#159 seed 改为 bootstrap-only)与 5b655e207(#170 输出上限 metadata)。两者都是 seed/生成器侧改动,不会修改已初始化生产目录(catalog 正在被 Admin 接管)。

此次发布真正新增的 feature 和影响面

feature 表只包含「各维度生产发布点之后」的变更:四个服务以 EKS Pod 实态为基线,DB migration 与 Catalog 以 Forgejo workflow 记录为基线。发布点之前已生效的内容见 「已在生产生效」。

结论:本轮只有 3 个待发布 feature:Link + Admin + Catalog seed 三处联动的 #170 后端输出上限(5b655e207,L,唯一改变线上调用方行为的变更,且 必须先在 Admin 里给既有后端配置上限再发 Link)、Admin 的 #234 CLI SSO 授权修复(69ffad4b0,M,修掉 10-10 刚上线的 CLI 登录在真实浏览器中被 403 / CSP 拦死的问题)、Catalog 的 #159 seed 初始化边界(cb6e014f4,S,已初始化环境 no-op)。Web、API、DB migration 无待发布内容:Web 发布点之后没有 apps/web/ 变更;API 发布点(3b3ea92c1,2026-09-30)之后 15 个 commit 全部不触及 services/api/;DB 发布点已是 10-10,之后没有新的 db/migrations/ 或 db/schema/。

Feature / Author / Commits Web API Link Admin DB schema / migration DB catalog(seed)
LLink 按后端配置上限 clamp 输出预算 + Admin「输出 token 上限」字段

修掉「调用方显式给的输出预算超过后端上限 → 上游 400」这一类失败(典型现场:MiMo 请求 max_tokens: 232000 → 400 Param Incorrect,2026-09-30 16:45 生产告警里有 6 次真实上游 400 可归因)。Link 现在在路由选定、请求解析之后、provider 兼容与 Bifrost 序列化之前,把显式请求的输出预算 clamp 到所选后端的上限:覆盖 Chat Completions(max_tokens / max_completion_tokens,保持既有优先级)、Responses(max_output_tokens,含 Chat facade)与 Anthropic Messages(max_tokens,含两种协议 facade),流式 / 非流式一致;小于上限或未传的预算保持原样(不会凭空注入预算,provider adapter 默认值继续生效);DashScope Qwen/Kimi 的 answer-only max_tokens 仍保持 answer-only 语义;每个后端各自解析上限,重试换后端会重新解析,不改写 caller 原始字节、reasoning 选项与目录元数据。

BytePlus deepseek-v4-1-flash-260910 原本写在兼容层里的本地硬拒绝被移除,改为「配置了上限就 clamp、没配置就不拦」;未配置上限的后端行为完全不变。注意:一旦发布 Link,这条路由的超限请求不再本地 400,而是转发上游(该 backend/route 在仓库 baseline 中为 inactive,但 Admin 里的既有配置可能不同,发布前需核对)。

Admin 侧给后端模型新增专门的 Output token limit / 输出 token 上限 字段(读写 metadata.max_output_tokens,整数 16..2147483647,清空即移除键),与公共模型的 context 容量 / compaction 预算完全独立;UI 与 CLI 共用同一 schema 校验,后端测试使用未保存的值,保存仍走候选校验 + 事务 + 审计 + 运行时 reload,失败保留输入。被 clamp 的请求在 span 上记录 link.output_limit.clamped / .requested / .applied,便于排障(推理预算 thinking.budget_tokens / reasoning.max_tokens 不被 clamp,原生 Anthropic 这类「推理预算必须小于输出预算」的供应商约束仍然存在)。

⚠️ 既有环境必须人工配置才生效:catalog 的数据库(由 Admin 管理)才是运行时的配置源,seed / JSON 只初始化空环境,不会回填既有环境。发布前需要在 Admin 里给 Xiaomi mimo-v2.6-flash / mimo-v2.6-pro 后端设 131072、给 BytePlus deepseek-v4-1-flash-260910 设 393216(保留已核实的既有上限与无关 metadata,不要顺手启用非 active 的 backend/route)。PR 建议的顺序是先发 Admin 并保存配置,再发 Link;不配置就直接发 Link 的话,MiMo 仍会把超限请求转给上游并继续 400,而 BytePlus 会失去原有的本地拒绝。

Author:mrcfps

  • 5b655e207 fix(link): clamp output budgets with Admin-configured backend limits(#170)

规格:specs/current/link/model-output-limits.md(含配置、分批发布、验证与回滚步骤)、specs/current/admin-model-configuration.md。

无。 无。 11 files,+556 / −8,全部在 services/link/:新增 internal/bifrostengine/output_limit.go(clampChatOutputLimit / clampResponsesOutputLimit / recordOutputLimitClamp)与 internal/domain/output_limit.go(BackendModel.MaxOutputTokens(),非法配置报 configuration error 而不是当成调用方 400)、internal/catalog/catalog.go 在构建运行时目录时校验上限、bifrostengine/engine.go 接入两条入口、anthropic_messages.go 接入 Messages facade;providercompat/adapter.go 与 text.go 删掉 BytePlus 的硬编码 393216 本地拒绝(−7 行)。测试 +538 行:output_limit_test.go(307 行,覆盖三种协议 × 流式/非流式、事故值、边界值、Kimi answer-only、原生 Anthropic reasoning、重试、多后端隔离)、catalog/output_limit_test.go、server/internal_catalog_configuration_test.go(经真实 Bifrost 引擎打到本地 HTTP 上游,验证 set / lower / clear)。
无路由、无公共响应结构、无计费口径变更;上限值本身不写死在代码里。
8 files,+241 / −8,全部在 apps/admin/:app/[locale]/(protected)/models/configuration/model-configuration-editor.tsx(+62)新增上限输入与「未配置上限」提示,lib/model-configuration.ts 把 metadata 收紧为带 max_output_tokens 的 passthrough schema(其余 metadata 键保持自由,清空即删除该键),i18n 中英文案(输出 token 上限 / Output token limit 与帮助文案),以及 4 个测试文件 +164(含保存失败保留输入、清理不误删无关 metadata、未保存 drafts 使用同一字段)。 无 schema / migration。上限复用既有 metadata jsonb 列。 2 files,+11 / −6:data/json/provider_routes.json 给 mimo-v2.6-flash / mimo-v2.6-pro 加 max_output_tokens: 131072、给 deepseek-v4-1-flash-260910 加 393216(保留 upstream_request_kind: responses 与各自 status);db/seeds/generate_model_routing_catalog.py(+5)校验该字段并写入 backend metadata,db/seeds/seed-model-routing-catalog.sql 重新生成(同一 metadata jsonb 里多出该键)。
这批 seed 改动只对空环境生效:已初始化的生产目录应用 seed 会被跳过(见第 3 行 feature),因此既有环境的上限必须通过 Admin 配置,不能指望这次 catalog 发布回填。
MAdmin:修通 CLI SSO 授权同意页与 loopback 回调

10-10 在 Admin 上线的 CLI 登录(#229,已随 f1b0d7643 生效)在真实浏览器里走不完:点 Authorize CLI 后同意页以 Referrer-Policy: no-referrer 响应,原生表单 POST 因此带上 Origin: null,被 Admin 拒绝为 403 invalid_origin;把 header 放宽后,Chromium 又按 form-action 'self' 拦掉了到 CLI 本地监听端口(http://127.0.0.1:<port>)的 303 跳转。本 PR 修掉这两道浏览器边界:同意页改用 referrer-policy: same-origin(auth / router 包装层只在响应没有该 header 时才补 no-referrer,普通鉴权响应仍保持 no-referrer),并把 grant schema 已校验过的精确 loopback origin 加进 CSP form-action(只加这一条 origin+port)。no-store、PKCE、state / member 绑定、其余 CSP 限制不变;缺失、null、跨域 approval Origin 仍被拒。

影响对象是内部运维 / Agent 驱动的 onboarding 流程(pnpm amr-admin catalog configure 与只读 catalog 查询都依赖这一步登录),不影响面向客户的产品路径与任何后端契约。

Author:Liu Yi

  • 69ffad4b0 fix(admin): restore CLI SSO consent and loopback redirect(#234)
无。 无。 无。 5 files,+161 / −8,全部在 apps/admin/:lib/server/admin/cli-auth.ts(同意页 header:form-action 'self' ${callbackOrigin} 与 same-origin,callback origin 取自已通过 schema 校验的 grant.redirectUri)、lib/server/admin/auth.ts 与 lib/server/admin/router.ts(仅在响应未设置时才覆写 referrer-policy)、测试 test/cli-auth.test.ts(+36 / −3)与新增 test/cli-consent-router.test.ts(114 行,覆盖策略穿透 router、精确回调端口、非法 destination 与非法 approval Origin)。
纯 header / CSP 策略修复,无路由、无鉴权逻辑、无数据写入路径变更。
无 schema / migration。 无 catalog 变更。
SCatalog seed 退化为「仅初始化」:bootstrap 守卫 + test activation 同守卫

#159 把「模型目录由 Admin + 数据库负责」定成契约后,仓库里的 catalog seed 必须从「可重复 apply 的同步脚本」退化成「只初始化空环境、绝不覆盖既有配置」。本 commit 的 catalog 侧改动:生成的 seed-model-routing-catalog.sql 整体包进 DO $catalog_bootstrap$ 守卫(先对 link.providers / backend_models / public_models / public_model_routes / public_model_release_gates / public_model_admin_overrides / route_realisations 取 SHARE ROW EXCLUSIVE 锁,若任一目录表已有数据则 RAISE NOTICE 'Catalog already initialized; seed skipped. Manage configuration in Admin.' 后直接返回),并用 vela.catalog_bootstrapped 会话变量把 test-activation overlay 与本地 smoke fixture 限制在「真正初始化了这个库的那一次事务」里;generate_model_routing_catalog.py 增加 bootstrap_guard() 与保留字校验,model_routing_catalog.py 的 help 文案同步为「initialize an empty catalog」,catalog workflow 的 apply job 也从 Apply Model Routing Catalog to Prod DB 改名为 Initialize Empty Prod Model Catalog。

对已经初始化的生产目录来说这是 no-op(重复 apply 只打印 notice);意义在于防住「用旧 seed 覆盖 Admin 改动 / 回滚已核实配置」这类事故。Admin 侧与 Link 侧的同名功能(#159 主体:目录 CRUD UI/CLI、tier 路由工作台、后端诊断、Link 内部校验与 reload 端点)已经随 10-10 的 f1b0d7643 发布生效,本轮只补 catalog 这个边界。

Author:mrcfps

  • cb6e014f4 feat(admin): manage model catalog via UI and CLI(#159)— 本轮仅其 db/seeds/ + workflow 命名部分待发布
无。 无。 无(services/link 侧的 #159 内部目录配置端点已于 10-10 生效)。 无(apps/admin 侧的 #159 CRUD / CLI / 诊断 UI 已于 10-10 生效)。 无 schema / migration。 4 files,+73 / −4 在 db/seeds/:generate_model_routing_catalog.py(+40 / −2,新增 bootstrap_guard() 并包裹 baseline seed、test activation 与 smoke fixture)、seed-model-routing-catalog.sql(+19)、seed-model-routing-catalog-test-activation.sql(+12)、model_routing_catalog.py(+2 / −2);另有 .forgejo/workflows/deploy-model-routing-catalog-prod.yml 的 apply job / step 改名(+2 / −2,功能不变)。
重新触发 catalog workflow 对既有生产目录是无净变更的幂等操作(会被守卫跳过),因此 catalog 本轮不需要发布。

范围内其余 commit 不产生六个维度的净变更:API 的 compare range(3b3ea92c1...5b655e207)里 15 个 commit 覆盖 apps/admin/、apps/web/、services/link/、db/seeds/、data/json/、.forgejo/workflows/ 与文档,但没有一个触及 services/api/;其中大多数已随 10-10 的 Web / Link / Admin / DB 发布生效,剩下的就是上表 3 个待发布 feature。

已在生产生效(发布点之前,本次不重复发布)

以下 feature / commit 均在对应生产发布点之前,已在生产运行。四个服务以 EKS Pod 实态为基线,DB migration 与 Catalog 以 Forgejo workflow 记录为基线;若从 main 重新发布,只分析各自基线之后的净新增。

发布建议

  1. Migration 判断:不需要跑 migration。BASE_DB f1b0d7643(10-10 apply)之后到 main 的 2 个 commit 中没有任何 db/migrations/ 或 db/schema/ 变更;最新 migration 20261010060924_admin_cli_credentials.sql 已在生产执行,无需触发 deploy-db-migrations-prod.yml。
  2. 服务发布顺序(本轮关键):#170 的 clamp 只有在 Admin 里配置了上限之后才有实际效果,PR 与 spec 给出的顺序是 先 Admin、后 Link:
    • 1) Admin:触发 deploy-admin-eks-prod.yml,一次性发布 69ffad4b0(#234)与 5b655e207(#170 Admin 侧,发布点之后 2 个 commit 全部触及 apps/admin/)。
    • 2) 在 Admin 里配置既有后端上限(seed 不回填):provider_xiaomi 的 mimo-v2.6-flash / mimo-v2.6-pro → 131072;provider_byteplus 的 deepseek-v4-1-flash-260910 → 393216(保留 upstream_request_kind: responses 与现有 status,不要顺带启用非 active 的 backend/route)。按供应商 + upstream model id 定位行(含 Admin 新建的 ID),已核实的既有上限不要静默提高;保存后重开确认持久化与 runtime reload 成功。
    • 3) Link:触发 deploy-link-eks-prod.yml,发布 5b655e207(1 个 commit,滚动后应等于 main HEAD);随后用一次超限请求核实 clamp 行为(见下)。如果直接把 Link 先发:MiMo 仍会把超限请求转上游(继续 400),并且 BytePlus 那条原本的本地拒绝会消失 —— 发布前先确认该 backend/route 在 Admin 中的真实状态。
    • Web / API 不必滚动(Web 之后没有 apps/web/ 变更,API 之后没有 services/api/ 变更);若为求 Pod 与 HEAD 一致而重发,不会带来运行时变化。Admin 与 Link 之间除「先配置后生效」外没有别的依赖,可与 #234 的验证合并到同一个变更窗口。
  3. Catalog 判断:不需要触发 deploy-model-routing-catalog-prod.yml。发布点之后的两个 catalog 变更分别是 #170 的 seed metadata(只初始化空环境)与 #159 的 bootstrap 守卫(对已初始化生产目录是 no-op),重跑只会打印 Catalog already initialized; seed skipped.。请勿用旧 seed / 未加守卫的 SQL 绕过这层边界去「回填」上限 —— 那会覆盖 Admin 的配置。
  4. Public Pricing(独立 workflow,本轮核对结论与上一份报告不同):deploy-public-pricing-prod.yml 的基线仍是 0ab43a985(2026-09-24)@ run #2813。但公开价格 revision 由 packages/shared/src/public-pricing.ts(+ services/api/src/public-pricing/、services/api/scripts/public-pricing-source.ts)计算,这几个文件自 0246fb3e1(09-24,早于基线)之后没有再变化,因此本轮重新触发该 workflow 会得到完全相同的 revision,属于幂等无变化 —— 不像上一份报告所述是「落后 3 个价格文件」。上一份报告引用的 data/json/public_pricing.json 是 catalog seed 的输入(public model 售价),它的变更已随 catalog 发布(f3ab9fccd 等)在 10-08 生效。
  5. 发布后观测:
    • Pod 实态:Admin、Link 镜像 tag 应变为 prod-5b655e207…,Ready 恢复为 Admin 2/2、Link 4/4(spec = updated = available);Web(prod-f1b0d7643…,digest sha256:d726100bfc0a3885c…)与 API(prod-3b3ea92c1…,digest sha256:6599559c5f91bdad4…)应保持本报告值不变。
    • #170 clamp:对 MiMo 的 active 路由发一次 max_tokens: 232000(或 > 上限)的请求,应不再出现上游 400,而是正常返回并以 finish_reason: length 收尾;Tempo 中该 span 应有 link.output_limit.clamped=true / .requested / .applied;未配置上限的后端与 ≤ 上限的请求行为不变;不要为验证启用 inactive 的 BytePlus backend/route(用本地 fixture 或明确授权的 backend test)。同时确认计量 / usage 仍按供应商实际 token 结算,reasoning 预算未被改写。
    • #234 CLI SSO:发布后在真实浏览器里跑完整链路 —— Authorize CLI → 同意页 → 同源 POST → 303 → loopback(http://127.0.0.1:<port>)→ PKCE 交换 → auth status 成功;同时确认同意页返回 referrer-policy: same-origin 与含精确 loopback origin 的 CSP form-action,而普通鉴权响应仍为 no-referrer,缺失 / null / 跨域 Origin 仍被拒。PR #234 自述测试环境尚未部署,线上 SSO 与 catalog 只读查询验收需要在这次发布后重跑。
    • Catalog 边界(可选):若因其它原因触发 catalog workflow,确认日志出现 Catalog already initialized; seed skipped.,且 link.providers / backend_models / public_models / public_model_routes 等表的内容与时间戳未变(可对比 apply 前后的表快照哈希)。

备注:本报告所有 workflow、commit、PR 与 compare range 均指向 Forgejo code.powerformer.net/core/amr,不使用 GitHub Actions。四个服务的 compare range 基于 Grafana / Prometheus 观察到的 Ready Pod 镜像 commit;DB migration 与 Catalog 的 compare range 基于各自 workflow 最近一次成功运行的 commit_sha;四个服务维度另附对应 deploy-*-eks-prod.yml 最近成功记录作为审计对照。四个服务的生产发布点均取自 EKS Ready Pod 实态(而非 GitOps / workflow 最近成功记录),DB migration 与 Catalog 取自 Forgejo workflow 记录。