Repository navigation
ci(test-leftovers): report what each test job leaves in the tree; retire Rerun Safety - #24211
Conversation
…ire Rerun Safety Ruling 乙: a report-only leftover check on the per-PR test path replaces the nightly double run. - scripts/ci/report-test-leftovers.mjs: `snapshot` lists every untracked and ignored path after the build (`git ls-files --others`, no exclude flags), `report` lists what the test run added, grouped by package directory, as a job-summary table and one warning per package. Both modes exit 0 on every path. A closed exclusion list (turbo state, turbo task logs, vitest's results cache, and the declared outputs of a build the test step runs itself, derived from turbo.json) is counted in the summary. Its --self-test runs in Lint & Repo Gates. - ci.yml: the snapshot and report steps in Test Core (k/6) and in the dogfood shards, above the attestation pair, no continue-on-error. - Rerun Safety retired: the workflow, its verdict script, its package.json alias and lint.yml step, its CARD_CADENCES entry and the self-test cases pinning it, its watched-list entry, and every reference a grep finds. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…mmary The job log lists at most 50 paths per package and counts the rest, so one package that leaves thousands of files cannot bury the others; the self-test pins it. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
check:declared-population-live read the snapshot schema id as a path declaration. The self-test reads no tracked file and the in-job modes run only inside ci.yml's test jobs, so the script now says so with the no-path-population marker, and the id no longer looks like a path. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…st-leftover-report
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对 · ACCEPT, Tier H · PR #24211 (draft, head
|
Fixes #23880
Clause-②: no
维护者速读
改了什么
Test Core的 6 个分片,以及main全量跑里的Dogfood Regression Gate3 个分片,在构建之后、测试之前给工作树拍一次快照(包括被.gitignore忽略的文件);测试结束后列出这次测试新增的每一个路径,按包目录分组,写成任务摘要里的一张表,每个包一条警告注解。脚本在任何情况下都以 0 退出,步骤上也没有continue-on-error:它不是门禁,不会让任何任务变红。Rerun Safety夜间双跑(在同一个工作树里把整个测试套件跑两遍)整体删除:工作流文件、判定脚本、package.json别名、lint 里的自测步骤、红灯卡片的登记(CARD_CADENCES条目与scheduled-full-run-card.yml的监听名单),以及其它文件里对它的引用。为什么改
裁决 乙(本卡评论
6235054403)。维护者先指出:「这个问题不是应该派卡解决吗?但测试运行时常会往工作目录里写东西:临时数据库、.objectstack/data/*.json、缓存、生成文件。写完不清理,下一次在同一个目录里跑,就会带着上次的残留开始。」席位列出甲、乙、丙三个选项后,维护者回复「同意 乙」。双跑在它被读过的三十个夜里没有抓到一次残留污染,真实的失败全是负载下的超时;它测的是"后果"(第二遍变红),从不点名文件。而
.objectstack在.gitignore里,git status根本看不见这一类残留。新报告直接点名"哪个包留下了哪个文件",每个 PR 都跑。风险与代价(含回滚)
$HOME、系统临时目录(/tmp)、数据库服务器。快照前已经存在、又被测试改写的文件,以及被测试改动的已跟踪文件(这一类git status本来就能看到),也不在报告里。main每小时全量跑里多数测试任务是回放,所以 CI 上的报告只覆盖真正执行了的包。下文的本地读数是强制全部执行后得到的。席位意见(
domain:devx2 号席位,2026-10-17T19:24Z 定稿)continue-on-error。if: always()。temporal-conformance不接报告,席位答 A。它只在全量跑里出现,测的套件 Test Core 已经覆盖。main上的报告只覆盖改动过的包。完整读数以下文的本地强制全量为准。examples/app-showcase/.objectstack/data/showcase_external.db,来自 dogfood 测试。落地后按裁决交给下一任席位开卡。你要做的
.github/workflows/**),需要os-zhuang或hotlong审批。main上第一次 CI 的报告,按包在各自的 lane 开卡(本地读数见下)。main报告干净后由你决定;本 PR 不做这件事。What the report says today: the local full-suite listing
On
015e6eab7(basecb8756b00; the later merge oforigin/mainbrought 13 commits, none touching a file in this diff). Commands: a full build (pnpm turbo run build --filter=!@objectstack/docs --concurrency=2, 76 of 76 tasks), thenreport-test-leftovers.mjs snapshot(88275 paths), then every test andtest:repotask forced to execute (pnpm turbo run test test:repo --only --force --continue --concurrency=2, underos-verify-lock, 2h48m), thenreport.examples/app-showcaseexamples/app-showcase/.objectstack/data/showcase_external.db@objectstack/dogfood#testwas the only task running. The showcase'sshowcase_externaldatasource andexternal-fixture.tsresolve.objectstack/data/showcase_external.dbagainst the process cwd.@objectstack/organizations#testwas killed by the container's memory cgroup at 16:41 (Memory cgroup out of memory: Killed process 26265 (node), exit 137), so its row is partial.@objectstack/cli#testran to the end with 3 failed files out of 464 (data-commands.absent-database.integration,hook-timeout-override-refusal,boot-preparation-parity), under a load average of about 8 to 10 on 4 cores. This diff touches the CLI by one comment line in another file, and the two unit files pass when re-run alone at076add1f6(2 passed,41 passed); the integration file was not re-run.examples/app-showcase.What this PR does
Adds a report-only leftover list to every job in
ci.ymlthat runs package tests, and retires Rerun Safety completely, per ruling 乙 (comment6235054403). No product code changes, and no test behaviour changes.scripts/ci/report-test-leftovers.mjs(new)snapshot --out FILElists every untracked path in the tree, ignored paths included, into a file under$RUNNER_TEMP(outside the tree; a path inside the tree is refused, since the report would count it).report --before FILE --label TEXTlists the tree again and prints every path the test run ADDED, grouped by the nearest workspace package directory ((root)for anything outside one): a table in$GITHUB_STEP_SUMMARY, the paths in the log (50 per package, then a count), and one::warning::annotation per package with its count and up to 5 example paths. GitHub shows 10 warnings per step, so past 10 packages the last annotation is a roll-up naming the rest; the summary always has every row.git ls-files --others -zwith no exclude flag. Without--exclude-standardgit applies no ignore rules, so ignored files are listed one by one.--exclude-standardis blind to.objectstack/;--ignored --directorycollapses an ignored directory to one entry, hiding a new file inside an existing.objectstack/data/;git status --porcelain --ignored --untracked-files=alllists the same paths but also refreshes the index and diffs tracked files. Measured on this worktree: 85066 paths in 0.4 s after install, 88275 after a full build.snapshotandreportexit 0 on findings, on bad arguments, on a missing snapshot and on a crash; a failure to measure is oneNOT MEASUREDwarning. The ci.yml steps carry nocontinue-on-error. The one failure the script cannot catch is its own source not loading; its--self-testloads it on every pull request inLint & Repo Gates, so that breakage is red there first.turbo-state(.turbo/at the root: turbo's cache and the--summarizerun summaries Test Core reads back and uploads);turbo-task-log(PKG/.turbo/turbo-TASK.log);vitest-results-cache(PKG/node_modules/.vite/vitest/, vitest 4.1.11's per-file duration and pass/fail record, read only to order the next run);own-build-output(the declared outputs of a task a test task depends on without^, derived fromturbo.jsonon every run: today@objectstack/cli#build,@objectstack/metadata#buildand@objectstack/spec#gen:migration-registry).ci.ymlTest Core (k/6):Snapshot the working tree before this shard's testssits after both dependency-closure builds and beforeRun this shard's tests;Report what this shard's tests left in the working treesits directly after the test step withif: always(), above the attestation pair.Dogfood Regression Gate (k/3)(full battery only): the same pair aroundBoot example apps and exercise real user flows.dogfood-verifyruns theobjectstack verifyCLI, not a package test suite;build-core,build-docsandconsole-pinrun no package tests;temporal-conformanceis a fork, below.Retirement
.github/workflows/rerun-safety-nightly.ymlandscripts/rerun-safety-verdict.mjs; removed thecheck:rerun-safety-verdictscript frompackage.jsonand itsRerun-safety verdict table self-teststep fromlint.yml.scripts/ci/scheduled-full-run.mjs: removed theRerun Safetyentry ofCARD_CADENCES, thecardIdentity('Rerun Safety')setup line and the three self-test cases that pinned it (its title/marker, its nightly body words, and the nightly-vs-hourly non-match). TheCIandLint & Type Checkcards are byte-identical (proof below).scheduled-full-run-card.yml:Rerun Safetyremoved fromworkflow_run.workflows; the comment keeps the rule that every watched name needs aCARD_CADENCESentry.scripts/check-stall-guard-budget.mjs: the population paragraph no longer namesrerun-safety; the census and self-test find sites from the workflow files, so no self-test case pinned it.board-snapshot.yml,docs-deploy.yml,prerelease-pin-watch.ymlandtest-nightly-tiers.yml; past tense inscripts/docs-audit/README.mdand in the comment ofpackages/cli/test/published-entry-node-env-source-reroute.test.ts(comment only).lint.ymlgainsTest-leftover report self-testinLint · CI, release & engine gates, beside the other ci-script self-tests, which is whatcheck:self-test-wiredrequires for a script CI runs.Retirement grep at
076add1f6,git grep -n -i "rerun.safety\|rerun-safety": 4 hits, 0 live. All four are history sentences in past tense, kept on purpose:packages/cli/test/published-entry-node-env-source-reroute.test.ts:112scripts/check-stall-guard-budget.mjs:193scripts/ci/report-test-leftovers.mjs:20scripts/docs-audit/README.md:902#24158 is not addressed by this diff; the seat retires it at landing, as the ruling says.
The premise, measured per job (ruling item 5)
Test Core (k/6)separates test-created paths from build outputs:Build this shard's dependency closure(--filter=PKG^...per package) andBuild the sliced package's dependency closure(--filter=PKGper slice).test test:repowith theirdependsOn. The^buildclosure is already on disk, so a replay rewrites existing paths and adds none. Three tasks without^can run after the snapshot:@objectstack/cli#build,@objectstack/metadata#buildand@objectstack/spec#gen:migration-registry, whose output exists frompnpm install. Their declared outputs are theown-build-outputrule, derived fromturbo.json.outputs: [], so a replayed test restores only its log.--summarizewrites.turbo/runs/, which the drift check, the timings capture and the artifact upload read:turbo-state.vitest.config.*enables it (git grep -n "enabled: true" -- '*vitest.config.*'gives 0 hits); coverage runs incoverage-nightly.yml.PKG/node_modules/.vite/vitest/:vitest-results-cache.Dogfood Regression Gate (k/3):Build the dogfood package's dependency closureruns before the snapshot, and@objectstack/dogfood#testdepends on^buildalone, so nothing is built after it. There is no--summarize.Measured: the own builds. On
015e6eab7, after deletingpackages/cli/distandpackages/metadata/dist: snapshot, thenpnpm turbo run build --filter=@objectstack/cli --filter=@objectstack/metadata --only --force, then report. 688 paths were added, all 688 underown-build-output, 0 counted. The build was forced, so it wrote everything it writes, not only what a replay restores.Not wired, reported as a fork:
temporal-conformance(full battery only). It runs package tests, but it interleaves four builds with four test runs: builddriver-sql...then test it; build the non-SQL backends then test them; buildmetadata-protocol...then test; buildruntime...then test. One snapshot after the first build would charge the next three builds'dist/to the tests. The ruling names the per-PR path and lists Test Core and the dogfood jobs, so this PR leaves it unwired and names it here instead of shipping a partial check. The options are in the report's open question.Proof
node scripts/ci/report-test-leftovers.mjs --self-testat076add1f6printsreport-test-leftovers --self-test: OK (47 cases over 6 batteries), exit 0. Batteries: the diff (the four dispatch cases: a new ignored.objectstack/data/x.jsonunder its package, build outputs in both snapshots not reported, a deleted path not reported, root paths as(root)); the fixed exclusions; theturbo.jsonderivation; rendering; a real throwaway git tree; and every in-job failure path exiting 0.30c07df010bcwithscripts/ablation-replace.mjs: the listing gained--exclude-standard, so it missed ignored paths (anchor 1 to 0, blob30c07df010bcto873a382177b1). Result:report-test-leftovers --self-test: 5 FAILED (of 47 cases), including "a NEW ignored file inside an ignored directory that already existed is listed". Restored: blob equals HEAD andgit diff HEADis empty. The direction was red, as expected.scheduled-full-run:--self-testprintsscheduled-full-run: self-test OKand--check-concurrencypasses for both workflows. A one-off byte comparison of the module atcb8756b00against the new one, forCIandLint & Type Check(workflowName, cadence, title, marker, labels, three body shapes), gavebyte-identical: 16 of 16, and the new module refusescardIdentity('Rerun Safety')by name.check:stall-guard-budget:OK (40 workflow file(s), 75 job(s), 832 step(s), 9 guard-wrapped step(s); ...), and its--self-testpasses with 61 assertions (62 before; the real-repository battery lost one job group and sits at its floor of 13).yaml(0 error(s), 0 warning(s)). Inci.ymlthe pair sits at steps 10 and 12 of 22 intestand 8 and 10 of 16 indogfood; the report step hasif: always()and nocontinue-on-error, and the last two steps are still the attestation pair.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat076add1f6(15 paths against merge based9a749bae) derived 105 commands. Each ran with its exit code captured before any pipe, and all 105 exited 0. They includecheck:stall-guard-budget,check:self-test-wired,check:self-test-workflow-commands,check:shard-attestation,check:workflow-status-functions,check:workflow-step-name-quoting,check:declared-population-live,check:pm-dispatch-gates(411 s) and the new self-test.--ranwith exit codes printedRun reconciliation — 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN.95219d01ehad one red.check:declared-population-liveread the snapshot schema idtest-leftovers-snapshot/1as a path declaration.015e6eab7fixes it with theno-path-populationmarker (the self-test reads no tracked file) and the idtest-leftovers-snapshot.v1.check:stall-guard-budgetruns its--self-testfirst, andscheduled-full-run.mjs --self-testis in the derived list. No*.test.*file names either script.node scripts/pm/check-governed-merges.mjs --branch claude/issue-23880-test-leftover-report: GOVERNED, landing tier H (.github/workflows/**×8), exit 3. Size: 2333 changed lines (+1008 / -1325) over 15 files.skip-changeset.NOT MEASURED
mainCI run after landing: Test Core on the hourly full run, and Dogfood on the full battery. This PR's ownTest Coreshards run the new steps for the affected set, so their summaries are a first reading.Acceptance notes
CADENCE_WORDSinscheduled-full-run.mjskeeps itsnightlyvocabulary, with no watched workflow mapped to it after this PR. The dispatch scoped the removal to theCARD_CADENCESentry and the self-test cases pinning it. A later nightly card can reuse it, or a later edit can drop it. Carrier: none.scheduled-full-runidentity battery keeps its floor of 34 with 36 cases left. The stall-guard real-repository battery is now exactly at its floor.test-nightly-tiers.ymlandcoverage-nightly.ymlalso run package tests, outsideci.yml. The ruling scopes the report toci.yml.Generated by Claude Code