Skip to content

chore(ci): nightly rerun-safety gate, job timeouts, compiled-tests-in-dist guard - #4156

Merged
os-zhuang merged 1 commit into
mainfrom
claude/inmemory-to-sqlite-memory-5f7n9e
Jul 30, 2026
Merged

chore(ci): nightly rerun-safety gate, job timeouts, compiled-tests-in-dist guard#4156
os-zhuang merged 1 commit into
mainfrom
claude/inmemory-to-sqlite-memory-5f7n9e

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4100#4065)的后续。三处 CI 改动,全是那次排查换来的教训。只动 CI 配置,不碰任何包。

1. Nightly rerun-safety —— 补上 CI 的结构性盲区

这个仓库每个 job 都跑在 fresh clone 上,这让 CI 在结构上不可能看见一类缺陷:污染自己工作树、因而只能通过一次的套件。CI 永远在跑第 1 遍,所以永远是绿的。

#4065 正是如此。InMemoryDriver 默认 persistence: 'auto',在 Node 下就是文件适配器——于是一个用固定 id 播种两行的套件把它们写进 .objectstack/data/memory-driver.json,下一次运行先载回来再断言四行,第 N 次读到 2N 行。它带着这个缺陷通过了历史上每一次 CI,最后是有人在做不相干的工作时在同一个 checkout 里跑了第二遍才浮出来——而且当时看起来像是他自己的改动把东西弄坏了。

新 job 在同一棵树里把全量套件跑两遍,第二遍与第一遍不一致就失败。

  • 为什么要 --force:不加的话 turbo 第二遍会直接回放缓存、什么都不执行就报绿——恰好是这里最不想要的答案。
  • 两遍之间会打印遗留的 .objectstack/ 目录(仅信息,不判定)。这样一次失败给出的是一个具体文件,而不是「疑似 flaky」。其中有些是合法的(example app 自己的项目目录),所以由第二遍来做断言,这一步只做报告。

2. 给 ci.yml 八个 job 补 timeout-minutes

原本一个都没有grep -c timeout-minutes = 0),全部继承 GitHub 的 6 小时默认值。

就在 #4100 上,Test Core 出现过 80 分钟无输出——若不干预会占住 runner 六小时,而且整个过程 PR 显示的是「还在跑」而不是「坏了」,这是一个门禁最糟糕的失败形态。(那一次事后证明是 API 状态陈旧、job 其实已完成,但 6 小时无上限这件事本身仍然要修。)

上限取健康耗时的 3–4 倍,真正偏慢的运行仍能通过:

job timeout 观察到的健康耗时
filter 10 ~8s
test 45 ~10.5min
temporal-conformance 30 ~3min
dogfood 45 ~11min
dogfood-gate 10 ~2s
build-core 30 ~5.5min
build-docs 30 ~2.5min
check-generated 20 ~47s

3. 禁止 dist 里出现编译后的测试

用裸 tsc 构建且没排除测试的包会产出 dist/**/*.test.js。两个代价,第二个才是危险的那个

  1. files: ["dist"] 会把测试发布到 npm
  2. 没有 vitest 配置的包会把这些编译副本和源码一起收集,于是每个 src/**/*.test.ts 又以一份停留在上次构建时刻dist/**/*.test.js 再跑一遍。

这会静默地让改动失效:对源码测试的修复看起来「没生效」,因为跑的仍是修复前的编译副本——#4100 里我就被这个骗了一轮,改对了、单文件验证通过、全量仍红。它同样允许源码测试被改成通过、而陈旧副本继续断言旧行为,两边都不显眼。

@objectstack/cli 当时就是这样(81 个测试文件 / 849 个用例,而源码只有 58 / 581),已在 #4100 修掉。仓库其余部分用 tsup 构建,只产出声明的入口——所以这道门是为了挡住下一个 tsc 构建的包,不是复查已修好的那个

验证

  • 两个 workflow 文件都能通过 YAML 解析;提取出的每个 run: 块都是合法 bash
  • dist 守卫在正确构建的树上报告干净,在植入一个 dist/**/*.test.js确实捕获(非空洞断言)

关联


Generated by Claude Code

…-dist guard

Three CI changes, all lessons #4065 taught the hard way. CI configuration only.

1. Nightly rerun-safety gate. Every job in this repo runs on a fresh clone,
   which makes CI structurally incapable of seeing a suite that pollutes its own
   working tree and therefore passes exactly once — CI always runs pass #1, so
   it is always green. #4065 sat in the repo through every CI run it ever had
   and surfaced only because somebody ran the full suite twice in one checkout
   while doing unrelated work, where it looked like THEIR change had broken
   something. The new job runs the full suite twice in one tree with `--force`
   (turbo would otherwise replay the cache and report green without executing
   anything) and fails if pass 2 disagrees with pass 1. It also prints any
   `.objectstack/` directories left between passes, so a failure names a file
   instead of reading as flakiness.

2. `timeout-minutes` on all eight ci.yml jobs. There were none, so every job
   inherited GitHub's 6-hour default. On PR #4100 the Test Core job hung with no
   output for 80 minutes and would have held a runner for six hours — and the
   whole time the PR read as "still running" rather than broken, which is the
   worst failure mode a gate can have. Ceilings are ~3-4x the healthy observed
   duration, so a genuinely slow run still passes.

3. A build-output guard against compiled test files. A package built with plain
   `tsc` that does not exclude tests emits `dist/**/*.test.js`. `files: ["dist"]`
   then publishes them to npm — and, worse, a package with no vitest config
   COLLECTS those compiled copies alongside its sources, so every
   `src/**/*.test.ts` also runs as a stale `dist/**/*.test.js` frozen at the last
   build. @objectstack/cli shipped exactly that (81 test files / 849 tests where
   its sources hold 58 / 581) until #4065 excluded them. That silently defeats
   edits: a fix to a source test appears not to work because the run is still
   executing the pre-fix duplicate. Everything else builds with tsup, which emits
   only declared entry points — so this gate exists to stop the NEXT tsc-built
   package repeating it, not to re-check the one already fixed.

Verified: both workflow files parse as YAML and every extracted `run:` block is
valid bash; the dist guard reports clean on a correctly-built tree and catches a
planted `dist/**/*.test.js`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5q3BZMpdiAueHf2emhDY9
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 12:55pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd tooling size/m labels Jul 30, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 30, 2026 13:09
@os-zhuang
os-zhuang merged commit a9801af into main Jul 30, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/inmemory-to-sqlite-memory-5f7n9e branch July 30, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants