Skip to content

Commit fa5d72e

Browse files
ci(workflows): measured timeout-minutes on 17 jobs, PR-only concurrency on 11 workflows, a paths filter on check-links (#26376)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9BFPQCAwNbKfEL5SeQxNa8m)_ Fixes #26085 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:给 17 个没有超时的 CI job 各配一个按自己近期运行时长定的 `timeout-minutes`;给 11 个 PR 触发的工作流加 `concurrency`,同一 PR 的新推送取消旧运行;`check-links` 只在改到它读的文档时才跑。 - **为什么改**:今天一个卡死的 job 会占住 runner 6 小时;一个 PR 连推三次就跑三遍这 11 个工作流;不碰文档的 PR 也跑链接检查。 - **风险与回滚**:风险在 `release.yml` 的 publish 超时(设 120 分钟,按各步骤上限求和再加一半,杀在 `changeset publish` 中途就是半发布);取消只对 `pull_request` 生效,push、merge_group、schedule、dispatch 一律不取消。回滚 = revert 本 PR,纯 YAML。 - **先看**:`.github/workflows/release.yml`(publish 的 120 分钟及注释)、`.github/workflows/pr-automation.yml`(concurrency 按「推送 / 标签与编辑」分两组)、`.github/workflows/check-links.yml`(paths 与目标侧盲区说明)。 - **两分钟验证**:看本 PR 自己的 Actions 页:开 PR 时贴标签触发的 `PR Automation` 运行没有取消 `opened` 那次运行(两次都完成);下一次推送后,上一轮 PR 工作流显示 cancelled。 - **需要你做的**:受管面(`.github/workflows/**`,Tier H):请 GOVERNED_APPROVERS(os-zhuang 或 hotlong)批准,批准后由席位落地。 ## What changes **1. `timeout-minutes` on the 17 jobs the card lists.** Re-read on `origin/main` 6390fe5 first: exactly the card's 17 jobs lacked one (no job moved), and after this PR every non-reusable job in `.github/workflows/` declares one (read below). Each value comes from that job's own recent runs and is written beside it in the YAML. Where only a run-level time exists, the run wall (`updated_at − run_started_at`, queue included) is used as an upper bound for the job. | Job | Readings (completed runs, success + failure) | p99 | max | Set | |:---|:---|---:|---:|---:| | `check-links / link-checker` | 100 runs, 2026-10-26..27 | 0.8 | 1.0 | 10 | | `codeql / analyze` | 22 green analyses in the last 100 runs (78 were cancelled), 2026-10-24..26 | 59.0 | 63.1 | 90 | | `cross-repo-issue-closer / close-foreign-issues` | 100 runs | 0.2 | 0.3 | 5 | | `docker-publish / publish` | all 4 job readings on record: 3.5, 3.5 (dispatch runs), 4.3 (18.1.0 attempt 2), 1.4 (attempt 1, red) | 4.3 | 4.3 | 30 | | `docs-drift-check / docs-drift` | 100 runs | 2.1 | 6.2 | 15 | | `duplicate-fix-guard / duplicate-fix-guard` | 100 runs | 0.3 | 0.8 | 5 | | `partof-closing-keyword-guard / partof-closing-keyword` | 100 runs | 0.8 | 0.8 | 5 | | `pr-automation / pr-size` | 100 runs (run wall, all three jobs) | 2.4 | 2.5 | 10 | | `pr-automation / auto-label` | same | 2.4 | 2.5 | 10 | | `pr-automation / changeset-check` | same | 2.4 | 2.5 | 10 | | `release / version-pr` | the 23 schedule + dispatch runs among the 400 most recent (2026-10-22..27) | 2.4 | 2.4 | 20 | | `release / release-integrity` | the 376 push runs among the 400 most recent (run wall, upper bound) | 4.6 | 6.9 | 20 | | `release / stale-prompts` | same 376 push runs; own job reading on 18.1.0 = 1.0 | 4.6 | 6.9 | 10 | | `release / publish` | 1 job reading: 18.1.0, run 40078805767 = 32.6 (publish step 15.5) | n/a | 32.6 | 120 | | `single-claim-path-guard / single-claim-path` | 100 runs | 0.8 | 0.9 | 5 | | `spec-liveness-check / liveness` | 100 runs (22 PR, 78 merge queue) | 1.3 | 1.4 | 10 | | `stale / stale` | 100 runs, 2026-07-19..10-26 | 0.6 | 26.6 | 45 | All minutes. Three values are deliberately wider than "p99 plus a bit", and each says why in the YAML: - **`publish` = 120.** One reading cannot give a p99, and a timeout here cancels mid-`changeset publish` — the half-published state that job's own concurrency note exists to prevent. So it is set from the steps' ceilings: build + Console build about 15 min cold (6.6 + 7.3 on 18.1.0), `release-verify-npm.mjs`'s retry budget of up to 35 min after `changeset publish`, and the dist-tags step's own `timeout-minutes: 15` — about 80 min in all, plus half again. The approval wait at the `release` environment is not job time. - **`docker-publish` = 30.** Four readings; the image is built for two platforms with arm64 under QEMU, and a cold layer cache was never measured. - **`stale` = 45.** p99 is 0.6 min, but one green run took 26.6 min (run 33285517150, 2026-08-30); the bound keeps it. **2. `concurrency` on the 11 PR-triggered workflows** (`check-links`, `docs-drift-check`, `duplicate-fix-guard`, `engine-split-metric`, `os-create-smoke`, `pack-smoke-optin`, `pr-automation`, `scaffold-e2e`, `spec-liveness-check`, `upgrade-harness-optin`, `validate-deps` — re-read on 6390fe5, the same 11). The group key is `ci.yml:112-114`'s, prefixed with the file stem: ```yaml concurrency: group: STEM-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} ``` `cancel-in-progress` is an expression rather than ci.yml's `true`, so only a `pull_request` run is ever cancelled: none of the 11 runs on `push`, and the one with `merge_group` (`spec-liveness-check`) can never cancel a queue run — its key is also the queue ref, unique per entry. Schedule and dispatch runs share a group per event and wait instead of cancelling. Two key refinements, because these workflows fire on PR activity other than a push: - **`pr-automation`** runs on `labeled` / `unlabeled` / `edited` too, and on those only `Check Changeset` runs (`pr-size` and `auto-label` skip). One group would let a label cancel a push run's size and label jobs — and the relay's `pr_create` writes labels right after the draft opens, so that would happen on almost every PR. The key gets a last segment, `meta` for those three actions and `code` otherwise, so each kind only supersedes its own. - **`pack-smoke-optin` / `upgrade-harness-optin`** run on `labeled`, but the job runs only for their own opt-in label. A run for any other label goes to a group of its own (`github.run_id`), so adding an unrelated label never cancels a 30–45 min opt-in run. **3. `check-links.yml` gets a `paths:` filter** (`workflow_dispatch` kept): the whole `content/` and `docs/` trees, the eight root pages in `LYCHEE_ARGS` since #26313, `lychee.toml`, `.lycheeignore`, and the ADR step's `scripts/check-adr-links.mjs` (+ its one import) and `docs/adr/**`, plus the workflow itself. Checked with lychee 0.24.2's `--dump-inputs` under CI's exact argv: 521 inputs, 0 outside the filter. On the last 49 landings of `main`, 10 touched one of these paths. The YAML states the blind spot (below) and that promoting the lane to required removes the filter in the same change. Not in this PR: the card's optional `permissions: {}` (out of the claim), `ci.yml` (untouched), and no job was renamed. ## Tests - `node scripts/ci/scheduled-full-run.mjs --check-concurrency` → exit 0 (ci.yml and lint.yml groups unchanged; this gate reads only those two files). - No job without `timeout-minutes`: a YAML walk of every `.github/workflows/*.yml` listing jobs with neither `timeout-minutes` nor `uses:` → empty (it listed the card's 17 on 6390fe5). The one `uses:` job, `release / docker`, is bounded by the callee's new `docker-publish / publish` value. - Every workflow parses as YAML. - lychee 0.24.2 (built from crates.io) with `LYCHEE_ARGS`: 2883 links, 0 errors; `--dump-inputs` 521 files, all inside the new `paths:`. ## Acceptance notes - **Target-side blind spot of the `check-links` filter.** Lychee also reads the files links point AT. Measured on 6390fe5, links from the inputs resolve to paths outside `content/` and `docs/` (about 100 links: `packages/*` directories from CONTRIBUTING.md, `LICENSE` and `ui/LICENSE` from LICENSING.md, `examples/*`, `skills/…`, `.github/workflows/…`). A PR that only renames or deletes one of those now skips this advisory lane; the break shows on the next PR that touches a doc or on a dispatch run. Covering it would mean `packages/**` in the filter (which nearly every PR touches) or a scheduled run; neither is in this card's scope. - `scripts/release-verify-npm.mjs` L80-82 says "the publish job declares no `timeout-minutes`, so GitHub's 360-minute job default bounds this step". After this PR the publish job's 120 min bounds it. That file is outside this claim's file surface, so the comment is left as is and reported to the seat. - The two 2026-10-27 scheduled Release runs (40426048088, 40375928411) are red in `version-pr`; not looked into here. ## Verification Local gate subset (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, 55 commands at HEAD 62f0d0f), each exit taken before any pipe, `--ran` reconciliation `55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN` (a DERIVED zero): <details><summary>Gate readings</summary> | Command | Exit | |:---|---:| | `node scripts/check-adr-links.mjs` | 0 | | `node scripts/check-adr-links.mjs --self-test` | 0 | | `node scripts/check-aggregator-roster.mjs` | 0 | | `node scripts/check-aggregator-roster.mjs --self-test` | 0 | | `node scripts/check-changeset-fixed.mjs` | 0 | | `node scripts/check-ci-filter-parity.mjs` | 0 | | `node scripts/check-closing-keyword-parity.mjs` | 0 | | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | | `node scripts/check-comment-mask-corpus.mjs` | 0 | | `node scripts/check-cross-repo-closer-outcome.mjs` | 0 | | `node scripts/check-cross-repo-closer-outcome.mjs --self-test` | 0 | | `node scripts/check-engine-split-ratio.mjs --days 90` | 0 | | `node scripts/check-osv-exemptions.mjs` | 0 | | `node scripts/check-osv-exemptions.mjs --self-test` | 0 | | `node scripts/check-self-test-wired.mjs` | 0 | | `node scripts/check-self-test-wired.mjs --self-test` | 0 | | `node scripts/check-self-test-workflow-commands.mjs` | 0 | | `node scripts/check-self-test-workflow-commands.mjs --self-test` | 0 | | `node scripts/check-step-collectors.mjs` | 0 | | `node scripts/check-step-collectors.mjs --self-test` | 0 | | `node scripts/check-whole-set-label-write.mjs` | 0 | | `node scripts/check-whole-set-label-write.mjs --self-test` | 0 | | `node scripts/ci/scheduled-full-run.mjs --self-test` | 0 | | `node scripts/docs-audit/check-affected-docs.mjs` | 0 | | `node scripts/docs-audit/check-drift-comment.mjs` | 0 | | `node scripts/osv-base-relative.mjs --self-test` | 0 | | `node scripts/pm/ci-failure.mjs --self-test` | 0 | | `node scripts/release-dist-tags.mjs --self-test` | 0 | | `node scripts/release-verify-npm.mjs --self-test` | 0 | | `pnpm check:agent-test-spelling` | 0 | | `pnpm check:cross-package-test-inputs` | 0 | | `pnpm check:declared-population-live` | 0 | | `pnpm check:driver-memory-census` | 0 | | `pnpm check:gitlink-declared` | 0 | | `pnpm check:node-version` | 0 | | `pnpm check:nul-bytes` | 0 | | `pnpm check:override-consistency` | 0 | | `pnpm check:partof-closing-keyword` | 0 | | `pnpm check:pm-dispatch-gates` | 0 | | `pnpm check:pm-expected-skips` | 0 | | `pnpm check:pm-governed-merges` | 0 | | `pnpm check:pm-post-stamped` | 0 | | `pnpm check:pm-record-recognisers` | 0 | | `pnpm check:pnpm-acquisition` | 0 | | `pnpm check:pnpm-filter-targets` | 0 | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:required-contexts` | 0 | | `pnpm check:shard-attestation` | 0 | | `pnpm check:single-claim-paths` | 0 | | `pnpm check:stall-guard-budget` | 0 | | `pnpm check:stall-guard-headroom` | 0 | | `pnpm check:vendor-export-contract-resolve` | 0 | | `pnpm check:watch-hint-literal` | 0 | | `pnpm check:workflow-status-functions` | 0 | | `pnpm check:workflow-step-name-quoting` | 0 | `node scripts/check-engine-split-ratio.mjs --days 90` first exited 2 (PREREQUISITE: the shallow clone sat inside its 90-day window); after `git fetch --shallow-since=2026-07-22 origin main` it exited 0 (ratio 97.6%). Also run, from the roster block that keeps its list under `.github/workflows`: `check-platform-checklist-watchdog` (+ `--self-test`), `check-ci-filter-parity --self-test`, `ci/select-shard-timings-run --self-test`, `pr-labels --self-test`, `check:pm-governed-prose`, `check:select-gate-families` → all exit 0. `check-governed-queue-guard.mjs` exits 1 locally because it reads only `GITHUB_EVENT_PATH`; its verdict is the required `Governed Surface Queue Guard` check on this PR. </details> Families that take a value from the workflow (`check-partof-closing-keyword`, `check-single-claim-paths`, `check-required-contexts --verify-required-set`, the shard attestations) are NOT MEASURED locally and are read from this PR's own check-runs. --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmSfkkJZXxR3bPjvRzh)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3acbeff commit fa5d72e

18 files changed

Lines changed: 181 additions & 0 deletions

‎.github/workflows/check-links.yml‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,56 @@ name: Check Links
2020
# "the anchors are fine". The fragment half is owned by `pnpm check:doc-anchors`
2121
# in `lint.yml`, which is REQUIRED where this lane is advisory; `lychee.toml`'s
2222
# `include_fragments` comment records why the enum is not the fix.
23+
#
24+
# `paths:` is what the two checks read: lychee's input globs in `LYCHEE_ARGS`
25+
# below (taken as whole `content/` and `docs/` trees, so an image or page a
26+
# link points at inside them counts too), its config, and the ADR step's
27+
# `docs/adr/**` and script. Measured on the last 49 landings of `main`
28+
# (6390fe52): 10 touched one of these, so the other 39 would not have run it.
29+
# ⚠️ The filter is blind to the TARGET side outside those trees: a PR that
30+
# only renames or deletes a file a doc links to (a `packages/*` directory
31+
# from CONTRIBUTING.md, `LICENSE` from LICENSING.md) skips this lane, and the
32+
# break shows on the next PR that touches a doc, or on a `workflow_dispatch`
33+
# run. Keep this list equal to `LYCHEE_ARGS`. ⛔ Promoting the lane into the
34+
# required set removes this filter in the same change: a required check
35+
# skipped by `paths:` never reports, the #6121 stall above.
2336
on:
2437
workflow_dispatch:
2538
pull_request:
2639
branches:
2740
- main
41+
paths:
42+
- 'content/**'
43+
- 'docs/**'
44+
- 'README.md'
45+
- 'ARCHITECTURE.md'
46+
- 'CHANGELOG.md'
47+
- 'RELEASE_NOTES.md'
48+
- 'CONTRIBUTING.md'
49+
- 'ROADMAP.md'
50+
- 'LICENSING.md'
51+
- 'CODE_OF_CONDUCT.md'
52+
- 'lychee.toml'
53+
- '.lycheeignore'
54+
- 'scripts/check-adr-links.mjs'
55+
- 'scripts/invoked-as.mjs'
56+
- '.github/workflows/check-links.yml'
57+
58+
# A newer run for the same pull request supersedes this one, so the older run is
59+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
60+
# `pull_request`, so no other event's run is ever cancelled.
61+
concurrency:
62+
group: check-links-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}
63+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2864

