From 8a367a76855175b73703cb7f6a3c4ef0aee0c16a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 02:59:15 +0000 Subject: [PATCH 1/2] ci(merge-queue): classify a no-runner red as infra:no-runner in the triage comment Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude --- .github/workflows/merge-queue-triage.yml | 191 +- .../check-run-111979038621.annotations.json | 34 + .../run-37371558473.jobs.json | 1023 +++++++++ .../run-37374282440.jobs.json | 1954 +++++++++++++++++ 4 files changed, 3199 insertions(+), 3 deletions(-) create mode 100644 scripts/fixtures/merge-queue-triage/check-run-111979038621.annotations.json create mode 100644 scripts/fixtures/merge-queue-triage/run-37371558473.jobs.json create mode 100644 scripts/fixtures/merge-queue-triage/run-37374282440.jobs.json diff --git a/.github/workflows/merge-queue-triage.yml b/.github/workflows/merge-queue-triage.yml index 60d87161a49..547995e0323 100644 --- a/.github/workflows/merge-queue-triage.yml +++ b/.github/workflows/merge-queue-triage.yml @@ -101,6 +101,45 @@ name: Merge Queue Triage # `skip`/`only` written anywhere. Weakening a gate stays a human act, and an # anchor issue is a place for that conversation, not a verdict in it. # +# ## A shard that never got a runner is not a test failure (limb ③) +# +# On 2026-10-05 one PR was ejected twice with nothing in it failing: queue +# build 37371558473 had ten jobs cancelled before any of them was handed a +# runner, and 37374282440 had one (`Test Core (5/6)`). In both, the only +# `failure` was an AGGREGATE gate (`Test Core`, `Dogfood Regression Gate`) +# refusing because a shard of its own family published no attestation. The +# comment printed those aggregates as the failure and "日志不可读" beside them, +# and a person had to open the jobs to tell a runner outage from a test red. +# +# So every run is now CLASSIFIED, from the job RECORD and never from log text: +# +# infra:no-runner a job ended `cancelled` with an empty `runner_name` and no +# steps, OR its check run carries the "not acquired by +# Runner" annotation (read only for a cancelled job the +# record did not already decide). +# +# The BUILD is `infra:no-runner` only when that explains EVERY red: each other +# red job is an aggregate whose failed steps are all `Verify … results` AND +# whose own shard family (` (k/n)`) has a no-runner member. Anything left +# unexplained — a real failing step, a cancelled job that did get a runner, an +# annotation that could not be read — makes it `failure`, read the way it +# always was. That direction is the safe one: a mis-read outage costs a person +# a look, a mis-read failure would be waved through as weather. +# +# ⛔ The attestation rule does not move (#6082): a shard that never ran still +# does not count as passing, and nothing here changes whether a build is +# green. This changes how a red is NAMED and what the comment advises. +# +# Re-queue budget: ONE per PR head sha. An `infra:no-runner` comment carries a +# per-head marker; the next `infra:no-runner` red on the SAME head finds it and +# says the budget is spent and a person has to look. A `failure` never earns +# the advice. ⚠️ The ACT is not wired: this job holds no credential that can +# enqueue a pull request (GitHub's auto-merge / enqueue mutations need +# `contents: write`, and a merge group created by this run's own GITHUB_TOKEN +# is not one CI is known to build), so the comment states the verdict and a +# person or seat performs the one re-queue. The harness enforces that: an +# enqueue call is an unmodelled API and fails the battery. +# # Fires on conclusion == failure ONLY. 'cancelled' is the queue evicting an # entry because something AHEAD of it failed (or a manual cancel) — it says # nothing about this PR, so it gets no comment (the same run-lifecycle @@ -110,7 +149,9 @@ name: Merge Queue Triage # workflow_run executes in the DEFAULT branch's context: this file must be on # main before it fires, it never checks out or runs PR code, and it holds the # minimum permissions (actions: read for logs, pull-requests: write for the -# comment, issues: write for the anchor issue limb ② files and refreshes). +# comment and the PR head limb ③ keys its budget on, issues: write for the +# anchor issue limb ② files and refreshes, checks: read for the "not acquired +# by Runner" annotation limb ③ consults). # # Delivery is retried, never assumed (#9424). A transient GitHub API failure used # to kill this job outright — github-script hands any throw from the script to @@ -156,6 +197,7 @@ jobs: actions: read pull-requests: write issues: write + checks: read steps: - name: Post the triage comment uses: actions/github-script@v9 @@ -244,12 +286,17 @@ jobs: // redelivered workflow_run stops HERE, before any sighting is recorded // and before any anchor is touched, so one queue build contributes its // signature exactly once however many times GitHub delivers it. + // + // The listing is kept (`prComments`) because limb ③ reads its + // per-head re-queue markers out of the same comments; `null` means + // it could not be read, which limb ③ must not mistake for "none". let alreadyPosted = false; + let prComments = null; try { - const existing = await github.rest.issues.listComments({ + prComments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: prNumber, per_page: 100, }); - alreadyPosted = existing.data.some((c) => (c.body ?? '').includes(marker)); + alreadyPosted = prComments.some((c) => (c.body ?? '').includes(marker)); } catch (error) { core.warning( `Could not read #${prNumber}'s comments to check for an existing triage ` @@ -269,6 +316,93 @@ jobs: }); const failedJobs = jobs.filter((j) => j.conclusion === 'failure'); + // ── Limb ③: classify the red — infra:no-runner or failure ────── + // + // Keyed on the job RECORD (see the header), never on log text: a + // job that never got a runner has no log to read. The annotation + // is consulted only for a cancelled job the record did not + // already decide, so the common case costs no extra request. + const NO_RUNNER_ANNOTATION = /not acquired by Runner/i; + const AGGREGATE_STEP = /^Verify .* results$/; + const GREEN = new Set(['success', 'skipped', 'neutral']); + const redJobs = jobs.filter((j) => !GREEN.has(j.conclusion)); + const noRunnerByRecord = (j) => j.conclusion === 'cancelled' + && String(j.runner_name ?? '') === '' + && (j.steps ?? []).length === 0; + const noRunner = []; + const annotationRefusals = []; + for (const j of redJobs.filter((x) => x.conclusion === 'cancelled')) { + if (noRunnerByRecord(j)) { noRunner.push({ job: j, via: 'record' }); continue; } + const checkRunId = Number(String(j.check_run_url ?? '').split('/').pop()) || j.id; + try { + const res = await github.rest.checks.listAnnotations({ + owner, repo, check_run_id: checkRunId, per_page: 100, + }); + if ((res.data ?? []).some((a) => NO_RUNNER_ANNOTATION.test(String(a?.message ?? '')))) { + noRunner.push({ job: j, via: 'annotation' }); + } + } catch (error) { + annotationRefusals.push(`${j.name}(${describe(error)})`); + } + } + const noRunnerNames = noRunner.map((x) => x.job.name); + // An aggregate red is EXPLAINED only by a no-runner member of its + // OWN shard family. A `Verify … results` failure with no such + // member is a shard that ran and did not attest — unexplained. + const explainedAggregate = (j) => { + const failedSteps = (j.steps ?? []).filter((s) => s.conclusion === 'failure'); + return j.conclusion === 'failure' + && failedSteps.length > 0 + && failedSteps.every((s) => AGGREGATE_STEP.test(s.name)) + && noRunnerNames.some((n) => n.startsWith(`${j.name} (`)); + }; + const explainedAggregates = redJobs.filter(explainedAggregate); + const unexplained = redJobs.filter((j) => + !noRunner.some((x) => x.job === j) && !explainedAggregates.includes(j)); + const infraOnly = noRunner.length > 0 && unexplained.length === 0; + const buildClass = infraOnly ? 'infra:no-runner' : 'failure'; + if (annotationRefusals.length > 0) { + core.warning( + `Could not read the check-run annotations of ${annotationRefusals.length} cancelled job(s), ` + + 'so they were NOT classified as infra:no-runner and the build reads as `failure`.', + { title: 'infra:no-runner classification incomplete' }, + ); + } + + // The re-queue budget: ONE per PR head sha. The head is read from + // the PR itself (the queue ref names the BASE the build stacked + // on, not the PR's head). Every way of not knowing — head + // unreadable, comments unreadable — is `unknown`, never `once`: + // a second automatic chance is the thing this must not invent. + const NO_RUNNER_MARK = //g; + let prHead = null; + let requeue = null; + if (infraOnly) { + let headRefusal = null; + try { + const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); + const sha = String(pr.data?.head?.sha ?? ''); + if (/^[0-9a-f]{40}$/.test(sha)) prHead = sha; + } catch (error) { + headRefusal = describe(error); + } + if (!prHead) { + requeue = { verdict: 'unknown', why: `读不到 PR 当前的 head sha(${headRefusal ?? 'head.sha 缺失'})` }; + } else if (prComments === null) { + requeue = { verdict: 'unknown', why: '读不到本 PR 的评论,无法确认这个 head 是否已经用掉过一次 infra:no-runner 重排' }; + } else { + const priorRuns = new Set(); + for (const c of prComments) { + for (const mm of String(c.body ?? '').matchAll(NO_RUNNER_MARK)) { + if (Number(mm[1]) === prNumber && mm[2] === prHead && mm[3] !== String(run.id)) priorRuns.add(mm[3]); + } + } + requeue = priorRuns.size === 0 + ? { verdict: 'once' } + : { verdict: 'person', priorRuns: [...priorRuns] }; + } + } + // ── Log harvest ──────────────────────────────────────────────── // // Best-effort: the lines a human would grep for first, plus the line @@ -834,6 +968,54 @@ jobs: ? `⚠️ **本 PR 过去 24h 已在队列失败 ${priorFailuresThisPr} 次(不含本次)。** 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。` : '本 PR 过去 24h 无队列失败记录(首次)。'; + // ── Limb ③ in the comment: the class, by name, and the verdict ── + const nameList = (list) => { + const shown = list.slice(0, 20).map((n) => `\`${n}\``).join('、'); + return list.length > 20 ? `${shown} 等共 ${list.length} 个` : shown; + }; + const classLines = []; + if (infraOnly) { + classLines.push( + '**分类:`infra:no-runner`** —— 这次的红**全部**来自没拿到 runner 的 job,**不是测试失败**。', + '判据读的是 job 记录,不是日志:`cancelled` + `runner_name` 为空 + 0 个步骤,或 check run 带 “not acquired by Runner” 注解。', + '', + `- 没拿到 runner 的 job(${noRunner.length} 个):${nameList(noRunnerNames)}`, + ); + if (explainedAggregates.length > 0) { + classLines.push(`- 聚合 job 的红只因为同族分片没跑:${nameList(explainedAggregates.map((j) => j.name))}(失败步骤只有 \`Verify … results\`)`); + } + classLines.push('- ⛔ 没跑的分片**仍然不算通过**(证明规则不变):这里只改「这次红怎么归类」,不改「构建算不算绿」。'); + if (requeue.verdict === 'once') { + classLines.push( + `- **重排判定:可以重排一次。** 这是 head \`${prHead.slice(0, 12)}\` 第一次因 infra:no-runner 被弹出。` + + '本 workflow 没有入队所需的凭据,**不会自动重排** —— 请人工重排**一次**。同一个 head 再次 infra:no-runner 红就交给人,不再重排。', + ); + } else if (requeue.verdict === 'person') { + classLines.push( + `- ⛔ **重排判定:交给人,不要再重排。** head \`${prHead.slice(0, 12)}\` 已经因 infra:no-runner 被弹出过` + + `(队列构建 ${requeue.priorRuns.map((id) => `[${id}](https://github.com/${owner}/${repo}/actions/runs/${id})`).join('、')}),` + + '每个 head 只给一次重排。连续没拿到 runner 需要人去看 runner 状态。', + ); + } else { + classLines.push(`- ⚠️ **重排判定:无法确认**(${requeue.why})⇒ 按「交给人」处理,不要据此重排。`); + } + } else { + classLines.push('**分类:`failure`** —— 按下面的日志分诊。'); + if (noRunner.length > 0) { + classLines.push( + '', + `- 另有 ${noRunner.length} 个 job 没拿到 runner(${nameList(noRunnerNames)}),` + + `但还有它们解释不了的红:${nameList(unexplained.map((j) => `${j.name} · ${j.conclusion}`))} ⇒ 不是纯 infra:no-runner,⛔ 不按 runner 故障重排。`, + ); + } + } + if (annotationRefusals.length > 0) { + classLines.push(`- ⚠️ ${annotationRefusals.length} 个 cancelled job 的注解读不到,未能判定是否 no-runner,已按 failure 计:${annotationRefusals.join('、')}`); + } + const noRunnerMarker = infraOnly && prHead + ? `` + : null; + // The sighting markers this comment contributes to the 24 h ledger. const sightingMarkers = runKeys.map((k) => ``); @@ -843,6 +1025,8 @@ jobs: `队列构建 [${run.id}](${run.html_url}) 红了。队列跑的是**全量**套件(PR 侧 CI 只跑 affected 子集),`, '所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。', '', + ...classLines, + '', '**失败的 job(日志抽取,best effort):**', '', jobSections || '- (没拿到 job 级信息,点上面的 run 链接看)', @@ -871,6 +1055,7 @@ jobs: '', marker, ...sightingMarkers, + ...(noRunnerMarker ? [noRunnerMarker] : []), '---', '_Generated by [Claude Code](https://claude.ai/code) · merge-queue-triage workflow (#4859)_', ].join('\n'); diff --git a/scripts/fixtures/merge-queue-triage/check-run-111979038621.annotations.json b/scripts/fixtures/merge-queue-triage/check-run-111979038621.annotations.json new file mode 100644 index 00000000000..8de11890ec1 --- /dev/null +++ b/scripts/fixtures/merge-queue-triage/check-run-111979038621.annotations.json @@ -0,0 +1,34 @@ +[ + { + "path": ".github", + "start_line": 1, + "end_line": 1, + "annotation_level": "failure", + "title": "", + "message": "The job was not acquired by Runner of type hosted even after multiple attempts" + }, + { + "path": ".github", + "start_line": 1, + "end_line": 1, + "annotation_level": "notice", + "title": "", + "message": "\"The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. For more information, see https://github.com/actions/runner-images/issues/14748\"" + }, + { + "path": ".github", + "start_line": 1, + "end_line": 1, + "annotation_level": "notice", + "title": "", + "message": "\"The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. For more information, see https://github.com/actions/runner-images/issues/14748\"" + }, + { + "path": ".github", + "start_line": 1, + "end_line": 1, + "annotation_level": "notice", + "title": "", + "message": "\"The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. For more information, see https://github.com/actions/runner-images/issues/14748\"" + } +] diff --git a/scripts/fixtures/merge-queue-triage/run-37371558473.jobs.json b/scripts/fixtures/merge-queue-triage/run-37371558473.jobs.json new file mode 100644 index 00000000000..31664d86761 --- /dev/null +++ b/scripts/fixtures/merge-queue-triage/run-37371558473.jobs.json @@ -0,0 +1,1023 @@ +{ + "run_id": 37371558473, + "total_count": 17, + "jobs": [ + { + "id": 111969698908, + "run_id": 37371558473, + "name": "filter", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111969698908", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111969698908", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213152, + "runner_name": "GitHub Actions 1001213152", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Run dorny/paths-filter@v4", + "status": "completed", + "conclusion": "skipped", + "number": 3 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 7 + } + ] + }, + { + "id": 111973256729, + "run_id": 37371558473, + "name": "Temporal Conformance (live PG + MySQL)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256729", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256729", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213230, + "runner_name": "GitHub Actions 1001213230", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Initialize containers", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Point both servers at a non-UTC timezone", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Build driver-sql and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Run driver-sql suite against both live servers", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Build the non-SQL temporal backends", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Run the non-SQL temporal backends under the skewed process zone", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Build metadata-protocol and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Run the metadata-protocol migration statements against live MySQL and PostgreSQL", + "status": "completed", + "conclusion": "success", + "number": 16 + }, + { + "name": "Build runtime and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 17 + }, + { + "name": "Run the runtime cascade-delete matrix against live PostgreSQL", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 33 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 34 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 35 + }, + { + "name": "Stop containers", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 37 + } + ] + }, + { + "id": 111973256742, + "run_id": 37371558473, + "name": "Build Core", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256742", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256742", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256751, + "run_id": 37371558473, + "name": "Dogfood Verify CLI", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256751", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256751", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213235, + "runner_name": "GitHub Actions 1001213235", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Verify example apps via the `objectstack verify` CLI", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Attest this leg ran and passed", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Publish this leg's attestation", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 20 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 21 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 22 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 23 + } + ] + }, + { + "id": 111973256812, + "run_id": 37371558473, + "name": "Dogfood Regression Gate (1/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256812", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256812", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213166, + "runner_name": "GitHub Actions 1001213166", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build the dogfood package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Boot example apps and exercise real user flows", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 12 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 13 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111973256846, + "run_id": 37371558473, + "name": "Console Pin Gate", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256846", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256846", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213174, + "runner_name": "GitHub Actions 1001213174", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build @objectstack/client and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Restore vendored Console dist (keyed on the pin and its other build inputs)", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Build the Console SPA at the pinned objectui SHA", + "status": "completed", + "conclusion": "skipped", + "number": 11 + }, + { + "name": "Assert a real Console dist is in place", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Verify the Console dist stamp matches the pin", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Verify the restored Console dist bundles this tree's spec", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Save vendored Console dist", + "status": "completed", + "conclusion": "skipped", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111973256854, + "run_id": 37371558473, + "name": "Test Core (2/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256854", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256854", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256890, + "run_id": 37371558473, + "name": "Test Core (6/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256890", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256890", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256891, + "run_id": 37371558473, + "name": "Build Docs", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256891", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256891", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213236, + "runner_name": "GitHub Actions 1001213236", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Setup Next.js cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build Docs (the command production runs)", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Assert the docs app actually built", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Post Setup Next.js cache", + "status": "completed", + "conclusion": "success", + "number": 17 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 20 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 21 + } + ] + }, + { + "id": 111973256927, + "run_id": 37371558473, + "name": "Test Core (4/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256927", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256927", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213173, + "runner_name": "GitHub Actions 1001213173", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111973256937, + "run_id": 37371558473, + "name": "Dogfood Regression Gate (3/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256937", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256937", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256946, + "run_id": 37371558473, + "name": "Test Core (5/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256946", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256946", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256949, + "run_id": 37371558473, + "name": "Test Core (3/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256949", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256949", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256982, + "run_id": 37371558473, + "name": "Test Core (1/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256982", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256982", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111973256996, + "run_id": 37371558473, + "name": "Dogfood Regression Gate (2/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111973256996", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111973256996", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111978265425, + "run_id": 37371558473, + "name": "Test Core", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111978265425", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111978265425", + "status": "completed", + "conclusion": "failure", + "runner_id": 1001213266, + "runner_name": "GitHub Actions 1001213266", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Download test shard attestations", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Verify test shard results", + "status": "completed", + "conclusion": "failure", + "number": 4 + }, + { + "name": "Download this run's shard test timings", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Publish the test timing table", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Publish the merged timing table artifact", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 15 + } + ] + }, + { + "id": 111978265628, + "run_id": 37371558473, + "name": "Dogfood Regression Gate", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37371558473/job/111978265628", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111978265628", + "status": "completed", + "conclusion": "failure", + "runner_id": 1001213267, + "runner_name": "GitHub Actions 1001213267", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Download dogfood attestations", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Verify dogfood shard results", + "status": "completed", + "conclusion": "failure", + "number": 4 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 9 + } + ] + } + ] +} diff --git a/scripts/fixtures/merge-queue-triage/run-37374282440.jobs.json b/scripts/fixtures/merge-queue-triage/run-37374282440.jobs.json new file mode 100644 index 00000000000..5799532329c --- /dev/null +++ b/scripts/fixtures/merge-queue-triage/run-37374282440.jobs.json @@ -0,0 +1,1954 @@ +{ + "run_id": 37374282440, + "total_count": 17, + "jobs": [ + { + "id": 111978791298, + "run_id": 37374282440, + "name": "filter", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111978791298", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111978791298", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213278, + "runner_name": "GitHub Actions 1001213278", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Run dorny/paths-filter@v4", + "status": "completed", + "conclusion": "skipped", + "number": 3 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 7 + } + ] + }, + { + "id": 111979038392, + "run_id": 37374282440, + "name": "Build Core", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038392", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038392", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213286, + "runner_name": "GitHub Actions 1001213286", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "check-dts-emitted self-test", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build packages (excluding docs)", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Sweep the built closure for vanished declarations", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "No compiled test files in any dist", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Verify build outputs", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Verify capability packages ship a runtime entry (no dts-only / half-built)", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Every published require entry point actually loads", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "No published source map embeds source text", + "status": "completed", + "conclusion": "success", + "number": 16 + }, + { + "name": "The lean engine entry loads no forbidden or unlisted package", + "status": "completed", + "conclusion": "success", + "number": 17 + }, + { + "name": "Analyze bundle size", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Upload build artifacts", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 20 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 39 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 40 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 41 + } + ] + }, + { + "id": 111979038408, + "run_id": 37374282440, + "name": "Temporal Conformance (live PG + MySQL)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038408", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038408", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213365, + "runner_name": "GitHub Actions 1001213365", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Initialize containers", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Point both servers at a non-UTC timezone", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Build driver-sql and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Run driver-sql suite against both live servers", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Build the non-SQL temporal backends", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Run the non-SQL temporal backends under the skewed process zone", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Build metadata-protocol and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Run the metadata-protocol migration statements against live MySQL and PostgreSQL", + "status": "completed", + "conclusion": "success", + "number": 16 + }, + { + "name": "Build runtime and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 17 + }, + { + "name": "Run the runtime cascade-delete matrix against live PostgreSQL", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 33 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 34 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 35 + }, + { + "name": "Stop containers", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 37 + } + ] + }, + { + "id": 111979038414, + "run_id": 37374282440, + "name": "Build Docs", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038414", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038414", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213367, + "runner_name": "GitHub Actions 1001213367", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Setup Next.js cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build Docs (the command production runs)", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Assert the docs app actually built", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Post Setup Next.js cache", + "status": "completed", + "conclusion": "success", + "number": 17 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 20 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 21 + } + ] + }, + { + "id": 111979038501, + "run_id": 37374282440, + "name": "Dogfood Regression Gate (2/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038501", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038501", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213370, + "runner_name": "GitHub Actions 1001213370", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build the dogfood package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Boot example apps and exercise real user flows", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 12 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 13 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111979038573, + "run_id": 37374282440, + "name": "Dogfood Verify CLI", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038573", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038573", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213413, + "runner_name": "GitHub Actions 1001213413", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Verify example apps via the `objectstack verify` CLI", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Attest this leg ran and passed", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Publish this leg's attestation", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 20 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 21 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 22 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 23 + } + ] + }, + { + "id": 111979038587, + "run_id": 37374282440, + "name": "Test Core (2/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038587", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038587", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213411, + "runner_name": "GitHub Actions 1001213411", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111979038621, + "run_id": 37374282440, + "name": "Test Core (5/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038621", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038621", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "steps": [] + }, + { + "id": 111979038639, + "run_id": 37374282440, + "name": "Console Pin Gate", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038639", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038639", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213377, + "runner_name": "GitHub Actions 1001213377", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build @objectstack/client and its dependencies", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Restore vendored Console dist (keyed on the pin and its other build inputs)", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Build the Console SPA at the pinned objectui SHA", + "status": "completed", + "conclusion": "skipped", + "number": 11 + }, + { + "name": "Assert a real Console dist is in place", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Verify the Console dist stamp matches the pin", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Verify the restored Console dist bundles this tree's spec", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Save vendored Console dist", + "status": "completed", + "conclusion": "skipped", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111979038654, + "run_id": 37374282440, + "name": "Test Core (1/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038654", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038654", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213369, + "runner_name": "GitHub Actions 1001213369", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111979038662, + "run_id": 37374282440, + "name": "Dogfood Regression Gate (1/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038662", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038662", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213374, + "runner_name": "GitHub Actions 1001213374", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build the dogfood package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Boot example apps and exercise real user flows", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 12 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 13 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111979038671, + "run_id": 37374282440, + "name": "Dogfood Regression Gate (3/3)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038671", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038671", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213371, + "runner_name": "GitHub Actions 1001213371", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Build the dogfood package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Boot example apps and exercise real user flows", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 12 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 13 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 28 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 29 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 30 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 31 + } + ] + }, + { + "id": 111979038738, + "run_id": 37374282440, + "name": "Test Core (6/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038738", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038738", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213412, + "runner_name": "GitHub Actions 1001213412", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111979038740, + "run_id": 37374282440, + "name": "Test Core (3/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038740", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038740", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213372, + "runner_name": "GitHub Actions 1001213372", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111979038748, + "run_id": 37374282440, + "name": "Test Core (4/6)", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111979038748", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111979038748", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213373, + "runner_name": "GitHub Actions 1001213373", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Setup pnpm", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Get pnpm store directory", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Restore Turbo cache", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Install dependencies", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Compute this shard's package set", + "status": "completed", + "conclusion": "success", + "number": 9 + }, + { + "name": "Build the sliced package's dependency closure", + "status": "completed", + "conclusion": "success", + "number": 10 + }, + { + "name": "Run this shard's tests", + "status": "completed", + "conclusion": "success", + "number": 11 + }, + { + "name": "Publish this shard's turbo run summary", + "status": "completed", + "conclusion": "success", + "number": 12 + }, + { + "name": "Test completeness guard", + "status": "completed", + "conclusion": "success", + "number": 13 + }, + { + "name": "Capture this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Publish this shard's test timings", + "status": "completed", + "conclusion": "success", + "number": 15 + }, + { + "name": "Upload stall diagnostic reports", + "status": "completed", + "conclusion": "skipped", + "number": 16 + }, + { + "name": "Save Turbo cache (main only)", + "status": "completed", + "conclusion": "skipped", + "number": 17 + }, + { + "name": "Attest this shard ran and passed", + "status": "completed", + "conclusion": "success", + "number": 18 + }, + { + "name": "Publish this shard's attestation", + "status": "completed", + "conclusion": "success", + "number": 19 + }, + { + "name": "Post Setup pnpm cache", + "status": "completed", + "conclusion": "success", + "number": 36 + }, + { + "name": "Post Setup Node.js", + "status": "completed", + "conclusion": "success", + "number": 37 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 38 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 39 + } + ] + }, + { + "id": 111985016801, + "run_id": 37374282440, + "name": "Dogfood Regression Gate", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111985016801", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111985016801", + "status": "completed", + "conclusion": "success", + "runner_id": 1001213437, + "runner_name": "GitHub Actions 1001213437", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Download dogfood attestations", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Verify dogfood shard results", + "status": "completed", + "conclusion": "success", + "number": 4 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 8 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 9 + } + ] + }, + { + "id": 111995193336, + "run_id": 37374282440, + "name": "Test Core", + "html_url": "https://github.com/objectstack-ai/objectstack/actions/runs/37374282440/job/111995193336", + "check_run_url": "https://api.github.com/repos/objectstack-ai/objectstack/check-runs/111995193336", + "status": "completed", + "conclusion": "failure", + "runner_id": 1001213546, + "runner_name": "GitHub Actions 1001213546", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1 + }, + { + "name": "Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 2 + }, + { + "name": "Download test shard attestations", + "status": "completed", + "conclusion": "success", + "number": 3 + }, + { + "name": "Verify test shard results", + "status": "completed", + "conclusion": "failure", + "number": 4 + }, + { + "name": "Download this run's shard test timings", + "status": "completed", + "conclusion": "success", + "number": 5 + }, + { + "name": "Publish the test timing table", + "status": "completed", + "conclusion": "success", + "number": 6 + }, + { + "name": "Publish the merged timing table artifact", + "status": "completed", + "conclusion": "success", + "number": 7 + }, + { + "name": "Post Checkout repository", + "status": "completed", + "conclusion": "success", + "number": 14 + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 15 + } + ] + } + ] +} From 499d0ae32e2259ee14eb10406e24c67013788548 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 03:01:28 +0000 Subject: [PATCH 2/2] ci(merge-queue): pin infra:no-runner classification and the per-head re-queue budget Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude --- .github/workflows/merge-queue-triage.yml | 2 +- scripts/check-merge-queue-triage-outcome.mjs | 357 +++++++++++++++++- scripts/fixtures/merge-queue-triage/README.md | 8 + 3 files changed, 362 insertions(+), 5 deletions(-) diff --git a/.github/workflows/merge-queue-triage.yml b/.github/workflows/merge-queue-triage.yml index 547995e0323..e85b6455812 100644 --- a/.github/workflows/merge-queue-triage.yml +++ b/.github/workflows/merge-queue-triage.yml @@ -104,7 +104,7 @@ name: Merge Queue Triage # ## A shard that never got a runner is not a test failure (limb ③) # # On 2026-10-05 one PR was ejected twice with nothing in it failing: queue -# build 37371558473 had ten jobs cancelled before any of them was handed a +# build 37371558473 had eight jobs cancelled before any of them was handed a # runner, and 37374282440 had one (`Test Core (5/6)`). In both, the only # `failure` was an AGGREGATE gate (`Test Core`, `Dogfood Regression Gate`) # refusing because a shard of its own family published no attestation. The diff --git a/scripts/check-merge-queue-triage-outcome.mjs b/scripts/check-merge-queue-triage-outcome.mjs index 2449f5f97a8..c1f664deb15 100644 --- a/scripts/check-merge-queue-triage-outcome.mjs +++ b/scripts/check-merge-queue-triage-outcome.mjs @@ -31,6 +31,16 @@ // ones, and the `listForRepo` double honours `state` and `labels` so they // can tell the two lookups apart at all. // +// limb (3) names the CLASS of a red -- `infra:no-runner` when every red is +// explained by jobs that never got a runner (read from the job RECORD and +// the check-run annotation, never from log text), `failure` otherwise -- +// and states a re-queue budget of ONE per PR head, carried by a per-head +// marker that the NEXT red on that head must find. Like limb (2), the +// property lives in the pair of runs (N3 drives it as one), and the real +// 2026-10-05 job records are the input (N1, N2). The act itself is not +// wired -- the workflow holds no credential that can enqueue -- so an +// enqueue call is unmodelled here on purpose (M28). +// // So the subject here is the shipped bytes, driven twice where it matters, with // real logs on the input side and a full call ledger on the output side. // @@ -52,8 +62,9 @@ // // ## The corpus, and where every byte of it came from // -// `scripts/fixtures/merge-queue-triage/` holds three files. None is invented, -// and each states which half of the argument it carries: +// `scripts/fixtures/merge-queue-triage/` holds three log files, plus the three +// limb (3) job-record captures its README describes. None is invented, and +// each states which half of the argument it carries: // // plugin-dev-timeout.job-log.txt / plugin-dev-assertion.job-log.txt // Real vitest 4.1.10 output, captured in this repo on 2026-08-20 by running @@ -241,6 +252,14 @@ function fixture(root, name) { return readFileSync(path, 'utf8').split('\\u001b').join(ESC); } +/** + * A trimmed REAL job-record capture -- `listJobsForWorkflowRun` for one queue + * build, cut down to the fields the shipped script reads (README: provenance). + */ +function jobsFixture(root, runId) { + return JSON.parse(fixture(root, `run-${runId}.jobs.json`)).jobs; +} + /** * Wrap captured tool output in the runner's line prefix. * @@ -265,6 +284,11 @@ const MODELLED_ISSUE_METHODS = new Set([ 'addLabels', ]); const MODELLED_ACTION_METHODS = new Set(['listJobsForWorkflowRun', 'listWorkflowRunsForRepo']); +// Limb (3) reads -- and ONLY reads -- these two. ⛔ An enqueue / auto-merge +// mutation is deliberately NOT modelled: the workflow holds no credential that +// can perform one, so a call to it fails the battery as an unmodelled access. +const MODELLED_PULL_METHODS = new Set(['get']); +const MODELLED_CHECK_METHODS = new Set(['listAnnotations']); /** * `github` / `context` / `core` doubles plus a full call ledger. @@ -307,12 +331,19 @@ function makeDoubles(world) { addLabelsError: null, queueRuns: [], prCommentPostError: null, + // Limb (3): the PR's current head (what the re-queue budget is keyed on) + // and the check-run annotations, keyed by check-run id. + prHeadSha: 'c'.repeat(40), + prGetError: null, + annotations: {}, + annotationErrors: {}, ...world, }; const calls = { listComments: [], listCommentsForRepo: [], listForRepo: [], createComment: [], issueCreate: [], issueUpdate: [], addLabels: [], logs: [], + pullsGet: [], listAnnotations: [], }; const log = { info: [], warning: [], failed: [], summary: [] }; const unstubbedCalls = []; @@ -377,6 +408,23 @@ function makeDoubles(world) { }, }; + const pullsApi = { + async get({ pull_number: n }) { + calls.pullsGet.push(n); + if (w.prGetError) throw w.prGetError; + return { data: { number: n, head: { sha: w.prHeadSha } } }; + }, + }; + + const checksApi = { + async listAnnotations({ check_run_id: id }) { + calls.listAnnotations.push(id); + const err = w.annotationErrors[id]; + if (err) throw err; + return { data: w.annotations[id] ?? [] }; + }, + }; + const actionsApi = { async listJobsForWorkflowRun() { return { data: w.jobs }; }, async listWorkflowRunsForRepo() { return { data: w.queueRuns }; }, @@ -406,9 +454,11 @@ function makeDoubles(world) { { issues: guard(issuesApi, 'github.rest.issues', MODELLED_ISSUE_METHODS), actions: guard(actionsApi, 'github.rest.actions', MODELLED_ACTION_METHODS), + pulls: guard(pullsApi, 'github.rest.pulls', MODELLED_PULL_METHODS), + checks: guard(checksApi, 'github.rest.checks', MODELLED_CHECK_METHODS), }, 'github.rest', - new Set(['issues', 'actions']), + new Set(['issues', 'actions', 'pulls', 'checks']), ); const github = guard( @@ -608,6 +658,15 @@ const guidanceProse = (r) => postedBody(r); const REFUSED = httpError(503, 'No server is currently available to service your request.'); +// Limb (3)'s readings, single-sourced so a scenario and a mutation cannot +// disagree about the spelling they look for. +const PR_HEAD = 'd'.repeat(40); +const INFRA_CLASS = '**分类:`infra:no-runner`**'; +const FAILURE_CLASS = '**分类:`failure`**'; +const VERDICT_ONCE = '重排判定:可以重排一次。'; +const VERDICT_PERSON = '重排判定:交给人,不要再重排。'; +const VERDICT_UNKNOWN = '重排判定:无法确认'; + function scenarios(root) { const TIMEOUT_LOG = asJobLog(fixture(root, 'plugin-dev-timeout.job-log.txt')); const ASSERTION_LOG = asJobLog(fixture(root, 'plugin-dev-assertion.job-log.txt')); @@ -638,6 +697,21 @@ function scenarios(root) { ...over, }); + // Limb (3)'s world: the REAL queue build that ejected #21872 on 2026-10-05 + // with nothing failing -- its run id, its queue ref and its job records. + // Only the PR head is synthetic (PR_HEAD, declared below): the budget is + // keyed on it, and any fixed 40-hex value exercises the same comparison. + const noRunnerWorld = (over = {}) => ({ + runId: 37374282440, + prNumber: 21872, + headBranch: 'gh-readonly-queue/main/pr-21872-cab639671528ef6f3a201e8995794378a4a28bfe', + headSha: '33486923c015218b3e203c948122c6aaa7bd1667', + prHeadSha: PR_HEAD, + jobs: jobsFixture(root, 37374282440), + logs: {}, + ...over, + }); + return [ // ── limb (1): the reason line ────────────────────────────────────────── { @@ -1157,6 +1231,187 @@ function scenarios(root) { ]; }, }, + // ── limb (3): infra:no-runner classification and the re-queue budget ── + { + id: 'N1', + name: 'REAL record (run 37374282440): one shard never got a runner -> `infra:no-runner`, re-queue ONCE', + world: () => noRunnerWorld(), + check(r, t) { + const body = postedBody(r); + return [ + t(body.includes(INFRA_CLASS), `the comment NAMES the class, got: ${JSON.stringify(body.slice(0, 600))}`), + t(body.includes('`Test Core (5/6)`'), 'the shard that never got a runner is named'), + t(/聚合 job 的红只因为同族分片没跑:`Test Core`/.test(body), 'the aggregate red is attributed to its own family\'s missing shard'), + t(body.includes(VERDICT_ONCE), 'the verdict is: re-queue ONCE'), + t(body.includes('仍然不算通过'), 'the comment restates that a shard that never ran still does not attest as passing'), + t(body.includes(``), + 'the durable per-head marker is written, keyed on the PR HEAD (not the queue sha)'), + t(r.calls.pullsGet.length === 1, `the PR head is read once, got ${r.calls.pullsGet.length}`), + t(r.calls.listAnnotations.length === 0, 'the record decided every cancelled job, so no annotation is fetched'), + t(r.log.failed.length === 0 && r.calls.createComment.length === 1, 'and the comment is delivered'), + ]; + }, + }, + { + id: 'N2', + name: 'REAL record (run 37371558473): eight jobs without a runner, two aggregates red -> `infra:no-runner`', + world: () => noRunnerWorld({ runId: 37371558473, jobs: jobsFixture(root, 37371558473) }), + check(r, t) { + const body = postedBody(r); + return [ + t(body.includes(INFRA_CLASS), 'the class is named'), + t(body.includes('没拿到 runner 的 job(8 个)'), `all eight no-runner jobs are counted, got: ${JSON.stringify(body.slice(0, 900))}`), + t(body.includes('`Build Core`'), 'a no-runner job outside any shard family (`Build Core`) is named too'), + t(body.includes('`Test Core`') && body.includes('`Dogfood Regression Gate`'), 'both aggregates are explained by their own families'), + t(body.includes(VERDICT_ONCE), 'first no-runner red on this head -> once'), + ]; + }, + }, + { + id: 'N3', + name: 'the SECOND no-runner red on the SAME head goes to a person, and is not re-queued', + // Driven as a pair, like A2: run 2 reads run 1's OWN comment, so the + // marker's spelling cannot drift between writer and reader unnoticed. + world: () => noRunnerWorld(), + rerun: (first) => noRunnerWorld({ runId: 37374999999, prComments: [postedBody(first)] }), + check(r, t, first) { + const body = postedBody(r); + return [ + t(postedBody(first).includes(VERDICT_ONCE), 'the first run granted the one re-queue'), + t(body.includes(INFRA_CLASS), 'the second run still names the class'), + t(body.includes(VERDICT_PERSON), `the second run sends it to a person, got: ${JSON.stringify(body.slice(0, 900))}`), + t(!body.includes(VERDICT_ONCE), 'and does NOT grant a second re-queue'), + t(body.includes('37374282440'), 'it names the earlier queue build that spent the budget'), + ]; + }, + }, + { + id: 'N4', + name: 'the budget is PER HEAD: a no-runner marker for an OLDER head does not spend the new head\'s re-queue', + world: () => noRunnerWorld({ + prComments: [`earlier\n`], + }), + check(r, t) { + return [ + t(postedBody(r).includes(VERDICT_ONCE), 'a new head gets its own one re-queue'), + t(!postedBody(r).includes(VERDICT_PERSON), 'and is not sent to a person on an old head\'s account'), + ]; + }, + }, + { + id: 'N5', + name: 'a REAL shard failure is `failure`, earns no re-queue, and writes no no-runner marker', + world: () => base(), + check(r, t) { + const body = postedBody(r); + return [ + t(body.includes(FAILURE_CLASS), `the class is named as failure, got: ${JSON.stringify(body.slice(0, 600))}`), + t(!body.includes(INFRA_CLASS) && !body.includes('重排判定'), 'no infra class and no re-queue verdict'), + t(!body.includes('merge-queue-infra:no-runner|'), 'no per-head marker is written -- a real failure spends no budget'), + t(r.calls.pullsGet.length === 0, 'the PR head is not even read'), + ]; + }, + }, + { + id: 'N6', + name: 'MIXED: a no-runner shard beside a REAL failing shard is `failure`, never re-queued', + world: () => noRunnerWorld({ + jobs: [ + ...jobsFixture(root, 37374282440).filter((j) => j.name !== 'Test Core (2/6)'), + shardJob(111, 'Test Core (2/6)'), + ], + logs: { 111: TIMEOUT_LOG }, + }), + check(r, t) { + const body = postedBody(r); + return [ + t(body.includes(FAILURE_CLASS), `the class is failure, got: ${JSON.stringify(body.slice(0, 900))}`), + t(!body.includes('重排判定'), 'no re-queue verdict at all'), + t(body.includes('另有 1 个 job 没拿到 runner') && body.includes('Test Core (2/6) · failure'), + 'the no-runner job is still reported, and the red it cannot explain is named'), + t(!body.includes('merge-queue-infra:no-runner|'), 'no per-head marker'), + ]; + }, + }, + { + id: 'N7', + name: 'ANNOTATION leg: a cancelled job that DID get a runner is no-runner only if its check run says so', + world: () => noRunnerWorld({ + jobs: jobsFixture(root, 37374282440).map((j) => (j.name === 'Test Core (5/6)' + ? { ...j, runner_name: 'GitHub Actions 1001213999', runner_id: 1001213999 } + : j)), + annotations: { 111979038621: JSON.parse(fixture(root, 'check-run-111979038621.annotations.json')) }, + }), + rerun: () => noRunnerWorld({ + // The control: same record, NO such annotation -- a job-level timeout + // or a manual cancel looks exactly like this, and is not weather. + jobs: jobsFixture(root, 37374282440).map((j) => (j.name === 'Test Core (5/6)' + ? { ...j, runner_name: 'GitHub Actions 1001213999', runner_id: 1001213999 } + : j)), + }), + check(r, t, first) { + return [ + t(first.calls.listAnnotations.includes(111979038621), 'the annotation is read for the undecided cancelled job'), + t(postedBody(first).includes(INFRA_CLASS) && postedBody(first).includes(VERDICT_ONCE), + 'the real "not acquired by Runner" annotation classifies it'), + t(postedBody(r).includes(FAILURE_CLASS) && !postedBody(r).includes('重排判定'), + 'without the annotation the same record is `failure`, with no re-queue'), + ]; + }, + }, + { + id: 'N8', + name: 'NOT KNOWING is never `once`: an unreadable PR head or comment list leaves the verdict unknown', + world: () => noRunnerWorld({ prGetError: REFUSED }), + rerun: () => noRunnerWorld({ prCommentsError: REFUSED }), + check(r, t, first) { + return [ + t(postedBody(first).includes(INFRA_CLASS) && postedBody(first).includes(VERDICT_UNKNOWN) + && !postedBody(first).includes(VERDICT_ONCE), 'head unreadable -> unknown, not once'), + t(!postedBody(first).includes('merge-queue-infra:no-runner|'), 'and no marker keyed on a head it never read'), + t(postedBody(r).includes(VERDICT_UNKNOWN) && !postedBody(r).includes(VERDICT_ONCE), + `comments unreadable -> unknown, not once, got: ${JSON.stringify(postedBody(r).slice(0, 900))}`), + t(postedBody(r).includes(`head=${PR_HEAD}|run=`), 'the sighting is still recorded when the head IS known'), + ]; + }, + }, + { + id: 'N9', + name: 'an aggregate red is explained only by its OWN family: a no-runner `Test Core` shard does not excuse a red `Dogfood Regression Gate`', + world: () => noRunnerWorld({ + jobs: [ + ...jobsFixture(root, 37374282440), + { + id: 222, name: 'Dogfood Regression Gate', conclusion: 'failure', runner_name: 'GitHub Actions 1', + html_url: 'https://github.com/objectstack-ai/objectstack/actions/runs/1/job/222', + steps: [{ name: 'Verify dogfood shard results', conclusion: 'failure' }], + }, + ], + }), + check(r, t) { + return [ + t(postedBody(r).includes(FAILURE_CLASS), `the unexplained aggregate keeps it failure, got: ${JSON.stringify(postedBody(r).slice(0, 900))}`), + t(!postedBody(r).includes('重排判定'), 'no re-queue verdict'), + ]; + }, + }, + { + id: 'N10', + name: 'an unreadable annotation is never read as no-runner, and says so', + world: () => noRunnerWorld({ + jobs: jobsFixture(root, 37374282440).map((j) => (j.name === 'Test Core (5/6)' + ? { ...j, runner_name: 'GitHub Actions 1001213999' } + : j)), + annotationErrors: { 111979038621: httpError(403, 'Resource not accessible by integration') }, + }), + check(r, t) { + return [ + t(postedBody(r).includes(FAILURE_CLASS), 'the build reads as failure'), + t(postedBody(r).includes('注解读不到'), 'the comment says the annotation could not be read'), + t(r.log.warning.some((x) => /classification incomplete/i.test(x.props?.title ?? '')), 'and the run is annotated'), + ]; + }, + }, ]; } @@ -1186,6 +1441,14 @@ export async function judge(source, root) { if (perms['pull-requests'] !== 'write') { failures.push({ id: 'P0', message: `job \`${JOB}\` must keep \`pull-requests: write\` -- the comment is the primary signal. Got: ${JSON.stringify(perms)}` }); } + checked++; + if (perms.checks !== 'read') { + failures.push({ id: 'P0', message: `job \`${JOB}\` must declare \`checks: read\` (and no more) -- limb (3) reads the "not acquired by Runner" annotation. Got: ${JSON.stringify(perms)}` }); + } + checked++; + if (perms.contents !== undefined) { + failures.push({ id: 'P0', message: `job \`${JOB}\` must not hold \`contents:\` -- nothing here enqueues, merges or writes a ref; a re-queue credential is a decision to take in the open, not a grant to slip in. Got: ${JSON.stringify(perms)}` }); + } for (const s of scenarios(root)) { const t = (cond, message) => { checked++; return cond ? null : { id: s.id, message }; }; @@ -1491,6 +1754,79 @@ const MUTATIONS = [ expect: ['E1'], keepGreen: ['A1'], }, + // ── limb (3) ────────────────────────────────────────────────────────── + { + id: 'M20', + what: 'the job-RECORD leg is dropped, so a cancelled job with no runner and no steps is no longer infra:no-runner', + from: " if (noRunnerByRecord(j)) { noRunner.push({ job: j, via: 'record' }); continue; }", + to: '', + expect: ['N1', 'N2'], + keepGreen: ['N5'], + }, + { + id: 'M21', + what: 'an aggregate red is excused by ANY no-runner job, not one of its own shard family', + from: ' && noRunnerNames.some((n) => n.startsWith(`${j.name} (`));', + to: ' && noRunner.length > 0;', + expect: ['N9'], + keepGreen: ['N1', 'N2'], + }, + { + id: 'M22', + what: 'a no-runner job anywhere makes the build infra:no-runner, so a REAL failure beside it earns a re-queue', + from: 'const infraOnly = noRunner.length > 0 && unexplained.length === 0;', + to: 'const infraOnly = noRunner.length > 0;', + expect: ['N6', 'N9'], + keepGreen: ['N5', 'N1'], + }, + { + id: 'M23', + what: 'the budget stops being keyed on the head, so ANY earlier no-runner marker on the PR spends it', + from: 'mm[2] === prHead && ', + to: '', + expect: ['N4'], + keepGreen: ['N3'], + }, + { + id: 'M24', + what: 'an earlier no-runner red on the same head is not counted, so the same head is re-queued again and again', + from: 'priorRuns.add(mm[3]);', + to: 'void mm;', + expect: ['N3'], + keepGreen: ['N4'], + }, + { + id: 'M25', + what: 'an unreadable comment listing stops being `unknown` (the script then reads the absent listing as if it were there)', + from: ' } else if (prComments === null) {', + to: ' } else if (false) {', + expect: ['N8'], + keepGreen: ['N1'], + }, + { + id: 'M26', + what: 'the "not acquired by Runner" annotation is no longer consulted', + from: "NO_RUNNER_ANNOTATION.test(String(a?.message ?? ''))", + to: 'false', + expect: ['N7'], + keepGreen: ['N1'], + }, + { + id: 'M27', + what: 'the per-head marker is no longer written, so a second no-runner red on the head cannot be recognised', + from: ' ...(noRunnerMarker ? [noRunnerMarker] : []),', + to: '', + expect: ['N1', 'N3'], + keepGreen: ['N5'], + }, + { + id: 'M28', + what: 'the script grows an enqueue call -- an act this workflow holds no credential for, so it must fail the battery as unmodelled', + from: 'const noRunnerMarker = infraOnly && prHead', + to: "if (infraOnly) await github.graphql('mutation { enqueuePullRequest }');\nconst noRunnerMarker = infraOnly && prHead", + expect: ['N1'], + keepGreen: ['N5'], + }, ]; // Returned by `selfTest()` only after its verdict is printed. The dispatch @@ -1571,7 +1907,10 @@ async function selfTest() { // 5. The corpus is the evidence. A fixture that quietly vanished would leave // every extraction scenario asserting over an empty string. battery('5. The corpus is the evidence. A fixture that quietly vanished would leave'); - for (const name of ['plugin-dev-timeout.job-log.txt', 'plugin-dev-assertion.job-log.txt', 'incident-32333709633-published-excerpt.job-log.txt']) { + for (const name of [ + 'plugin-dev-timeout.job-log.txt', 'plugin-dev-assertion.job-log.txt', 'incident-32333709633-published-excerpt.job-log.txt', + 'run-37374282440.jobs.json', 'run-37371558473.jobs.json', 'check-run-111979038621.annotations.json', + ]) { assert(existsSync(join(root, FIXTURES, name)), `corpus: ${FIXTURES}/${name} exists`); } const timeoutFixture = fixture(root, 'plugin-dev-timeout.job-log.txt'); @@ -1582,6 +1921,16 @@ async function selfTest() { const failOf = (t) => t.split('\n').find((l) => / FAIL src\//.test(l)); assert(failOf(timeoutFixture) === failOf(assertionFixture), 'corpus: the two captures share a byte-identical FAIL line -- that identity IS the defect limb (1) exists for'); + // Limb (3)'s corpus must keep carrying the shape it is evidence of: a job + // that ended `cancelled` with no runner and no steps, and the platform's own + // annotation for it. A recapture that lost either would leave N1/N2/N7 + // asserting over a world that no longer exhibits the outage. + const shard56 = jobsFixture(root, 37374282440).find((j) => j.name === 'Test Core (5/6)'); + assert(shard56?.conclusion === 'cancelled' && shard56?.runner_name === '' && shard56?.steps?.length === 0, + 'corpus: run 37374282440 still records `Test Core (5/6)` as cancelled with no runner and no steps'); + assert(JSON.parse(fixture(root, 'check-run-111979038621.annotations.json')) + .some((a) => /not acquired by Runner/.test(a.message)), + 'corpus: the captured annotation still carries the platform\'s "not acquired by Runner" message'); // 6. Wiring. A check nobody runs is the #4449 shape this repo keeps paying for. battery('6. Wiring. A check nobody runs is the #4449 shape this repo keeps paying for.'); diff --git a/scripts/fixtures/merge-queue-triage/README.md b/scripts/fixtures/merge-queue-triage/README.md index a9280acab3c..f9ca56dd9a3 100644 --- a/scripts/fixtures/merge-queue-triage/README.md +++ b/scripts/fixtures/merge-queue-triage/README.md @@ -13,6 +13,9 @@ extractor. | `plugin-dev-timeout.job-log.txt` | Real vitest 4.1.10 output, captured 2026-08-20 in this repo. `packages/plugins/plugin-dev/src/dev-plugin-security-enforcement-warning.test.ts` at revision `7552e03375` (pre-#10120), unbuilt dependency closure, `vitest run --testTimeout=1`. | | `plugin-dev-assertion.job-log.txt` | The same file, same revision, same unbuilt closure, no timeout override -- the exact condition #10112 measured, reproducing `AssertionError: start() published the service: expected false to be true` verbatim. | | `incident-32333709633-published-excerpt.job-log.txt` | Verbatim from the triage comment the bot **posted** on PR #10008 for queue build `32333709633` (comment `5351634659`), read back through the API. This is what a human actually saw during the 2026-08-20 incident. | +| `run-37374282440.jobs.json` | The CI queue build that ejected PR #21872 on 2026-10-05 with nothing failing, read back through `GET /repos/{owner}/{repo}/actions/runs/37374282440/jobs?filter=all` on 2026-10-06 and trimmed to the fields the script reads (id, names, urls, status, conclusion, runner id/name, step name/status/conclusion/number). `Test Core (5/6)` is `cancelled` with `runner_name: ""` and no steps; `Test Core` fails only in `Verify test shard results`. | +| `run-37371558473.jobs.json` | The same PR's earlier CI queue build that day, read and trimmed the same way: eight jobs `cancelled` with no runner (`Build Core`, five `Test Core` shards, two `Dogfood Regression Gate` shards), and two aggregates failing only in their `Verify … results` steps. | +| `check-run-111979038621.annotations.json` | The annotations of `Test Core (5/6)` above, read through `GET /repos/{owner}/{repo}/check-runs/111979038621/annotations`, trimmed to path, lines, level, title and message. The `failure` entry is the platform's own "The job was not acquired by Runner of type hosted even after multiple attempts". | The first two are a **pair**, and the pairing is the point: their `FAIL` lines are byte-identical and their reason lines are opposite diagnoses. Scenario `E3` @@ -29,3 +32,8 @@ worktree with no `packages/plugins/plugin-security/dist`, run `packages/plugins/plugin-dev/node_modules/.bin/vitest run ` (add `--testTimeout=1` for the timeout leg), then replace every ESC byte with `\u001b` before writing the file back here. + +The three limb (3) files are the records of a runner outage, not of a test +failure: scenarios `N1`/`N2` run the shipped script over them unchanged, and +`N7` uses the annotation with the job's `runner_name` filled in, so the +annotation leg is exercised on its own.