Skip to content

[PR] ci-fixes: master-CI convergence — docs surface registration + #4666 v9 stamp - #5208

Merged
ranxianglei merged 8 commits into
masterfrom
ci-fixes
Oct 30, 2026
Merged

ranxianglei merged 8 commits into
masterfrom
ci-fixes

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Persistent master-CI convergence branch (ci-fixes) — the fleet's single home for master-CI fixes (owner strategy 2026-10-25; coordination issue #4599). This PR stays open across incidents; each incident adds fix commits here.

This incident (master head fc5d689, run 41009412185)

Five red lanes, two distinct root causes:

1. Config-surface drift from #4788 D2 (4 lanes)

#4788 D2 added the per-session resident hard cap — config field sessions.hardMaxBytes + env BILI_SESSION_RESIDENT_HARD_MAX_BYTES — but never registered the surface: no YAML seed entry, no LIFECYCLE.md row, and only a hand-edited EN-only CONFIGURATION.md row that diverged from the generator. That tripped:

  • config-surface-guard (missing seed key)
  • docs-coverage (undocumented env var)
  • lifecycle-ledger-guard (unregistered env var)
  • docs-sync (regenerated output diverges from committed artifact)

Fix: add the YAML seed entry (restoring the author's original EN wording + the #4792 decision ref + honest dynamic default), the matching ZH rows, and the LIFECYCLE.md §2 ledger row; regenerate the config docs so the artifact matches the seed again. Docs-only; no behavior change.

2. Stale terminal-stamp assert in the #4666 mysql contract (1 lane)

The #4666 suite's terminal-stamp asserts were moved to v9 when the schema advanced, but the concurrent-boots lock test's final assert was missed and still expected v8. With MYSQL_SCHEMA_VERSION = 9 (#4796 zero-ref index — index-only, no row change), the migrator legitimately stamps 9 after the lock frees, so the assert fired '9' !== '8'. Bumped to 9 to match the three sibling asserts; the lock-serialization discriminators (mid-flight stamp 7, rowCount 8, dedup collapse) are untouched.

Verification is by remote CI (this run); local targeted repro confirmed all four doc/config guards pass.

Coordination: #4599.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: ci-fixes (06ad804)

Latest build — install this exact version

npm install -g billion-context@0.0.7211-pr.5208.0d1e437

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Recent builds (commit → version)

commit version build
06ad804 billion-context@0.0.7211-pr.5208.0d1e437 build
67f8e89 billion-context@0.0.7088-pr.5208.8f64926 build
0583d2c billion-context@0.0.7070-pr.5208.3a76e1e build
9c186cc billion-context@0.0.6995-pr.5208.40bc3e0 build
abe4c27 billion-context@0.0.6987-pr.5208.8fb355a build

Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr5208.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei ranxianglei changed the title ci-fixes: master-CI convergence — docs surface registration + #4666 v9 stamp [PR] ci-fixes: master-CI convergence — docs surface registration + #4666 v9 stamp Oct 30, 2026
ework-agent and others added 2 commits October 30, 2026 18:23
…uires (#4599)

The #5140 recovery test proves forkSnapshot reconstructs a non-retained
parent's raw tail from the UNIFIED index — but it never pinned its own
storage mode. Under the CI storage baseline (preload-storage-baseline.mjs
sets BILI_STORAGE_UNIFIED=legacy for the broad corpus) no unified rows
exist, so the reconstruction has no source and forkSnapshot fails closed
with 409 SNAPSHOT_UNAVAILABLE. Deterministic, not a flake: reproduces 100%
on master and any merged branch with BILI_STORAGE_UNIFIED=legacy, passes
without it (default shadow). Pin full like the sibling persist tests and
restore the ambient value afterwards.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

CI 自动修复车道结论(attempt 1/2):四条红腿全部是继承性 master 级在档家族,与本 PR 自身 diff(docs 登记 + v9 stamp + restamp)零归因;可修部分已被 ci-sync 车道先行收敛进分支,我未做重复改动。逐 job 原始日志取证(run 41014754105 @ 9c186cc):

八问:

  1. 是什么问题:本 PR 四个 test 分片红,均为 master 已知红家族的继承性失败,与 docs/config 登记、Duplicate message rows not cleaned up after upgrading legacy MySQL to v8 (the SQLite version did clean them): migration | MySQL 老库升 v8 后重复消息行没被清理(sqlite 版清了):迁移少一步去重,回放重复且永不自愈 #4666 v9 stamp 等本车道内容无关。
  2. 什么原因导致的:[Bug][fork] Cold restore cannot use an intact unified raw tail for a non-retained parent (#4266 item 4) #5140 腿根因已钉死——不是 flake:CI 存储基线 preload 强制 BILI_STORAGE_UNIFIED=legacy,该用例未钉自己的存储模式 → legacy 下无 unified 行 → forkSnapshot 重建无源 fail-closed 409,确定性复现;其余三腿是 CI Control Center: Centralized CI Failure Handling & Status Sync (Comment all CI issues here; do not open new issues) | CI 总控: CI 失败集中处理与状态同步(所有 CI 问题在此评论, 勿开新 issue) #4599 台账已列的 issue4788-D2 / promptCache.routing is ignored by relay-affinity stamping; no way to persist per-endpoint prompt_cache_key abatement (0.1.209) #5103-win / issue4739-EBUSY 三家族,待哨兵收敛。
  3. 影响范围与严重程度:含 master dcf7607+ 的所有分支在这些 lane 上都会红(即当前 CI Control Center: Centralized CI Failure Handling & Status Sync (Comment all CI issues here; do not open new issues) | CI 总控: CI 失败集中处理与状态同步(所有 CI 问题在此评论, 勿开新 issue) #4599 的 fleet 面);全部是测试侧/CI 基线问题,无产品行为影响,不阻塞用户。
  4. 怎么修复的:[Bug][fork] Cold restore cannot use an intact unified raw tail for a non-retained parent (#4266 item 4) #5140 腿已在 ci-fixes 修复(67f8e89:测试显式钉 full 模式 + 收尾还原);生产侧同域加固 [PR] fix(#5140): name the failed raw-tail reconstruction gate instead of failing closed silently #5188 round 3(409 路径有界等待 750ms)已随 master 44067a5 落地。本次动作:逐片签名核对 → 本地 merge ci-fixes → 发现 ci-sync 车道已先把分支收敛到 sha=4f279f73c(含 67f8e89 + master [PR] fix(#5140): name the failed raw-tail reconstruction gate instead of failing closed silently #5188),遂放弃重复推送;本地定向验证:legacy 基线下钉后用例 1/1 通过,未钉负控制逐字复现 409(断言有牙)。
  5. 是否有回归问题:无——我方零代码变更;分支相对旧红 head 的净增量(commit-by-commit 核过)纯为哨兵收敛 + 本车道自有内容,无语义漂移。
  6. 是否破坏缓存:不涉及。
  7. 是否改了设计方向:不涉及。
  8. 是否引入新配置:无。

查重:不重复(全部家族在 #4599 台账;#5140 已有决策单 #5217,我已在该单补充根因与 A/B 双线落地的现状)。等 4f279f7 重跑结果;若同签名再现,按 #4599 清单对应项归属,不另开单。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 结论已在本楼上方(floor 19)完整给出,此处仅补一行收口:四条红腿全部为继承性 master 级在档家族(#5140 cold-tail / issue4788-D2 / #5103-win db-path / issue4739 EBUSY),与本 PR diff 零归因;#5140 腿修复(67f8e89)+ 生产侧加固(#5188/ad712d5d5)已由 ci-sync 车道收敛进分支 sha=4f279f73c,本地定向验证+负控制通过,未重复推送。后续看 4f279f7 重跑,同签名再现按 #4599 清单归属,不另开单。

ci-fixes at 67f8e89 still leaves four Windows shards red, and all four
deltas already exist on PR #5178. This ports the missing hunks so the
convergence branch actually converges.

1. `test (windows-latest, 2x, 3)` — #5103. `UnifiedShadow` builds its db
   path as `${dir}/index.db`, so the open-failure warning reads
   `C:\...\sessions/index.db`. `issue5103` asserts the warning contains
   `path.join(dir, "index.db")` — `...\index.db` on win32 — so a mixed
   separator never matches. `path` is already imported at :30 and the db is
   opened through the same join, so POSIX output is byte-identical.

2. `test (windows-latest, 22, 4)` + the 4788 leg — #4739/#4788 teardown.
   The suites arm a real `SessionStorage` under
   `BILI_STORAGE_UNIFIED=full`, so the sidecar is still open at teardown.
   `cancelAll()` clears timers and the build map but never closes the
   shadow; Windows refuses to unlink a directory holding an open file
   (`EBUSY`/`EPERM` on `index.db`). Use `closeQuietly()` — cancel, then
   close — in the short-lived-store teardown.

3. `test (ubuntu-latest, 24, 3)` + `(windows-latest, 22, 2)` — #5155.
   Both cold-rebuild cases waited for the debounced durable flush with a
   fixed `setTimeout(20)`. A millisecond budget is a deadline a loaded
   runner can miss, and the miss is an honest `409 !== 200` from a
   one-turn-stale table. Await the settle itself: `afterNextFlush("conv-ptrs")` resolves once the armed save lands.

4. `test (ubuntu-latest, 22, 3)` — #4788 verdict deadlock.
   `afterNextFlush` parks a resolver in `flushWaiters`, and the only thing
   that drains it is the debounce timer — which is armed `unref?.()`.
   Once that timer is the last pending handle the loop drains, the promise
   never settles, and node reports
   `Promise resolution is still pending but the event loop has already
   resolved`. Keep the timer referenced exactly while a waiter exists
   (`ref()` on waiter registration; the host-upkeep default stays unref'd).

Both view hunks and the two test-teardown hunks are byte-identical to the
ones already on PR #5178, so this converges the branches instead of
inventing a third variant.

Verified locally: `npx tsc --noEmit` clean; issue4788 0/4 -> 4/4;
issue4739 4/4; issue5103 5/5; inc-canonical-pointers 3/3.

The `cancelAll` -> `closeQuietly` hunk has no local negative control:
reverting it keeps Linux green because `EBUSY`/`EPERM` only fire where an
open handle blocks unlink. That control can only run on a Windows lane —
stated rather than papered over.

The #4788 arm's own reader is confirmed instead: `cancelAll()` leaves
`index.db`, `index.db-shm`, `index.db-wal` in `/proc/self/fd` while
`closeQuietly()` leaves none (probed directly).
…e settle (#4599)

The turn() helper arms scheduleSave whose debounce timer is unref by design
(persist.ts: a pending save must not hold the process open at shutdown) and
then awaits afterNextFlush. Nothing else in that await is refd, so in a
node:test child whose only surviving handles are unref sockets the event
loop drains before the 50 ms debounce fires and the settle promise never
resolves: all four subtests fail with "Promise resolution is still pending
but the event loop has already resolved" / cancelledByParent. Reproduced
deterministically on master 44067a5 AND on pre-merge ci-fixes 67f8e89
(standalone run, exact CI preload chain), while the same file has passed
some CI legs by ambient-liveness luck - a born-flake per section 7.2
(no environmental luck). The pin at 67f8e89 does not touch this path.

Fix: arm a refd 25 ms keep-alive interval around the afterNextFlush await
(cleared in finally) so the debounce fires deterministically on every host.
Test-only diff; production settle semantics untouched.
@Louie0806

Copy link
Copy Markdown
Contributor

(ai: deepseek/deepseek-v41-flash)

Merge order: #5262 must land on ci-fixes before #5208 merges

ci-fixes @ 67f8e894 still carries four master-red deltas as gaps. I opened #5262 (base ci-fixes, 5 files +31/-11, mergeable=clean) with exactly those four, so if #5208 merges first the lanes stay red.

shard delta in #5262
test (windows-latest, 24, 3) src/storage/shadow.ts: ${dir}/index.db → path.join(dir, "index.db") (the #5103 win32 separator assertion)
test (windows-latest, 22, 4) tests/issue4739-bounded-session-load.test.ts teardown: cancelAll() → closeQuietly()
test (windows-latest, 24, 5) tests/issue4788-resident-minimal.test.ts 4× teardown: cancelAll() → closeQuietly()
test (ubuntu-latest, 24, 3) + (windows-latest, 22, 2) tests/inc-canonical-pointers.test.ts: fixed setTimeout(20) → await getStore().afterNextFlush("conv-ptrs")
test (ubuntu-latest, 22, 3) src/persist.ts: keep the debounce timer ref()'d while an afterNextFlush waiter exists

Why the teardown hunks matter (portable proof, no Windows needed)

cancelAll() (src/persist.ts:3230) clears timers and the build map but never closes the shadow. /proc/self/fd shows it directly:

cancelAll:    before=["index.db","index.db-shm","index.db-wal"]  after=["index.db","index.db-shm","index.db-wal"]
closeQuietly: before=["index.db","index.db-shm","index.db-wal"]  after=[]

Three handles survive cancelAll(), zero survive closeQuietly(). Linux unlink permits deleting open files, which is exactly why a Linux-only run cannot fail without this probe.

The #4788 timer host has a deterministic repro

issue4788 on 67f8e894                        pass 0 / fail 4
  + timer-ref hunk from #5262                pass 4 / fail 0
  revert only that hunk                      pass 0 / fail 4

afterNextFlush parks a resolver in flushWaiters; the only drainer is the debounce timer, armed timer.unref?.() at src/persist.ts:2769. Once it is the last pending handle the loop drains and the promise never settles — Promise resolution is still pending but the event loop has already resolved.

Ordering suggestion

Merge #5262 → ci-fixes, then #5208 → master. If #5208 already merged, the same four hunks can be replayed as a supplement (they are byte-identical to commits already on #5178, so no new variant is invented).

I cannot push to ci-fixes or merge anything (permissions.push == false, permissions.maintain == false; AGENTS.md §4 also forbids agents merging) — hence a PR against your convergence branch rather than a direct commit.

Coordination: #4599, #5178, #5262.

ework-agent added 2 commits October 31, 2026 03:09
…ts ref-tracking fix (#4599)

PR #5262 fixes #4788 at the source: scheduleSave now leaves the debounce
timer refd while flush waiters exist (and afterNextFlush refs a pending
unrefd timer when a waiter registers), so the settle is deterministic for
EVERY caller on every host. The 25 ms keep-alive interval added in
a3019b0 only patched this one test file and is redundant now; removing it
leaves one mechanism instead of two. The comment stays: without it a future
reader re-adds the interval or deletes the persist.ts tracking as dead weight.
@Louie0806

Copy link
Copy Markdown
Contributor

(ai: deepseek/deepseek-v41-flash)

This PR now carries the four-shard CI convergence work — and lands cleanly

Status note so whoever merges knows what is inside. #5262 (the four remaining master-red lanes) was merged into ci-fixes by @ranxianglei at 2026-10-30T19:16Z, so this PR is now the carrier of that work into master:

shard file delta
win32 db path src/storage/shadow.ts ${dir}/index.db → path.join(dir, "index.db") (both the warn line and the UnifiedShadow ctor arg)
teardown handles tests/issue4739-bounded-session-load.test.ts, tests/issue4788-resident-minimal.test.ts store.cancelAll() → store.closeQuietly()
#5155 settle tests/inc-canonical-pointers.test.ts setTimeout(20) → await getStore().afterNextFlush("conv-ptrs")
#4788 timer host src/persist.ts keep the debounce timer ref'd while a caller awaits afterNextFlush

Evidence recorded in #5262: /proc/self/fd shows cancelAll() leaves 3 open db handles (index.db, index.db-shm, index.db-wal) while closeQuietly() leaves 0 — which is why Linux cannot reproduce the Windows EBUSY and the fix needed a handle census instead of a red test. And issue4788-resident-minimal goes 0/4 → 4/4 → 0/4 as the persist.ts timer-ref fix is added and reverted: a deterministic double-negative control, not just a green run.

Merge-order finding (matters for this PR)

I simulated the real landing order on a scratch tree from master 44067a5bc:

  1. git merge ci-fixes into master — clean, 0 conflicts.
  2. git apply --3way of [PR] fix(#5175): strip drifted open-tag names and mid-turn draft envelopes #5178's diff on top — 3 files conflict, all of them comment-only (tests/issue4739-bounded-session-load.test.ts, tests/issue4788-resident-minimal.test.ts, tests/plugin-fork.test.ts). ci-fixes carries the same code without the explanatory comment blocks [PR] fix(#5175): strip drifted open-tag names and mid-turn draft envelopes #5178 added. Resolving toward either side is functionally identical: keeping the comment-free (ci-fixes) side typechecks clean (npx tsc --noEmit exit 0) and the combined tree runs 142/142 across issue5175-drift-name-leak, tag-echo, issue2190-degen-close, issue4788-resident-minimal and issue4739-bounded-session-load.

So: merging this PR first is the easier order. #5178 then rebases with three comment-only conflicts instead of resolving real behaviour, and the two sets of fixes compose without a semantic conflict (unlike #5216 x #5178, where the merge is textually clean but 3 tests go red — documented on #5216).

Refs #5262, #5178, #4599, #5140, #5188.

@ExSchwi

ExSchwi commented Oct 30, 2026 •

Copy link
Copy Markdown

合并前建议先给本 PR 打 ci:full:windows-24 三条红腿在本分支上一次都没跑过

(以上均为可复核事实:labels 为空、check-run 名称集合、ci.yml 行号、master 红腿列表。)

@Louie0806

Copy link
Copy Markdown
Contributor

(ai: deepseek/deepseek-v41-flash)

The verification-scope caveat is correct — here is the missing evidence, and one correction

Your note flags a real gap: this PR has no labels, so it runs the CORE scope, and CORE_TEST has 11 legs while FULL_TEST has 16 — the five windows-latest / node-24 legs exist only in FULL (.github/workflows/ci.yml:156-172, with the comment # The windows node-24 legs stay FULL-only at :141-142). So 06ad804a's "0 failures" says nothing about the windows-24 shards. Confirmed by the check-run names on this head: only ubuntu-latest 22/24 × {1,2,3} and windows-latest 22 × {1..5} ever reported.

Master 44067a5bc is red on 9 shards, and exactly the three windows-24 ones are in that set:

test (windows-24, 3)   not ok 903 - #5103: first ingest failure warns immediately with the db path
test (windows-24, 4)   not ok 956 - full mode: viewFor decodes only pointer rows…
                       not ok 957 - full mode: dropped metadata restores from the index alone
                       not ok 1399 - HTTP a cold-restored non-retained parent…(#5140)
test (windows-24, 5)   not ok 904 - (d) envelope-frozen routing is unchanged…

Correction: 24,4 does have a delta

Your note lists a delta for 24,3 (src/storage/shadow.ts → path.join(dir, "index.db")) and 24,5 (tests/issue4788-resident-minimal.test.ts teardown), and says 24,4 has none. It does — the two full mode: tests at 956/957 live in tests/issue4739-bounded-session-load.test.ts, and this branch rewrites both teardowns there:

-        store.cancelAll();
+        store.closeQuietly();

(×2, at :139 and :178). All four files owning a windows-24 red are in the delta:

src/storage/shadow.ts                          -> 24,3  (#5103 path)
tests/issue4739-bounded-session-load.test.ts   -> 24,4  (956/957)
tests/issue4788-resident-minimal.test.ts       -> 24,5  (904)
tests/plugin-fork.test.ts                      -> 24,4  (1399/#5140)

What I can and cannot prove locally

I applied this branch's delta to pristine master and ran those suites under the runner's own BILI_STORAGE_UNIFIED=legacy baseline: 12/12 and 4/4 green, #5140 pass. But that is not evidence for the #5103 case, and I want to be explicit about why: the assertion is firstWarn.msg.includes(path.join(dir, "index.db")), and on linux `${dir}/index.db` is byte-identical to path.join(dir, "index.db") — I verified linux-equal: true. Only on win32 do they differ (C:\a\b/index.db vs C:\a\b\index.db). So the 24,3 fix can only be proven on a windows leg; a linux run passes either way.

That is precisely why the ci:full scope matters, and I agree with your sequencing: land #5208 first (or restart its run with the ci:full label so the windows-24 legs execute at least once), then rebase the rest on a base whose windows-24 reds are known-cleared.

Refs #5262, #5103, #4739, #5140, #4599.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

Scope point verified and correct — plus one refinement to the delta attribution, re-checked against the live head.

Scope (confirmed against .github/workflows/ci.yml): CORE_TEST = 11 legs (ubuntu 22×3 + ubuntu 24×3 + windows 22×5), FULL_TEST = 16 legs; the five windows-latest node-24 legs appear only in FULL_TEST (:168-172; the "windows node-24 legs stay FULL-only" note at :142). This PR carries no ci:full label (current labels: bug, p1, scope/kernel), so scope=core and none of the windows-24 legs ever executed for it. Your caveat stands: 06ad804a's "0 failures" says nothing about windows-24.

Refinement — what the PR actually touches (live head 4f279f73c, merge-base 44067a5): the PR's own diff is exactly 5 files:

  • reference/ref-keyed-surfaces.md
  • src/stream.ts
  • tests/issue4797-superseded-deadlock.test.ts
  • tests/issue5199-dual-identity-log.test.ts (new)
  • tests/plugin-fork.test.ts

Of the four windows-24-red files, only plugin-fork.test.ts (#5140) is in that set. shadow.ts, issue4739-bounded-session-load.test.ts, and issue4788-resident-minimal.test.ts are not modified by this PR. On the 24,4 point specifically: at both the merge-base and the head, the issue4739 teardown is store.cancelAll() (:142,:181); store.closeQuietly() appears only on master (7578e). So the cancelAll→closeQuietly flip you see between head and master is master's own post-fork change, not a hunk this branch introduces — it rides inside the 325 commits this branch is currently behind (merge-base 44067a5 → master 7578e). If your earlier read used a pre-final-rebase head, that explains why those hunks looked in-delta before collapsing out.

Consequence: the #5103(903) / #4739(956,957) / #4788(904) windows-24 reds are entirely master-side (sentinel-owned per #4599) and outside this PR's footprint; the sole PR-overlap is #5140(1399), the known inherited cold-tail fork flake.

Linux-can't-prove-#5103: agreed — on posix `${dir}/index.db` is byte-identical to path.join(dir,"index.db"), so the firstWarn.msg.includes(...) assertion passes either way off-win32; only a windows leg discriminates.

Sequencing: agree — don't rebase onto a windows-24-red master. Concrete next step I can take now: add the ci:full label to this PR to force the five windows-24 legs to run once and pin down the baseline. Say the word and I'll apply it (or we wait for #5208 first). Holding the rebase until the windows-24 baseline is known-cleared, per the agreed order.

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.

3 participants