2965
jobs:
3066
link-checker:
3167
name: Check Documentation Links
3268
runs-on: ubuntu-latest
69+
# Run wall over the 100 most recent completed runs to 2026-10-27: p99 0.8 min, max
70+
# 1.0. The bound covers the lychee retry path (two binary downloads and the 15 s wait)
71+
# with headroom.
72+
timeout-minutes: 10
3373
# Least privilege: the job only reads the tree and runs lychee offline.
3474
# There is no issue-filing step, and `--offline` makes zero network
3575
# requests, so neither `issues: write` nor a GITHUB_TOKEN is needed.

‎.github/workflows/codeql.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ jobs:
2727
analyze:
2828
name: Analyze
2929
runs-on: ubuntu-latest
30+
# Run wall of the 22 analyses that completed (success) in the last 100 runs,
31+
# 2026-10-24..26: p99 59.0 min, max 63.1. The bound is that p99 plus about half again.
32+
timeout-minutes: 90
3033
permissions:
3134
actions: read
3235
contents: read

‎.github/workflows/cross-repo-issue-closer.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ jobs:
124124
name: Close issues referenced in other repositories
125125
if: github.event.pull_request.merged == true
126126
runs-on: ubuntu-latest
127+
# Run wall over the 100 most recent completed runs to 2026-10-27: p99 0.2 min, max
128+
# 0.3.
129+
timeout-minutes: 5
127130
steps:
128131
- name: Close (or report) cross-repo closing keywords
129132
uses: actions/github-script@v9

