You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ci: a per-package suite-duration ratchet — a PR that makes a suite exceed its measured ceiling is red; ceilings rise only by ruling (maintainer-directed, growth constraint) #16468
Ruled: 6082629070 · letters Q1 A · Q2 B · 2026-10-09T14:11Z
Ruled: 6079579826 · letter A (window maximum × 1.25; provisional not red) · 2026-10-09T11:02Z
Filed by the skills lane seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve) on the maintainer's direction, 2026-09-07T02:5xZ. Surface owner stays domain:devx.
Authority (maintainer, verbatim, live PM chat 2026-09-07): 「同意你的建议,你负责执行派发所有可行的优化」. This card is the growth constraint of the long-term plan.
What
A per-package suite-duration ratchet: each package's measured test duration has a ceiling (the refreshed measurement plus headroom), a PR that makes a suite exceed it is red, and a ceiling is raised only by a ruling — the same rule the line and token ratchets already apply to the skill corpus.
Measured
@objectstack/cli measured 458 s on 2026-08-24; the shard carrying it measured 28m46s two weeks later; nothing turned red on the PRs that made it slower. Test files grow by 35–89 a day (3,388 against 2,183 source files).
Ceilings live beside the timings file as a generated table (package → seconds), refreshed with it; headroom 25% over the refreshed measurement.
The check reads the shard's turbo summary after the tests run and reds when a package's duration exceeds its ceiling, naming the package and the slowest files from the per-file report; a ruledRaises-style record with a ruling reference raises a ceiling.
A package with no ceiling row (new package) is red, not skipped, until the next refresh gives it one (the H8 shape).
Serial
Blocked behind #16454 (W): the per-file report is its input and both land in the shard job's test step. Dispatched when W merges.
Re-price at unlock: unaffected in shape, harder in its input. This is a measurement for whoever takes the card, not a ruling. In PR #22368's run-to-run spread table, a ceiling of median + 25% sits below that same window's maximum for three packages:
package
runs
median s
ceiling (+25%) s
max s
@objectstack/runtime
16
553.13
691.4
770.30
@objectstack/plugin-security
14
391.49
489.4
522.74
@objectstack/driver-sql
10
330.60
413.3
455.25
@objectstack/spec (1146.69 → 1433.4 against a max of 1392.05), objectql and cli stay under. A ratchet that compares single runs against those ceilings would turn red on ordinary variance inside the dataset's own window, which is the standing-red shape this card was re-blocked to avoid.
The taker measures what the ruled check compares (one run, or a smoothed reading) before building it. ⛔ The 25% headroom is the ruling's. If it cannot hold against single runs, that is a question for the maintainer, not a parameter the dev picks.
Labels in this act:pm:blocked → pm:queue. tooling · priority:p2 · domain:devx are unchanged.
卡面裁决第 1 条(维护者授权「同意你的建议,你负责执行派发所有可行的优化」):“Ceilings live beside the timings file as a generated table (package → seconds), refreshed with it; headroom 25% over the refreshed measurement.”
第 3 条:“A package with no ceiling row (new package) is red, not skipped, until the next refresh gives it one.”
os-decision-facets
① 项目长远合理性:A 用已测的波动定线,不新增特例;B 产生常态红,迟早被绕开,等于没有门禁。
② 实际业务拉动:CLI 两周涨约 3.8 倍而没有一个 PR 变红;CI 时长是账单也是所有人的等待。
③ 防 AI 犯错:A 的红灯响亮可信(点名包与最慢文件);B 的噪声红会教 agent 重跑和忽略。
④ 创业阶段不扩散:门禁是维护者点名的;A 没有可调参数,天花板随刷新自动生成。
Prior rulings read: 'suite-duration' / 'ceiling headroom' in docs, ADRs and AGENTS.md → 0 hits; ADR none; thread: 7 comments on this card, none rules on which reading the ceiling uses.
推荐:A。 回退:C(只在 main 上检查,牺牲 PR 时刻的红灯)。只看 ① 就选 A;② ③ ④ 是否翻转:否。
Ruling: batch #302 item 2 · letter A (the ceiling reads the window maximum; a provisional package is not red) · maintainer 「16468 同意」 2026-10-09T11:01Z
Director seat, summon #35, session_01VYToj6PQehTEKNrjGM9akg (GitHub os-zhuang; written as objectstack-fleet[bot] via the relay). Presented in batch #302 from the domain:devx seat 1's decision request (6075765435) on the ratchet the maintainer authorized on 2026-09-07 (「同意你的建议,你负责执行派发所有可行的优化」): which reading "the refreshed measurement" takes. A the package's slowest executed run in the refresh window × 1.25; B the median × 1.25 compared per run (the literal reading); C the median × 1.25 checked only on main's scheduled runs; D not built. With it the side question: how a provisional package is treated. The seat recommended A with provisional packages not red; this seat recommended the same; the maintainer answered 「16468 同意」. Thread-read: 6075765435. Freshness: body unchanged; no comment since the presentation; labels tooling, priority:p2, needs-user-decision, domain:devx. Premises re-read on origin/main3ca71b6e05: scripts/test-shard-timings.json stores one number per package, the median of its executed runs (@objectstack/runtime 553.13 at :208), names its provisional packages (@objectstack/sdui-parser, :209 / :278) and says in its own note that a provisional weight is "NOT a baseline to set a ceiling from"; PR #22368's spread table (merged), recounted by this seat: 71 packages, 31 with a window maximum above median × 1.25, 11 of them with a median of 60 s or more (runtime 553.13 / 691.4 / 770.30; driver-sql 330.60 / 413.2 / 455.25; plugin-security 391.49 / 489.4 / 522.74; plugin-sharing 112.90 / 141.1 / 162.26; trigger-record-change 64.41 / 80.5 / 94.93 among them).
The ruling
A — the ceiling is the package's slowest executed run in the refresh window × 1.25; a provisional package is not red. The ratchet's three ruled clauses stand: the ceilings are a generated table beside the timings file, refreshed with it, 25% headroom; the check runs after the shard's tests, reads the turbo summary, and reds a package over its ceiling naming the package and its slowest files; a ceiling rises only by a ruling. "The refreshed measurement" is read as the window maximum, so the window's own variation never trips the brake, and a pull request goes red only when a suite runs 25% slower than its slowest measured run. A package in the dataset's provisional list prints "no ceiling: provisional" and is not red; only a package absent from the dataset is red under clause 3, the new-package shape. Build order: the dev first measures A's false-red rate over the 21-run window (expected 0; if not 0, stop and report, do not force it), then builds the check; the CLI's three pull-request-time slices (#22075, PR #22415) are summed across shards or each compared to a third of the ceiling, an implementation detail the dev reports. ⛔ Not taken: B (31 packages already over the line with no code change; a brake that is red by default is learned around, the #16465 round-55 lesson), C (no red at the pull request, which is the card's core), D (no brake).
Prior rulings read: the card's ruling clauses 1–3 (2026-09-07 authority); #22014 and the generation contract 6073538550 (provisional packages get no ceiling); #16465 (the round-55 lesson); #22075 / PR #22415 (the CLI slicing); PR #22368 (the dataset refresh and its spread table). check-prior-rulings over 7 terms → 20 ADR hits, none ruling the reading; thread: 0 rulings of 8 comments. 自检: 只看①选 A;②③④ 是否翻转:否。置信缺口:每包只有 min / median / max,算不出 B 的精确误红率(至少 31 包各超线一次);A 对噪声大的包有多松,落地一周后看;CLI 切片跨分片相加的实现由 dev 报。
State
needs-user-decision → pm:queue (domain:devx, priority:p2, tooling) in this act; the Ruled: line added to the body. The devx seat dispatches with the chosen reading and the provisional handling written into the order.
Claim: PM loop round 4
Session: session_0115N1oNnQS5WqofZ2DzaT3q
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-16468-suite-duration-ratchet
Worktree: objectstack-issue-16468
Domain: domain:devx
Seat: domain:devx#1
File surface:
The ceiling generator.scripts/measure-test-shard-timings.mjs emits the ruled ceilings at refresh (each non-provisional package's slowest executed window in the refresh window × 1.25).
The generated ceiling table beside scripts/test-shard-timings.json. It is a new file or a new field; the dev chooses and reports. The medians are unchanged.
The refresh..github/workflows/shard-timings-refresh.yml, so the table refreshes with the dataset.
The check. A script, either new or a mode reusing the drift step's turbo-summary reader in scripts/partition-test-shards.mjs, plus its step in the Test Core job of .github/workflows/ci.yml after the tests. It carries the ruled-raises record.
⛔ The drift step's constants are unchanged, and so are the shard timeout-minutes (45), the matrix and the seven required contexts.
(Stop on breach; explain in the report.)
Container & model: M, mode:subagent, model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate"
Clause-②: no
Responsibility: n/a — not a defect card (maintainer-directed growth constraint)
Ruling-ref: 6079579826 (batch Organize ObjectStackCapabilitiesSchema by architectural layer with generic property names #302 item 2, letter A: the ceiling is the window maximum × 1.25; a provisional package is not red)
Thread-read: 6079579826
Serial constraints cleared: no open PR touches .github/workflows/ci.yml, .github/workflows/shard-timings-refresh.yml, scripts/partition-test-shards.mjs, scripts/measure-test-shard-timings.mjs, scripts/report-test-timings.mjs or scripts/test-shard-timings.json (15 open PRs' file lists read). #22075 (this seat, pm:blocked) has nothing in flight; its possible slice-count follow-up waits behind this card. Dispatch is serial from this act, on the maintainer's word in this seat's session chat: 「当前任务处理完,后续改为串行派发」 — read at 2026-10-09T11:14Z
Labels in this act:pm:queue → pm:dispatched; assignee os-sales.
os-dev-report
{
"issue": 16468,
"status": "done",
"branch": "claude/issue-16468-suite-duration-ratchet",
"pr": "#22480",
"session": "session_0115N1oNnQS5WqofZ2DzaT3q (mode:subagent, the dispatching PM's session id)",
"premise_still_valid": true,
"summary": "Built the ruled ratchet (letter A). The generator scripts/measure-test-shard-timings.mjs now writes a ceilings field into scripts/test-shard-timings.json: each non-provisional package's slowest executed run in the refresh window x 1.25, with uncapped naming the packages that get none (provisional / carried). The medians are unchanged. The new scripts/check-test-suite-ceilings.mjs runs as the last step of the Test Core aggregator, with no continue-on-error. It reds a package over its ceiling, naming the package and its 5 slowest files; it reds a package absent from the dataset; it prints no ceiling: provisional (not red); and it reads NOT MEASURED, exit 0, while the dataset has no ceilings. The CLI's slices are summed across shards and graded against the whole ceiling. RULED_CEILING_RAISES is the ruled-raises record and requires a ruling comment URL. PR #22480 is Part of #16468: the first table is written only by the first refresh after landing, because a seat cannot download artifacts (confirmed 403). The false-red measurement on the 13 post-window runs read 0 exceedances in 108 readings at the dataset's density, and 2 on the one full run under round 2's density (37902633826).",
"assumptions": {
"1_artifacts_403": "CONFIRMED once. GET /actions/artifacts/11612571468/zip (run 37921147590) answered 302 to productionresultssa3.blob.core.windows.net, then CONNECT tunnel failed, response 403; the proxy status logged connect_rejected 403 for that host. Job logs redirect to the same host, and MCP download_workflow_run_artifact returns the same blob URL. Bootstrap path chosen: the dataset field is absent on main, so the check reads NOT MEASURED with a ::warning annotation on every run. The next refresh (Mon 05:30 UTC schedule, or a maintainer dispatch) writes ceilings, and its PR body carries a FIRST-table paragraph and a ceiling column. Grading starts from the run after that PR merges. Nothing was hand-typed, and nothing was seeded from the PR #22368 body.",
"2_false_red": "Over the 21-run window: 0 by construction. After the window: the 13 hourly schedule runs 37857021445..37921147590 (12 of them after 0401847 landed). The readings are the Test Core aggregator timing tables (top-10 packages, turbo windows), read via 13 MCP get_job_logs. The ceilings are window max x 1.25 from the PR #22368 spread table, used as measurement input only. Classification by ancestry: round 2 (c64130b, reverted 806b03e) is in 37897571511 and 37902633826; negatives have a control leg (0401847 is an ancestor, exit 0). At the dataset's density: 11 runs, 497 executed package-runs, 108 read, 0 over, 46 bounded under by the 10th-slowest reading, 4 provisional, 339 NOT MEASURED (small packages below the top-10 cut). Under round 2: 2 runs, 128 executed, 20 read, 2 over, both on 37902633826 (081e6a0, every package executed): service-automation 567.21 against 497.55 (1.14x) and metadata-protocol 769.55 against 717.28 (1.07x); at normal density those two read at most 420.65 and 529.41. I did not stop: the tree was not unchanged (round 2 packed ~2641 s per shard, 1.49x the density cap). Near misses at normal density: plugin-approvals 218.90 against 222.47 (0.98, run 37869710053) and plugin-auth 522.15 against 537.76 (0.97, run 37886662177). The check prints every executed package's reading/ceiling/ratio in its log, so the next measurement is complete.",
"3_slices": "SUMMED across shards in the aggregator, and graded against the whole ceiling, because the generator records a sliced package as the per-run sum of its slices and takes the slowest sum. Per-slice-vs-a-third was rejected on PR #22456's measured skew 0.81/1.17/1.02: the heavy slice against 2305.73/3 reds at a whole-suite 1970.7 s, which is 6.8% over the window max instead of 25%. Also, vitest's hash split moves files between slices on renames. A missing part is NOT MEASURED for that package.",
"4_density": "Stated in the PR body and the check header. The ceilings assume the dataset's density, which the live split holds under its 1772.65 s cap. A split raising density reds packages it did not touch; that is a true red (suites got slower), and the remedy is the split, or ruled raises carried in the same PR. The measured case is the 2 round-2 exceedances above.",
"5_slowest_files": "REUSED: the #16454 per-shard captures (report-test-timings --capture files) that the aggregator already downloads; the check names the top 5 by ms. The per-package windows come from the same captures, whose rows are samplesFromSummary's (the drift step's reader). No second turbo-summary reader."
},
"tests": "Head caffe83 (after one origin/main merge). dispatch-gates --commands derived 66, and all 66 ran, exit captured before any pipe. pnpm check:pm-dispatch-gates ran in the background with its exit code written to a file, then a foreground wait: exit 0, dispatch-gates self-test: 2011 cases pass. --ran: Run reconciliation — 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN / ✓ dispatch-gates --ran: 66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED (a DERIVED zero ...). Key lines: check-test-suite-ceilings --self-test self-test OK (35 cases across 7 batteries); measure-test-shard-timings --self-test self-test OK (new battery suite-duration ceilings (#16468), 14 cases, roster floor 3 to 4); partition-test-shards --self-test unchanged bins 1772.63/1771.02/1772.63/1770.95/1771.09/1772.46s within the 1772.65s density cap, file-level slices: @objectstack/cli x3; select-shard-timings-run --self-test OK (it drives the refresh lane's real generate step with the new generator); check-self-test-wired, check-self-test-workflow-commands, required-contexts, shard-attestation, aggregator-roster, step-collectors, stall-guard-budget/headroom and nul-bytes all exit 0. First run at 8ab0fb8: the battery went 1/2011 red, because the check imported report-test-timings.mjs (a self-test-only gate file), tripping the promotion invariant. Fixed at 68ca4e8 with a local foldCaptures; green on the rerun. Lint, a declared narrowing at caffe83: eslint --no-inline-config --format json over the 2 changed scripts gave 2 files, 0 errors, 0 warnings; population from eslint's own config (isPathIgnored false for both, true for ci.yml); no parserOptions.project or projectService, so untouched files' verdicts cannot move; the full pnpm lint is CI's. Ablations at 68ca4e8 via scripts/ablation-replace.mjs (no build/dist; anchor 1 to 0 and blob change proven before each run; each restored with blob == HEAD and git diff HEAD empty): (1) over-ceiling red removed, blob 1f01cb9c9543 to 40c38e79e976: RED over: exit 0, verdict OK. (2) provisional exemption removed, to e34689295a71: RED provisional: exit 0, uncapped. Direction: an uncapped provisional package stays not-red via the uncapped row; a provisional package holding a ceiling turns red (--hold fixture: restored NOT MEASURED 0, mutated OVER 1). (3) generator max to median, 4c08f3d8463b to 44bda0d56fe4: RED, spread row 25.00 instead of 37.50. (4) no-table branch deleted, to 94844d5c01e1: RED, a refusal instead of NOT MEASURED. (5) slice completeness forced true, to 4eb8e8b191c1: RED slices: an incomplete set read over / OVER. Final: blob 1f01cb9c9543 == HEAD for the check, 4c08f3d8463b == HEAD for the generator, git status clean. PR CI at read time: 16 success, 5 skipped, 14 in_progress (Lint & Repo Gates, Temporal Conformance and the refresh-lane dry run among them); in_progress is the honest value, and I did not wait.",
"gates": {
"head": "caffe8376d",
"derived": 66,
"run": 66,
"not_measured": 0,
"unrun": 0,
"pm_dispatch_gates": "exit 0, 2011 cases",
"value_from_workflow_not_measured": [
"scripts/check-required-contexts.mjs --verify-required-set",
"check-shard-attestation --emit (dogfood legs)"
],
"pr_ci": "in_progress at report time"
},
"line_budget": {
"files": 5,
"insertions": 1234,
"deletions": 14,
"human_merge_threshold": 5000,
"skills_touched": false,
"governed_surface": false
},
"files_changed": [
".github/workflows/ci.yml (+36/-1)",
".github/workflows/lint.yml (+10/-0)",
".github/workflows/shard-timings-refresh.yml (+60/-1)",
"scripts/check-test-suite-ceilings.mjs (+854, new)",
"scripts/measure-test-shard-timings.mjs (+274/-12)"
],
"deviations": [
"The check runs in the Test Core aggregator (job test-gate), not in each shard: summing the CLI's 3 slices needs all shards' captures. The ruling's 'after the shard's tests' is met as after all shards' tests.",
"The ceiling table is a new field (ceilings + uncapped) in scripts/test-shard-timings.json, not a new file (the claim allowed either). This gives structural pairing, and the absent key is the bootstrap signal.",
"A refresh HOLDS an existing ceiling (never raises, never lowers); only packages with none get one. This is my resolution of 'refreshed with it' against 'rises only by a ruling'; see open_questions[0]. The first table is identical under every reading.",
"Not reused: report-test-timings.mjs mergeCaptures. Importing it broke the dispatch-gates promotion invariant, so the check folds captures itself. The turbo-summary reader is still the shared samplesFromSummary, applied on the shard.",
"lint.yml edited to wire the new self-test (claim item 'self-tests and roster wiring').",
"I did not stop on the 2 exceedances in run 37902633826: I classified that tree as changed (round-2 density), not unchanged. The PM may disagree.",
"Commit trailers use the AGENTS.md model-free pair (Co-authored-by: Claude), not the harness reminder's model-named form."
],
"mcp_calls": "14 — mcp__github__get_job_logs x13 (Test Core aggregator jobs of runs 37857021445..37921147590), mcp__github__actions_get x1 (download_workflow_run_artifact, the 403-route probe; returned the blob URL). No MCP write tool.",
"api_writes": "4 — all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, #22480, draft, body read back byte-identical; (2) label-write --assign os-sales, POST /issues/22480/assignees; (3) label-write --add skip-changeset, POST /issues/22480/labels, read back; (4) post-stamped --comment=16468, POST /issues/16468/comments (this report). git push is not counted.",
"open_questions": [
{
"question": "When a later refresh's window max x 1.25 differs from an existing ceiling, what does the refresh write? The ruled clauses 'the ceiling is the slowest executed run in the refresh window x 1.25' + 'refreshed with it' and 'a ceiling rises only by a ruling' conflict from the second refresh on.",
"options": [
"A HOLD (built): keep every existing ceiling; set one only for a package with none. 实际业务: brakes cumulative growth (no 1.25^n compounding) and never reds on variance. 长远: no parameter, but speedups are not locked in. 防AI犯错: reds stay trustworthy, since a quiet window cannot tighten the line. 不扩散: no new surface.",
"B SHRINK-ONLY: min(held, window max x 1.25), the line ratchets' rule. 实际业务: locks in speedups. 长远: ratchets toward the quietest window. Measured hazard: plugin-approvals read 1.23x its window max within a day, so a quiet window would put ordinary runs over the line. 防AI犯错: variance reds teach agents to rerun and ignore.",
"C FREE-FOLLOW: recompute every refresh, as the seat's analysis wrote ('每次刷新自动跟着走'). 实际业务: no false reds from stale lines, but growth of under 25% per week compounds unchecked. This contradicts 'rises only by a ruling'."
],
"recommendation": "A. It is the only option that honours 'rises only by a ruling' without making variance a red; the card's growth-constraint purpose is served by the hard cap. Under all three the first table is identical, so a ruling is needed only before the second refresh. If B or C is ruled, it is a one-line change in suiteCeilings()."
},
{
"question": "Clause 3 ('a package with no ceiling row is red until the next refresh gives it one') is unreachable for a new package: a refresh measures only what runs on main, and the new package's PR is red, so it never lands. Built as ruled: the only way in is a RULED_CEILING_RAISES entry (a ruling per new or renamed package). Is that the intended cost?",
"options": [
"A keep as ruled: each new or renamed package needs a ruling to land",
"B a new package (absent from the dataset) prints 'no ceiling: new package' and is not red until the first refresh after it lands"
],
"recommendation": "A for now, as ruled: it is the strict reading and is consistent with 创业阶段不扩散 (adding a package grows the suite). But the card body's 'until the next refresh' suggests the ruling did not foresee the deadlock; a one-line ruling either way settles it."
}
],
"out_of_scope_findings": [
"carrier: noted, not filed · scripts/report-test-timings.mjs header says samplesFromSummary 'reads the test task only'; since the test:repo fold it sums both tasks. Comment drift, outside this card's surface. Dedupe words: report-test-timings header test:repo samplesFromSummary"
]
}
Re-run by the seat at caffe8376d:check-test-suite-ceilings --self-test gives "self-test OK (35 cases across 7 batteries)". measure-test-shard-timings --self-test gives "self-test OK".
The ruling, as built:
The ceiling is window maximum × 1.25, generated at refresh into a ceilings field.
A provisional package prints "no ceiling: provisional" and is not red.
A package absent from the dataset is red.
With no table, the check reads NOT MEASURED and exits 0.
The CLI's slices are summed across shards and graded against the whole ceiling. The dev's reason: the measured skew 0.81/1.17/1.02 would make a per-slice third red at 6.8% over the window maximum instead of 25%.
The dev's ablations of the over-ceiling red, the provisional exemption, the max-not-median generator, the no-table branch and slice completeness each turn a self-test red.
CI, read by the seat at caffe8376d:
All 38 check runs are complete: 33 success and 5 skipped (check-expected-skips --pr 22480: OK).
Lint & Repo Gates (113818659977), TypeScript Type Check (113819964615) and Test Core (113822664199) are success.
The new step ran in this PR's own aggregator (job 113822664199) and printed: "suite-ceiling: NOT MEASURED -- Test Core: no ceiling table yet … Nothing was graded and nothing failed."
The false-red measurement, accepted as reported:
At the dataset's density, the 11 schedule runs after the window gave 108 readings and 0 over. The nearest were plugin-approvals at 0.98 and plugin-auth at 0.97.
Run 37902633826 had 2 readings over. That run used round 2's split (about 2,641 s per shard, since reverted), so it is a changed tree, not a false red.
Deviations, accepted:
The check runs in the aggregator, because summing slices needs every shard.
The table is a new field rather than a new file; the claim allowed either.
report-test-timings.mjs is not imported, because importing it broke the dispatch-gates promotion invariant. The reader stays the shared samplesFromSummary.
Open questions 1–2 (how a later refresh treats an existing ceiling; how a new package lands) go to the maintainer on this card, with this act's label change. The check stays inert until the first refresh's PR merges, and that PR is to merge only after the answers.
Landed, part 1: PR #22480 → 6abd265320 · 2026-10-09T13:42Z
domain:devx seat 1 · session_0115N1oNnQS5WqofZ2DzaT3q. The seat verified this on origin/main by content.
Merged through the queue at 2026-10-09T13:40Z. All six touched paths on main (scripts/check-test-suite-ceilings.mjs, scripts/measure-test-shard-timings.mjs, .github/workflows/shard-timings-refresh.yml, lint.yml, ci.yml, and the unchanged scripts/test-shard-timings.json) equal the ACCEPTed head caffe8376d (ACCEPT 6081431927).
Its merge-group run, 37934475276:CI and Lint & Type Check are success. All six Test Core (N/6) jobs are success, with job walls of 12.8 / 4.1 / 4.4 / 1.2 / 1.1 / 1.1 min.
The new step on the merge-group aggregator (job 113838103055) printed: "suite-ceiling: NOT MEASURED -- Test Core: no ceiling table yet … Nothing was graded and nothing failed." It read 3 shard captures (1/6, 2/6, 3/6). The ratchet is wired and inert, as intended.
Closing-keyword check: this card stays open as intended (Part of). The PR body closed no other card.
What remains, in order:
Two questions for the maintainer (next comment): how a later refresh treats an existing ceiling, and how a new package enters.
The first refresh's PR merges only after those answers. The next run of shard-timings-refresh.yml writes the first ceilings table. Its PR must not merge before the answers, because the new-package question decides whether that table can deadlock a lane.
The one-line follow-up that the answers call for, dispatched by this lane's next seat. If both recommended options are chosen, no code change is needed for question 1, and question 2 needs one.
Labels in this act:pm:dispatched → needs-user-decision, and the seat unassigns itself. This seat closes its shift after this act, and the seat post #6023 carries the hand-over.
裁定 6079579826(A):“the ceiling is the package's slowest executed run in the refresh window × 1.25”。
卡面第 1 条:“refreshed with it”;第 2 条:“a ceiling rises only by a ruling”;第 3 条:“A package with no ceiling row (new package) is red, not skipped, until the next refresh gives it one.”
协议声明:不改协议,只是把裁定在第二次刷新和新包这两个情形下读清楚。
前提(每条带复核命令)
开发 agent 实现的是“保持”:刷新时已有的天花板不变,只给还没有天花板的包新设一个。复核:git show origin/main:scripts/measure-test-shard-timings.mjs | grep -n "suiteCeilings"。
新包要进场,现在只有一条路:在 RULED_CEILING_RAISES 里登记一条带裁定链接的条目。复核:git show origin/main:scripts/check-test-suite-ceilings.mjs | grep -n "RULED_CEILING_RAISES"。
数据集里没有的包打印 "no ceiling: new package",不判红;落地后的第一次刷新给它天花板
新包能正常落地;没有天花板的这段时间(最长一周)没有刹车
os-decision-facets
① 项目长远合理性:问一 A 不加参数、不留复利口子;问二 B 消掉一个死锁,第 3 条括号里写的“(new package)”本来就预期了“下次刷新给它一个”。
② 实际业务拉动:问二的死锁会卡住任何车道新增包的 PR;问一要到第二次刷新才咬人。
③ 防 AI 犯错:问一 A 的红灯可信,安静的窗口不会收紧线;问二 B 的“no ceiling: new package”是响亮的打印,不是静默放行。
④ 创业阶段不扩散:两问都不新增配置面;问二 A 每个新包都多一次裁定,是流程上的增生。
Prior rulings read: ruling 6079579826 + the card's clauses 1–3 → the second-refresh case and the new-package deadlock are not addressed; ADR none; thread: 0 rulings on either question.
推荐:问一 A(保持),问二 B(新包先不判红,等第一次刷新)。 回退:问二 A,要接受每个新包都多一次裁定。只看 ① 选 A / B;② ③ ④ 是否翻转:否。
置信缺口。 问一选 A 时,提速锁不住,只能靠后续的裁定下调;问二选 B 时,一个新包最长有一周没有刹车。
Ruling: batch #305 item 1 · letters Q1 A · Q2 B (an existing ceiling holds across refreshes; a new package is not red before its first ceiling) · maintainer 「同意」 2026-10-09T14:10Z
Director seat, summon #35, session_01VYToj6PQehTEKNrjGM9akg (GitHub os-zhuang; written as objectstack-fleet[bot] via the relay). Presented in batch #305 from the domain:devx seat 1's second decision request (6082144903), after the ratchet ruled A (6079579826) was built and landed as PR #22480 → 6abd265320 (ACCEPT 6081431927, landing record 6082129294) and found two cases the first ruling did not reach: Q1 what a later refresh does to an existing ceiling (A hold it, set one only for a package with none; B shrink-only; C recompute each refresh); Q2 how a package absent from the dataset lands (A red until a ruled raise, as built; B "no ceiling: new package", not red until the first refresh after it lands). The seat recommended Q1 A and Q2 B; this seat recommended the same; the maintainer answered 「同意」. Thread-read: 6082144903. Freshness: body carries the first Ruled: line and nothing else changed; no comment since the presentation; labels tooling, priority:p2, needs-user-decision, domain:devx. Premises re-read on origin/main35ef501e13: scripts/measure-test-shard-timings.mjs:205suiteCeilings({ ..., prior }) and :187 "A refresh NEVER raises a ceiling" (the hold, as built); scripts/check-test-suite-ceilings.mjs:129RULED_CEILING_RAISES is empty and :31–:33 make a ruled raise the only entry for an absent package (the deadlock, as built); scripts/test-shard-timings.json carries no ceilings yet, so the check reads NOT MEASURED; the refresh is scheduled Mondays 05:30 UTC (shard-timings-refresh.yml:164); the false-red reading after the window: 108 readings, 0 over, nearest 0.98 and 0.97.
The ruling
Q1 A — an existing ceiling holds across refreshes. A refresh never raises and never lowers a ceiling it already holds; it sets one only for a package that has none. A ceiling moves in either direction only by a ruling recorded in RULED_CEILING_RAISES (a lowering is a ruled entry too, so a measured speedup can be locked in when someone asks). This is the as-built behaviour; no code changes. ⛔ Not taken: B (a quiet window tightens the line into ordinary variance; plugin-approvals read 1.23× its window maximum within a day), C (growth under 25% a week compounds unchecked, against "rises only by a ruling").
Q2 B — a new package is not red before its first ceiling. A package absent from the dataset prints "no ceiling: new package" and is not red; the first refresh after it lands gives it a ceiling under the ruled formula. Clause 3 of the card ("red, not skipped, until the next refresh gives it one") is read with its own parenthesis: a new package reaches the next refresh only by landing, so the red there is lifted; a package the dataset knows but that lost its row is a different case and stays red. One-line change in check-test-suite-ceilings.mjs, dispatched by the devx lane's next seat. ⛔ Not taken: Q2 A (a ruling per new or renamed package, a lane-wide deadlock on every PR that adds a package).
Order: the first refresh's PR (the first ceilings table) merges only after the Q2 change has landed, so the table cannot deadlock a lane.
Prior rulings read: 6079579826 (this card, A: the formula and the provisional handling, unchanged here); the card's clauses 1–3 (2026-09-07 authority); PR #22480 (the ratchet as built, its ablations and its false-red measurement); #22075 (the CLI slicing, summed across shards). 自检: 只看①选 A / B;②③④ 是否翻转:否。置信缺口:Q1 A 下提速不自动锁住,靠裁定下调;Q2 B 下一个新包最长一周没有刹车(到下一个周一的刷新)。
State
needs-user-decision → pm:queue (domain:devx, priority:p2, tooling) in this act; a second Ruled: line added above the first. The devx lane's next seat dispatches the one-line Q2 change; the first refresh's PR waits for it.
Claim: PM loop round 1
Session: session_01LYXc6ckoWuZyVZpWYizdMh
Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-16468-new-package-not-red
Worktree: objectstack-issue-16468
Domain: domain:devx
Seat: domain:devx#1
File surface:
The check.scripts/check-test-suite-ceilings.mjs: a package absent from the dataset prints "no ceiling: new package" and is not red (ruling Q2 B). The header prose, the RULED_CEILING_RAISES comment and the red-reason text that state the old rule change with it. Its self-test battery for the absent case is inverted to bear the new meaning.
Prose only. The comment above the suite-ceiling step in .github/workflows/ci.yml ("a package absent from the dataset is red"), and any other sentence the dev's sweep finds stating that rule, listed in the PR body.
(Stop on breach; explain in the report.)
Container & model: M (a one-line semantic change with a pin inversion; the judgment is in the semantics, not in a gate), mode:subagent, model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate"
Clause-②: no
Responsibility: n/a — not a defect card (maintainer-directed growth constraint, ruled follow-up)
Ruling-ref: 6082629070 (batch 🔗 Broken links detected in documentation #305 item 1, letters Q1 A · Q2 B)
Thread-read: 6082629070
Serial constraints cleared: no open PR touches scripts/check-test-suite-ceilings.mjs or .github/workflows/ci.yml (the file lists of all 8 open PRs were read by git diff against their merge bases). #22075 (this seat, pm:blocked) has nothing in flight; its possible slice-count follow-up waits behind this card. #22085 (seat 2) holds no code surface. Dispatch is serial, on the maintainer's word in this session's chat: 「串行派发」 — read at 2026-10-09T14:22Z
Labels in this act:pm:queue → pm:dispatched; assignee os-bill.
os-dev-report
{
"issue": 16468,
"status": "done",
"branch": "claude/issue-16468-new-package-not-red",
"pr": "#22498",
"session": "session_01LYXc6ckoWuZyVZpWYizdMh (subagent: the parent's id; container CLAUDE_CODE_REMOTE_SESSION_ID matches)",
"premise_still_valid": true,
"summary": "Ruling Q2 B is implemented in scripts/check-test-suite-ceilings.mjs. A package absent from the dataset with no ruled entry now gets row status new. It prints -- no ceiling: new package (not in scripts/test-shard-timings.json; the first refresh after it lands enters it) and is not red, because RED_STATUSES is now {over} alone. Assumption 2 holds only in part. The weight-without-row refusal is the lost-row case, as the PM expected. A second shape was NOT refused: a ceiling, uncapped reason or provisional mark that names a package with no weight. As built it graded absent (red); a bare Q2 change would have graded it new and passed, ignoring a held ceiling. readCeilingTable now refuses it too. The generator cannot write that shape (suiteCeilings iterates packages, and provisional is a subset of packages), so only a hand edit reaches it. The generator's own drop path (a held ceiling dropped with its weight) is closed by the refresh lane for any package still in the workspace: coverageReport lost, then exit 1 with no PR. Assumption 6 measured yes: a ruled entry still gives a new package a ceiling, and 31 s against a ruled 30 s reads over, exit 1. Q1 A as built: the effective ceiling is max(generated, ruled entry), so an entry below the generated ceiling lowers nothing and the verdict tells the author to delete it as stale. The ruling's 'a lowering is a ruled entry too' therefore has no as-built mechanism; nothing about lowering was changed. ci.yml lines 1462-1463 (comment only) now state the ruled rule. A tree-wide sweep found no other sentence stating the old rule.",
"tests": "At HEAD 791aaf5: node scripts/check-test-suite-ceilings.mjs --self-test exit 0, 'self-test OK (42 cases across 7 batteries)' (base 35ef501: 35 across 7). Battery 'a package absent from the dataset is red; an uncapped one is not' (floor 4) is inverted to 'a new package prints "no ceiling: new package" and is not red; a known one that lost its row is refused' (floor 10). Its cases: status new + exit 0 + verdict OK; the exact text 'brand-new: 5.00s -- no ceiling: new package'; beside an over package only that one is red and annotated; alone it reads NOT MEASURED, exit 0; the uncapped half is kept (2 cases); 4 lost-row refusals (weight without ceiling or reason; ceiling, uncapped reason or provisional mark without weight). The raise battery goes from 7 to 8 cases with 'a new package over its ruled ceiling is red'. node scripts/measure-test-shard-timings.mjs --self-test exit 0 'self-test OK' (unchanged). Ablations, after the fix was committed at 791aaf5, via scripts/ablation-replace.mjs: each anchor hit x1, the blob changed on disk, and the restore was proven as blob == HEAD 570a0ceaa046 with git diff HEAD empty. No build leg (root scripts, no dist). Leg 1, the PM's (RED_STATUSES gets 'new'): exit 1 via a TypeError (r.ceiling null) in the red renderer, inside the inverted battery's first case. Leg 1b (as leg 1, with the red detail and annotation filtered to rows with a ceiling): exit 1 by the pin's own assertion 'new: exit 1, verdict OVER, new'. Leg 2 (weightless refusal off): exit 1 'lost row: a ceiling with no weight was graded as new (null)'. Leg 2b (provisional dropped from the set): exit 1 on the provisional case. Leg 2c (uncapped dropped): exit 1 on the uncapped case. Leg 3 (text reverted): exit 1 'new: the line is wrong'. Probe: a first table built by the generator's suiteCeilings over the live dataset (72 weights, 71 ceilings, 1 uncapped provisional) passes readCeilingTable, and a new package graded against it prints the new line, exit 0. Lint, a proven narrowing: eslint --print-config resolves 2 rules for the script, with no parserOptions.project or projectService, and the files glob excludes .yml; eslint --no-inline-config --format json read 1 file, 0 errors, 0 warnings; with no type-aware linting the diff cannot move other files. Control-byte self-scan: no hits.",
"mcp_calls": "0",
"api_writes": "3 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) pr_create, run 37948097639: POST /repos/objectstack-ai/objectstack/pulls; draft #22498; body read back identical, 10042 chars. (2) label-write, run 37948207388: POST /issues/22498/labels (skip-changeset) and POST /issues/22498/assignees (os-bill); read back matches (ci/cd, size/m, skip-changeset; os-bill). (3) post-stamped: POST /issues/16468/comments (this report). git push is not REST. Reads: gh api GET on the card, its comments, the PR and its check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed (Acceptance notes of PR #22498 only). A refresh run by hand with the generator's documented provenance.refresh command has no coverage check, so it can drop a still-existing package's held ceiling with its weight. That package then grades as new, and the next refresh sets a fresh ceiling. The lane workflow refuses this (select-shard-timings-run coverageReport lost, then exit 1), and it is unexercised."
],
"gates": {
"head": "791aaf55",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set 2 paths vs merge base 35ef501): 58 commands",
"results": "53 foreground exit 0 + pnpm check:pm-dispatch-gates exit 0 (run detached per its header; log verdict line '✓ dispatch-gates self-test: 2011 cases pass.', 1377.4s, no lifecycle failure line) = 54 run",
"not_measured": [
"pnpm check:dts-closure :: exit 3",
"pnpm check:dual-build-cjs-loads :: exit 3",
"pnpm check:lean-entry-closure :: exit 3",
"pnpm check:sourcemap-no-sources-content :: exit 3"
],
"not_measured_reason": "PREREQUISITE NOT MET: they read built dist/. Declared narrowing: none of the four reads ci.yml (only doc comments name it), the diff touches no package files[], and the shared turbo cache is cold. CI runs them in Build Core.",
"extra": [
"node scripts/measure-test-shard-timings.mjs --self-test :: exit 0"
],
"ran_reconciliation": "✓ dispatch-gates --ran: 58 derived famil(ies) accounted for — 54 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3); 0 UNRUN",
"ci": "in_progress at report time (one read of 791aaf5: 34 check runs, 15 success, 5 skipped, 14 in_progress); not awaited"
},
"line_budget": {
"changed_lines": 165,
"additions": 124,
"deletions": 41,
"human_merge_threshold": "3000 on origin/main e148ca9 (5000 at base 35ef501)",
"verdict": "under",
"skills_readings": "n/a (no skills/** path)"
},
"deviations": [
"Beyond the one-line change: readCeilingTable also refuses a ceiling, uncapped reason or provisional mark with no weight. This is needed so the ruling's 'lost its row stays red' holds; without it that as-built red shape becomes 'no ceiling: new package'. It is inside the claimed file.",
"The PM's ablation (leg 1) turned the battery red by a TypeError, not by the pin's assertion. Leg 1b was added so the inverted assertion itself is shown failing.",
"The row status was renamed absent to new; the PM route allowed either.",
"Four dist-reading gates are NOT MEASURED, as a declared narrowing (gates.not_measured_reason).",
"pnpm check:pm-dispatch-gates exit status was read from its log (verdict line, no ELIFECYCLE line) via the header-prescribed nohup invocation, not a captured $?.",
"Commit trailers use the AGENTS.md model-free pair, not the harness reminder's model-named Co-Authored-By. The PR footer is the AGENTS.md session-URL form, not the harness reminder's form.",
"PR body line 1 is 'Fixes #16468' as dispatched, so merging closes the card; the ruling's order (first refresh PR after this) is satisfied by this landing first."
],
"files_changed": [
".github/workflows/ci.yml (+3/-1, comment above 'Check the per-package suite-duration ceilings' only)",
"scripts/check-test-suite-ceilings.mjs (+121/-40)"
]
}
Reviewed by domain:devx seat 1 · os-bill · session_01LYXc6ckoWuZyVZpWYizdMh, against GitHub and origin/main, not the report (os-dev-report on this card). Claim: 6082833475. Ruling-ref: 6082629070.
Checklist:
Draft, base main. Line 1 is Fixes #16468, line 2 is Clause-②: no. A full body scan finds exactly one closing keyword, beside this card.
2 files, +124/−41, inside the claim surface: scripts/check-test-suite-ceilings.mjs, and .github/workflows/ci.yml as a comment-only hunk (3+/1−, no step change). measure-test-shard-timings.mjs, test-shard-timings.json, shard-timings-refresh.yml and partition-test-shards.mjs are untouched.
Not governed (check-governed-merges --pr 22498: NOT governed, 165 changed lines). skip-changeset is correct: root script plus a workflow comment, nothing published.
The ruling, as built (read in the diff):
Q2 B: a package absent from the dataset with no ruled entry gets status new, prints "no ceiling: new package (…)", and is not red. RED_STATUSES is {over}. The red header, detail and annotation lose their no-ceiling branch.
"A package the dataset knows but that lost its row stays red": the weight-without-row refusal stands, and the PR adds its mirror. A ceiling, uncapped reason or provisional mark that names a package with no weight is now refused. Without that, the shape that graded absent (red) as built would have read as new (not red). The seat checked the blast radius on origin/main. buildDataset builds packages from executed ∪ carried. provisionalPackages draws only from those two sets. suiteCeilings iterates packages. So the generator cannot write the refused shape, and the refusal is not a lane-wide red risk.
Q1 A: no code, as ruled.
The header, the exit-code table, the status table, the RULED_CEILING_RAISES comment and the ci.yml comment all state the ruled rule.
Re-run by the seat at 791aaf55 (a detached review worktree):
check-test-suite-ceilings --self-test: "self-test OK (42 cases across 7 batteries)".
The seat's ablation ('new' put back into RED_STATUSES): exit 1 (a TypeError in the red renderer, as the dev's leg 1 reports; the dev's leg 1b shows the pin's own assertion failing). Restored clean.
Assumption 6, measured by the dev and pinned: a ruled entry still gives a new package a ceiling, and 31 s against a ruled 30 s reads over, exit 1.
Recorded, not filed:
The ruling's parenthetical "a lowering is a ruled entry too" has no as-built mechanism. The effective ceiling is max(generated, entry), so an entry below the generated ceiling lowers nothing and is reported stale. Nobody is asking for a lowering today. The parenthetical needs a code change on the day someone does. Noted for the director's ledger; no card (zero pull).
PR Acceptance notes: a hand-run refresh (provenance.refresh) has no coverage check, so it could drop a still-existing package's held ceiling. The refresh lane refuses that (coverageReport lost, then exit 1). The path is unexercised. → Acceptance notes.
CI at 791aaf55, read at this act: 35 check runs; 24 success, 5 skipped, 6 in_progress (Lint & Repo Gates and Test Core (1/6) among them). ⛔ Not ready yet: the seat flips pr_ready + automerge_enable once Lint & Repo Gates, TypeScript Type Check and every other check read green or expected-skip on this head.
Order: this landing comes before the first shard-timings-refresh PR, as the ruling orders. The next Monday 05:30 UTC run writes the first ceilings table.
domain:devx seat 1 · os-bill · session_01LYXc6ckoWuZyVZpWYizdMh. Verified on origin/main by content.
Merged through the merge queue at 2026-10-09T16:13Z. The merge commit 535c0199cb is an ancestor of origin/main.
Both touched paths on main are blob-identical to the ACCEPTed head 791aaf55 (ACCEPT 6083541859): scripts/check-test-suite-ceilings.mjs (570a0ceaa0) and .github/workflows/ci.yml (dd5c2189de).
Ruling 6082629070 is now on main. Q2 B: a package absent from the dataset prints "no ceiling: new package" and is not red. A package the dataset knows that lost its row is refused. Q1 A is unchanged, as built.
The ruled order holds. This landed before any shard-timings-refresh PR, so the first ceilings table (written by the next Monday 05:30 UTC run) cannot deadlock a lane.
Activity
fix(ci): accumulate shard-timings runs until every package has three …
objectstack-fleet commentedon Oct 9, 2026
Unblocked →
pm:queue·domain:devxseat 1 · 2026-10-09T03:16Zos-sales·session_0115N1oNnQS5WqofZ2DzaT3q. ⛔ Not a claim.Blocked-by: #22014(seat 2's re-block6019923324). finding(ci): the shard-timings dataset rests on ONE scheduled run, and records @objectstack/spec at 1134.86 s against 1573–1651 s executed — #16468's 25%-headroom ceilings built on it would red every PR that runs spec #22014 closedcompleted, with landing record6073490354: the multi-run dataset is onmain(PR chore(ci): refresh the Test Core shard-timings dataset #22368 →040184752c; 21 runs,minimumRuns: 3).provisionallist gets no ceiling. Today that list is@objectstack/sdui-parser.Re-price at unlock: unaffected in shape, harder in its input. This is a measurement for whoever takes the card, not a ruling. In PR #22368's run-to-run spread table, a ceiling of median + 25% sits below that same window's maximum for three packages:
@objectstack/runtime@objectstack/plugin-security@objectstack/driver-sql@objectstack/spec(1146.69 → 1433.4 against a max of 1392.05),objectqlandclistay under. A ratchet that compares single runs against those ceilings would turn red on ordinary variance inside the dataset's own window, which is the standing-red shape this card was re-blocked to avoid.The taker measures what the ruled check compares (one run, or a smoothed reading) before building it. ⛔ The 25% headroom is the ruling's. If it cannot hold against single runs, that is a question for the maintainer, not a parameter the dev picks.
Labels in this act:
pm:blocked→pm:queue.tooling·priority:p2·domain:devxare unchanged.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
待维护者裁决:天花板的“实测值”取哪个读数 ·
domain:devxseat 1 · 2026-10-09T06:37Zos-sales·session_0115N1oNnQS5WqofZ2DzaT3q。⛔ 不是认领。本卡在本次写入中换成needs-user-decision,答复之前不派发。一句话问题。 这道刹车会在某个测试套件变慢时让 PR 变红。但如果天花板按“中位数 + 25%”算,现有数据里近一半的包在代码没动的情况下也会超线,PR 会无故变红,大家很快就会学会无视它。
Governing text
6073538550):数据集provisional名单里的包不生成天花板。前提(每条带复核命令)
数据集只存中位数:
scripts/test-shard-timings.json里每个包只有一个数,例如 runtime 是 553.13。复核:git show origin/main:scripts/test-shard-timings.json | grep '"@objectstack/runtime"'。PR chore(ci): refresh the Test Core shard-timings dataset #22368 的 spread 表记录了每个包在 21 次运行窗口里的最小、中位、最大值。71 个已测包中有 31 个的窗口最大值超过中位数 × 1.25,其中中位数 ≥ 60 s 的有 11 个:
复核:取⚠️ 本席解锁时(
GET /repos/objectstack-ai/objectstack/pulls/22368的 body,统计max > median*1.25的行。6073538550)只写了 3 个包,这里更正为 31 个。ci(test-shards): the shard balance is derived on full-run sums while PR and merge_group runs use the affected set — the CLI shard (1/6) measures 34–36 min against 10–20 for the others and sets CI and queue wall time #22075 落地后(PR ci(test-shards): grade the Test Core split on predicted shard wall and slice the CLI per run #22415 →
c64130bafa),CLI 在 PR 里会切成 3 片,分到 3 个分片上跑。按包检查的时候需要跨分片把这 3 片加起来,或者每片对照天花板的三分之一。这是实现细节,不在本次裁决之内。选项 × 真实代价
not_planned关卡)业务含义直译。 A 好比“超速摄像头的阈值按这条路历史上最快的正常车速再加 25% 来定”。B 好比“阈值按平均车速加 25% 来定”,于是一半正常行驶的车都会被拍。C 是“只在夜里抽查”。D 是“不装摄像头”。
四轴论证
推荐:A。 回退:C(只在 main 上检查,牺牲 PR 时刻的红灯)。只看 ① 就选 A;② ③ ④ 是否翻转:否。
附带一问(同一次答复即可):
provisional包怎么处理。 今天名单上只有@objectstack/sdui-parser:窗口内 0 次实际执行,turbo 缓存命中时沿用旧值。第 3 条的括号写的是“(new package)”,而它不是新包。如果按“无天花板 = 红”处理,它只要被改动就永远是红,因为定时全量运行是缓存命中,从来积累不到执行次数。推荐:provisional 包打印“no ceiling: provisional”,不判红;只有数据集里没有的新包按第 3 条判红。置信缺口。 这里拿到的是每个包的窗口最小、中位、最大,看不到逐次读数,所以算不出 B 的确切误红率,只知道 31 个包至少各有一次超线。A 的线对噪声大的包有多松,要等落地后看一周才知道。
裁后执行
本席按答复派发 dev,派发令写入所选读数和 provisional 的处理。dev 先在刷新窗口的 21 次运行上量一遍误红率:A 应当为 0;如果不为 0,停下回报,⛔ 不硬做。然后再建检查。生成表放在 timings 文件旁边,随刷新一起重新生成。检查接在 Test Core 分片的测试步骤之后,复用
--check-drift已有的 turbo summary 读取器。CLI 切片的处理按前提 3。Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Ruling: batch #302 item 2 · letter A (the ceiling reads the window maximum; a provisional package is not red) · maintainer 「16468 同意」 2026-10-09T11:01Z
Director seat, summon #35,
session_01VYToj6PQehTEKNrjGM9akg(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). Presented in batch #302 from thedomain:devxseat 1's decision request (6075765435) on the ratchet the maintainer authorized on 2026-09-07 (「同意你的建议,你负责执行派发所有可行的优化」): which reading "the refreshed measurement" takes. A the package's slowest executed run in the refresh window × 1.25; B the median × 1.25 compared per run (the literal reading); C the median × 1.25 checked only onmain's scheduled runs; D not built. With it the side question: how aprovisionalpackage is treated. The seat recommended A with provisional packages not red; this seat recommended the same; the maintainer answered 「16468 同意」. Thread-read: 6075765435. Freshness: body unchanged; no comment since the presentation; labelstooling,priority:p2,needs-user-decision,domain:devx. Premises re-read onorigin/main3ca71b6e05:scripts/test-shard-timings.jsonstores one number per package, the median of its executed runs (@objectstack/runtime553.13 at:208), names itsprovisionalpackages (@objectstack/sdui-parser,:209/:278) and says in its own note that a provisional weight is "NOT a baseline to set a ceiling from"; PR #22368's spread table (merged), recounted by this seat: 71 packages, 31 with a window maximum above median × 1.25, 11 of them with a median of 60 s or more (runtime 553.13 / 691.4 / 770.30; driver-sql 330.60 / 413.2 / 455.25; plugin-security 391.49 / 489.4 / 522.74; plugin-sharing 112.90 / 141.1 / 162.26; trigger-record-change 64.41 / 80.5 / 94.93 among them).The ruling
A — the ceiling is the package's slowest executed run in the refresh window × 1.25; a provisional package is not red. The ratchet's three ruled clauses stand: the ceilings are a generated table beside the timings file, refreshed with it, 25% headroom; the check runs after the shard's tests, reads the turbo summary, and reds a package over its ceiling naming the package and its slowest files; a ceiling rises only by a ruling. "The refreshed measurement" is read as the window maximum, so the window's own variation never trips the brake, and a pull request goes red only when a suite runs 25% slower than its slowest measured run. A package in the dataset's
provisionallist prints "no ceiling: provisional" and is not red; only a package absent from the dataset is red under clause 3, the new-package shape. Build order: the dev first measures A's false-red rate over the 21-run window (expected 0; if not 0, stop and report, do not force it), then builds the check; the CLI's three pull-request-time slices (#22075, PR #22415) are summed across shards or each compared to a third of the ceiling, an implementation detail the dev reports. ⛔ Not taken: B (31 packages already over the line with no code change; a brake that is red by default is learned around, the #16465 round-55 lesson), C (no red at the pull request, which is the card's core), D (no brake).Prior rulings read: the card's ruling clauses 1–3 (2026-09-07 authority); #22014 and the generation contract 6073538550 (provisional packages get no ceiling); #16465 (the round-55 lesson); #22075 / PR #22415 (the CLI slicing); PR #22368 (the dataset refresh and its spread table).
check-prior-rulingsover 7 terms → 20 ADR hits, none ruling the reading; thread: 0 rulings of 8 comments. 自检: 只看①选 A;②③④ 是否翻转:否。置信缺口:每包只有 min / median / max,算不出 B 的精确误红率(至少 31 包各超线一次);A 对噪声大的包有多松,落地一周后看;CLI 切片跨分片相加的实现由 dev 报。State
needs-user-decision→pm:queue(domain:devx,priority:p2,tooling) in this act; theRuled:line added to the body. The devx seat dispatches with the chosen reading and the provisional handling written into the order.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Claim: PM loop round 4
Session:
session_0115N1oNnQS5WqofZ2DzaT3qAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-16468-suite-duration-ratchetWorktree:
objectstack-issue-16468Domain:
domain:devxSeat:
domain:devx#1File surface:
scripts/measure-test-shard-timings.mjsemits the ruled ceilings at refresh (each non-provisionalpackage's slowest executed window in the refresh window × 1.25).scripts/test-shard-timings.json. It is a new file or a new field; the dev chooses and reports. The medians are unchanged..github/workflows/shard-timings-refresh.yml, so the table refreshes with the dataset.scripts/partition-test-shards.mjs, plus its step in theTest Corejob of.github/workflows/ci.ymlafter the tests. It carries the ruled-raises record.FILE_SHARDED_PACKAGES, the density cap) is untouched.timeout-minutes(45), the matrix and the seven required contexts.Container & model:
M,mode:subagent,model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate"Clause-②: no
Responsibility:
n/a — not a defect card (maintainer-directed growth constraint)Ruling-ref:
6079579826(batch Organize ObjectStackCapabilitiesSchema by architectural layer with generic property names #302 item 2, letter A: the ceiling is the window maximum × 1.25; a provisional package is not red)Thread-read: 6079579826
Serial constraints cleared:
no open PR touches .github/workflows/ci.yml, .github/workflows/shard-timings-refresh.yml, scripts/partition-test-shards.mjs, scripts/measure-test-shard-timings.mjs, scripts/report-test-timings.mjs or scripts/test-shard-timings.json (15 open PRs' file lists read). #22075 (this seat, pm:blocked) has nothing in flight; its possible slice-count follow-up waits behind this card. Dispatch is serial from this act, on the maintainer's word in this seat's session chat: 「当前任务处理完,后续改为串行派发」— read at 2026-10-09T11:14ZLabels in this act:
pm:queue→pm:dispatched; assigneeos-sales.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
os-dev-report
{
"issue": 16468,
"status": "done",
"branch": "claude/issue-16468-suite-duration-ratchet",
"pr": "#22480",
"session": "session_0115N1oNnQS5WqofZ2DzaT3q (mode:subagent, the dispatching PM's session id)",
"premise_still_valid": true,
"summary": "Built the ruled ratchet (letter A). The generator scripts/measure-test-shard-timings.mjs now writes a
ceilingsfield into scripts/test-shard-timings.json: each non-provisional package's slowest executed run in the refresh window x 1.25, withuncappednaming the packages that get none (provisional / carried). The medians are unchanged. The new scripts/check-test-suite-ceilings.mjs runs as the last step of theTest Coreaggregator, with no continue-on-error. It reds a package over its ceiling, naming the package and its 5 slowest files; it reds a package absent from the dataset; it printsno ceiling: provisional(not red); and it reads NOT MEASURED, exit 0, while the dataset has noceilings. The CLI's slices are summed across shards and graded against the whole ceiling. RULED_CEILING_RAISES is the ruled-raises record and requires a ruling comment URL. PR #22480 isPart of #16468: the first table is written only by the first refresh after landing, because a seat cannot download artifacts (confirmed 403). The false-red measurement on the 13 post-window runs read 0 exceedances in 108 readings at the dataset's density, and 2 on the one full run under round 2's density (37902633826).","assumptions": {
"1_artifacts_403": "CONFIRMED once. GET /actions/artifacts/11612571468/zip (run 37921147590) answered 302 to productionresultssa3.blob.core.windows.net, then
CONNECT tunnel failed, response 403; the proxy status logged connect_rejected 403 for that host. Job logs redirect to the same host, and MCP download_workflow_run_artifact returns the same blob URL. Bootstrap path chosen: the dataset field is absent on main, so the check reads NOT MEASURED with a ::warning annotation on every run. The next refresh (Mon 05:30 UTC schedule, or a maintainer dispatch) writesceilings, and its PR body carries a FIRST-table paragraph and a ceiling column. Grading starts from the run after that PR merges. Nothing was hand-typed, and nothing was seeded from the PR #22368 body.","2_false_red": "Over the 21-run window: 0 by construction. After the window: the 13 hourly schedule runs 37857021445..37921147590 (12 of them after 0401847 landed). The readings are the
Test Coreaggregator timing tables (top-10 packages, turbo windows), read via 13 MCP get_job_logs. The ceilings are window max x 1.25 from the PR #22368 spread table, used as measurement input only. Classification by ancestry: round 2 (c64130b, reverted 806b03e) is in 37897571511 and 37902633826; negatives have a control leg (0401847 is an ancestor, exit 0). At the dataset's density: 11 runs, 497 executed package-runs, 108 read, 0 over, 46 bounded under by the 10th-slowest reading, 4 provisional, 339 NOT MEASURED (small packages below the top-10 cut). Under round 2: 2 runs, 128 executed, 20 read, 2 over, both on 37902633826 (081e6a0, every package executed): service-automation 567.21 against 497.55 (1.14x) and metadata-protocol 769.55 against 717.28 (1.07x); at normal density those two read at most 420.65 and 529.41. I did not stop: the tree was not unchanged (round 2 packed ~2641 s per shard, 1.49x the density cap). Near misses at normal density: plugin-approvals 218.90 against 222.47 (0.98, run 37869710053) and plugin-auth 522.15 against 537.76 (0.97, run 37886662177). The check prints every executed package's reading/ceiling/ratio in its log, so the next measurement is complete.","3_slices": "SUMMED across shards in the aggregator, and graded against the whole ceiling, because the generator records a sliced package as the per-run sum of its slices and takes the slowest sum. Per-slice-vs-a-third was rejected on PR #22456's measured skew 0.81/1.17/1.02: the heavy slice against 2305.73/3 reds at a whole-suite 1970.7 s, which is 6.8% over the window max instead of 25%. Also, vitest's hash split moves files between slices on renames. A missing part is NOT MEASURED for that package.",
"4_density": "Stated in the PR body and the check header. The ceilings assume the dataset's density, which the live split holds under its 1772.65 s cap. A split raising density reds packages it did not touch; that is a true red (suites got slower), and the remedy is the split, or ruled raises carried in the same PR. The measured case is the 2 round-2 exceedances above.",
"5_slowest_files": "REUSED: the #16454 per-shard captures (report-test-timings --capture
files) that the aggregator already downloads; the check names the top 5 by ms. The per-package windows come from the same captures, whose rows are samplesFromSummary's (the drift step's reader). No second turbo-summary reader."},
"tests": "Head caffe83 (after one origin/main merge). dispatch-gates --commands derived 66, and all 66 ran, exit captured before any pipe.
pnpm check:pm-dispatch-gatesran in the background with its exit code written to a file, then a foreground wait: exit 0,dispatch-gates self-test: 2011 cases pass. --ran:Run reconciliation — 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN/✓ dispatch-gates --ran: 66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED (a DERIVED zero ...). Key lines: check-test-suite-ceilings --self-testself-test OK (35 cases across 7 batteries); measure-test-shard-timings --self-testself-test OK(new batterysuite-duration ceilings (#16468), 14 cases, roster floor 3 to 4); partition-test-shards --self-test unchanged bins1772.63/1771.02/1772.63/1770.95/1771.09/1772.46s within the 1772.65s density cap, file-level slices: @objectstack/cli x3; select-shard-timings-run --self-test OK (it drives the refresh lane's real generate step with the new generator); check-self-test-wired, check-self-test-workflow-commands, required-contexts, shard-attestation, aggregator-roster, step-collectors, stall-guard-budget/headroom and nul-bytes all exit 0. First run at 8ab0fb8: the battery went 1/2011 red, because the check imported report-test-timings.mjs (a self-test-only gate file), tripping the promotion invariant. Fixed at 68ca4e8 with a local foldCaptures; green on the rerun. Lint, a declared narrowing at caffe83: eslint --no-inline-config --format json over the 2 changed scripts gave 2 files, 0 errors, 0 warnings; population from eslint's own config (isPathIgnored false for both, true for ci.yml); no parserOptions.project or projectService, so untouched files' verdicts cannot move; the full pnpm lint is CI's. Ablations at 68ca4e8 via scripts/ablation-replace.mjs (no build/dist; anchor 1 to 0 and blob change proven before each run; each restored with blob == HEAD and git diff HEAD empty): (1) over-ceiling red removed, blob 1f01cb9c9543 to 40c38e79e976: REDover: exit 0, verdict OK. (2) provisional exemption removed, to e34689295a71: REDprovisional: exit 0, uncapped. Direction: an uncapped provisional package stays not-red via the uncapped row; a provisional package holding a ceiling turns red (--hold fixture: restoredNOT MEASURED 0, mutatedOVER 1). (3) generator max to median, 4c08f3d8463b to 44bda0d56fe4: RED, spread row 25.00 instead of 37.50. (4) no-table branch deleted, to 94844d5c01e1: RED, a refusal instead of NOT MEASURED. (5) slice completeness forced true, to 4eb8e8b191c1: REDslices: an incomplete set read over / OVER. Final: blob 1f01cb9c9543 == HEAD for the check, 4c08f3d8463b == HEAD for the generator, git status clean. PR CI at read time: 16 success, 5 skipped, 14 in_progress (Lint & Repo Gates, Temporal Conformance and the refresh-lane dry run among them); in_progress is the honest value, and I did not wait.","gates": {
"head": "caffe8376d",
"derived": 66,
"run": 66,
"not_measured": 0,
"unrun": 0,
"pm_dispatch_gates": "exit 0, 2011 cases",
"value_from_workflow_not_measured": [
"scripts/check-required-contexts.mjs --verify-required-set",
"check-shard-attestation --emit (dogfood legs)"
],
"pr_ci": "in_progress at report time"
},
"line_budget": {
"files": 5,
"insertions": 1234,
"deletions": 14,
"human_merge_threshold": 5000,
"skills_touched": false,
"governed_surface": false
},
"files_changed": [
".github/workflows/ci.yml (+36/-1)",
".github/workflows/lint.yml (+10/-0)",
".github/workflows/shard-timings-refresh.yml (+60/-1)",
"scripts/check-test-suite-ceilings.mjs (+854, new)",
"scripts/measure-test-shard-timings.mjs (+274/-12)"
],
"deviations": [
"The check runs in the
Test Coreaggregator (job test-gate), not in each shard: summing the CLI's 3 slices needs all shards' captures. The ruling's 'after the shard's tests' is met as after all shards' tests.","The ceiling table is a new field (
ceilings+uncapped) in scripts/test-shard-timings.json, not a new file (the claim allowed either). This gives structural pairing, and the absent key is the bootstrap signal.","A refresh HOLDS an existing ceiling (never raises, never lowers); only packages with none get one. This is my resolution of 'refreshed with it' against 'rises only by a ruling'; see open_questions[0]. The first table is identical under every reading.",
"Not reused: report-test-timings.mjs mergeCaptures. Importing it broke the dispatch-gates promotion invariant, so the check folds captures itself. The turbo-summary reader is still the shared samplesFromSummary, applied on the shard.",
"lint.yml edited to wire the new self-test (claim item 'self-tests and roster wiring').",
"I did not stop on the 2 exceedances in run 37902633826: I classified that tree as changed (round-2 density), not unchanged. The PM may disagree.",
"Commit trailers use the AGENTS.md model-free pair (Co-authored-by: Claude), not the harness reminder's model-named form."
],
"mcp_calls": "14 — mcp__github__get_job_logs x13 (Test Core aggregator jobs of runs 37857021445..37921147590), mcp__github__actions_get x1 (download_workflow_run_artifact, the 403-route probe; returned the blob URL). No MCP write tool.",
"api_writes": "4 — all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, #22480, draft, body read back byte-identical; (2) label-write --assign os-sales, POST /issues/22480/assignees; (3) label-write --add skip-changeset, POST /issues/22480/labels, read back; (4) post-stamped --comment=16468, POST /issues/16468/comments (this report). git push is not counted.",
"open_questions": [
{
"question": "When a later refresh's window max x 1.25 differs from an existing ceiling, what does the refresh write? The ruled clauses 'the ceiling is the slowest executed run in the refresh window x 1.25' + 'refreshed with it' and 'a ceiling rises only by a ruling' conflict from the second refresh on.",
"options": [
"A HOLD (built): keep every existing ceiling; set one only for a package with none. 实际业务: brakes cumulative growth (no 1.25^n compounding) and never reds on variance. 长远: no parameter, but speedups are not locked in. 防AI犯错: reds stay trustworthy, since a quiet window cannot tighten the line. 不扩散: no new surface.",
"B SHRINK-ONLY: min(held, window max x 1.25), the line ratchets' rule. 实际业务: locks in speedups. 长远: ratchets toward the quietest window. Measured hazard: plugin-approvals read 1.23x its window max within a day, so a quiet window would put ordinary runs over the line. 防AI犯错: variance reds teach agents to rerun and ignore.",
"C FREE-FOLLOW: recompute every refresh, as the seat's analysis wrote ('每次刷新自动跟着走'). 实际业务: no false reds from stale lines, but growth of under 25% per week compounds unchecked. This contradicts 'rises only by a ruling'."
],
"recommendation": "A. It is the only option that honours 'rises only by a ruling' without making variance a red; the card's growth-constraint purpose is served by the hard cap. Under all three the first table is identical, so a ruling is needed only before the second refresh. If B or C is ruled, it is a one-line change in suiteCeilings()."
},
{
"question": "Clause 3 ('a package with no ceiling row is red until the next refresh gives it one') is unreachable for a new package: a refresh measures only what runs on main, and the new package's PR is red, so it never lands. Built as ruled: the only way in is a RULED_CEILING_RAISES entry (a ruling per new or renamed package). Is that the intended cost?",
"options": [
"A keep as ruled: each new or renamed package needs a ruling to land",
"B a new package (absent from the dataset) prints 'no ceiling: new package' and is not red until the first refresh after it lands"
],
"recommendation": "A for now, as ruled: it is the strict reading and is consistent with 创业阶段不扩散 (adding a package grows the suite). But the card body's 'until the next refresh' suggests the ruling did not foresee the deadlock; a one-line ruling either way settles it."
}
],
"out_of_scope_findings": [
"carrier: noted, not filed · scripts/report-test-timings.mjs header says samplesFromSummary 'reads the
testtask only'; since the test:repo fold it sums both tasks. Comment drift, outside this card's surface. Dedupe words: report-test-timings header test:repo samplesFromSummary"]
}
objectstack-fleet commentedon Oct 9, 2026
ACCEPT — PR #22480 (head
caffe8376d) · 2026-10-09T13:02ZReviewed by
domain:devxseat 1 ·session_0115N1oNnQS5WqofZ2DzaT3q, against GitHub andorigin/main, not the report.Checklist:
main. The first line isPart of #16468and the second isClause-②: no. A full body scan finds no closing keyword beside any card number.6079762551).lint.ymlonly wires the new self-test, as the claim's roster item allows.check-governed-merges --pr 22480: 0 of 5 paths).skip-changesetis correct.scripts/test-shard-timings.jsonis unchanged. It carries noceilingsyet, so nothing is hand-typed or seeded.partition-test-shards --self-testat this head reads the same density-capped bins and@objectstack/cli x3.Re-run by the seat at
caffe8376d:check-test-suite-ceilings --self-testgives "self-test OK (35 cases across 7 batteries)".measure-test-shard-timings --self-testgives "self-test OK".The ruling, as built:
ceilingsfield.CI, read by the seat at
caffe8376d:successand 5 skipped (check-expected-skips --pr 22480: OK).Lint & Repo Gates(113818659977),TypeScript Type Check(113819964615) andTest Core(113822664199) aresuccess.113822664199) and printed: "suite-ceiling: NOT MEASURED -- Test Core: no ceiling table yet … Nothing was graded and nothing failed."The false-red measurement, accepted as reported:
plugin-approvalsat 0.98 andplugin-authat 0.97.Deviations, accepted:
report-test-timings.mjsis not imported, because importing it broke the dispatch-gates promotion invariant. The reader stays the sharedsamplesFromSummary.Open questions 1–2 (how a later refresh treats an existing ceiling; how a new package lands) go to the maintainer on this card, with this act's label change. The check stays inert until the first refresh's PR merges, and that PR is to merge only after the answers.
Landing: ready + auto-merge now.
Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Landed, part 1: PR #22480 →
6abd265320· 2026-10-09T13:42Zdomain:devxseat 1 ·session_0115N1oNnQS5WqofZ2DzaT3q. The seat verified this onorigin/mainby content.main(scripts/check-test-suite-ceilings.mjs,scripts/measure-test-shard-timings.mjs,.github/workflows/shard-timings-refresh.yml,lint.yml,ci.yml, and the unchangedscripts/test-shard-timings.json) equal the ACCEPTed headcaffe8376d(ACCEPT6081431927).CIandLint & Type Checkaresuccess. All sixTest Core (N/6)jobs aresuccess, with job walls of 12.8 / 4.1 / 4.4 / 1.2 / 1.1 / 1.1 min.113838103055) printed: "suite-ceiling: NOT MEASURED -- Test Core: no ceiling table yet … Nothing was graded and nothing failed." It read 3 shard captures (1/6, 2/6, 3/6). The ratchet is wired and inert, as intended.Part of). The PR body closed no other card.What remains, in order:
shard-timings-refresh.ymlwrites the firstceilingstable. Its PR must not merge before the answers, because the new-package question decides whether that table can deadlock a lane.Labels in this act:
pm:dispatched→needs-user-decision, and the seat unassigns itself. This seat closes its shift after this act, and the seat post #6023 carries the hand-over.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
待维护者裁决(两问,同一次答复即可):天花板在第二次刷新时怎么变;新包怎么进场 ·
domain:devxseat 1 · 2026-10-09T13:43Zos-sales·session_0115N1oNnQS5WqofZ2DzaT3q。⛔ 不是认领。棘轮已随 PR #22480 落地(6abd265320,见上一条落地记录6082129294),但还没启用:数据集里还没有ceilings,检查读 NOT MEASURED,不判红。第一张表由落地后的第一次刷新写入(定时任务在周一 05:30 UTC,也可以由维护者手动触发)。那次刷新的 PR 合入之前,必须先有下面两个答复。 本卡在本次写入中换成needs-user-decision,席位同时取消自己的 assign。一句话问题。 第一,刷新时已有的天花板要不要跟着变?第二,一个全新的包(数据集里还没有它)在第一次刷新给它天花板之前,PR 是红还是不红?如果按现在的写法判红,它就永远合不进来,也就永远等不到那次刷新。
Governing text
6079579826(A):“the ceiling is the package's slowest executed run in the refresh window × 1.25”。前提(每条带复核命令)
git show origin/main:scripts/measure-test-shard-timings.mjs | grep -n "suiteCeilings"。RULED_CEILING_RAISES里登记一条带裁定链接的条目。复核:git show origin/main:scripts/check-test-suite-ceilings.mjs | grep -n "RULED_CEILING_RAISES"。6080943264):在正常分片负载下,13 次窗口之后的定时运行、108 个读数,0 个超线。最接近的两个:plugin-approvals0.98、plugin-auth0.97。另外,plugin-approvals一天之内读到过自己窗口最大值的 1.23 倍。问一:第二次刷新以后,已有天花板怎么变
plugin-approvals这类一天内就能波动 1.23 倍的包会开始误红业务直译:A 好比“限速一旦定下就只能靠审批改”;B 是“哪天路上车少,限速就自动下调”;C 是“限速每周按上周最快的车重新定”。
问二:新包怎么进场
RULED_CEILING_RAISES推荐:问一 A(保持),问二 B(新包先不判红,等第一次刷新)。 回退:问二 A,要接受每个新包都多一次裁定。只看 ① 选 A / B;② ③ ④ 是否翻转:否。
置信缺口。 问一选 A 时,提速锁不住,只能靠后续的裁定下调;问二选 B 时,一个新包最长有一周没有刹车。
裁后执行
两问都是
scripts/measure-test-shard-timings.mjs的suiteCeilings()或检查脚本里的一行改动,交给本车道下一任席位派发。答复落定后,再合入第一次刷新的 PR。Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Ruling: batch #305 item 1 · letters Q1 A · Q2 B (an existing ceiling holds across refreshes; a new package is not red before its first ceiling) · maintainer 「同意」 2026-10-09T14:10Z
Director seat, summon #35,
session_01VYToj6PQehTEKNrjGM9akg(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). Presented in batch #305 from thedomain:devxseat 1's second decision request (6082144903), after the ratchet ruled A (6079579826) was built and landed as PR #22480 →6abd265320(ACCEPT 6081431927, landing record 6082129294) and found two cases the first ruling did not reach: Q1 what a later refresh does to an existing ceiling (A hold it, set one only for a package with none; B shrink-only; C recompute each refresh); Q2 how a package absent from the dataset lands (A red until a ruled raise, as built; B "no ceiling: new package", not red until the first refresh after it lands). The seat recommended Q1 A and Q2 B; this seat recommended the same; the maintainer answered 「同意」. Thread-read: 6082144903. Freshness: body carries the firstRuled:line and nothing else changed; no comment since the presentation; labelstooling,priority:p2,needs-user-decision,domain:devx. Premises re-read onorigin/main35ef501e13:scripts/measure-test-shard-timings.mjs:205suiteCeilings({ ..., prior })and:187"A refresh NEVER raises a ceiling" (the hold, as built);scripts/check-test-suite-ceilings.mjs:129RULED_CEILING_RAISESis empty and:31–:33make a ruled raise the only entry for an absent package (the deadlock, as built);scripts/test-shard-timings.jsoncarries noceilingsyet, so the check reads NOT MEASURED; the refresh is scheduled Mondays 05:30 UTC (shard-timings-refresh.yml:164); the false-red reading after the window: 108 readings, 0 over, nearest 0.98 and 0.97.The ruling
Q1 A — an existing ceiling holds across refreshes. A refresh never raises and never lowers a ceiling it already holds; it sets one only for a package that has none. A ceiling moves in either direction only by a ruling recorded in
RULED_CEILING_RAISES(a lowering is a ruled entry too, so a measured speedup can be locked in when someone asks). This is the as-built behaviour; no code changes. ⛔ Not taken: B (a quiet window tightens the line into ordinary variance;plugin-approvalsread 1.23× its window maximum within a day), C (growth under 25% a week compounds unchecked, against "rises only by a ruling").Q2 B — a new package is not red before its first ceiling. A package absent from the dataset prints "no ceiling: new package" and is not red; the first refresh after it lands gives it a ceiling under the ruled formula. Clause 3 of the card ("red, not skipped, until the next refresh gives it one") is read with its own parenthesis: a new package reaches the next refresh only by landing, so the red there is lifted; a package the dataset knows but that lost its row is a different case and stays red. One-line change in
check-test-suite-ceilings.mjs, dispatched by the devx lane's next seat. ⛔ Not taken: Q2 A (a ruling per new or renamed package, a lane-wide deadlock on every PR that adds a package).Order: the first refresh's PR (the first
ceilingstable) merges only after the Q2 change has landed, so the table cannot deadlock a lane.Prior rulings read: 6079579826 (this card, A: the formula and the provisional handling, unchanged here); the card's clauses 1–3 (2026-09-07 authority); PR #22480 (the ratchet as built, its ablations and its false-red measurement); #22075 (the CLI slicing, summed across shards). 自检: 只看①选 A / B;②③④ 是否翻转:否。置信缺口:Q1 A 下提速不自动锁住,靠裁定下调;Q2 B 下一个新包最长一周没有刹车(到下一个周一的刷新)。
State
needs-user-decision→pm:queue(domain:devx,priority:p2,tooling) in this act; a secondRuled:line added above the first. The devx lane's next seat dispatches the one-line Q2 change; the first refresh's PR waits for it.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Claim: PM loop round 1
Session:
session_01LYXc6ckoWuZyVZpWYizdMhAccount:
os-bill(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-16468-new-package-not-redWorktree:
objectstack-issue-16468Domain:
domain:devxSeat:
domain:devx#1File surface:
scripts/check-test-suite-ceilings.mjs: a package absent from the dataset prints "no ceiling: new package" and is not red (ruling Q2 B). The header prose, theRULED_CEILING_RAISEScomment and the red-reason text that state the old rule change with it. Its self-test battery for the absent case is inverted to bear the new meaning..github/workflows/ci.yml("a package absent from the dataset is red"), and any other sentence the dev's sweep finds stating that rule, listed in the PR body.scripts/measure-test-shard-timings.mjs(Q1 A is the as-built behaviour). ⛔scripts/test-shard-timings.json,shard-timings-refresh.yml,scripts/partition-test-shards.mjsand ci(test-shards): the shard balance is derived on full-run sums while PR and merge_group runs use the affected set — the CLI shard (1/6) measures 34–36 min against 10–20 for the others and sets CI and queue wall time #22075's split are untouched.Container & model:
M(a one-line semantic change with a pin inversion; the judgment is in the semantics, not in a gate),mode:subagent,model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate"Clause-②: no
Responsibility:
n/a — not a defect card (maintainer-directed growth constraint, ruled follow-up)Ruling-ref:
6082629070(batch 🔗 Broken links detected in documentation #305 item 1, letters Q1 A · Q2 B)Thread-read: 6082629070
Serial constraints cleared:
no open PR touches scripts/check-test-suite-ceilings.mjs or .github/workflows/ci.yml (the file lists of all 8 open PRs were read by git diff against their merge bases). #22075 (this seat, pm:blocked) has nothing in flight; its possible slice-count follow-up waits behind this card. #22085 (seat 2) holds no code surface. Dispatch is serial, on the maintainer's word in this session's chat: 「串行派发」— read at 2026-10-09T14:22ZLabels in this act:
pm:queue→pm:dispatched; assigneeos-bill.objectstack-fleet commentedon Oct 9, 2026
os-dev-report
{
"issue": 16468,
"status": "done",
"branch": "claude/issue-16468-new-package-not-red",
"pr": "#22498",
"session": "session_01LYXc6ckoWuZyVZpWYizdMh (subagent: the parent's id; container CLAUDE_CODE_REMOTE_SESSION_ID matches)",
"premise_still_valid": true,
"summary": "Ruling Q2 B is implemented in scripts/check-test-suite-ceilings.mjs. A package absent from the dataset with no ruled entry now gets row status
new. It prints-- no ceiling: new package (not in scripts/test-shard-timings.json; the first refresh after it lands enters it)and is not red, because RED_STATUSES is now {over} alone. Assumption 2 holds only in part. The weight-without-row refusal is the lost-row case, as the PM expected. A second shape was NOT refused: a ceiling,uncappedreason orprovisionalmark that names a package with no weight. As built it gradedabsent(red); a bare Q2 change would have graded it new and passed, ignoring a held ceiling. readCeilingTable now refuses it too. The generator cannot write that shape (suiteCeilings iteratespackages, and provisional is a subset of packages), so only a hand edit reaches it. The generator's own drop path (a held ceiling dropped with its weight) is closed by the refresh lane for any package still in the workspace: coverageReportlost, then exit 1 with no PR. Assumption 6 measured yes: a ruled entry still gives a new package a ceiling, and 31 s against a ruled 30 s reads over, exit 1. Q1 A as built: the effective ceiling is max(generated, ruled entry), so an entry below the generated ceiling lowers nothing and the verdict tells the author to delete it as stale. The ruling's 'a lowering is a ruled entry too' therefore has no as-built mechanism; nothing about lowering was changed. ci.yml lines 1462-1463 (comment only) now state the ruled rule. A tree-wide sweep found no other sentence stating the old rule.","tests": "At HEAD 791aaf5:
node scripts/check-test-suite-ceilings.mjs --self-testexit 0, 'self-test OK (42 cases across 7 batteries)' (base 35ef501: 35 across 7). Battery 'a package absent from the dataset is red; an uncapped one is not' (floor 4) is inverted to 'a new package prints "no ceiling: new package" and is not red; a known one that lost its row is refused' (floor 10). Its cases: status new + exit 0 + verdict OK; the exact text 'brand-new: 5.00s -- no ceiling: new package'; beside an over package only that one is red and annotated; alone it reads NOT MEASURED, exit 0; the uncapped half is kept (2 cases); 4 lost-row refusals (weight without ceiling or reason; ceiling, uncapped reason or provisional mark without weight). The raise battery goes from 7 to 8 cases with 'a new package over its ruled ceiling is red'.node scripts/measure-test-shard-timings.mjs --self-testexit 0 'self-test OK' (unchanged). Ablations, after the fix was committed at 791aaf5, via scripts/ablation-replace.mjs: each anchor hit x1, the blob changed on disk, and the restore was proven as blob == HEAD 570a0ceaa046 with git diff HEAD empty. No build leg (root scripts, no dist). Leg 1, the PM's (RED_STATUSES gets 'new'): exit 1 via a TypeError (r.ceiling null) in the red renderer, inside the inverted battery's first case. Leg 1b (as leg 1, with the red detail and annotation filtered to rows with a ceiling): exit 1 by the pin's own assertion 'new: exit 1, verdict OVER, new'. Leg 2 (weightless refusal off): exit 1 'lost row: a ceiling with no weight was graded as new (null)'. Leg 2b (provisional dropped from the set): exit 1 on the provisional case. Leg 2c (uncapped dropped): exit 1 on the uncapped case. Leg 3 (text reverted): exit 1 'new: the line is wrong'. Probe: a first table built by the generator's suiteCeilings over the live dataset (72 weights, 71 ceilings, 1 uncapped provisional) passes readCeilingTable, and a new package graded against it prints the new line, exit 0. Lint, a proven narrowing: eslint --print-config resolves 2 rules for the script, with no parserOptions.project or projectService, and the files glob excludes .yml; eslint --no-inline-config --format json read 1 file, 0 errors, 0 warnings; with no type-aware linting the diff cannot move other files. Control-byte self-scan: no hits.","mcp_calls": "0",
"api_writes": "3 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) pr_create, run 37948097639: POST /repos/objectstack-ai/objectstack/pulls; draft #22498; body read back identical, 10042 chars. (2) label-write, run 37948207388: POST /issues/22498/labels (skip-changeset) and POST /issues/22498/assignees (os-bill); read back matches (ci/cd, size/m, skip-changeset; os-bill). (3) post-stamped: POST /issues/16468/comments (this report). git push is not REST. Reads: gh api GET on the card, its comments, the PR and its check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed (Acceptance notes of PR #22498 only). A refresh run by hand with the generator's documented provenance.refresh command has no coverage check, so it can drop a still-existing package's held ceiling with its weight. That package then grades as new, and the next refresh sets a fresh ceiling. The lane workflow refuses this (select-shard-timings-run coverageReport lost, then exit 1), and it is unexercised."
],
"gates": {
"head": "791aaf55",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set 2 paths vs merge base 35ef501): 58 commands",
"results": "53 foreground exit 0 + pnpm check:pm-dispatch-gates exit 0 (run detached per its header; log verdict line '✓ dispatch-gates self-test: 2011 cases pass.', 1377.4s, no lifecycle failure line) = 54 run",
"not_measured": [
"pnpm check:dts-closure :: exit 3",
"pnpm check:dual-build-cjs-loads :: exit 3",
"pnpm check:lean-entry-closure :: exit 3",
"pnpm check:sourcemap-no-sources-content :: exit 3"
],
"not_measured_reason": "PREREQUISITE NOT MET: they read built dist/. Declared narrowing: none of the four reads ci.yml (only doc comments name it), the diff touches no package files[], and the shared turbo cache is cold. CI runs them in Build Core.",
"extra": [
"node scripts/measure-test-shard-timings.mjs --self-test :: exit 0"
],
"ran_reconciliation": "✓ dispatch-gates --ran: 58 derived famil(ies) accounted for — 54 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3); 0 UNRUN",
"ci": "in_progress at report time (one read of 791aaf5: 34 check runs, 15 success, 5 skipped, 14 in_progress); not awaited"
},
"line_budget": {
"changed_lines": 165,
"additions": 124,
"deletions": 41,
"human_merge_threshold": "3000 on origin/main e148ca9 (5000 at base 35ef501)",
"verdict": "under",
"skills_readings": "n/a (no skills/** path)"
},
"deviations": [
"Beyond the one-line change: readCeilingTable also refuses a ceiling, uncapped reason or provisional mark with no weight. This is needed so the ruling's 'lost its row stays red' holds; without it that as-built red shape becomes 'no ceiling: new package'. It is inside the claimed file.",
"The PM's ablation (leg 1) turned the battery red by a TypeError, not by the pin's assertion. Leg 1b was added so the inverted assertion itself is shown failing.",
"The row status was renamed absent to new; the PM route allowed either.",
"Four dist-reading gates are NOT MEASURED, as a declared narrowing (gates.not_measured_reason).",
"pnpm check:pm-dispatch-gates exit status was read from its log (verdict line, no ELIFECYCLE line) via the header-prescribed nohup invocation, not a captured $?.",
"Commit trailers use the AGENTS.md model-free pair, not the harness reminder's model-named Co-Authored-By. The PR footer is the AGENTS.md session-URL form, not the harness reminder's form.",
"PR body line 1 is 'Fixes #16468' as dispatched, so merging closes the card; the ruling's order (first refresh PR after this) is satisfied by this landing first."
],
"files_changed": [
".github/workflows/ci.yml (+3/-1, comment above 'Check the per-package suite-duration ceilings' only)",
"scripts/check-test-suite-ceilings.mjs (+121/-40)"
]
}
objectstack-fleet commentedon Oct 9, 2026
ACCEPT — PR #22498 (head
791aaf55) · 2026-10-09T15:03ZReviewed by
domain:devxseat 1 ·os-bill·session_01LYXc6ckoWuZyVZpWYizdMh, against GitHub andorigin/main, not the report (os-dev-reporton this card). Claim:6082833475. Ruling-ref:6082629070.Checklist:
main. Line 1 isFixes #16468, line 2 isClause-②: no. A full body scan finds exactly one closing keyword, beside this card.scripts/check-test-suite-ceilings.mjs, and.github/workflows/ci.ymlas a comment-only hunk (3+/1−, no step change).measure-test-shard-timings.mjs,test-shard-timings.json,shard-timings-refresh.ymlandpartition-test-shards.mjsare untouched.check-governed-merges --pr 22498: NOT governed, 165 changed lines).skip-changesetis correct: root script plus a workflow comment, nothing published.The ruling, as built (read in the diff):
new, prints "no ceiling: new package (…)", and is not red.RED_STATUSESis{over}. The red header, detail and annotation lose their no-ceiling branch.uncappedreason orprovisionalmark that names a package with no weight is now refused. Without that, the shape that gradedabsent(red) as built would have read as new (not red). The seat checked the blast radius onorigin/main.buildDatasetbuildspackagesfrom executed ∪ carried.provisionalPackagesdraws only from those two sets.suiteCeilingsiteratespackages. So the generator cannot write the refused shape, and the refusal is not a lane-wide red risk.RULED_CEILING_RAISEScomment and theci.ymlcomment all state the ruled rule.Re-run by the seat at
791aaf55(a detached review worktree):check-test-suite-ceilings --self-test: "self-test OK (42 cases across 7 batteries)".measure-test-shard-timings --self-test: "self-test OK".'new'put back intoRED_STATUSES): exit 1 (aTypeErrorin the red renderer, as the dev's leg 1 reports; the dev's leg 1b shows the pin's own assertion failing). Restored clean.Assumption 6, measured by the dev and pinned: a ruled entry still gives a new package a ceiling, and 31 s against a ruled 30 s reads
over, exit 1.Recorded, not filed:
max(generated, entry), so an entry below the generated ceiling lowers nothing and is reported stale. Nobody is asking for a lowering today. The parenthetical needs a code change on the day someone does. Noted for the director's ledger; no card (zero pull).provenance.refresh) has no coverage check, so it could drop a still-existing package's held ceiling. The refresh lane refuses that (coverageReportlost, then exit 1). The path is unexercised. → Acceptance notes.CI at
791aaf55, read at this act: 35 check runs; 24success, 5 skipped, 6in_progress(Lint & Repo GatesandTest Core (1/6)among them). ⛔ Not ready yet: the seat flipspr_ready+automerge_enableonceLint & Repo Gates,TypeScript Type Checkand every other check read green or expected-skip on this head.Order: this landing comes before the first
shard-timings-refreshPR, as the ruling orders. The next Monday 05:30 UTC run writes the firstceilingstable.objectstack-fleet commentedon Oct 9, 2026
Landed: PR #22498 →
535c0199cb· 2026-10-09T16:13Zdomain:devxseat 1 ·os-bill·session_01LYXc6ckoWuZyVZpWYizdMh. Verified onorigin/mainby content.535c0199cbis an ancestor oforigin/main.mainare blob-identical to the ACCEPTed head791aaf55(ACCEPT6083541859):scripts/check-test-suite-ceilings.mjs(570a0ceaa0) and.github/workflows/ci.yml(dd5c2189de).6082629070is now onmain. Q2 B: a package absent from the dataset prints "no ceiling: new package" and is not red. A package the dataset knows that lost its row is refused. Q1 A is unchanged, as built.shard-timings-refreshPR, so the firstceilingstable (written by the next Monday 05:30 UTC run) cannot deadlock a lane.Fixes #16468), so this card closedcompletedand no other card did.6abd265320, part 2 this PR. The check stays NOT MEASURED until the first refresh writesceilings. A persistent NOT MEASURED after that run is a defect the step itself flags.Labels in this act:
pm:dispatchedremoved (the card closed with it on).tooling·priority:p2·domain:devxstay.