Repository navigation
[PR] Full regression review: v0.1.209 → master(empty vehicle PR) - #5341
ranxianglei wants to merge 14 commits into
Conversation
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.7584-pr.5341.55b69e4This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr5341.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——检索 open+closed issue/PR,没有其它针对 v0.1.209→master 区间的回归审查落点;本 PR 是该区间唯一的整体审查载体。 标签: pr + regression + scope/ci + p3(版本区间回归审计请求,属发版/CI 域;本身不是缺陷,无客户端归属)。 审查计划(按发版类 PR 的区间回归审计流程执行):
非回归性发现如有,按规则汇总为且仅开 1 个 issue(带来源标记);本 PR 不合并、不动代码。开始干活,结论稍后回。 |
ranxianglei
left a comment
There was a problem hiding this comment.
全面回归评审:v0.1.209 → master (f70055c7)
范围:v0.1.209 之后的全部合并净效果(67 commits,~598 files)。审查方式是整段 diff 一次性 review,不是逐 PR——重点看多个 PR 叠加后的净行为、跨文件交互、以及"每个单看都合理、合起来出问题"的面。四条独立审查线(存储 / 压缩缓存会话 / 启动可靠性与 CI / 性能热路径+dsh+协议)+ 主线程独立核验(差分模糊、逐字节协议核对、本地全量测试)。
一句话总评:没有发现功能性回归或数据损坏风险,本区间净方向是加固(接管安全、故障恢复、测试网密度都在变强);但当前 master 自身挂着三条红守卫(见 §A),在修掉之前所有后续 PR 的 CI 都会带假失败。另有一批 6 个 P2 + 12 个 P3 需要登记跟进(§B/§C),其中 3 个是可直接一行修复的机械问题。真模型 gate 本次运行环境无效(GPU 被占满,见 §E),重跑结果将作为补充评论追加到本 PR。
§A 当前 master 自身的三条红守卫(先修这个)
master 现在过不了自己的门禁,任何后续 PR 都会继承这三条假失败。全部是"功能合进来了、配套义务没做完",修复各只需几行:
| # | 守卫 | 现象 | 引入者 | 修法 |
|---|---|---|---|---|
| A1 | docs-sync | #5319(chat prompt pack)改了 EN/ZH 文档,但 website/config-reference/ 里 compress.promptPack 行的 builtin 列表漏了 "chat" |
06ad804 一带(#5319,10-31 15:20 合入) | 两语言该行的 builtin 列表补 "chat" |
| A2 | unused-exports | src/codex-models.ts 导出 CodexWindowSource 但全仓库无消费者(grep 证实零引用) |
0629480(#5243,codex model_catalog_json) | 删掉 export 或补消费点 |
| A3 | lifecycle-ledger-guard | src/ 读两个 env 变量但 LIFECYCLE.md 没登记:BILI_SESSION_CONTENT_STORE_PIN(← c8d7a80,#4788 phase 2)、BILI_SPAWN_FINGERPRINT(← fc53fd4,#4125) |
同左 | LIFECYCLE.md 按既有格式补两行 |
§B P2 发现列表(建议下个发布前处理;均为窄窗口或成本类,非数据损坏)
B1 · 每轮请求收尾新增一次同步全图遍历(sessionWeight 深算)— 性能域,置信度高
人话:代理跑长会话时,每一轮都会在响应收尾处把整个驻留会话对象树从头量一遍字节数(判断要不要溢出到磁盘),量得越久卡得越死,且是同步阻塞事件循环的。
机制链:src/server/handle.ts:2910 spillBetweenTurns(session)(#4788 phase 2 新增,每个代理请求 finally 必跑)→ src/persist.ts:2974 → debounce save 稳态下 afterNextFlush 恒真 → :2985 cap>0 && sessionWeight(session)>cap(默认 cap=512MiB)>0)→ src/session.ts:1073 权重按 revisionEpoch 缓存,但 markDirty 每轮 bump epoch ⇒ 每轮全新计算 → src/session.ts:1045 deepSizeOf 递归遍历 kernel 全量历史 + 永不收缩的 messageRefs Map。
实测(verbatim 拷贝函数,/tmp/bili-perf/sizeof3.mjs 可复跑):消息 2k≈2ms / 8k≈5.3ms / 20k≈14ms / 50k≈58ms;成本随对象数线性。v0.1.209 中 sessionWeight 仅诊断端点调用。这直接违反本区间自己立的 #5130「每轮禁止对整段历史做重复 O(n) 重扫」纪律(#5131 刚删掉一批全量扫描,#4788 p2 又添了一个)。
修法:权重估计改增量记账(字段级增删累加)、或 N 轮采样+事件触发、或先用上次权重做快速门(超 cap 才精确重测)。cap=0 可应急关闭。
B2 · 插件注入车道存储打开失败会硬抛进宿主代码 — 存储域,置信度高
人话:dsh/pi 这类把 bili 当插件嵌的宿主,如果磁盘出错(文件句柄耗尽 EMFILE、杀毒锁文件)持续几分钟,旧版(209)静默降级继续服务,新版每一回合都会往宿主代码里抛错。
机制链:UnifiedShadow.maybeOpen 对非损坏类 open 失败改为 THROW(owner 政策 2026-10-30"拒绝即拒绝");src/persist.ts:595-596 ctor 不设防调用;懒单例无失败 latch(persist.ts:3960-3966)⇒ 每次 getStore() 热路径调用(incremental-lane.ts:232/295/313/423、handle.ts:93/502/1660 等)整体重试构造并打 ERROR。独立 server(server.ts:566-571)有 try/catch→clean exit、threads-lane 有完整 guard(:179-225)——插件宿主这条腿没有(它不走 startServer,server.ts:673 注释确认)。
验证:issue5188/replicas 20/20 绿;未实机构造 EMFILE(只读约束)。
修法:给插件车道 getStore() 加统一错误边界或失败 latch(复用 #5188 pace)。属 owner 已批准政策的未包裹残留面,可合并,建议发版前补。
B3 · 子代理车道键把 system prompt 编进了键,派发中途 system 一变就劈开压缩状态 — 缓存域,置信度中高
人话:Claude Code 子代理的车道键 = 会话id + system哈希 + 随机号。如果同一个子代理任务跑到一半,system 文本被宿主改写一个字节(版本注入、模板刷新),后续轮次就掉进一条全新的空车道:之前积累的折叠、缓存基线全部弃置,一次缓存重建+重新折叠。另有一个文档化的必然降级:首条 user 消息一旦被折叠,随机号从 wire 上消失,续跑落另一条车道——即任何足够长的子代理派发恰好丢一次压缩进度。均不丢数据(客户端全量重发、新车道重建),是成本/延迟抖动。
位置:src/session-resolve.ts:242-252(随机号车道优先于一切连续性启发式,绕过 #2241/#2250 锚迁移)。另见同文件注释自认的已知切换("post-fold head loss")
升级条件:若生产上同一 dispatch 相邻两轮 system 字节不稳定 → 升 P1(每轮劈道)。证据需求:生产日志取同一 dispatch 两轮 system 字段 diff。这是本清单唯一可能升级的项,优先取证。
B4 · Responses 线把"第三方客户端用自己会话 id 当缓存 key"误判为 bili 自印产物并默认吞掉 — 协议域,主线程独立复核确认
人话:Responses 协议线新逻辑:只要请求体里 prompt_cache_key === 会话 id,就认定是 bili 自己打回去的,默认配置下直接丢弃。但真正惯用"自己的会话 id 作前缀缓存键"的第三方中继客户端(OpenAI body-keyed 常见做法)会被无声剥离 → 上游前缀缓存亲和丢失,token 账单上涨。chat 线没这个问题——它额外要求 identity 来自 bili 内部 header。
位置:src/server/prepare-responses.ts:853-854(判据仅 pck===sessionId && source!=="prompt-cache-key")vs src/session-id.ts:381-382(要求 internalLane)。注释只认领 pi(PI_CACHE_RETENTION=long)和 hermes 两个有意目标。现有测试(tests/issue5079-pck-default-off.test.ts:207)只钉了 key≠session-id 的正例,key===session-id 的非 bili 客户端行无覆盖。
修法:responses 线收窄到已知发射方标记,或在升级通知文案里明说这类 key 也会被吞 + 补负例测试。暴露面待流量统计。
B5 · 压缩循环打满时"温柔收场"的代价:跨请求无熔断,病态场景每轮付 10 次全历史请求 — 压缩域,机制已钉死
人话:模型反复发出注定失败的 compress/decompress 调用时,单个请求内最多烧 10 轮(每轮=一次全量历史的真实上游往返+计费),然后以 stop_reason=max_tokens 结束。Claude Code 对 max_tokens 自动续轮→ 下一个全新请求又允许再烧 10 轮。去重集(failedSigs)是请求作用域(src/loop/core.ts:654),会话级没有任何计数或开关。若失败是结构性的(如某版客户端持续传陈旧块 id)且模型不听回执劝告,就是无限循环:N×10 次全历史请求直到任务自然结束。
缓解面(所以定 P2 不是 P1):失败回执逐字节对齐客户端视图(C 队验证:receipt==marker 字节同源,四适配器一致),行为正常的模型 1-2 个周期就会改道;每轮有 warn 日志(core.ts:1537)可观测。
修法:会话作用域连续耗尽计数器——连 N 次请求全失败耗尽后记日志并在下一请求注入更强指令(或直接停用 compress 工具结果一轮回退旧契约)。改动小。
B6 · 订单骨架表 1024 上限只挡写盘,内存无淘汰,淘汰后可复活"首认领污染" — 缓存/存储域,置信度中高
人话:记住"哪个会话 id 先声明自己是主线"的那张表,超过 1024 条后老条目被挤出磁盘;重启后那些 id 变回"初次见面",而初次见面规则是"长得像主线就直接认领"。本区间子代理车道的两个新修复(随机号键 + 内容指纹键)让每次子代理派发都新增一个键,键增速远快于 209。触发需:活跃 id 被挤出 + 重启 + 陈旧子代理长历史重放先到--概率低但机制真实(#3397 当年修的正是这种孤儿化)。
位置:src/order-persist.ts:29-30,83-85 + src/session-resolve.ts:256-267 first-sight 分支。
修法/监控:长期主机定期看 session-order.json 条目规模;或对 main claim 单独 pin 不参与 LRU。
§C P3 台账(登记备案,多数一行可修)
| # | 域 | 位置 | 一句话 |
|---|---|---|---|
| C1 | 性能 | src/plugin.ts:1187-1188 |
reconMissGate/reconMissLastLogMs 两个诊断 Map 无界(只有成功重建才删,后者从不删);同文件有 EVICTED_WEIGHT_CAP=256 先例,加 TTL sweep 即可 |
| C2 | 性能 | src/preflight.ts estimate memo |
键含 textLength,同长同 id 原地改写命中陈旧值;误差个位数 token,备案(#5131 已接受同类) |
| C3 | 压缩 | src/fold-reconcile.ts:1896 |
METADATA_EXPECTED_REBASE 无清理路径(dsh 车道每折叠退休一条 carrier +1,永久序列化进 metadata);对照 offloadPending 有 MAX_REBASE_MEMO=8192(:290)——同一 PR 只给一半设了限,像遗漏。复用 pruneRebaseMemo 即修 |
| C4 | 压缩 | src/fold-reconcile.ts:1847-1866 |
drift 通道未命中行每轮原样重扫(≤256 行 ×7 marker includes,行可含 MB 级 base64);永久漂移会话每轮纳税。加内存级负结果记忆化 |
| C5 | 压缩 | src/durable-message-guards.ts |
三个载体分类器前缀/子串误伤面("Background job…"开头的真人提问会被标成载体);常态无害(持久消息不消失),宿主改写场景下告警语义失效。注释已论证 benign-FP,接受备案 |
| C6 | 存储 | src/session.ts:1327-1331 |
publishForkSession 存在性探针改 async 后 check-then-add 多一个 await 窗口;#4942 响亮失败保证在 mysql 延迟下略放宽(sqlite 为 microtask);窗口极窄,记录 |
| C7 | 存储 | src/stream.ts:512 |
summaryFingerprintLine O(k²) 去重(receipt 路径低频),换 Set 一行 |
| C8 | 存储 | src/persist.ts durableRevision 族 |
lastSaveLanded/capWarnAt 从不 delete;每 id 一个布尔/数字,可忽略 |
| C9 | 存储 | tsStamps(kernel state 新持久字段) | #4313 flag 默认 off 零增长;开则随消息 id 数无界,与 #4788 minimal-resident 目标有张力,记入其 phase-3 |
| C10 | 存储 | v8→v10 存量库升级 | fresh 建库被隐式全覆盖且 migrate 单事务,但带数据的旧库 ALTER 路径无 fixture;补 user_version=8 fixture |
| C11 | 启动 | src/server.ts:676 |
event-loop-watchdog 返回的 .stop() 无人调用(全仓库无调用点);worker/beatTimer 均 unref 不阻退出,唯一理论窗是 SIGTERM drain 期连续 wedge ≥5min 被 worker exit(1) 打断。要么接进 shutdown() 要么删 |
| C12 | 启动/dsh | src/launcher.ts:5315-5380 |
失败诊断文案:head 采样被改坏(span>256B 永远取尾,label="tail"),tail 采样又取同样尾巴 ⇒ 同一段内容打印两遍、头部永远不可见;两个 PR 各自落地未对账。纯文案,一行修 |
| C13 | 协议 | src/server/side-request.ts:191+ |
title sidecar 谓词(恰一条 user、零 tools、\x3csession\x3e 开头)理论误伤窗口:--disable-tools 姿势 + 首条消息恰好以此开头 → 静默绕过一轮压缩面;现实风险极低,备案 |
| C14 | 其他 | benchmarks/third-party-1500 |
9 文件全新增,口径无变化,但基准数字钉在 v0.1.202(早于本区间)⇒ README 宣称不能覆盖已合并改动;PNG/原始输出本机不可复现。文档面,不阻断 |
| C15 | 启动 | watchdog 硬超时 vs spawn budget | WATCHDOG_HARD_TIMEOUT_MS=300_000 硬编码无逃生口,而文档承诺 store open 可 >60s 且用户可用 BILI_SPAWN_WAIT_MS/BILI_SLOW_START_GRACE_MS 放宽;Windows AV + 数 GB index.db 场景下单段 >5min 阻塞式 open 会在用户预算内被杀。需现场 [event-loop-watchdog] trip 证据定性 |
§D 分域结论
| 域 | 结论 | 要点 |
|---|---|---|
| 回归(总) | ✅ 未发现功能性回归 | kernel 全量 1507/1507、存储域 ~380 相关测试、压缩/会话 ~300 断言/14 套件、启动族(wire-exit-matrix 7/7、issue5096 9/9、tunnel-guard 21/21、issue5140 3/3)本地实跑全绿;tsc 0 error |
| dsh 相关 | ✅ 纪律守住,1 个成本残留 | #5167 Connection-close 轮询前提在 Node 25 实测成立(close probe 每次新 socket、普通 fetch 走池);#5095 hook 安装 managed-block 纪律成立(只动自己的条目)、fresh install 产物齐全(tsup entry+files:["dist"]);adopt/接管 rung 全部要求 pid 级证明,未发现可 attach 他人端口的路径;残留=B1(sessionWeight 每轮遍历,#4788 p2 引入)+ 子代理骨架键增长(B6/C1) |
| 性能 | ✅ 净正,但自相矛盾 | #5131 去重扫正确(cache-ledger 二分化经 30000 例差分模糊 0 分歧,消息级 indexOf 跳跃与旧状态机截断语义字节等价;nImages 用终串重数不 stale);但 B1 抵消了一部分红利——本区间一边删全量扫描一边新增一个更大的,应入 #5130 清单 |
| 缓存 | ✅ 无回归,潜伏键空间问题 | fold credit T1-T7+E2E、splice #5155(marksVersion 失效、指针收敛)、多 fold 断点(cache-control.ts 区间零改动+交互面套件绿)、post-fold credit 字节不变;潜伏=B3(system 劈道)/B6(骨架 LRU 复活) |
| 协议 | ✅ 净效果正确 | #5179/#5220/#5307/#5330 stop_reason saga 四连改动的净行为:失败回执与客户端所见 marker 逐字节同源(streamedMarkers 镜像,四适配器一致);dedup 时空文本帧不发;签名断线器保留;探测族 ≤MAX_LOOP_ROUNDS=10;visibilityMarkers=false 回退旧契约;core.ts:1430 _marker_ 分支不可达(死代码良性);Anthropic stop_reason 合法值纪律守住;残留=B5(跨请求熔断缺失)+C13 |
| 存储 | ✅ 可迁移,1 个真实证据缺口 | v9/v10 migration probe-first 幂等双向兼容、单事务;read-your-writes 仲裁与 loadSync 逐条一致(lazy-narrow-reads 33/33 含 drift 三例);GC refcount 每批 refilter、integrity FAIL 永不 memoize;#5188 sidecar reopen 有界;缺口:MySQL 全是 hermetic stub,v9/v10 首升 ALTER 零真实服务器覆盖 + sessionViewRows 混合世代未实机(A 队 P2-3,后果判定:方向安全,最坏诊断噪音,自愈);writer-off 损失窗=owner 已批准取舍(注释引用 10-30 决定) |
| 其他(启动/CI/升级) | ✅ 全部加固或不变 | CI 门禁全是追加 timeout/更严(cancel 收窄、release-bugfix 恢复但双重确认、test-suite-lock leak guard ON);watchdog/owner-beat worker 线程独立于主线程 stall(issue5152 6/6 绿);优雅关闭语义不变;zcode memory isolation 三门硬控全覆盖(4/4);头透传白名单区间零收窄;update 通道 src 零改动;指纹 env 投毒有 sha256 正则校验+回退;tsup 仅 +hook entry;verif exceptions 纯增有理由;package.json 仅 test preload 变更 |
§E 证据基础与局限(严格性声明)
已完成的证据:
- 四条独立审查线各自跑了其域的测试子集(合计本地 >1000 断言,tsc 全绿);
- 主线程独立差分模糊:cache-ledger 新旧实现 30000 例恶意 JSON,0 分歧;
- 逐项
git log -S/-L溯源关键行到引入 commit; - PR CI(#5341 ci:full 车)按平台常规跑,以推送头为准。
未完成/无效的证据(明确标注,不许当通过用):
- 真模型 release gate:f70055c7 候选包实际跑过一轮,但环境无效,不算数。 运行期间 GPU 被其他任务占满(sglang :8199 的
/v1/chat/completions全程零字节应答,GPU 95-100% util、~94/98GB VRAM),所有 qwen 车道 turn 被 bili 在 idle 预算处正确中断(bili 侧行为本身教科书级:emergency-shrink armed、SIGTERM flush clean persist、prefix-affinity reattach、冷载恢复均有日志佐证);glm relay 链路正常(Lane B 切上游后缓存命中 50%→93% 有 usage 记录);verdaccio latest tag 未挂到候选包(update 车道读到的是 0.1.209)。GPU 空闲后必须重跑并通过,才能声称"f70055c7 通过真模型 gate"。重跑结果会追评到本 PR。 - MySQL 实机:见 D 域缺口,发版前需
BILI_MYSQL_TEST_DSN跑一次 e2e(+issue4665 mysql lane)。 - 下述生产证据项(§F)。
局限性:审查是静态读码+本地实验,无法替代生产遥测;"查过没问题"项的置信度受限于采样深度,已尽量给出测试名和断言数。
§F 待证实疑点(需要生产数据,按优先级)
| # | 问题 | 需要的证据 | 影响 |
|---|---|---|---|
| F1 | Claude Code 子代理同一 dispatch 内 system prompt 是否字节稳定? | 生产日志取同一 dispatch 相邻两轮 system 字段 diff | 不稳定 ⇒ B3 升 P1(每轮劈道、缓存白建) |
| F2 | 长期主机 lane/order 键总量离 1024 多远? | 长寿命 proxy 的 session-order.json entries 计数 |
接近 ⇒ B6 提级 |
| F3 | 有多少非 pi/hermes 客户端发 pck === x-session-id(Responses 线)? |
流量统计 | >0 ⇒ B4 有真实受害面 |
| F4 | 大上下文会话(50k+ 消息)是否已达用户可感卡顿? | billing log 请求间隔 vs __bili/status/event-loop lag 直方图 |
是 ⇒ B1 提前处理 |
| F5 | #5220 边角:失败轮同时流了 prose 时,双 assistant 结构的 reconcile 判决? | 活体 Claude Code 构造 compress 必败+先吐 prose,FOLD_RECONCILE_DEBUG=1 看判决类别 | 异常判决 ⇒ 协议域补丁 |
| F6 | #5134 跨重启:credit>0 持久化后重启,首个 prepare 是否恰消耗一次? | 现有 T6/T7 不含重启维度,补一个用例即可 | 重复记功 ⇒ credit 虚高 |
§G 建议行动(按优先级)
- **【阻塞项】**修 §A 三条红守卫(A1/A2/A3 合计约 10 行改动,可拆三个 micro-PR 也可以一个)。
- **【取证优先】**F1(system 稳定性 diff)——唯一可能改变严重度排序的证据项。
- B1:sessionWeight 移出每轮热路径(#5130 清单追加条目;cap=0 可做过渡)。
- B2:插件车道 getStore() 错误边界/failure latch。
- B5:跨请求连续耗尽计数器(小改动,防病理成本环)。
- B4/B3/B6:按 F1/F2/F3 证据落地(收窄/监控/修)。
- P3 台账:C3/C7/C12/C1/A3-A 三行这类一行修的可打包成一个 chore PR。
- 发版前置:MySQL DSN e2e 实机一轮 + GPU 空闲后 gate 重跑(本文档 §E 两条都关闭才算 f70055c 完成验签)。
审查方式说明:整段 v0.1.209→f70055c7 diff 一次性 review(非逐 PR),四线并行+主线程交叉复核;所有引用 file:line 均可在当前 head 复现;实测脚本路径已在正文注明可复跑。
Converging master's red gates onto ci-fixes. lifecycle-ledger-guard is already fixed here (LIFECYCLE.md entries for BILI_SESSION_CONTENT_STORE_PIN / BILI_SPAWN_FINGERPRINT from the prior sentinel pass, preserved through the master merge); this commit clears the other two. docs-sync: #5319 documented the new 'chat' builtin prompt pack in CONFIGURATION.md/.zh-CN.md but never updated the single-source seed, so regeneration dropped it and the gate failed. Add 'chat' to the compress.promptPack seed (type/desc_en/desc_zh) and regenerate website/docs/index.html to match. unused-exports: CodexWindowSource (src/codex-models.ts) and PackInstallOptions (src/pack-install.ts) are referenced only inside their own modules and imported nowhere else; drop the leading export (INTERNAL_ONLY) as the guard prescribes. Verified locally: lifecycle-ledger-guard OK, check-unused-exports clean, gen-config-docs regenerate matches committed artifacts (zero drift).
…safe family gate) pack-install.ts took the last 3 npm stderr lines via split+slice(-3), tripping the #816/#1615 family gate in tests/text-safe.test.ts even though the receiver is an array of lines (code-unit-safe). Use a non-negative computed start index so the literal negative-slice token is gone; output is identical (last up-to-3 lines clamped to 300 chars). Inherited from #5340, which shipped without satisfying the gate.
(ai: vllm-qwen/qwen3.8-27b) v0.1.209 → master 全窗回归审查结论(窗口实测 451 commits / 324 files,+23865 −2113;审查头 origin/master 八问
行为变更清单(旧→新,均有意且已披露)
CI 说明本 PR 自身矩阵红(lifecycle-ledger-guard / unused-exports / docs-sync failure,test 分片 + red-signal cancelled)= 继承性 master 红,与 #4599 在档家族同一签名(codex-models/pack-install de-export、promptPack 种子 "chat" 等已在 ci-fixes 哨兵分支收敛);空载体 PR 无自身因果,不构成窗口回归证据,不另开 issue(CI 协调政策)。 判定本版本引入回归:无。 窗口净效果健康:kernel Boundary 合规(declaration/bump 链/两层测试/golden pin 齐备)、config surface 纪律达标、docs en+zh 全套无漂移、pi-subagents 窗口内零改动。唯一待办 = #5350 拍板(statusLine plugin-mode 注入:A 加门 or B 文档化,推荐 B)。本 PR 按你的指示不合并,审查交付完毕。 |
…ked relay URLs Local gate harness broke on its own master without anyone noticing because GitHub CI never runs the local gate (needs real model/dsh/registry): - #5012 mutes the stdout/stderr tee for managed (launch-token) processes once the log-file leg proves live, so a detached gate proxy now leaves proxy.log with only the boot banner while every log line goes to the state-dir bili.log. mark()/slice() therefore saw an empty stream and every log-pattern row failed even though all mechanisms fired correctly. - Log URL masking (#4191/#4254) renders private hosts as <private-host> in bili.log, so the Lane-B routing assertion's exact-URL match missed. Fix: assert against xdg/state/billion-context/bili.log (append-only within a run), and match relay egress on :port/path which survives masking.
Gate 重跑结果:f70055c7 真模型 gate A 9/9 · B 6/6 全绿(272s)兑现原评审 §E 的承诺(上轮环境无效已重跑)。 结果
Lane U 跳过:候选包未发布到本地注册表(挂 重跑中发现并直接修复:master 打断了自己的本地 gate 脚本(commit
|
Inherited red resolved by convergence, not rewrite: ci-fixes was 12 commits behind master and its two unique fix commits (cbdd67d negative-slice, 701688d docs-sync/unused-export) are strict subsets of what master already merged better (e030d3b+4a12c1a4 safePrefix+windows npm shim, 3491e2d dead export, b76f0c9 lifecycle reconcile). Both failing shards were inherited from master's own newer commits that ci-fixes lacked: - #5103 refusal-remedy test -> pinned by 892a378 - flushAll dirty content store -> fixed by 1f209ff (src/store.ts adopt) Conflicts (codex-models.ts, pack-install.ts) resolved toward master = superset. Resulting ci-fixes tree is byte-identical to origin/master (git diff empty).
(ai: vllm-qwen/qwen3.8-27b) 收到 gate 重跑结果。逐条核验了分支状态、脚本修复 diff、根因归属、以及我评审头之后的 master 漂移: 1. 分支与脚本修复核验 ✅
2. 根因归属确认(一处小更正)
3. 评审头之后的 master 漂移(补充审计)我的评审头是 485c5ea;master 现已到 f9cec9f(+10 个内容 commit),已逐个过完:
扩展判定:至当前 master f9cec9f 仍无回归。 gate 证据(f70055c)覆盖除这 10 项外的一切;这 10 项没有一个触碰 lane A/B 演练的折叠/nudge/preflight/缓存/fork-adoption 面(tool-guardrails 是 pi-lane 扩展 API,store.ts 那条只是移除硬错路径)⇒ A/B 绿证据对回归问题仍然有效。在 f9cec9f 上再跑一遍属 nice-to-have,非必须。 4. 小观察(📦 标记复读进 final reply)同意备案非阻断。若日后要修,落点是出向 prose 过滤的标记集( 5. CI 与发版前置
八问(本轮增量)
判定:gate A/B 证据收下,§E 撤销成立;回归结论扩展至 f9cec9f 不变——本版本(v0.1.209 → master)未引入回归。发版前置剩余两项不变:三条红守卫(等 ci-fixes→master 收敛后重跑,PR 侧我已推 64fc687)+ MySQL DSN 实机 e2e。 |
§A「三条红守卫」更正:逐条对最新 master(f9cec9f)复核后,无需修复 PR准备按评审开修前发现 master 又前进了一截(#5327 已合入,含 A1(#4788 p3 孤儿行漏读)→ 已被
|
(ai: vllm-qwen/qwen3.8-27b) §A 三条更正逐条独立复核完毕,全部接受——其中两条与我的既有审计记录交叉印证,一条我重新独立查证: A1(#4788 孤儿行漏读)→ 接受,与我的独立审计互证
A3(#4125 指纹时序)→ 接受升级为"已验证无问题"行号引用我抽查了原文: A2(legacy summary 行永不删)→ 接受作废,独立查证如下
附带观察与剩余项
八问(本轮增量)
判定:§A 更正全部收下,发版阻断撤销;总回归结论维持——v0.1.209 → master 未引入回归。发版前置收敛为一项硬项:§G G8 MySQL DSN 实机 e2e(待环境授权);win24-s3 签名观察复现即可,不阻塞。 |
CI 红灯根因 + 修复(本地已验证,推送受阻待处理)红的是哪几项:master push(
根因(都是 #5345 pack editor 引入的,i18n 内容合并进 master 后每次跑必红):
修复(commit
本地验证(origin/master 44fcbe4 + 本提交): 卡住的环节:remote URL 内嵌的 # 方案 A:轮换 bot PAT 后直接推已备好的分支
git -C ~/tmp/review2/bili-base remote set-url origin https://billion-bot:<新token>@github.com/ranxianglei/billion-context.git
git -C ~/tmp/review2/bili-base push -u origin HEAD:fix/i18n-pack-editor-parity # commit 7fad432a
gh pr create --repo ranxianglei/billion-context --base master --head ranxianglei:fix/i18n-pack-editor-parity \
--title 'fix(web): pack-editor i18n — {need} placeholder + scannable key refs (#1024)' \
--body-file <(head -6 /tmp/i18n-pack-editor-parity.patch | tail -1) # 或贴下面补丁说明
# 方案 B:人工应用补丁后自行开 PR(git am)
git -C <master工作区> am /tmp/i18n-pack-editor-parity.patch安全提醒:那个失效 token 已经明文暴露在多处(远端 URL),建议直接在 GitHub 作废并换新,别只改本地。 合入后的效果:master push CI 与 #5341 车分支(test 矩阵的 补丁全文(92 行,commit 7fad432a)From 7fad432a3f94d70e3ef892d914f76043a9724fea Mon Sep 17 00:00:00 2001
From: ranxianglei <ranxianglei@gmail.com>
Date: Sat, 31 Oct 2026 22:43:47 +0800
Subject: [PATCH] =?UTF-8?q?fix(web):=20pack-editor=20i18n=20=E2=80=94=20{n?=
=?UTF-8?q?eed}=20placeholder=20in=20en=20p1,=20keep=20base-option=20keys?=
=?UTF-8?q?=20visible=20to=20the=20#1024=20dead-key=20guard?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The guided pack editor (#5345) broke the i18n parity tests on master push CI:
1) en cfg.q_pack_ai_p1 was missing the {need} placeholder its zh twin has
(client.ts interpolates t("cfg.q_pack_ai_p1", { need })).
2) Four catalog keys were passed as bare strings through helper parameters
(option array / packSectionField), which the #1024 reference scanner
(literal t(\"key\") only) cannot see — flagged as dead keys. Labels are now
translated at the call sites so every key has a scannable literal reference.
Verified locally at origin/master (44fcbe47): tests/web-i18n.test.ts +
web-user-packs.test.ts 11/11 (was 9/11); all web/pack/i18n suites 257/257;
tsc --noEmit clean.
---
src/web/client.ts | 19 +++++++++++--------
src/web/i18n.ts | 2 +-
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/src/web/client.ts b/src/web/client.ts
index 2ad50f2c..14bdfcc0 100644
--- a/src/web/client.ts
+++ b/src/web/client.ts
@@ -4126,10 +4126,13 @@ if (d.keepalive) kv(parts, t("det.keepalive"), d.keepalive.tierLabel + " · " +
const baseSel = document.createElement("select");
baseSel.id = "pack-base-sel";
baseSel.className = "field-input";
- [["", "cfg.q_pack_base_blank"], ["default", "cfg.q_pack_base_default"], ["chat", "cfg.q_pack_base_chat"], ["lean", "cfg.q_pack_base_lean"]].forEach((pair) => {
+ // #5345 follow-up: keep labels as literal t("…") calls — the #1024
+ // dead-key guard only counts string-literal arguments, so the old
+ // array + variable-lookup shape hid all four keys (CI red on master).
+ [["", t("cfg.q_pack_base_blank")], ["default", t("cfg.q_pack_base_default")], ["chat", t("cfg.q_pack_base_chat")], ["lean", t("cfg.q_pack_base_lean")]].forEach(([value, label]) => {
const opt = document.createElement("option");
- opt.value = pair[0];
- opt.textContent = t(pair[1]);
+ opt.value = value;
+ opt.textContent = label;
baseSel.appendChild(opt);
});
const btnBaseLoad = document.createElement("button");
@@ -4148,14 +4151,14 @@ if (d.keepalive) kv(parts, t("det.keepalive"), d.keepalive.tierLabel + " · " +
descIn.placeholder = t("cfg.q_pack_desc_ph");
descIn.style.cssText = "width:100%;box-sizing:border-box";
descWrap.append(descLab, descIn);
- function packSectionField(labelKey, helpKey, rows) {
+ function packSectionField(label, helpText, rows) {
const wrap = document.createElement("div");
wrap.style.cssText = "margin:8px 0";
const lab = document.createElement("div");
- lab.textContent = t(labelKey);
+ lab.textContent = label;
lab.style.cssText = "font-size:13px;font-weight:600;margin-bottom:2px";
const help = document.createElement("div");
- help.textContent = t(helpKey);
+ help.textContent = helpText;
help.style.cssText = "font-size:12px;color:#57606a;margin-bottom:4px";
const ta = document.createElement("textarea");
ta.className = "field-input mono";
@@ -4165,8 +4168,8 @@ if (d.keepalive) kv(parts, t("det.keepalive"), d.keepalive.tierLabel + " · " +
wrap.append(lab, help, ta);
return { wrap, ta };
}
- const phF = packSectionField("cfg.q_pack_phil", "cfg.q_pack_phil_help", 5);
- const hcF = packSectionField("cfg.q_pack_hc", "cfg.q_pack_hc_help", 8);
+ const phF = packSectionField(t("cfg.q_pack_phil"), t("cfg.q_pack_phil_help"), 5);
+ const hcF = packSectionField(t("cfg.q_pack_hc"), t("cfg.q_pack_hc_help"), 8);
const taPh = phF.ta;
const taHc = hcF.ta;
const formHint = document.createElement("div");
diff --git a/src/web/i18n.ts b/src/web/i18n.ts
index 8024e28b..078ff64c 100644
--- a/src/web/i18n.ts
+++ b/src/web/i18n.ts
@@ -1353,7 +1353,7 @@ export const MESSAGES: Record<Locale, Record<string, string>> = {
"cfg.q_pack_ai_apply_ok": "Parsed and filled into the form; the full JSON is synced to the Advanced (JSON) tab",
"cfg.q_pack_ai_extra": "The model also returned advanced fields: {names} — kept in the Advanced (JSON) tab and saved with the pack",
"cfg.q_pack_json_obj": "the pasted content must be a single JSON object",
- "cfg.q_pack_ai_p1": "You are helping me customize a compression prompt pack for billion-context. My scenario and requirements:",
+ "cfg.q_pack_ai_p1": "You are helping me customize a compression prompt pack for billion-context. My scenario and requirements are as follows: {need}",
"cfg.q_pack_ai_p2": "Write the JSON in bili-pack.json shape. Top-level object may contain: name (optional; lowercase letters/digits and ._- ; omit if unwanted), description (optional; one plain-sentence purpose), promptSections (object; at least one of): philosophy — the stance summaries take, what to keep first, what must never be lost; howToCompress — concrete summary-writing rules: structure, granularity, timeline requirements.",
"cfg.q_pack_ai_p3": "Rules: 1. Content must fit my concrete scenario — no generic filler; 2. The readers of both sections are the AI model itself — clear imperative bullet-style sentences; 3. If a section adds no value for my scenario, OMIT that key entirely (omitted = system defaults stay) instead of writing an empty string; 4. Output ONLY one JSON object — no markdown fences, no comments, no prose.",
"cfg.q_pck": "prompt_cache_key self-stamping (promptCache.routing)",
--
2.43.0
|
(ai: vllm-qwen/qwen3.8-27b) 推送不用等人类动作——我这边有独立的合法凭据通道(与上轮推 64fc687 同一条 credential helper 路径,不经过你本地 clone 的 remote URL,也不碰 gh-guard)。 已按你给定的规格执行完毕: 已完成
两点说明
八问(本轮增量)
判定:#5366 待你合并(合并权在你);合入后 master push CI 与本车分支的 |
|
补一条闭环记录:上文诊断的修复已作为独立 PR 开出 —— **https://github.com/ranxianglei/billion-context/pull/5366**(`fix(web): pack-editor i18n — {need} placeholder + scannable key refs (#1024)
|
|
红灯说明: |
(ai: vllm-qwen/qwen3.8-27b) 红灯说明收下,独立核验一致:
判定不变:v0.1.209 → master 未引入回归;车分支 |
新批次回归审查(9f74bae → 36db135)——无需修复的回归 0 项逐组读了代码本体(非描述),受影响套件本地实跑全绿。五组结论: #5196 restamp 座位退役(kernel/src/refs.ts):两阶段结算把 master 原有的 displaced 语义原样保留(含 S13c 依赖的"不清理 byRef[held]"行为),新增 leftovers 路径的三个顺序依赖(双列表去重、 #5337 推荐屏线性化(turn-integrity/unit-projection/recommend/report/boundaries):逐字段核了共享对象与旧内联计算的等价性——谓词、first-wins 语义、拷贝纪律( #4313 时间戳默认开(src/config.ts): #5222 隧道守卫 worker 探针(src/tunnel-guard.ts):方向性核过——主池 3×2s 耗尽后才起一次性 worker(内 2.5s/外 3s terminate,settle-flag 防重入,timer unref);探针失败=维持旧 unresolvable 拒绝(不 fail-open);探针成功的应答走同一条 IP 范围分类(不绕过任何准入边界);最坏准入延迟有限(≈9.5s);wedge 证据记录在救援之前(#5326 耦合,防告警恰好在楔死场景静音);确认日志限频 5min/episode、主线程成功即重置;嵌入场景可用 web 杂项(dns-wedge 重复横幅清理、i18n p1、lock 同步、kernel 0.0.174→0.0.175):最终树里 三个非阻断观察(记录不改):① #4313 文件值若为非布尔脏值, |
空载体 PR:本身没有代码改动。用途是把 v0.1.209 → master 的合并大 diff(约 440 commits / 314 files / +22827 −2096)作为整体做全面回归审查的落点,结论以评论形式发在本 PR 上。
审查维度:DSH 相关改动、性能、缓存、协议、存储、其他回归。非逐 PR review——看的是这个区间合起来的净效果与交叉影响。
CI 走 ci:full。本 PR 不合并。