‎.github/workflows/docker-publish.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ jobs:
3636
publish:
3737
name: Build & push ghcr.io/objectstack-ai/objectstack
3838
runs-on: ubuntu-latest
39+
# Job wall, all four readings on record: 3.5 and 3.5 min (the two dispatch runs), 4.3
40+
# min (18.1.0, Release run 40078805767, attempt 2) and 1.4 min (attempt 1, red). Four
41+
# readings make p99 the max, 4.3; the bound is wide because the image is built for two
42+
# platforms, arm64 under QEMU, and a cold layer cache is unmeasured.
43+
timeout-minutes: 30
3944
permissions:
4045
contents: read
4146
packages: write

‎.github/workflows/docs-drift-check.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,20 @@ permissions:
7272
contents: read
7373
pull-requests: write
7474

75+
# A newer run for the same pull request supersedes this one, so the older run is
76+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
77+
# `pull_request`, so no other event's run is ever cancelled.
78+
concurrency:
79+
group: docs-drift-check-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}
80+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
81+
7582
jobs:
7683
docs-drift:
7784
name: Flag docs affected by code changes
7885
runs-on: ubuntu-latest
86+
# Run wall over the 100 most recent completed runs to 2026-10-27: p99 2.1 min, max
87+
# 6.2.
88+
timeout-minutes: 15
7989
steps:
8090
- name: Checkout repository
8191
uses: actions/checkout@v7

