Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(models,devin-acp): add swe-2 + route Devin Local-only models through ACP - #266

Open
Jakevin wants to merge 1 commit into
dwgx:masterfrom
Jakevin:feat/devin-local-swe-2
Open

feat(models,devin-acp): add swe-2 + route Devin Local-only models through ACP#266
Jakevin wants to merge 1 commit into
dwgx:masterfrom
Jakevin:feat/devin-local-swe-2

Conversation

@Jakevin

@Jakevin Jakevin commented Sep 13, 2026

Copy link
Copy Markdown

改了什么 / What changed

Cascade 对 gpt-5.6*swe-2* 会返回 403 This model is only in Devin Local。在 DEVIN_CLI_ENABLED=1 时,这些模型改走本机 devin acp(与 Devin Desktop 的 Devin Local 同一条路径)。SWE-2(medium / high / max)补进 catalog。Gemini 等 Cascade 模型不变。

为什么 / Why

SWE-2 于 2026-09-10 上线 Devin Desktop / CLI(docs.devin.ai:swe-2-medium / swe-2-high / swe-2-max),v3.9.31 catalog 没有这些 selector。gpt-5.6 同样是 Devin Local-only。

Enterprise 账号没有 CLI HTTP 权限(GetCliModelConfigs 403),但 Desktop ACP 可用。实测:ACP initializeclientInfo.name 必须是 windsurf,且 authenticate 要带 api_server_url(默认 https://server.codeium.com),否则 team settings 403。--model / DEVIN_MODEL 让 session 用请求的模型,而不是 CLI 默认。

未改 DEVIN_CONNECT;未新增默认开的开关。

测试 / Testing

Node v22.23.2。跑过:

node --test test/backend-router.test.js test/tool-emulation.test.js test/devin-connect-catalog-drift.test.js

327 pass / 0 fail。

Live smoke(enterprise session + bundled devin acpDEVIN_CLI_ENABLED=1):

  • POST /v1/chat/completions model=swe-2 → HTTP 200 pong(log: special-agent backend (devin-acp)
  • model=gpt-5.6-luna → HTTP 200 pong via ACP
  • model=gemini-3-8-flash-low → HTTP 200 via Cascade(未改道)

未跑全量 npm run test:release(本机 Node 22)。

Checklist

  • 代码风格和现有文件一致 / Code style matches existing files
  • 没有引入 npm 运行时依赖 / No new npm runtime dependencies (project is zero-dep)
  • 测试跑过了,贴了结果 / Tests run, output pasted above
  • 新行为有测试,且断言行为而不是 grep 源码文本 / New behaviour has tests that assert behaviour
  • 新开关默认关;若默认开,已加进 test/default-on-switch-registry.test.js 台账 / no new default-on switches
  • 加了开关就同步了文档 —— 未新增 reader-facing 开关(复用 DEVIN_CLI_ENABLED / DEVIN_CLI_MODE
  • 涉及 wire 协议改动时,注明字段号来源 — ACP handshake 对齐 Devin Desktop(clientInfo.name=windsurf),非 proto 字段号
  • 不涉及 dashboard UI
  • commit message 没有任何 AI 署名尾注

@dwgx

dwgx commented Sep 13, 2026

Copy link
Copy Markdown
Owner

评审:方向有价值,但这一版不能合。 你自己也说了只跑了 3 个测试文件 —— 我跑了全量,它新增了 1 条失败;而且那条失败背后是一个真实的行为变更,不是等价重写。下面 6 条都能单独验,改完仍然欢迎。

head 4889dfc,worktree 隔离。下表是相对当时origin/master = e725c7d 量的;master 之后前进了(5ec9194,含我自己的修复),合并前我会按那时的 master 再跑一遍全量

一条你该知道的:本仓库 CI 上逐文件的测试数量会在两次全绿之间漂移(同一份代码、两次 run 都绿,而 test/openai-error-vocab.test.js 一次 pass 47、一次 pass 39)。所以我下面只把"哪几条失败"当证据,不把总数当证据 —— 你那条"新增 1 条失败"的判据不受影响,因为它是失败不是计数。

结果
全量 test/*.test.js 4181 / 4175 / 6 fail —— master 基线是 4169 / 4164 / 5 fail新增 1 条
你自陈的 3 个文件 327 pass / 0 fail,我复现一致
devin-acp-integration.test.js(你没跑的) master 上 12/12 绿,打上你的改动后红
secret-scan / git diff --check exit 0 / clean
突变 anchor 无漂移

M1(阻塞)— api_server_url 从"没有就不发"改成"没有就发公共端点"

失败的就是这条:

test/devin-acp-integration.test.js:186
  "omits api_server_url from the authenticate frame when the pool account has none"

master : 12/12 绿
+ 你的改动: 红 —— 实得 'KEY=key-only|SERVER=https://server.codeium.com|...'

你把 ...(apiServerUrl ? { api_server_url: apiServerUrl } : {}) 改成了无条件
api_server_url: apiServerUrl || 'https://server.codeium.com'

这不只是让一条测试红了:它把"账号没有记录自定义 server"的部署从"不声明"改成"声明成公共端点"。那是行为变更,得由证据支撑(哪次实测证明不发这个字段会过不了 authenticate?),而不是为了避免一次 403 顺手加上。要么保留原不变式,要么按 mode 分支,别改默认值。


M2(设计)— 新增的 devin_local_only 分支对 swe-2*死代码

分支插在 isSpecialAgentInfo 之后(backend-router.js:138 附近),但你给 swe-2*models.js 里写了 backend: 'special_agent',而那一支在 :120-126 先短路返回。实测 reason 永远是 modelInfo.backend=special_agent,走不到你的新分支。

你的测试为什么是绿的:它传的是没有 backend 字段的裸对象 { name: 'swe-2-medium', modelUid: 'swe-2-medium' } —— 那个形状在生产里不存在。断言了一条走不到的路径。


M3(可达性)— DEVIN_CONNECT=1 时整条分支不可达,而同一个 PR 又把 swe-2* 塞进 Connect 白名单

docker-compose.yml:37DEVIN_CONNECT 默认就是 1selectBackend 顶部先返回 devin_connect ⇒ 你新增的 devin_local_only 分支在默认部署下永远不执行

同时你又把 swe-2-medium/high/max 加进了 src/data/devin-catalog-snapshot.json(DEVIN_CONNECT 的白名单)。两件事放在一起是矛盾的:要么走 Connect(那 ACP 分支是多余的),要么走 ACP(那不该往 Connect 白名单里加)。请说明目标部署是哪一个,我按那个来定。


M4(写错了)— 标题说 ACP,默认走的是 print;标注 --model 也是重复的

devinCliMode() 只在 DEVIN_CLI_MODE === 'acp' 时回 DEVIN_ACP,否则回 DEVIN_PRINT;而 print 对带 tools 的请求直接 400 unsupported_tool_boundary。你在描述里的实测是 DEVIN_CLI_ENABLED=1(没写 mode),也就是默认落在了 print 上

另外 devin-acp.js 里同时设了 env.DEVIN_MODEL = modelKey args.push('--model', modelKey) —— 二选一即可,两份都发将来会打架。


M5(请收紧)— 正则没有锚定,会劫持别的模型名

/gpt-5[.-]6//swe-2(?:[.-]|$)/ 会命中 gpt-5.60swe-2-0swe-2.5swe-2-pro

同一个 PR 里 tool-emulation.js 的 swe-2 正则却是锚定的/^swe-2(?:-(medium|high|max))?$/)—— 两处标准不一致。仓库里有一条现成的教训:93ae320 修的就是"glm-5-2 这个拼法两个前缀测试都不中、落到错的方言",症状是模型只回文本、不发工具调用、客户端一直等。请把两处都锚成只认观测到的 SKU


M6(请改成显式 opt-in)— 两个"默认放行"的味道

  1. DEVIN_PERMISSION_MODE=auto 兜底if (!env.DEVIN_PERMISSION_MODE) env.DEVIN_PERMISSION_MODE = 'auto' —— 这是对所有 ACP 用户默认自动放行。你的 checklist 勾的是"新开关默认关",实际相反。
  2. clientInfo.name 默认 windsurf:你自己的提交历史是 f647810「不要冒充 Devin Desktop 的 client name」→ 4889dfc「默认冒充、可 env 覆盖」,两分钟内反转。这个仓库对"对外自称是谁"是有立场的(handlers/identity-neutralize.js 那一族、[Bug] Grok CLI "You are Grok ... released by xAI" self-ID 触发 Devin content policy (permission_denied) #223 Grok 自述触发上游 content policy),默认冒充需要维护者点名,不能由 PR 顺手改

顺带:test/default-on-switch-registry.test.js 只扫 DEVIN_CONNECT_ / WINDSURFAPI_ / CASCADE_ 三个前缀,所以 DEVIN_PERMISSION_MODE 这个"默认开"逃过了台账。这条是我们的洞,我会单独修,但你这边要把它变成显式 opt-in。


你做对的部分(我独立证实过)

  • swe-2-medium/high/max 的 credit 6 / 9 / 12 是准确的。 我拉了 https://docs.devin.ai/desktop/models.md(HTTP 200),TEAMS_TIER_ENTERPRISE_SAAS 段确实有这三行,credit_multiplier 逐字相符,$0.75 / $3.75 per 1M。这条不是编的。
  • "上游对 gpt-5.6 回 403 This model is only in Devin Local"这个观察有价值* —— 如果成立,它解释了为什么这一族只能走本机。
  • "ACP initialize 的 clientInfo / authenticate 要带 api_server_url 才能过 team settings" —— 这个观察也重要,问题只在于你把它实现成了改默认值,而不是一个显式开关。

一条我要你补依据的pickToolDialect('swe-2*') === 'kimi_k2' 的理由写的是"SWE-2 是 Kimi K3 post-train"。官方 modelCostData 里 Kimi K3 是独立的一行(provider MODEL_PROVIDER_MOONSHOTkimi-k3-{low,high,max},credit 4/6/9),而本仓库全树 grep kimi-k3 0 命中。所以这个方言判断目前没有来源。要么给出你的实测(一段 swe-2 的真实工具调用原始输出就够),要么先降级成 openai_json_xml(仓库里 test/tool-emulation.test.js:242 现在就是这么断言的)。


下一步

M1 是硬失败,先修。M2–M6 都是可以一轮改完的。改完 ping 我,我重跑全量 + 对抗验证。

最后说清一件事:SWE-2 这件事本身值得做,我这次的结论是**"这一版不能合",不是"这件事不做"**。你先把 M1/M2 的可达性理清,我们再谈怎么落地。

…gpt-5.6

SWE-2 medium/high/max from docs.devin.ai (credit 6/9/12). Connect deployments
read them from the snapshot; Cascade/LS deployments with the CLI enabled use
backend=special_agent (same as swe-1.6).

gpt-5.6 luna/sol/terra stay on Cascade unless DEVIN_CLI_ENABLED=1. ACP does not
invent api_server_url, does not default DEVIN_PERMISSION_MODE, and names itself
WindsurfAPI unless DEVIN_ACP_CLIENT_NAME is set. Model is passed as --model only.
@Jakevin
Jakevin force-pushed the feat/devin-local-swe-2 branch from 4889dfc to 9bfaa8a Compare September 13, 2026 16:21
@Jakevin

Jakevin commented Sep 13, 2026

Copy link
Copy Markdown
Author

改完了,head 9bfaa8ab7,已 rebase 到当前 origin/master

按你列的逐条:

M1 — 恢复 ...(apiServerUrl ? { api_server_url: apiServerUrl } : {})。账号没记 server 时不再声明公共端点。test/devin-acp-integration.test.js--import ./test/setup-env.mjs 下 12/12。

M2swe-2* 只走 backend: 'special_agent'(和 swe-1.6 同一条短路)。devin_local_only 不再匹配 swe-2。测试改成生产形状 { backend: 'special_agent', name, modelUid },断言 reason === 'modelInfo.backend=special_agent'

M3 — 两个部署面共用 catalog id,不抢一条路径:

  • DEVIN_CONNECT=1(compose 默认):snapshot 白名单,selectBackend 仍是 devin_connect;ACP 分支故意不跑。
  • DEVIN_CONNECT 关 + 打开 CLI:swe-2* 走 special_agent。测试钉了 DEVIN_CONNECT=1 赢过 special_agent。

M4 — 只发 devin acp --model,不再同时写 DEVIN_MODELDEVIN_CLI_ENABLED=1 且 mode 不是 acp 时走 print(有测试)。本机实测当时 .env 里有 DEVIN_CLI_MODE=acp,描述漏写了。

M5 — gpt-5.6 正则改成锚定 SKU:luna|sol|terra + 已观测的 effort / -prioritygpt-5.60 不再命中。

M6 — 去掉 DEVIN_PERMISSION_MODE=auto 兜底。clientInfo.name 默认回 WindsurfAPI;要 Desktop 兼容再显式设 DEVIN_ACP_CLIENT_NAME=windsurf

方言:swe-2* 先保持 openai_json_xml。没有 swe-2 的 live tool-call 原文,不把 Kimi K3 post-train 当 kimi_k2 的依据。

本机(Node 22.23.2):

node --import ./test/setup-env.mjs --test --test-force-exit \
  test/devin-acp-integration.test.js \
  test/backend-router.test.js \
  test/tool-emulation.test.js \
  test/devin-connect-catalog-drift.test.js

exit 0。请再跑全量。

@dwgx

dwgx commented Sep 13, 2026

Copy link
Copy Markdown
Owner

复审:M1–M6 我逐条驱动验过,没有一条是"diff 看起来像修好了",全部真修好了。当前形态我判"可合,带两条小项"。

head 9bfaa8ab744a8194bf87f785209e32fc3ed33745 —— 我核了 ref:gh/git ls-remote refs/pull/266/head 给出的就是这一串,与你写的 9bfaa8ab7 逐字节相同。它在 origin/master = 2265f59 上只多 1 个提交(0 1),改动面正好是 7 个文件 / +119 −2,没有夹带 package.jsonscripts/.github/


★ 先说一件和你这次改动无关、但直接影响你上一轮证据的事

--test-force-exit 今天从本仓库移除了,调用点 package.jsonscripts/run-test-shard.mjsscripts/mutate-verify.mjs(提交 939872btest: drop --test-force-exit, which silently ends runs early and still exits 0)。你回复里的命令仍然是:

node --import ./test/setup-env.mjs --test --test-force-exit test/...

而且这不怪你:.github/PULL_REQUEST_TEMPLATE.md:24 当时写的正是这条命令 —— 模板是给外部作者的第一手指示,比 CONTRIBUTING 更显眼,你照它抄是对的。这条我们已经改了8e1977ddocs(pr-template): stop handing contributors --test-force-exit),所以现在从模板里复制出来的命令不会再有那个 flag。这个疏漏在我们这边,不在你。所以下面的意思是**"绿色不再是证据",不是在说你乱跑测试。** 它的真实行为是:在 Linux runner 上跑完一部分 suite 就让进程退出,而且仍然 exit 0。同一份字节、逐轮交错跑同一个文件:带 flag 得到 34 x2, 36, 40, 41, 42, 48, 50, 72 x28 个不同值),不带 flag 72 x10(完全稳定)。被截断的那一轮写出的是自洽完整、退出码 0 的 TAP1..3 / # tests 40),而文件实际有 11 个 suite / 72 个测试。所以"带这个 flag 的全绿"不构成证据:它可能只跑了半个文件。请把你的本地命令里的 --test-force-exit 去掉再跑 —— 我这边去掉它重跑你点名的 4 个文件,是 349 tests / 349 pass / 0 fail(见下表),结论不变,只是证据站得住了。


我驱动了什么(全部在 worktree .claude/worktrees/pr266-recheck 里跑)

命令 / 手段 结果
全量 ×2 npm test(= node --import ./test/setup-env.mjs --test test/*.test.js --test-force-exit 4291 / 4286 / 5 ×2 次逐字相同;失败的文件正好是那 5 个 Unix-git 机器闸
master 基线 另开 worktree 到 2265f59 跑同一命令 4276 / 4271 / 5 —— 失败文件集合与你的 head 完全相同 ⇒ 零新增失败
你点名的 4 个文件 --test 4 个文件(不带 force-exit) 349 / 349 / 0,exit 0
稳定性抽样 --test-reporter=tap test/tool-emulation.test.js ×5 # tests 72 / # pass 72 / # fail 0 ×5,逐次相同
三道轻门 node --check src/index.js / node src/dashboard/check-i18n.js / npm run secret-scan 全部 exit 0
突变 anchor node scripts/spec-static-check.mjs exit 0,48 specs / 576 mutations ✓ anchors unique(与 master 同;本 PR 未碰任何 spec,所以没有 anchor 漂移面)
M1 替换 __setDevinAcpSpawnForTest spawn 缝,抓 authenticate 帧原文 见下
M2/M3/M5 src/models.js 的真实解析 + backend-router.selectBackend + 真 handler handleChatCompletions 见下
M4 真 handler,CLI 开 + mode 默认(print)+ 带 tools[] 见下

M1(阻塞项)— 已修,我拿到了帧本身

api_server_url 只有账号真有才发:

场景 子进程 env DEVIN_MODEL 子进程 env DEVIN_PERMISSION_MODE initialize.clientInfo authenticate._meta
池账号 apiServerUrl 不存在 不存在 {name:'WindsurfAPI',version:'3.9.32'} {"api_key":"K"}
池账号 apiServerUrl 不存在 不存在 同上 {"api_key":"K","api_server_url":"https://server.self-serve.windsurf.com"}
DEVIN_ACP_CLIENT_NAME=windsurf 不存在 不存在 {name:'windsurf'} {"api_key":"K"}

...(apiServerUrl ? { api_server_url: apiServerUrl } : {}) 已经在 src/devin-acp.js:556不再无条件声明公共端点test/devin-acp-integration.test.js 12/12,含那条 omits api_server_url ...SERVER=|)。我按你上一条 review 记的量复现了:master 上 12/12 绿、旧实现(|| 'https://server.codeium.com')会红。这条我判 fixed,而且是帧级证据,不是读代码。

M2(设计)— 已修,我用生产形状驱动了

devin_local_only 现在碰不到 swe-2*,走的是既有的 backend === 'special_agent' 短路:

swe-2 / swe-2-medium / swe-2-high / swe-2-max / swe-2.0 / swe-2-0 / swe2
  backend=special_agent  -> reason=modelInfo.backend=special_agent

真 handler 上也一样:handleChatCompletions({model:'swe-2-medium'}),CLI 关闭时拿到
503 backend_unavailable(= 确实进了 special-agent 流,而不是 Cascade/legacy)。旧测试那种
裸对象 {name, modelUid}backend 字段缺失) 的形状我单独驱动过:它走不到你的新分支(devin_local_only 只接受 gpt-5.6 luna/sol/terra 那族名字),所以在生产形状下才会落到 modelInfo.backend=special_agent —— 你的新测试现在钉的正是生产形状,这点改对了。

M3(可达性)— 已修,两个面各自可验证、互不抢路

请求 DEVIN_CONNECT DEVIN_CONNECT 关 + CLI 开 DEVIN_CONNECT 关 + CLI 关
swe-2-medium devin-connectreason=devin_connect赢过 special_agent devin-acp/devin-print 503 backend_unavailable
gpt-5.6-luna devin-connect devin-acpreason=devin_local_only cascade

resolveConnectSelectorswe-2 / swe-2.0 / swe-2-medium/high/max → mapped:true,快照 _count 120→123 与新增 3 行一致(devin-connect-catalog-drift.test.js 有一条"快照行数 == _count"的断言,绿)。另外我确认了:swe-2* 在 Cascade 面不会被 /v1/models 广告出来(与 swe-1.6 同族行为),所以没有"列出来但调不动"的新裂缝。M3 的"两个部署面共用 catalog id、不抢一条路径"这个回答我接受。

M4(写错了)— 已修

  • --model 单通道:子进程 env 里不再有 DEVIN_MODEL(全仓 git grep DEVIN_MODEL 只剩那条注释);--model 参数仍在(master 本来就有)。
  • 默认仍是 print:CLI 开、DEVIN_CLI_MODE 不写时 → devin-print。我按你 review 里那条"print 对带 tools 的请求直接 400"驱动了真 handler:status=400 type=unsupported_tool_boundary,逐字对上。"描述漏写了 .env 里的 DEVIN_CLI_MODE=acp"这个解释成立,而且现在有测试钉住 print/acp 两条分支。

M5(正则)— 已修,锚住了,我拿"会被劫持的那几个名字"打过

输入 现在 旧正则
gpt-5.60 / gpt-5-60 不命中(真 handler:400 Unsupported model /gpt-5[.-]6/ 命中
gpt-5.6-luna-ultra / gpt-5.6-lunaish 不命中 /gpt-5[.-]6/ 命中
swe-2-0 / swe-2-pro / swe-2.5 不命中 `/swe-2(?:[.-]
gpt-5.6-luna / gpt-5-6-luna / gpt-5.6-sol / gpt-5.6-terra-high / gpt-5.6-luna-high-priority 命中 devin_local_only

两点补充(都不是反对,是记账):① swe-2* 全族已被 M2 的短路接管,所以新正则里 swe-2 那半边是按设计不生效的 —— 你自己在注释里也写了;② sol/terra 目前到不了那条分支chat.js:4039if (!modelInfo) 400 Unsupported modelselectBackend 之前,而 MODELS 里只有 gpt-5.6-luna-*。也就是说 luna|sol|terra 里的 sol|terra 现在是给云端 catalog 合并进来的行预留的,本版无法驱动(我如实报"未验证",不当作缺陷)。

M6(默认放行)— 已修

  1. DEVIN_PERMISSION_MODE=auto 兜底整行删除:子进程 env 里 DEVIN_PERMISSION_MODE 不存在(上表实测)。你 checklist 的"新开关默认关"现在与代码一致。
  2. clientInfo.name 默认回 WindsurfAPI,冒充改成显式 opt-in DEVIN_ACP_CLIENT_NAME。这与 master 的默认值相同,所以对既有部署是零行为变更,新加的只是一个开关 —— 对 [Bug] Grok CLI "You are Grok ... released by xAI" self-ID 触发 Devin content policy (permission_denied) #223 那一族立场来说这是正确的方向。

方言那条按你说的降级成 openai_json_xml 我也核了:swe-2 / swe-2-medium / swe-2-0 都落在 openai_json_xml,与 test/tool-emulation.test.js:242 的断言一致。没有真实工具调用原文就不写 kimi_k2,这个处理是对的。


新发现(都在"小项"级,不阻塞)

N1 — swe-2 的别名表只加了一半,两个面不一致。 src/models.js:395-400 里你加了
swe-2.0 / swe-2-0 / swe2 / swe-2.0-medium / swe-2.0-high / swe-2.0-max
src/devin-connect-models.jsSELECTOR_MAP 只收了 swe-2 / swe-2.0 / swe-2-medium/high/max。实测后果:

请求名 DEVIN_CONNECT=1 说明
swe-2swe-2.0 swe-2-medium
swe-2-0swe2swe-2.0-mediumswe-2.0-highswe-2.0-max mapped:false → 默认 STRICT_MODEL=1400 model_not_foundSTRICT_MODEL=0静默降级到 swe-1-6-slow 这几个拼法是你这次刚加进 _lookup,所以在 Connect 面上"名字存在、却调不动"

这条我在真 handler 上驱动过(DEVIN_CONNECT=1):swe-2swe-2.0 都走过了 whitelist
(我这边没有 DEVIN_CONNECT token,所以停在 401 no session token,即"名字被接受"),
swe-2.0-high 停在

status=400 type=invalid_request_error
message: The model `swe-2.0-high` is not a valid model for this endpoint.

resolveModel('swe-2.0-high')有结果的(-> swe-2-high)。这一处差异就是 N1 的全部:
同一张 _lookup 里加进来的名字,在 Connect 面上一个能过一个不能过。

两个面的解析表本来就不同(Cascade 面 swe-2.0-high 解析成 swe-2-high,Connect 面不走这条),
93ae320 那条教训的形状就是这个:同一个名字在不同解析器里差异对待。要么把这几条补进
SELECTOR_MAP,要么别把它们写进 _lookup —— 别让它们只在一半面上成立。

N2 — 新开关没进文档,而且它是这个功能唯一的操作者入口。 DEVIN_ACP_CLIENT_NAME
全仓只出现在声明处(src/devin-acp.js:550),.env.example 里没有。这个仓库的
docs-consistency-guard 对开关台账是认真的(test/default-on-switch-registry.test.js
只扫 DEVIN_CONNECT_ / WINDSURFAPI_ / CASCADE_ 三个前缀,所以它逃过了这份台账 ——
和上一轮 DEVIN_PERMISSION_MODE 是同一个盲区)。盲区在我这边,我会单独修
但麻烦你在 .env.example 的 Devin CLI 段补一行说明,否则没人知道这个开关存在。

N3 — 三条测试形状的记账(不影响结论,但别让它们被当成更强的证据):
① 负例 gpt-5.60 用的是手写对象 {name:'gpt-5.60', modelUid:'gpt-5-60'} —— 生产里它到不了
selectBackendchat.js:4039 先 400 了),所以那条其实是纯正则单元测试,不是可达性测试;
luna|sol|terrasol/terra 本版不可达(同上);
devin_local_only 分支本身没有"非 SKU 名字不被劫持"的断言 —— 只有
swe-2 那一侧钉住了。要补的话,一条断言就够:{name:'gpt-5.60', modelUid:'gpt-5-60'}
CLI 打开时 flow === 'legacy'(我在真机器上量到的就是这个结果)。


下一步

没有阻塞项了,可以进合并流程。 建议顺序:

  1. N1 在本 PR 里顺手收掉(补 SELECTOR_MAP 或撤掉多余的 _lookup 行,二选一,别留半张表)——
    这是我唯一希望在本 PR 里改的东西,因为它是你这轮新引入的不一致。
  2. N2 补 .env.example 一行;N3 可选。
  3. 你改完 ping 我,我会按那时的 origin/master 重跑全量 + 这轮同一套驱动,再走
    branch → git merge --ff-only → push。合并时按惯例更新 src/dashboard/data/contributors.json

再说一次结论的边界:我判"可合"针对的是这 7 个文件、这个 headswe-2-medium/high/max
在 DEVIN_CONNECT 面上的真实可达性、以及 devin acp --model <key> 对这些 key 的接受度
我在这台机器上没有 Devin CLI、也没有 live 账号,没有验证,也不假装验证过
credits 6/9/12 我沿用你引的 docs.devin.ai 数字,没有再拉一次。如果这两条里有哪条后来被证伪,
那不是小项,是要重新判的。

dwgx added a commit that referenced this pull request Sep 13, 2026
User-visible: the tool-preamble ladder no longer builds the schema-compact tier it would
discard. A padded \ diamond goes 11.357 -> 0.911 ms on the request (-92.0%) and 10.741 -> 0.455 ms
on the ladder (-95.8%); twelve levels -82.0%/-89.7%. The ordinary path is NOT claimed as faster:
a 2x2 factorial over arm position x file gives a position bias of +3.6% and a file effect of -0.6%
that flips sign inside the arms, while the ladder is -9.3% and same-signed in both. Responses are
byte-identical, pinned three independent ways (per-iteration sha256; 96 goldens unmoved plus 48
master-captured boundary rows; a new test against an unbounded reference across all 96 settings).

Also: .github/PULL_REQUEST_TEMPLATE.md no longer hands contributors --test-force-exit. It was
removed from the scripts in 939872b but left in the one file a contributor actually reads, which
is why PR #266's author used it; a green run under that flag may have executed less than half of
a file while exiting 0.

Engineering: mutation specs 48 -> 49, mutations 576 -> 588, the new spec's 12 mutations all CAUGHT
on the runner with all 12 anchors in new code; the 10 specs whose anchors live in the touched files
all OK (cascade-metadata-egress 88/88, schema-ref-fanout-budget 19/19), guard-5 refusals 0. The
long-standing reasoning-dedup-incremental survivor is closed as an equivalent mutant with a
mutation that actually bites.

No API breakage: 2xx bodies byte-identical, status codes untouched. ACU ^22 still default off.
FREE_TIER_SELECTOR still swe-1-6-slow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants