Repository navigation
ci(refresh lane): each refresh diffs its window against the previous dataset, density-guarded, and lists test files over 10 s; the lane's seat files the cards - #23066
Conversation
…against the previous dataset Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
…inding, a code rise is Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
…ide its run summaries Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
…per run and the files over budget, and the lane's header says who files what Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
…d brake; the generator declares no path population Under ruling E the suite-duration ceiling reports on every event, so the refresh PR body and the generator's comment no longer call a window run over its held ceiling a brake that fired on main. The capture schema constant is joined rather than quoted, and the script carries a no-path-population marker, so the dispatch derivation does not read it as a dead population. Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB Co-authored-by: Claude <noreply@anthropic.com>
维护者速读(终稿)· PR #23066 · 席位已对照 diff 逐行校正,2026-10-12T15:39Z改了什么:每周一的测试耗时刷新 PR(机器人开的那张)正文最上面多出一节「回归读数」:比上一份数据集( 为什么改:#22949 裁决 E 第 2、3 项(「22949 同意」)。墙钟上限改成只报告之后,回归要有新的落点:同一个包在安静和繁忙小时的耗时差 2.5 倍,直接比数字会把「整台机器更忙」当成「代码变慢」;密度标签和「占比不变就不算」这道闸门就是用来分辨两者的。席位用 head 的代码回放了 main 上最近两份数据集:12 个包上升、33 个下降、0 个会立卡(两份都没有密度,判为基线), 风险与代价(含回滚):不改任何门禁、公式、触发条件和写路径,不会让任何构建变红;数据集多出的 席位意见:建议批准。这是裁决 E 的第 2、3 项,diff 逐条对得上;本 PR 自己的 你要做的:在 PR 上 Approve;席位等 |
Fixes #23059
Clause-②: no
Items 2 and 3 of ruling E on #22949 (the maintainer's word 「22949 同意」, comment 6125775640): regression detection moves from the wall ceiling to the refresh lane. Item 1, both timing steps report-only, is #23058, in flight; this PR touches none of its files.
What changed
scripts/measure-test-shard-timings.mjsdensityfield beside the weights:runs(each run's count of EXECUTED packages; cache replays, failed suites and incomplete slice sets do not count, and a run that executed nothing carries no tag) andshares(each measured package's median, over the runs that executed it, of its seconds over that run's executed total). No reader of the dataset changes: the partitioner, the drift step, the ceiling check and the timing table readpackages/ceilingsonly.--diff: the refresh PR's regression section, from the previous dataset, the refreshed one and the window's per-shard captures. Rises of 1.25x or more with both numbers, the share before and after, the slowest file and a verdict; downs in one line; every test file over 10 s with its package and slowest run; the packages with no per-file reading and why; and a "What the seat landing this PR does" section with each finding'sProtects:line ready to paste. Exit 0 on every reading; non-zero only on a refusal (an unknown flag, a malformed generateddensity).RISE_FACTOR=DENSITY_COMPARABLE_FACTOR=CEILING_HEADROOM(1.25);FILE_BUDGET_SECONDS= 10.scripts/ci/select-shard-timings-run.mjs:runIsEligiblealso returns each run'stest-core-timings-N-of-6capture ids (timing_artifact_idson--candidates). Not an eligibility input: the capture step inci.ymlis report-only, so a run is a complete measurement without one..github/workflows/shard-timings-refresh.yml: a header section, REGRESSION DETECTION LIVES HERE, AND WHO FILES WHAT; a new step that downloads the window's per-file captures for the body and cannot fail the job; the compose step runs--diffagainstgit show HEAD:scripts/test-shard-timings.jsonand puts the section at the top of the body (a refusal's text goes in its place and the PR still opens); and the over-ceiling sentence no longer says "the brake fired on main" (see Deviations).Not touched: the ceiling formula,
CEILING_FLOOR_SECONDS,RULED_CEILING_RAISES, the two timing steps,ci.yml,lint.yml, the workflow's triggers and its write path.The rules, as numbers
speclands executes nearly everything, the rest replay most of it): the median of a bimodal set jumps from one mode to the other, while the mean moves with the mix, which is what the contention behind a weight follows.density only, not a finding.finding(a card) ·density only·provisional(either weight rests on fewer than 3 executed runs) ·no prior share(the previous window replayed it) ·baseline(no comparable density reading on both sides).density. Its body says the diff is a BASELINE: the rises are listed as readings, none is filed, and its window is the reference the next comparable window is judged against. The runs it takes are the runs that refresh accumulates (newest first, until every package is executed 3 times), each tagged in the body, and they are taken because they are the first runs a density can be read from at all.Premises, checked against origin/main (0cbfb7c)
mainand already reads the previous dataset withgit show HEAD:(the bins step has copied the refreshed file over the working copy by then). The diff reads it the same way, with no extra fetch.carriedOver, ceilings, note, packages, provenance, provisional, secondsPerTestFileFallback, skippedAsCached, skippedIncompleteSlices, uncapped;provenance.runsnames the runs and nothing records a package per run. So the generator records it from this refresh on, as H2 anticipated, and the first diff can tag only its own window's runs.Slowest file:is printed bycheck-test-suite-ceilings.mjsfrom the per-shard capturesreport-test-timings.mjs --captureuploads astest-core-timings-N-of-6. Those come from the same runs the refresh reads and are retained exactly as long as the run summaries (measured on run 38195222269: all six captures carry the sameexpires_atday as its six run summaries). So the per-file list is still a read of an existing measurement, from those captures. Packages without a per-file reading are named in the body with the reason: carried (every run of the window replayed it) or executed with no vitest file line captured. A run whose captures did not arrive is named too.echoblock; the section is onecatof the diff's output. Lists that grow with the workspace are capped: non-finding rises at 25 rows, files at 100, each with a count and the full list printed to the job log. Findings are never capped.Replay: what the diff says over the last two datasets on main
--diff --priorthe #22368 dataset (0401847, measured 2026-10-08, 21 runs)--refreshedthe #22923 dataset (1bd612b, measured 2026-10-12, 12 runs, the first with ceilings). Stderr:12 rise(s) of 1.25x or more, 0 finding(s) (BASELINE: none judged); 33 down; files over budget NOT MEASURED.@objectstack/trigger-api@objectstack/example-todo@objectstack/example-showcase@objectstack/service-realtime@objectstack/service-cluster-redis@objectstack/plugin-auth@objectstack/organizations@objectstack/metadata-core@objectstack/plugin-dev@objectstack/plugin-pinyin-search@objectstack/connector-slack@objectstack/service-settingsDown: 33 lighter, 12 of them to 80 % or less of the previous weight (
cloud-connection153.64 to 81.68 s,service-analytics401.06 to 220.50 s,formula,client,connector-rest,sdui-parser,service-cluster,driver-sqlite-wasm,plugin-hono-server,example-crm, +2). Not compared: 0 carried, 0 new, 0 gone.Read it as: neither dataset carries a
densityreading, so the replay is a BASELINE by construction and none of the 12 rises would have been a card. Twelve packages up and twelve down by the same factor across a 4-day gap is consistent with the density problem the ruling diagnosed (cloud-connection,service-analyticsandclient, three of the four packages the hourly reds named, are among the downs), and without a tag there is no telling load from code in either list. The first live diff after this lands differs in two ways: its refreshed dataset carriesdensity, so the reason reads "the previous dataset carries no density reading" (the self-test pins that text), and its files list is read from that window's captures. Density and files for these two windows: NOT MEASURED. The #22368 window's artifacts are past their 1-day retention; #22923's captures are still retained, but this container cannot download an artifact (measured:GET /actions/artifacts/11723746884/zipredirects to the blob host, which answers 403, as the workflow header records).Evidence, on head 72443a3
node scripts/measure-test-shard-timings.mjs --self-test→self-test OK, exit 0. New batteryrefresh diff and per-file budget (#22949 E), floor 22 = cases registered (a floor of 23 reds, measured); roster floor 4 → 5. The two acceptance fixtures come first: a density-only rise (a fifth package executes, 4 to 5 per run, every suite 1.3x slower,aup 1.3x with its share down to 0.81x →density only) and a code rise (same density,aalone 2.5x, share 2.17x →finding).node scripts/ci/select-shard-timings-run.mjs --self-test→self-test OK, exit 0. +2 cases in its first battery (floor 35 → 37 = registered) and a new batteryshard-timings-refresh per-file captures (#22949 E)(floor 5; roster floor 5 → 6). It drives the new download step's ownrun:block underbash -ewith stubcurl/unzip(one capture fails to download, one run retained none) and then the real--diffover what landed.scripts/ablation-replace.mjs, each restored togit diff HEADempty:else verdict = finding): generator self-test exit 1,diff: a rise explained by density alone was read as finding (findings 4).exit 7on a failed capture: selector self-test exit 1,captures step: a missing capture failed the step (exit 7). The first attempt at this one was a NO-OP, refused by the tool (its replacement contained its anchor, so the anchor count did not move); rerun with a disjoint anchor.actionlint1.7.7 on the workflow: exit 0. Control leg: a copy with a stepif:reading an undefined step id is reported (property "nosuch" is not defined). Noshellcheckon this box, so the step scripts were not shellchecked: NOT MEASURED.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; derived from the merge base): 51 commands, all run on 72443a3, all exit 0;--ranreconciliation: 51 derived, 51 run, 0 unrun.check:declared-population-livewas red on an earlier head (the capture schema constant was a quoted, path-shaped module-scope literal that reached no tracked file) and is green after the fix described under Deviations.mainon its own schedule; the first scheduled refresh after merge is the measurement. The closest live reading is this PR's ownShard Timings Refreshcheck: onpull_requestit runs the real selection, regeneration, capture download and--diffagainst main's dataset, and renders the would-be body (BASELINE, with real density tags and real files over budget) into its step summary. A job summary has no REST endpoint, so that reading is in the web UI, and the diff's one-line verdict and the captures read are in the job log.Deviations
scheduleincluded — the per-package wall ceiling and the shard drift stay readings, never a red (#22949 ruling E, item 1) #23058 dev): "a reading, not a red -- under ci: merge-queueTest Corereds on the new suite-duration ceiling —@objectstack/cloud-connectionmeasured 177.5 s and 200.3 s vs its 163.89 s ceiling, in two groups of PRs that do not touch it (live since1bd612bd6) #22949 ruling E the ceiling reports on every event, and a regression is judged by the diff at the top of this body". The generator's matching code comment is reworded the same way.scripts/measure-test-shard-timings.mjsnow carries adispatch-gates: no-path-populationmarker, and the capture schema is spelled['test-timing-capture', 1].join('/'), with a comment saying why, which is the remedycheck:declared-population-liveitself prescribes for a gate that reads no tracked path. The family is still placed by its own file (re-derived:matched via scripts/measure-test-shard-timings.mjs).Acceptance notes
Test Core"grades every executed package" against the ceilings. Under ruling E it reports; I left it because it is not on this card's lines and it describes ci(timing gates): the two timing steps report-only on every event,scheduleincluded — the per-package wall ceiling and the shard drift stay readings, never a red (#22949 ruling E, item 1) #23058's step. Carrier: none.维护者速读(草稿)
改了什么
每周的测试耗时刷新 PR(机器人开的那张)从此在正文最上面给出三张清单:比上一份数据集慢了 25% 以上的包(两个数字、最慢的测试文件、判定)、本窗口每次运行实际执行了多少个包(密度)、以及单个耗时超过 10 秒的测试文件。数据集里新增一个
density字段记录密度。工作流头注释写明:机器人不立卡,落地这张刷新 PR 的席位按清单立卡,永远不让构建变红。为什么改
裁定 E 已把墙钟上限改成只报告,回归检测需要一个新的落点。同一个包在空闲小时和繁忙小时的耗时能差 2.5 倍,所以只比数字会把"整台机器更忙"误判成"代码变慢";密度标签和"占比不变就不算"这道闸门就是用来区分这两者的。
风险与代价(含回滚)
不改任何门禁、公式、触发条件和写路径,不会让任何构建变红。代价是每次刷新多下载每次运行的 6 个小文件(各约 15 KB),下载失败只会在正文里点名,不会丢掉这次刷新。合并后的第一次刷新只能当基线(上一份数据集没有密度),从第二次可比较的刷新起才会出现需要立卡的条目。回滚:还原这一个 PR 即可;数据集里多出的
density字段没有任何读者,留着无害。席位意见
你要做的
批准这张 PR(工作流改动按 Tier H 处理,保持草稿)。合并后看第一次周一刷新 PR 的正文是否带着三张清单和"这是基线"的说明。
Generated by Claude Code