‎.github/workflows/duplicate-fix-guard.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,20 @@ on:
2727
permissions:
2828
pull-requests: read
2929

30+
# A newer run for the same pull request supersedes this one, so the older run is
31+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
32+
# `pull_request`, so no other event's run is ever cancelled.
33+
concurrency:
34+
group: duplicate-fix-guard-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}
35+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
36+
3037
jobs:
3138
duplicate-fix-guard:
3239
name: No other open PR may claim the same issue
3340
runs-on: ubuntu-latest
41+
# Run wall over the 100 most recent completed runs to 2026-10-27: p99 0.3 min, max
42+
# 0.8.
43+
timeout-minutes: 5
3444
steps:
3545
- name: Check declared issues against other open PRs
3646
uses: actions/github-script@v9

‎.github/workflows/engine-split-metric.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ on:
1818
permissions:
1919
contents: read
2020

21+
# A newer run for the same pull request supersedes this one, so the older run is
22+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
23+
# `pull_request`, so no other event's run is ever cancelled. The schedule and dispatch
24+
# runs share a group keyed on their own event and wait for each other instead.
25+
concurrency:
26+
group: engine-split-metric-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}
27+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
28+
2129
jobs:
2230
ratio:
2331
name: Engine cross-package commit ratio

‎.github/workflows/os-create-smoke.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ on:
6666
permissions:
6767
contents: read
6868

