ci(lint): measure where Lint & Repo Gates' 198 serial steps spend 14–37 minutes, and bring the maintainer a split plan (maintainer-directed) #22991
Description
Protects: the merge queue's required context Lint & Repo Gates (AGENTS.md:519), the published check-run name every PR, merge group and release of @objectstack/* passes through; this card keeps the name and cuts its wall.
Ruled: 6120820653 · letter D · 2026-10-12T08:44Z
Filing gate ③: a maintainer-directed task. In the domain:devx seat 1 session (session_012yK1ddAdAqfzwigRHZq3r2), the maintainer asked「22973 一个 ci 30 分钟还没跑完,其他主流类似的系统也是这么慢吗」. The seat answered with the readings below and offered a card to measure the job's step-time distribution and bring back a split plan. The maintainer replied, verbatim:「开卡吧」.
Reader who acts: domain:devx seat 1 dispatches the measurement (part 1). The split plan (part 2) goes to the maintainer as a decision with the four-axis analysis. ⛔ lint.yml is not restructured before that ruling. Lane and grade are triage's.
What is measured (seat 1, 2026-10-12)
-
PR ci: Test Core's two timing gates exit-grade on
schedulealone (C2), with a 20s ceiling floor #22973 (headef3d0764b): 31 of its 32 non-skipped checks finished within 8.5 minutes. Test Core's slowest shard was 8.5 min, Type Check 3–4 min, Build Docs 3.6 min.Lint & Repo Gatesalone was still running at 32 minutes, with 128 of its 197 steps done. -
Lint & Repo Gates, newest completed runs (lint.yml, jobs API, 05:06Z–07:50Z):event runs min median p90 max pull_request24 6.1 14.3 20.9 35.9 merge_group25 10.5 15.5 34.5 37.6 Every run has 198 steps in one job on one runner.
-
It is the job the queue waits on about half the time. In 13 of those 25 merge groups,
Lint & Repo Gatesfinished after the wholeCIworkflow of the same group, by up to 23.3 minutes. The differences, lint end minus CI end in minutes, run −14.3 … +23.3. -
The workflow's size:
lint.ymlhas 7186 lines and 284- name:steps, 199pnpm check:invocations and 118node scripts/invocations.scripts/holds 165check-*.mjsfiles.
What to do
- Measure (dispatchable). Read the step-level
started_at/completed_atthat the jobs API returns, over a window of PR and merge_group runs. Report:- each step's median and p90;
- the top steps by share of the job;
- which steps are already path-scoped by
scripts/ci/select-gate-families.sh(ci(lint): Lint & Repo Gates is the merge-group critical path after #16453; run each gate family only on the paths it reads (PM dispatch-gates self-test 518 s first) #16496), and which run on every PR; - the
needschain, if a later step needs an earlier one's output.
⛔ No change tolint.ymlin this part.
- Propose (for the maintainer's ruling). Give options with workload and four-axis analysis, at least these:
- (a) split the job into N parallel lanes behind an aggregator that keeps the required
Lint & Repo Gatescontext. The precedent isTypeScript Type Checkand itsType Check · *lanes in the same file. - (b) extend ci(lint): Lint & Repo Gates is the merge-group critical path after #16453; run each gate family only on the paths it reads (PM dispatch-gates self-test 518 s first) #16496's per-family path scoping to the steps that still run on every PR.
- (c) a cache for gates whose inputs did not change.
- (d) a combination.
Name the critical path each option is expected to reach, as a measurement-backed estimate, never a guess.
- (a) split the job into N parallel lanes behind an aggregator that keeps the required
Prior work (read before proposing)
- ci(lint): Lint & Repo Gates is the merge-group critical path after #16453; run each gate family only on the paths it reads (PM dispatch-gates self-test 518 s first) #16496 (closed
completed, PR ci(lint): run each scoped Lint & Repo Gates family only on the paths it reads #16754):select-gate-families.sh, path-scoped families on merge groups. - ci(lint)/pm: the PM dispatch-gates self-test (7.6–13.1 min, one step) is Lint & Repo Gates' critical path on PR and merge_group runs; its family read-set selects it on most PRs — tier the battery or narrow the read-set #22076 (closed
completed): the PM dispatch-gates self-test, 7.6–13.1 min, was this job's critical path; it was tiered. - [finding]
Lint & Repo Gates在 merge group 里 ≥26 分钟不结论,队列在它仍 in_progress 时踢掉 PR —— #16496 修过的关键路径形状再现,而且连着两组都命中 #19189 (closednot_plannedon 2026-09-25, triage5831394361): "Lint & Repo Gates is the merge-group critical path". It was closed because the job then measured 8–14 min at parity with the other legs. Its reopen condition: "a merge group'sLint & Repo Gatesis again the job the queue waits on". The table above meets that condition in 13 of 25 groups. - Split
Lint & Repo Gatesinto parallel lanes behind an aggregator, the wayTypeScript Type Checkalready is: 193 serial steps are 28 minutes of every PR's critical path and one step is 41% of them #19686 (closedduplicateof [finding]Lint & Repo Gates在 merge group 里 ≥26 分钟不结论,队列在它仍 in_progress 时踢掉 PR —— #16496 修过的关键路径形状再现,而且连着两组都命中 #19189): the earlier measured split proposal, "193 serial steps are 28 minutes … the wayTypeScript Type Checkalready is". - ci(lint): every job in lint.yml carries a
timeout-minutesderived from its measured duration —Lint & Repo Gateshas none, so a hang holds the merge-queue head until GitHub's 360-minute default #21050: this job'stimeout-minutes.
Dedupe
- MCP
search_issueson this repo, closed included: "Lint & Repo Gates job duration serial steps shard parallel matrix split lint.yml wall time" → 31 hits, all read by title. None is open. - The nearest are [finding]
Lint & Repo Gates在 merge group 里 ≥26 分钟不结论,队列在它仍 in_progress 时踢掉 PR —— #16496 修过的关键路径形状再现,而且连着两组都命中 #19189 and SplitLint & Repo Gatesinto parallel lanes behind an aggregator, the wayTypeScript Type Checkalready is: 193 serial steps are 28 minutes of every PR's critical path and one step is 41% of them #19686, both closed. This card is new because it is maintainer-directed, and it asks for a measurement and a plan, not an immediate split. - Dedupe words: Lint & Repo Gates; serial steps; critical path; split lanes; aggregator; select-gate-families.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 12, 2026
Ruling: batch #326 item 1 · letter D · maintainer 「同意,立卡」 2026-10-12T08:44Z
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). The maintainer asked for this card's analysis on the direct channel (「22991 帮我综合分析,和我讨论」); this seat measured the job (part 1 of the card, below), presented the four options of part 2 with the critical path each reaches, and recommended D. The maintainer stated the governing principle verbatim, 「我觉得能让整个车队提速的方案就是好方案,main 很多天才会发一个版本,我只要确保发版本之前做好各种测试就可以。」, and answered 「同意,立卡」. The principle is filed as the governance card #22998 (two-speed CI); this card is its first execution.The ruling
D: option (a), lanes behind an aggregator, plus the PM dispatch-gates self-test's slow tier leaves the
pull_request/merge_grouppath.Lint & Repo Gatesbecomes an aggregator job that onlyneeds:the lanes, name kept (the required context is matched by name, AGENTS.md:519); the lanes get new names. The roster discipline ofTypeScript Type Checkapplies:OS_AGGREGATOR_MEMBERSin YAML,scripts/check-aggregator-roster.mjsstatically,toJSON(needs)at run time.pnpm check:pm-dispatch-gatesonpull_request/merge_groupruns the FAST tier (--self-test --fast); the SLOW tier runs onscheduleandpush: main, the full battery. ci(lint)/pm: the PM dispatch-gates self-test (7.6–13.1 min, one step) is Lint & Repo Gates' critical path on PR and merge_group runs; its family read-set selects it on most PRs — tier the battery or narrow the read-set #22076's constraint stands: every case still runs at least daily. The slow tier's red is a main-red under the governance card's rule.CIworkflow (13.6 min median), which the governance card addresses.What this seat measured (part 1 of the card; 31 completed
Lint & Repo Gatesruns, 18pull_requestand 13merge_group, read from the jobs API on 2026-10-12)steps.gate-families.outputs.*(9 reads); one step id in the jobCIworkflow of the same groups 13.6 medianThe per-run and per-step data sit in this seat's scratchpad (
audit/lint-steps.json); the reading script uses only the jobs API and the dev can re-run it.Prior rulings read: #19189 (closed
not_planned; its reopen condition is met by the table above, and this card carries it), #19686 (the aggregator pattern and roster discipline), #22076 (the two tiers; CI runs both today per the header ofscripts/pm/check-dispatch-gates.mjs), #16496 (five scoped families), #21050 (timeouts), AGENTS.md:519 (required contexts by name).After this ruling
tooling·domain:devx·area:devpath·priority:p1(the grade [finding]Lint & Repo Gates在 merge group 里 ≥26 分钟不结论,队列在它仍 in_progress 时踢掉 PR —— #16496 修过的关键路径形状再现,而且连着两组都命中 #19189 carried when this job stalled the queue; triage may re-grade) ·pm:queue. Body line 1 names the protected surface (the tooling queue-entry rule).Type Check · workspace(11.8 min median on merge groups, the next bottleneck) and theTest Coreshard variance (3.7 min median, 15.2 p90), on the governance card's lane.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 · the director seat dispatching on the maintainer's order, verbatim 「三仓的 ci 优化卡你负责派发」 (this seat's session, 2026-10-12T08:55Z)
Session:
session_01LqNgdPZB2Jnr5HpGbaqLeBAccount:
os-zhuangBranch:
claude/issue-22991-lint-lanesWorktree:
objectstack-issue-22991Domain:
domain:devxSeat:
domain:devx#9File surface:
.github/workflows/lint.yml(thelintjob becomes lanes plus an aggregator namedLint & Repo Gates),scripts/check-aggregator-roster.mjs(only if a second aggregator needs a parameter),scripts/pm/check-dispatch-gates.mjsand the rootpackage.jsonscriptcheck:pm-dispatch-gates(the fast tier onpull_request/merge_group, both tiers onschedule/push);scripts/ci/select-gate-families.shonly if a lane needs the selector's output as a job output (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(the default judgment tier;dispatch-gates --tier --repo objectstack-ai/objectstackon the paths answers "no path-derived mandate … floor sonnet · default opus · ceiling fable"; the lane cut is a design judgment, so the default, not the floor)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: 6120820653
Ruling-ref: 6120820653
Serial constraints cleared: #22998 (this seat's sibling claim, same round) edits
.github/workflows/ci.yml,.github/workflows/release.yml,AGENTS.mdand tworeferences/*.mdfiles, notlint.yml; its AGENTS.md paragraph namesLint & Repo Gates, which this card keeps by name. In-flightpm:dispatchedclaims read in this act (14 cards): none declareslint.yml(#22979 declaresSKILL.mdandplatform-readings.md). PR #22916 (S2,pm:blocked) does not touchlint.yml. Mergeorigin/mainbefore opening the PR and again after #22998 lands.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 22991,
"status": "done",
"branch": "claude/issue-22991-lint-lanes",
"pr": "#23014",
"session": "session_01LqNgdPZB2Jnr5HpGbaqLeB (subagent: the dispatching director seat's id)",
"premise_still_valid": true,
"summary": "Ruling D is implemented in draft PR #23014 (head 79b0fd6). The 193-step
lintjob is now eight parallelLint · *lanes. The aggregator keeps job idlintand the nameLint & Repo Gatesbyte-identical, and follows the typecheck roster discipline: OS_AGGREGATOR_MEMBERS equals needs:, toJSON(needs) is checked at run time, and the job runs under if: always(). Every gate step moved verbatim, proven by a parse-and-compare: 184 steps, same run/if/comment, same relative order, other jobs and triggers unchanged.check:pm-dispatch-gatesnow runs the fast tier on pull_request and merge_group, and both tiers on every other CI event. The PR's own run 38233160614 is green: all 8 lanes, the aggregate, andLint & Repo Gateson the check list. The aggregate verdict landed 5.57 min after run start, against a 35.5 min median for comparable runs before.","lanes_measured_on_pr_run": {
"run": "38233160614 (pull_request, head 79b0fd6; every scoped family selected, so this is the heaviest PR shape)",
"note": "One run, so these are single-run walls, not medians. 'predicted p50' is the 70-run rebuild. Wall = job started_at to completed_at; body = wall minus the checkout step.",
"rows": [
"lint-docs-code | Lint · docs & code guards | wall 5.42 | body 2.65 | checkout 166 s (outlier; window median 32, max 42) | predicted p50 4.1",
"lint-scoped-erasure | Lint · scoped · query-options erasure | wall 4.37 | body 3.82 | erasure ratchet 167 s | predicted p50 3.5",
"lint-scoped-slot-corpus | Lint · scoped · slot lookup & corpus | wall 4.30 | body 3.82 | slot-lookup 87 s, corpus 72 s, entry-guard 34 s | predicted p50 3.5",
"lint-scoped-tooling | Lint · scoped · tooling self-tests | wall 3.92 | body 3.38 | verify-lock 99 s, workflow-cmd 52 s, bare-root 21 s | predicted p50 1.1",
"lint-pm-dispatch-gates | Lint · PM dispatch-gates | wall 3.88 | body 3.42 | PM self-test 174 s (fast tier; both-tier window n=23 min 477 / median 847 / max 903 s) | predicted p50 1.1",
"lint-eslint | Lint · ESLint | wall 3.52 | body 2.97 | ESLint 105 s, engine test-double 42 s | predicted p50 3.3",
"lint-ci-engine | Lint · CI, release & engine gates | wall 2.98 | body 2.55 | predicted p50 3.9",
"lint-repo-tooling | Lint · repo & tooling gates | wall 2.92 | body 2.52 | predicted p50 4.2",
"lint (aggregate) | Lint & Repo Gates | wall 0.07 | verdict at +5.57 min after run start"
]
},
"critical_path": {
"before": "Old single job, 70 successful runs 2026-10-12T04:40Z to 08:45Z: p50 17.8 / p90 35.9 / max 38.5 min. Runs where every heavy scoped family ran (n=22, the shape of this PR): min 22.9 / median 35.5 / max 38.5. Runs without the PM self-test (n=47): median 14.3, max 20.9.",
"after_on_pr_run": "5.57 min from run start to the
Lint & Repo Gatesverdict. The slowest lane is docs-code at 5.42, and that comes from its 166 s checkout. Without the outlier the slowest is the erasure lane at 4.37.Type Check · workspace(9.62) is now the longer required leg in the same run, the next bottleneck the ruling names.","after_predicted": "From the 70-run rebuild: p50 4.2 / p90 4.7 / max 5.1 min on pull_request and merge_group. On schedule and push the PM lane runs both tiers, about 15–16 min."
},
"files_changed": [
".github/workflows/lint.yml +1443/-615 (the lint job becomes the aggregator plus 8 lanes; comments now name the step a moved neighbour referred to)",
"scripts/pm/check-dispatch-gates.mjs +91/-28 (tier by GITHUB_EVENT_NAME under GITHUB_ACTIONS; the self-test covers each event)",
"scripts/check-required-contexts.mjs +101/-12 (the pin may live in a lane the lint aggregate declares AND needs, with no if: at step or lane level; 4 new fixtures)",
"scripts/check-aggregator-roster.mjs +23/-6 (lint registered as a required aggregate; lint positive controls; typecheck fixtures anchored)",
"PR total +1658/-661 = 2319 changed lines (under the 3000 human-merge size line)"
],
"gates": [
"node scripts/check-aggregator-roster.mjs :: exit 0",
"node scripts/check-aggregator-roster.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
"node scripts/check-merged-branch-reaper-outcome.mjs :: exit 0",
"node scripts/check-merged-branch-reaper-outcome.mjs --self-test :: exit 0",
"node scripts/check-position-name-fold-loaders.mjs :: exit 0",
"node scripts/check-position-name-fold-loaders.mjs --self-test :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-self-test-wired.mjs :: exit 0",
"node scripts/check-self-test-wired.mjs --self-test :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
"node scripts/check-step-collectors.mjs :: exit 0",
"node scripts/check-step-collectors.mjs --self-test :: exit 0",
"node scripts/check-whole-set-label-write.mjs :: exit 0",
"node scripts/check-whole-set-label-write.mjs --self-test :: exit 0",
"node scripts/ci/scheduled-full-run.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0",
"node scripts/pm/ci-failure.mjs --self-test :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:bash32-floor :: exit 0",
"pnpm check:cli-command-ids :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:declared-population-live :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:entry-guard :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:node-version :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:parse-guard :: exit 0",
"pnpm check:pm-expected-skips :: exit 0",
"pnpm check:pm-post-stamped :: exit 0",
"pnpm check:pnpm-acquisition :: exit 0",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:ratchet-remedy-authority :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:required-contexts :: exit 0",
"pnpm check:select-gate-families :: exit 0",
"pnpm check:shard-attestation :: exit 0",
"pnpm check:stall-guard-budget :: exit 0",
"pnpm check:stall-guard-headroom :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:workflow-status-functions :: exit 0",
"pnpm check:workflow-step-name-quoting :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0",
"NOT-MEASURED pnpm check:type-check-debt :: its --self-test half passed and its coverage verdict printed OK on f9c1ba0; the --re-measure half runs tsc over the ledgered package and was cut at this container's 500 s timeout (exit 124) before a verdict, and needs the build CI's Type Check · debt ledger lane does first. This diff touches no TypeScript program; the family is matched only because the script reads lint.yml.",
"node scripts/pm/dispatch-gates.mjs --ran ran.txt --repo objectstack-ai/objectstack :: exit 0 (54 derived, 53 run, 1 NOT-MEASURED, 0 UNRUN; at 79b0fd6)"
],
"tests": "The local union was run at 79b0fd6, the final head after merging origin/main. The set came from
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 54 commands. 52 ran in the foreground, all exit 0 (482 s).pnpm check:pm-dispatch-gatesran detached, exit 0: fast tier, 1828 cases, 264.9 s.pnpm check:type-check-debtis NOT MEASURED: its --self-test passed, but --re-measure is tsc over the ledgered package, needs the lane's build, and was cut at 500 s (exit 124); this diff touches no TS program.--ranreconciled 54/53/1/0. YAML was parsed by bothyamland PyYAML; actionlint is not installed. Move proof: a one-off parse-and-compare of old vs new lint.yml reported 0 problems over 184 gate steps. Ablations, each through scripts/ablation-replace.mjs, mutation confirmed on disk and restored blob == HEAD:\n(1) FAST_TIER_EVENTS emptied: first run EXIT 0, because the self-test looped over the constant under test. Fixed by naming the two events literally; rerun red, 2 cases.\n(2) Event branch forced to always-fast: red, 5 cases.\n(3)lint-ci-enginedeleted from the aggregate's needs: check:required-contexts red, naming the unwired pin.\n(4) The needs: read in jobsCarriedByLint deleted: red on the declared-only fixture.\n(5) lint dropped from REQUIRED_AGGREGATORS: roster self-test red on the lint refusal fixture.\nTwo earlier ablation attempts exited 1 from the tool refusing an empty --replacement; no mutation landed there, and both were rerun with --delete. CI: run 38233160614 is green on every lane and the aggregate.","deviations": [
"H3 variant: the fast tier is a CLOSED list (pull_request, merge_group) rather than 'fast unless schedule or push'. workflow_dispatch and any unlisted event keep both tiers. The ruling moved the slow tier off those two events only, and this keeps the safe failure direction.",
"H2 cut refined: H2's single scoped lane rebuilds to p50 6.4 / max 11.5 min, over the ruled 5-minute budget. The scoped families are split over three lanes plus the PM lane, and query-options erasure and slot-lookup are kept apart.",
"File surface: scripts/check-required-contexts.mjs is outside the claim's declared surface. It was edited because its wiring pin read jobs.lint.steps literally and went red when the pin step moved into a lane. The edit is an honest re-pin, not a loosening. check-aggregator-roster.mjs got a registry row and fixtures, not a parameter.",
"The root package.json
check:pm-dispatch-gatesscript is unchanged: the event decision lives inside the .mjs, so no workflow or manifest edit was needed.","New keys beyond pure moves: each lane's checkout step has
timeout-minutes: 20, the Type Check lanes' checkout-budget scheme. Lane walls are 30, and 50 for the PM lane. The aggregate wall dropped from 55 to 10. The tail-reporter pair is repeated in every lane and the selector in the four scoped lanes.","The PR body carries the PREDICTED lane table: the body is written once, at create, before its own run existed. The measured walls are in this report; the seat may fold them into the body.",
"PM fast tier on CI is evidenced by duration only: 174 s, against a both-tier minimum of 477 s (n=23). The job log is blob-hosted and not reachable over REST from this container, and no MCP read was made.",
"Commit trailers use the model-free pair AGENTS.md prescribes, not the harness reminder's model-named trailer.",
"origin/main was merged once, before the PR opened (79b0fd6). main has moved at least one commit since, and #22998 has not landed, so the claim's 'merge again after #22998 lands' is still owed."
],
"mcp_calls": "0",
"api_writes": "3 REST writes, each one relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches) that the board workflow executed as objectstack-fleet[bot]: (1) pr_create: POST /pulls (draft) + POST /issues/23014/assignees, run 38233112121; (2) label-write skip-changeset: POST /issues/23014/labels, run 38233208427; (3) this os-dev-report comment: POST /issues/22991/comments. Separately, 6
git pushto claude/issue-22991-lint-lanes (not REST).","open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed. check:select-gate-families pins the nine scoped if: spellings as text, but does not pair each with a selector step in the SAME job. A lane that loses its selector fails OPEN (the step runs on every PR), never silent-skip. Recorded in the PR's Acceptance notes.",
"carrier: 承接者:无 · noted, not filed. Every lane keeps fetch-depth: 0, because which of the ~190 gates read history was not re-derived. A per-lane shallow checkout would save about 20 s a lane. Recorded in Acceptance notes.",
"carrier: 承接者:无 · noted, not filed. The PM step's comment says 'Same family as the
Changeset-family gate self-testsstep above', but that step was already BELOW it before this split. Pre-existing prose drift; left as is."]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Review: ACCEPT · PR #23014 (draft, head
79b0fd6d) · director seat dispatching on the maintainer's order, 2026-10-12T10:12ZChecked on GitHub, not on the report's say-so:
main, body line 1Fixes #22991, assigneeos-zhuang, labelsci/cd·size/xl·skip-changeset(no package changes; the classifier's label is right for a workflow-and-scripts diff). 4 files, +1658/−661 = 2,319 changed lines, under the 3,000 human-merge line:.github/workflows/lint.yml(+1443/−615),scripts/pm/check-dispatch-gates.mjs(+91/−28),scripts/check-required-contexts.mjs(+101/−12),scripts/check-aggregator-roster.mjs(+23/−6). Nocontent/docs/releases/.scripts/check-required-contexts.mjs, whose wiring pin readjobs.lint.stepsliterally and went red when the pin step moved into a lane; the edit is a re-pin with four new fixtures, not a loosening (ablation 3 in the report: deletinglint-ci-enginefrom the aggregate'sneeds:turns it red).lintjob id and the nameLint & Repo Gatesare byte-identical on the PR's check list; the aggregate declaresOS_AGGREGATOR_MEMBERS, equalsneeds:, re-checkstoJSON(needs)at run time underif: always(); 184 gate steps moved verbatim (the dev's parse-and-compare: 0 problems); the fast tier runs onpull_requestandmerge_groupas a closed list, both tiers on every other event (a stricter variant of H3, accepted: the safe failure direction). Lane cut refined from H2 to eight lanes so no lane rebuilds over the 5-minute budget.79b0fd6d:Lint & Repo Gatessuccess; the eightLint · *lanes success;TypeScript Type Checkand its four lanes success (Type Check · debt ledgercovers the one locally NOT MEASURED half ofcheck:type-check-debt, cut at the container's 500 s cap);Governed Surface Queue Guardsuccess;Build Core,Temporal Conformanceskipped by the path filter (no package touched).dispatch-gates --commands, 53 run exit 0 (the PM battery detached: fast tier, 1,828 cases, 264.9 s), 1 NOT MEASURED with its reason,--ranreconciled 54/53/1/0 at the head. Five ablations throughscripts/ablation-replace.mjs, mutation confirmed on disk and restored blob == HEAD; the first ablation's false green (the self-test looping over the constant under test) was fixed by naming the events literally and re-run red.Lint & Repo Gatesat 5.57 min from run start. Lanes (wall min): docs & code guards 5.42 (a 166 s checkout outlier; window median 32 s), scoped · query-options erasure 4.37, scoped · slot lookup & corpus 4.30, scoped · tooling self-tests 3.92, PM dispatch-gates 3.88 (fast tier 174 s against a both-tier minimum of 477 s), ESLint 3.52, CI/release/engine gates 2.98, repo & tooling gates 2.92. Before, on 70 runs the same morning: p50 17.8 / p90 35.9 / max 38.5; runs of this shape (n=22) median 35.5. Predicted from the 70-run rebuild: p50 4.2 / p90 4.7 / max 5.1 onpull_requestandmerge_group; 15–16 min onscheduleandpush, where the PM lane runs both tiers.Type Check · workspace(9.62 min in the same run) is now the longer required leg: objectstack#23017.timeout-minutes: 20(the Type Check lanes' scheme), lane walls 30 (PM lane 50), aggregate wall 10; the selector repeated in the four scoped lanes and the tail-reporter pair in every lane; the PR body carries the predicted table, the measured one is above; the fast tier on CI is evidenced by duration (the job log host is unreachable from a container). Writes: 3 REST (pr_create, label-write, the report);mcp_calls0.origin/mainagain after skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998 lands (the claim's serial line;mainhas moved since79b0fd6d). The dev or this seat does it at landing time; a bare base merge carries no other change.Landing: Tier H; the maintainer approves and merges by hand. This card stays
pm:dispatcheduntil the PR lands; the landing record follows the merge.objectstack-fleet commentedon Oct 12, 2026
LANDED — PR #23014 is MERGED (2026-10-12T11:29Z, merge commit
5dda6002) and read in place onorigin/main5dda6002. Director seat, summon #37,session_01LqNgdPZB2Jnr5HpGbaqLeB, 2026-10-12T11:33Z.origin/mainlint.ymllanes namedLint · *OS_AGGREGATOR_MEMBERSinlint.yml(the lint aggregate joins the typecheck one)Lint & Repo GatesGITHUB_EVENT_NAMEread inscripts/pm/check-dispatch-gates.mjs(the fast/slow tier by event)Fixesline;pm:dispatchedstripped and the assignee released in this act. The queue closed no other card (Fixes #22991alone).origin/mainagain after skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998 lands") is moot: the maintainer queued ci(lint): Lint & Repo Gates becomes an aggregate over eight parallel lanes; PM dispatch-gates runs its fast tier on PR and queue (#22991) #23014 first; ci: two-speed CI — PR and queue runs carry the fast set, the full battery runs on main and gates the publish lane, required set 7 → 5 #23020 (skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998) builds on thismainin the queue and carries the second edit toscripts/check-required-contexts.mjs(disjoint hunks).pull_requestandmerge_groupruns ofmain's new shape (the PR's own run gave 5.57 min against a 35.5 median for its shape; predicted p50 4.2 / p90 4.7), and the slow-tier wall on the nextschedulerun (predicted 15–16 min). Recorded here after ten groups.Type Check · workspace, the same file), claimed and dispatched next.