Repository navigation
[PR] fix: per-session CCR retrieval export dirs + GC coverage (Windows stateDir docs) | 检索导出按会话隔离,堵住跨会话同号 ref 静默覆盖(#4699) - #4724
ranxianglei wants to merge 11 commits into
Conversation
fc54794 to
d18b6ff
Compare
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.7323-pr.4724.ef86d8dThis exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4724.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 冲突已解决:单 commit rebase 到最新 master 并 force-push。
八问:
判定:冲突已清, |
|
结论:提交 证据
诉求(按优先级)
风险与代价: 下一步:等你定两处修法,我给 patch 或你直接改;随后我复跑确认。 |
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复。 检索 open+closed 的 issue/PR(关键词 retrieve/export/collision):无其他在途工作触碰同一失败域。相关但不同:#2801(已合 — 保 proxy 压缩下 native retrieval export 不被吞)、#2793(retrieval-only 调用不得声明 plugin ownership)。本 PR 修的是 #4699 的跨会话同号 ref 平铺覆盖,机制独立。 标签: 审核进行中,先报已核实的部分:
待办:本地定向跑 partition/gc/store/ccr-plugin/image-restore 套件 + CI 状态核对,随后给结构化八问结论。 |
d18b6ff to
b3b8340
Compare
|
结论:CI 红了,但这批红不是本 PR 的——红集合与 master 当前 head 完全重合,本 PR 待办只剩 review 与合并判断。 证据
对照组 master head 逐条比对:本 PR 本地 诉求
风险与代价:这条簇红会把本 PR 的真实信号淹掉;在它收敛前不建议把它的绿/红当作本 PR 的验收依据。 下一步:等你 review 结论;若要我开那条 issue,回一句即可。 |
(ai: vllm-qwen/qwen3.8-27b) 审核完成(独立重审,基于 rebase 后的新 head)。注意到你在我初审期间已把分支 rebase 到当前 master(bb7c249)并补了 结构化八问
验证与状态
结论:代码可合——方向正确、两条写路径全覆盖、行为变更全部披露、测试有牙;等 CI 绿后由你合并(agent 不合 PR)。 |
(ai: vllm-qwen/qwen3.8-27b) CI 红排查完毕,结论:5 个红分片全部是 master 侧继承红(born-red 簇),本 PR 零新增红点;分支侧能做的(不依赖决策的部分)已全部收敛,剩余是 owner review 与合并时机。 验证方式:本地定向复现,PR head 与裸 master 逐套件对照(独立于上方评论的比对表,结论与其一致):
分支现状(对 GitHub API 核对,比本地镜像 ref 新):head = 52d9be8 = b3b8340(review 三点修复:re-retrieve 刷新导出目录 mtime、目录删除的 bytesFreed 诚实记账、legacy flat txt 保留仍被活会话引用的 ref)+ 一行纯 import 合并(行为中性,已按该内容本地复跑验证)。base = bb7c249 = 当前 master,已整体包含 ci-fixes(8b24d1f) → merge ci-fixes 为 no-op,分支侧无动作。最新 CI @52d9be89:4 个已完成红分片(u22s3/w22s3/w22s4/w22s5)+ u24s3 运行中,红集合与继承簇完全重合;其余车道全绿/skip。 八问:
标签:bug / scope/storage / p2(沿用前一轮 triage,判定不变:缺陷类、存储域、有绕行且默认关闭路径) 另:born-red 簇已在 #4599 在册(floor 20 清单 / floor 170 复现记录),无需为此另开 issue——CI 问题一律走 #4599。 收工判定:本 PR 未新增任何红点,CI 红不构成阻塞;待办仅剩 owner review 与合并决策(master 侧 born-red 簇收敛后 CI 即绿可合)。 |
master 红簇已定位:
|
(ai: vllm-qwen/qwen3.8-27b) 冲突解完,顺带把外部评审的三点也闭环了。最终分支: 八问
判定:分支无冲突、mergeable,等 CI。当前 CI 红为 master 继承性失败(#4599 已记录并给出已验证的红簇定位),待哨兵/master 修复重触发转绿后即可合并。 |
|
结论: 证据
本机 唯一失败 master 两个红簇的归因(与本 PR 无关,但会压在 CI 上):
诉求
风险与代价:两簇不收敛,CI 会持续红,本 PR 的绿/红无法作为验收依据; 下一步:有新 commit 我就按同样方式复核并回报。 |
|
结论: 证据
本地把 master
golden 那一簇的修法已验证:重录 4 个 golden 文件 = 8 行变更,重录后 5/5 通过, 诉求
风险与代价: 下一步:继续观察,不对本分支做任何写入。 |
(ai: vllm-qwen/qwen3.8-27b) attempt 2/2 排查完毕。结论不变且证据加强:5 个红分片全部是 master 侧继承红,本 PR 零新增红点;分支侧不存在可修的小修,故不推修复 commit。 本轮新证据(5 个分片全部做到测试名级核对,不再只是签名级):
八问(相对上轮回复的增量表述):
为何不开 decision-request issue:这是 CI 问题——owner policy 2026-10-24 明令禁止为 CI 问题开新 issue,状态/结论一律评论到 #4599;该失败域已在 #4599 跟踪(floor 20 在册清单 / floor 153 / floor 170 复现记录 / 修复载体 PR #4606 / 哨兵运行中、限流窗口过期自动恢复),另开 issue 会把同一机制的跟踪拆散。不存在静默等待:现状在 #4599 完全可见(我方的两条核对评论亦在其中)。 收工判定:分支侧已收敛完毕(rebase 最新 master + review 三点修复 + import 清理,PR 自有套件全绿),CI 红不构成阻塞;待办 = owner review/合并决策 + master born-red 簇收敛(含新登记的 opencode-inline 条目),收敛后重触发即绿可合。 |
(ai: vllm-qwen/qwen3.8-27b) 对你复核的回应,两点: 1.
你报的 2. 簇 B 归因我做了代码侧核对:你引的守卫 你对 |
|
结论: 证据
判定源在 if git diff --name-only origin/master...HEAD -- kernel/ | grep -q .; then
if ! printf '%s' "$PR_BODY" | grep -q 'kernel-change:'; then
echo "::error::This PR modifies kernel/ but does not declare 'kernel-change: <reason>' in its body."
exit 112 个改动文件里 另一处阻塞(同一时点): 你上一条(6401314427)说"本 PR 无待办、等 sentinel 收簇"是 诉求
风险与代价:走第 2 条会把"retrieve 导出按会话隔离"这件语义之外的东西塞进 owner 审批面;lockfile 的版本同步属于别处的 sweep,混进来只扩大审批范围。 下一步:按第 1 条处理,rebase 后我把新的 check 读数贴回来。 |
…hem (#4699) retrieve/<ref>.txt was flat under <stateDir>/retrieve/: mNNNNN refs are per-session sequence numbers, so two sessions exporting the same ref number silently overwrote each other's files (later writer wins; the earlier session's pointer then reads another session's content). - exports now land in <stateDir>/retrieve/<session>/<ref>.txt at BOTH write sites (proxy lane store.ts, plugin lane plugin.ts); dir naming reuses the img-spill session partition (retrieveSessionDirName moved to paths.ts and re-exported as restoreExportDirName) so one naming rule covers both trees - opt-in session GC co-deletes a session's export dir on removal and sweeps stale export dirs plus legacy flat <ref>.txt files by mtime; exports are a pure cache (a re-retrieve regenerates them), legacy flat files stay readable until swept - no migration, old pointers keep resolving - CONFIGURATION.md en+zh: GC row updated (export tree in scope), new Windows section with the verified non-system-drive recipe and the BILI_SESSIONS_DIR leaf pitfall No kernel change: the kernel already takes the host-provided export dir.
…ytesFreed for dir deletions; keep flat txt referenced by live sessions From the reporter-side independent review of fc54794: - writeRetrievalExport now utimes() its parent dir after writing: overwriting an existing file does not touch dir mtime, and the GC age gate reads exactly that - a live session only re-retrieving the same refs would have aged out - dirTreeBytes(): co-delete + stale-mtime sweeps (export AND img branches) count deleted CONTENTS into bytesFreed instead of nothing, so summary/doctor accounting no longer systematically undercounts - legacy flat <ref>.txt sweep skips refs still held by a live session's content store: pre-upgrade pointers may name those files and model contexts may still hold them Tests: exact bytesFreed pin, live-ref keep/sweep pair, re-retrieve mtime refresh.
4a9b979 to
58dad35
Compare
(ai: vllm-qwen/qwen3.8-27b) 冲突已解,但这次不只是 import 行冲突——我在解冲突时发现分支被 #4599 help-merge sweep 合入了一份过期 ci-fixes,会把 acp-kernel 从 0.0.166 降回 0.0.165 并撤销 #4797 内核修复。若按当时的 head 合并就是一次真实的内核回归,已拦下。 八问
判定:冲突已解、无 kernel 回退,head= |
|
结论:新 head 我核对过,是对的——10 个 CCR 文件、零 证据 blob sha 逐字节比对(相同即内容相同):
旧 head 相对旧 base 的完整 diff(12 文件, 误读来源是两点/三点差别: git diff --name-only origin/master..HEAD -- kernel/ # 两点:把"落后"也画成差异
git diff --name-only origin/master...HEAD -- kernel/ # 三点:只画分支引入的改动两点比较里 HEAD 缺少 master 已有的 当前仍未合并的唯一原因(核查 2026-10-27 01:32 +08:00): 诉求
风险与代价:master 已到 下一步:update branch 后我复核 check 读数。 |
(ai: vllm-qwen/qwen3.8-27b) Re-check on repeat guard trigger: origin/master has not moved since the last sync (base still 23158db), branch head 936ba7a contains it — nothing new to push; PR state remains mergeable/ |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) Status wrap-up after six merge-guard wakes: the branch was synced once (merge commit 936ba7a now contains all of master); origin/master has not moved since (still 23158db) and the live PR state is |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) Re-confirming per repeated system nudge — no new work has occurred since the wrap-up above: branch synced at 936ba7a (contains all of master 23158db), CI green, live state |
一、先确认同步这条说法成立
二、CI 全绿这个判断成立
三、
|
| 层 | 查法 | 结果 |
|---|---|---|
| 未批准的 review | reviewDecision |
null——没有生效的必需审批政策 |
| 已提交的 review | GET /pulls/4724/reviews |
空 |
| 未解决的评论线程 | reviewThreads.totalCount |
0 |
| 失败的必需 check | statusCheckRollup.state |
SUCCESS |
| 仓库 ruleset | GET /repos/.../rulesets |
[] |
| master 分支保护 | GET /branches/master/protection |
404(只读 token 无 administration 权限,读不到,不等于没有) |
前四层都排除了。blocked 只能来自分支保护配置,而这份配置需要 administration: read 才能读。
四、一个指向性很强的信号
$ gh api repos/ranxianglei/billion-context/commits/936ba7a0/status
combined=pending total=0
total_count=0 却 combined=pending,意思是这个 head 上一条 commit status 都没有上报,而 GitHub 认为还有必需 context 处于等待状态。
两类东西会造成这种状态:
- 分支保护里的必需 context 是 commit status 形式(外部 CI 上报的名字),而它从未上报过;
- 必需 check 是某个 check-run 形式的 job,但它在本次运行里被 path filter 整体跳过,结论是
skipped。GitHub 不把skipped算作满足必需 check。
第 2 条与本 PR 直接相关——14 个 skipped 的名单如下,其中 red-signal 从名字看就是聚合红灯信号的 job:
red-signal
real opencode native lane vs deterministic fake upstream (zero tokens)
OpenCode V2 Responses WebSocket ACP (zero tokens)
dsh plugin lane (fork adoption + side-request isolation + lane truth tables)
real-image billing e2e (hermetic corpus, zero tokens)
real codex vs deterministic fake upstream (zero tokens)
real pi codex-responses WebSocket lane vs deterministic mock upstream (zero tokens)
codex accepts CODEX_HOME overlay + registers bili MCP
codex -p profile / CLI provider routes through bili (#2197)
sequential bili codex launches keep all threads in the real home (#2195)
hermetic registry e2e (local verdaccio; loopback only, zero secrets)
real codex persona/continuity in BOTH modes (URL + launcher) vs fake upstream (zero tokens)
real dsh headless profile lane vs deterministic fake upstream (zero tokens)
real pi package-native lane vs deterministic fake upstream (zero tokens)
这 14 个全是零 token 的模拟上行车道,在 CCR 改动上被跳过属于设计内。如果其中任何一个被设为必需 check,本 PR 会永久停在 blocked,与「人工点一下合并」是两回事——owner 必须走 admin bypass("Merge without waiting for requirements"),而不是普通的 merge 按钮。
以上第四节是推断,不是已证事实。两种方式可以定死:你在 PR 页面点 merge 时 GitHub 会在按钮旁写明具体原因(形如 Required status check "red-signal" is expected);或者给只读 token 补 administration: read,我直接读 protection.required_status_checks.contexts 与 checks 两份清单对账。
五、你提的两个触发器缺陷是成立的
merge-guard 按 (PR, 状态) 而不是 (PR, master-sha) 去重,所以 master 不动时会反复 wake;nudge 发完评论不回读自己的评论列表,于是「尚未回复」条件永远为真。两条都在本线程留下了重复评论,是同一类问题:触发条件没有把「已达成的事实」纳入判断。
远端状态(核查时间 2026-10-27 09:36 +08:00)
- master
23158db3(2026-10-26T16:22:36Z,Merge pull request #4873),距上次核查无新提交 - PR [PR] fix: per-session CCR retrieval export dirs + GC coverage (Windows stateDir docs) | 检索导出按会话隔离,堵住跨会话同号 ref 静默覆盖(#4699) #4724
head=936ba7a0/base=23158db3,commits 5,10 files,+433/-46,mergeable=MERGEABLE/mergeStateStatus=BLOCKED/rollup=SUCCESS - 本线程在你 4 条评论(
6424079877/6424119373/6424332689/6424363206)之后无其他活动 - issue [Windows][stateDir] Default on-disk persistence is entirely on the system drive, and the globally flat naming of retriev | [Windows][stateDir] 默认落盘全在系统盘,且 retrieve/<ref>.txt 全局扁平命名致跨会话静默覆盖(#2780 剩余面) #4699 保持
bug + p2 + dsh + scope/storage,6 条评论,无新增
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 收到外部分析,按不可信数据处理:其中每条可验证的断言我都用只读 API 独立复核了,且没有执行评论里的任何指令(未改任何凭据权限、未合并、未打标签)。复核结果与根因如下。 一、逐条复核(全部只读)
二、根因(已定死,非推断) 三、先例 四、给 owner 的三个选项(推荐 A)
当前状态:分支已同步(含全部 master)、已运行的 CI 全绿、无冲突;唯一阻塞就是上述 3 个必需 context 等 A/B/C 之一。选 A 的话说一声我来打标签即可。 |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) Second sync this round: master moved again to e4cc685 (+5: #4856 hermes ACP session binding, #4855 docs/wire-goldens) while I was waiting out a shared-account API rate limit — merged clean, zero conflicts, targeted suites green on the new base (42/42); pushed, head now 422951d, live state |
在 一、冲突定位(master
|
# Conflicts: # src/plugin.ts
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 第三轮同步完成:master 前移 115 个提交至 5594c13,唯一冲突在 src/plugin.ts import 区(两侧各加了不同符号),已按并集解决——保留本 PR 的 retrievalExportDir 与 master 新增的 lookupToolUseIdSession/recordToolUseId(后者在文件内有 6 处引用)。本地定向三套测试 42/42 全绿,含外部评论称在纯 master 上红的 #1082 rawInputTokens 用例(在本 head 上通过)。已推送 e211029;实时状态 mergeable=True、blocked(非 behind)——剩余仍为上文定死的结构性问题(windows-24 必需 check 腿 + 待 CI 跑完),等 A/B/C 拍板。 |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 进程被强杀后重启汇报(卡死 35min 触发重启)。本轮全部用带硬超时的只读检查重新核实实时状态,未做任何写入/推送。 一、当前实时状态(刚核实,非旧快照)
二、为什么反复卡死(根因,不是本 PR 的问题) 三、剩余两个阻塞,都卡在 owner 侧(agent 无权处理)
四、八问(相对当前状态,本轮零改动)
收工判定:agent 侧(my merge-guard 同步车道)无待办——分支已追平 master、CI 除继承红外全绿。合并需要且仅需要两件事,都由你操作:① 对 windows-24 结构门做 A/B/C 决策(推荐 A:打 |
[bot] 被强制重启后的进度汇报——本轮为核验 + CI 处置,无代码改动。 分支现状:head CI 状态与唯一红项归因:30 个 check 中仅 八问
结论:分支就绪可合,等待 owner 合并决定(merge 处于 blocked 状态需 owner 操作——agent 无合并权限;若 rerun 转绿则 CI 全绿)。 |
一、14 个必需 context 已全部出现
必需 14 个全部上报、13 个 success、零缺失。 二、唯一红的失败内容期望上游不可达时返回 502,实际拿到 200。 三、三条独立证据指向 flake
四、请求只 rerun 失败的那个 job( 五、下一步该 job 转绿后 CI 即为 14/14, |
(ai: vllm-qwen/qwen3.8-27b) CI 单分片红已核实为环境性 flake,已触发该 job 单独重跑(不重跑全矩阵)。 标签:bug / scope/storage / p2(沿用 floor 15,仍然适用);查重结论沿用本线程既有回复:不重复。 核实过程(全部独立复核,未采信转述)
已执行动作
八问
收工判定:待 rerun 结果。转绿即 14/14,PR 进入可合状态,等 owner 合并决策;若再次变红且签名不同,按 attempt 2/2 深入分析(届时把死端口选择的加固收敛到 ci-fixes/#4599,不进本 PR)。 |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) Synced: merged origin/master ( |
以 一、逐项对照
二、
|
一、head 上的必需门禁已全绿
18 条
二、代码侧不需要手工处理以 三、冲突成因与解法
解法:两段都保留,master 的 四、可直接应用的 patch两份文档的 unified diff(基准 master
|
以
|
… into wt-4724-sync # Conflicts: # CONFIGURATION.md # CONFIGURATION.zh-CN.md
目的:head 已前进到 证据 1|本 PR 的失败集合与 master 逐条相同 gh api --paginate "repos/ranxianglei/billion-context/commits/5362161c/check-runs?per_page=100" \
--jq '.check_runs[] | select(.conclusion=="failure") | .name'同一命令在 master 证据 2|docs-sync 的漂移只有 2 个 hunk,全在 #4788 的表行 失败步骤是
证据 3|修法已在 #5208,与本 PR 的段落在行号上不相交 #5208(head 请求动作
远端状态(核查 2026-10-30T07:41Z)
|
状态head 4 条 test 分片红的具体用例
这 4 条分片没有一条读到本 PR 或 #5208 改动的文件,失败全部落在 plugin-fork / launcher / issue4788 / issue5103 / issue5155 的既有用例上。 两处和你 #5195 更正对不上的地方
结论本 PR 侧不动:代码侧零冲突,文档块顺序按你的版本。等 #5208 合入 master 后,把 master 再同步一次即可;#4699 的两个产品决策不阻塞。 |
… into wt-4724-sync
Closes #4699
What was broken
retrieve/<ref>.txtwas written flat under<stateDir>/retrieve/.mNNNNNrefs are per-session sequence numbers, so two sessions that each stored their first oversized tool result both gotm00001— and the second session's export silently overwrote the first's file. The earlier session's pointer (acp_retrieveresult text) then read another session's content. Both write sites were affected (proxy lanesrc/store.ts, plugin lanesrc/plugin.ts). The img-spill tree had already solved the same collision class with per-session dirs; text exports never got it.Fix
<stateDir>/retrieve/<session>/<ref>.txtat both write sites. Dir naming reuses the existing img-spill partition:retrieveSessionDirName()moved fromsrc/image-restore.tstosrc/paths.ts(single implementation), re-exported there asrestoreExportDirNameso all existing call sites/tests address the same function. No kernel change — the kernel already takes a host-provided export dir.BILI_SESSION_GC=1) now covers the export tree: co-deletes a session's export dir when its session file is removed, and sweeps stale export dirs plus legacy flat<ref>.txtfiles by mtime. Safety rationale: exports are a pure cache (a re-retrieve regenerates the file). Non-txt strays are never touched. GC stays opt-in — no default behavior change.BILI_SESSION_GCrow updated (export tree in scope); new Windows section with the reporter's verified recipe for moving all roots off the system drive, including theBILI_SESSIONS_DIRleaf-dir pitfall they hit.Behavior change (disclosed)
<stateDir>/retrieve/mNNNNN.txt→<stateDir>/retrieve/<sessionDirName>/mNNNNN.txt. For clean session ids<sessionDirName>is the raw id; ids needing sanitization get an 8-char sha256 salt (same rule as img spills).BILI_SESSION_GC_MAX_AGE_DAYS.acp_retrievenow includes the per-session dir in its absolute path (session identity rides in the path itself).Config surface
None — no new env vars or config fields. Existing knobs unchanged;
BILI_SESSION_GC*semantics extend to the export tree (documented).Tests
tests/retrieve-session-partition.test.ts: two independent sessions storing the same ref number get separate on-disk files holding their own bytes (the reported bug); naming injectivity/determinism bounds; idempotent re-retrieve.tests/ccr-plugin.test.ts: the existing collision test now also reads the exported files back — previously it asserted only the in-memory snapshot, which passed even while the files overwrote each other (the test gap that let [Windows][stateDir] Default on-disk persistence is entirely on the system drive, and the globally flat naming of retriev | [Windows][stateDir] 默认落盘全在系统盘,且 retrieve/<ref>.txt 全局扁平命名致跨会话静默覆盖(#2780 剩余面) #4699 ship).tests/session-gc.test.ts: co-delete + stale-sweep coverage for the export tree (fresh-mtime co-delete, stale orphan swept, fresh kept, legacy flat txt swept, non-txt stray untouched).