69+
# A newer run for the same pull request supersedes this one, so the older run is
70+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
71+
# `pull_request`, so no other event's run is ever cancelled. The schedule and dispatch
72+
# runs share a group keyed on their own event and wait for each other instead.
73+
concurrency:
74+
group: os-create-smoke-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}
75+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
76+
6977
jobs:
7078
create-scaffold-smoke:
7179
name: Scaffold outside the monorepo, install, build

‎.github/workflows/pack-smoke-optin.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ on:
8888
permissions:
8989
contents: read
9090

91+
# A newer run for the same pull request supersedes this one, so the older run is
92+
# cancelled. The group key is ci.yml's shape; `cancel-in-progress` is true only on
93+
# `pull_request`, so no other event's run is ever cancelled. An unrelated label's run
94+
# gets a group of its own (its job is skipped anyway), so adding some other label never
95+
# cancels a running `needs:pack-smoke` run.
96+
concurrency:
97+
group: pack-smoke-optin-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.after || github.ref }}-${{ github.event.action == 'labeled' && github.event.label.name != 'needs:pack-smoke' && github.run_id || 'run' }}
98+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
99+
91100
jobs:
92101
pack-smoke:
93102
# ~45 minutes of build + pack + clean install. Opt-in only — see the header.

‎.github/workflows/partof-closing-keyword-guard.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ jobs:
5050
partof-closing-keyword:
5151
name: Part-of PR must not also close its card
5252
runs-on: ubuntu-latest
53+
# Run wall over the 100 most recent completed runs to 2026-10-27: p99 0.8 min, max
54+
# 0.8.
55+
timeout-minutes: 5
5356
steps:
5457
- name: Checkout repository
5558
uses: actions/checkout@v7

0 commit comments

Comments
 (0)