Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ec7c832
feat: add read-only actions queue health evidence
seonghobae Aug 19, 2026
5950d6f
fix(queue-health): retry transient PR identity reads
seonghobae Aug 19, 2026
f92f08e
fix: use cross-repository queue health credentials
seonghobae Aug 19, 2026
3196c2d
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 20, 2026
3f7c33b
Merge protected main into queue health evidence
seonghobae Aug 20, 2026
47bb2bc
fix(actions): bound queue run response size
seonghobae Aug 20, 2026
8b914ef
fix(actions): bound queue collector failure modes
seonghobae Aug 20, 2026
a6ac262
fix(queue-health): isolate repository collection errors
seonghobae Aug 20, 2026
94e41c6
docs(queue-health): cite evidence boundaries
seonghobae Aug 20, 2026
7bbd133
docs(ci): complete fixture constructor coverage
seonghobae Aug 20, 2026
4467d5a
docs(tests): complete queue health test docstrings
seonghobae Aug 20, 2026
af65a69
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Aug 20, 2026
8fcb9b1
Merge current main into actions queue health
seonghobae Aug 21, 2026
2df6b86
fix(queue-health): accept collector snapshot identities
seonghobae Aug 21, 2026
77557e9
fix(ci): reject incomplete paginated queue evidence
seonghobae Aug 21, 2026
31dde70
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Aug 21, 2026
2d857f6
fix(queue-health): count duplicate lanes by run
seonghobae Aug 21, 2026
6469a76
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Aug 21, 2026
db2c4c0
Merge branch 'main' into codex/pr1142-current-main-successor
seonghobae Aug 21, 2026
92c419b
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Aug 21, 2026
efa7788
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Aug 21, 2026
2fb5c2e
Merge remote-tracking branch 'origin/main' into codex/pr1142-current-…
claude Aug 30, 2026
a6be225
fix(actions-queue-health): fix pull_request_target identity, waiting …
claude Aug 30, 2026
5830553
Merge origin/main and harden queue health collection
seonghobae Aug 31, 2026
e005330
fix(actions-queue-health): snapshot active runs atomically
seonghobae Aug 31, 2026
b050db5
fix(actions-queue-health): stabilize bounded active snapshots
seonghobae Aug 31, 2026
0988f42
fix(actions-queue-health): cap active sweep requests
seonghobae Aug 31, 2026
1c4d3f5
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Sep 1, 2026
d95dc3f
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Sep 1, 2026
dbd1428
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Sep 1, 2026
b6d5858
Merge branch 'main' into codex/pr1142-current-main-successor
opencode-agent[bot] Sep 1, 2026
55f151a
test(orchestrator): align free-pool policy with current credential co…
seonghobae Sep 1, 2026
4af941f
merge(main): restack queue-health evidence on current control plane
seonghobae Sep 1, 2026
8080387
test(queue-health): reproduce snapshot identity races
seonghobae Sep 1, 2026
02a1ad9
test(queue-health): require least-privilege scheduler token
seonghobae Sep 1, 2026
61cbf58
refactor(queue-health): isolate reviewed collector core
seonghobae Sep 1, 2026
d08617b
fix(queue-health): drop unused pull request permission
seonghobae Sep 2, 2026
42a855a
fix(queue-health): bind reports to stable pull and workflow identity
seonghobae Sep 2, 2026
2f57e71
docs(queue-health): document stable identity evidence
seonghobae Sep 2, 2026
5018822
test(queue-health): require ConceptWeave canary
seonghobae Sep 2, 2026
b49c51b
feat(queue-health): include ConceptWeave canary
seonghobae Sep 2, 2026
fd81689
test(queue-health): require ELUNVERA canary
seonghobae Sep 2, 2026
9154ddb
feat(queue-health): include ELUNVERA canary
seonghobae Sep 2, 2026
c842ee7
test(ci): cover zero-job startup failures
seonghobae Sep 2, 2026
39d2f51
fix(ci): observe pre-job startup failures
seonghobae Sep 2, 2026
0738d77
test(ci): bound startup-failure history
seonghobae Sep 2, 2026
5e66f34
fix(ci): bound startup-failure diagnostics
seonghobae Sep 2, 2026
b0f1222
test(ci): retain old current-head startup failures
seonghobae Sep 2, 2026
7981728
fix(ci): retain exact-head startup failures
seonghobae Sep 2, 2026
af72a26
test(queue-health): classify cancelled pre-runner evidence
seonghobae Sep 2, 2026
79e0758
fix(queue-health): retain cancelled pre-runner evidence
seonghobae Sep 2, 2026
ebc4c80
docs(queue-health): record pre-runner cancellation evidence
seonghobae Sep 2, 2026
1cee249
chore(queue-health): reconcile protected main without force
seonghobae Sep 2, 2026
b4f95bc
test(queue-health): cover target cancellations and skipped jobs
seonghobae Sep 2, 2026
5a4950b
fix(queue-health): retain target cancellations by linked head
seonghobae Sep 2, 2026
b0b9aed
docs(queue-health): record target cancellation identity boundary
seonghobae Sep 2, 2026
b99839b
test(queue-health): reject unsupported startup-failure status query
seonghobae Sep 2, 2026
f567b11
fix(queue-health): use supported target cancellation filter
seonghobae Sep 2, 2026
00c1a78
docs(queue-health): doctor supported workflow-run status filters
seonghobae Sep 2, 2026
d3a1138
test(queue-health): extend PR identity boundary through evidence reads
seonghobae Sep 2, 2026
7683d22
fix(queue-health): revalidate PR identity after evidence reads
seonghobae Sep 2, 2026
a30548d
docs(queue-health): extend identity trace through terminal evidence
seonghobae Sep 2, 2026
9a187a0
fix(queue-health): observe fast-mlsirm admission failures
seonghobae Sep 2, 2026
5031e0b
test(queue-health): require post-evidence identity retry
seonghobae Sep 2, 2026
36639d0
fix(queue-health): retry post-evidence identity reads
seonghobae Sep 2, 2026
7d80a06
chore(queue-health): reconcile with protected Noema repair
seonghobae Sep 2, 2026
bbacf9e
chore(queue-health): reconcile with protected main 8c085835
seonghobae Sep 2, 2026
a9b73ed
Merge remote-tracking branch 'origin/main' into codex/pr1150-current-…
seonghobae Sep 5, 2026
e6622a4
fix(queue-health): align terminal evidence contracts
seonghobae Sep 5, 2026
c5d59c1
test(queue-health): reject duplicate paginated evidence
seonghobae Sep 6, 2026
dcb857a
test(queue-health): preserve unavailable step evidence
seonghobae Sep 6, 2026
e01e033
fix(queue-health): validate paginated and step evidence
seonghobae Sep 6, 2026
889b095
fix(queue-health): require explicit zero-step evidence
seonghobae Sep 6, 2026
de676cb
test(queue-health): require initial identity bracket
seonghobae Sep 6, 2026
7c4e42d
fix(queue-health): retry identity before run collection
seonghobae Sep 6, 2026
a0b05c0
chore(queue-health): reconcile current protected main
seonghobae Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .github/actions/noema-review/two_phase.py
100755 → 100644
Empty file.
55 changes: 55 additions & 0 deletions .github/workflows/actions-queue-health.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: GitHub Actions queue health

on:
schedule:
- cron: "7 * * * *"

concurrency:
group: github-actions-queue-health
cancel-in-progress: false

permissions:
contents: read
actions: read

jobs:
collect:
name: Collect exact-head queue evidence
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
actions: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
with:
egress-policy: audit

- name: Checkout trusted queue-health source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Collect read-only repository and runner evidence
env:
GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }}
run: |
if [ -z "${GH_TOKEN:-}" ]; then
echo "::error::PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN is required for cross-repository queue reads."
exit 1
fi
echo "::add-mask::$GH_TOKEN"
python3 scripts/ci/actions_queue_health.py \
--allowlist config/actions_queue_health_repositories.json \
--output-json "$RUNNER_TEMP/actions-queue-health.json" \
--output-html "$RUNNER_TEMP/actions-queue-health.html"

- name: Upload queue-health evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: github-actions-queue-health-${{ github.run_id }}
path: |
${{ runner.temp }}/actions-queue-health.json
${{ runner.temp }}/actions-queue-health.html
if-no-files-found: error
11 changes: 11 additions & 0 deletions config/actions_queue_health_repositories.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"repositories": [
"ContextualWisdomLab/.github",
"ContextualWisdomLab/ConceptWeave",
"ContextualWisdomLab/ELUNVERA",
"ContextualWisdomLab/TEPP",
"ContextualWisdomLab/contextual-orchestrator",
"ContextualWisdomLab/fast-mlsirm",
"ContextualWisdomLab/naruon"
]
}
56 changes: 56 additions & 0 deletions docs/doctoring/actions-queue-cancelled-before-runner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Actions queue cancellation before runner assignment

## Status

Proposed owner-side diagnostic extension for `ContextualWisdomLab/.github#1150` and the organization Actions incident tracked by `ContextualWisdomLab/.github#712`.

## Problem

A current pull-request head can produce a terminal GitHub Actions run whose job was cancelled before any runner was assigned or any step executed. Treating that evidence as a generic terminal job loses the first non-executed boundary and can mislead incident triage even though it must never count as passing evidence.

Observed organization evidence on 2026-09-02 includes `.github#1653`, where a current-head `Repository Metadata Reconcile` job terminated `cancelled` after previously showing `runner_id=0`, empty runner identity, and `steps=[]`. Separate ContextualWisdomLab repositories also reproduce zero-job `startup_failure` and long-lived unassigned queue states, so these states must remain distinct rather than being collapsed into a product-source failure.

A second adapter-boundary case is `pull_request_target`: GitHub records the workflow run against the base commit while the linked pull-request object carries the exact pull-request head. A terminal diagnostic collector that searches only by the current pull-request `head_sha` therefore cannot see a target-triggered cancellation even when its linked pull-request identity is current. Conversely, once target evidence is collected, a concurrent push or close can make the identity snapshot used for classification stale unless the collector revalidates the PR view after all terminal/job reads.

## Decision

The queue-health collector keeps external GitHub conclusion values unchanged at the adapter boundary and adds a semantic internal/report classification. For exact current heads it now:

- retains `startup_failure` and `cancelled` terminal diagnostics from the bounded exact-`head_sha` `status=completed` query for ordinary pull-request/head-bound runs, filtering the returned conclusion locally;
- performs a bounded `status=cancelled&event=pull_request_target` candidate read for the target-triggered cancellation case and retains a candidate only after the existing linked pull-request number/head identity resolver proves it belongs to an exact current head;
- does **not** send `status=startup_failure` to GitHub's workflow-run list endpoint because that value is not in the endpoint's documented `status`/conclusion filter enumeration; target-triggered zero-job startup-failure discovery therefore remains a separate unresolved diagnostic gap rather than being implemented through an invalid REST request;
- fetches job evidence only for retained current-head terminal diagnostics;
- re-reads the bounded open-PR identity view after terminal and job evidence collection and fails the repository snapshot if PR number/state/head identity differs from the view used for classification;
- classifies a job as `cancelled_before_runner_assignment` only when both the run and that job conclude `cancelled`, the job has no runner assignment, and it has zero executed/materialized steps;
- never reclassifies sibling jobs that concluded `skipped`, `success`, or another non-cancelled state merely because their parent run concluded `cancelled`;
- keeps ordinary exact-head zero-job startup failures as `startup_failure_before_job_materialization`;
- reports an additive `admission_state` and a summary count without changing any GitHub check conclusion or synthesizing success;
- recommends inspection of Actions runner admission, billing/usage, runner-group policy, scheduler capacity, concurrency, and cancellation provenance rather than leaf-source churn or gate weakening.

## TDD lineage

RED commit `af72a26e0d1d845a7b447a63c7d4de4867815a87` added the first deterministic regression whose current-head cancelled run has one job with `runner_id=0`, an empty runner name, and `steps=[]`. GREEN commit `79e0758d0583474934327039b065956976c64453` introduced the initial cancellation classification.

RED commit `b4f95bc290e625649b8ce7ae59e157c3869466f2` then captured two successor defects found on the live writer: a `pull_request_target` cancellation whose run-level SHA is the base commit but whose linked pull-request head is current, and a skipped sibling job inside a cancelled run that must not be counted as a pre-runner cancellation incident. GREEN commit `5a4950bb996f80f7be2519432a3f5b74bea02d58` added target-event candidate collection with linked-head verification and required the matched job itself to conclude `CANCELLED` before applying the semantic incident classification.

Primary-source verification then found that GitHub's documented repository workflow-run `status` filter accepts `completed`, `action_required`, `cancelled`, `failure`, `neutral`, `skipped`, `stale`, `success`, `timed_out`, `in_progress`, `queued`, `requested`, `waiting`, and `pending`, but not `startup_failure`. RED `b99839bdcffecccc88b364a9813676b3964535b9` rejects any attempted `status=startup_failure` request in the deterministic target-cancellation fixture. GREEN `f567b1182308e4b45e22bf2f13b214998f59f5d0` narrows target-event filtering to the supported `cancelled` conclusion while leaving ordinary exact-head `status=completed` collection and local `startup_failure` conclusion classification intact.

Review of that successor exposed a final consistency-window defect: target cancellation/job reads occurred after the collector's prior `final_pull_requests` read, so a later push or close could allow stale target evidence to survive. RED `d3a11383ce717217ec4c80a5d65c84aa947570e3` changes the PR head only after target and job evidence has been read and requires fail-closed rejection. GREEN `7683d2219c8007f9e7fa6001c98d0944290fa756` adds the post-evidence identity read and rejects any number/state/head divergence before a repository snapshot is emitted.

## Compatibility and risk

This is an additive diagnostic-contract change. It does not mutate repository branches outside the canonical PR, cancel/rerun Actions, alter branch protection, change database state, or modify an external GitHub schema. `status`, `conclusion`, `runner_id`, and related GitHub payload keys remain vendor-owned adapter fields; organization-owned report vocabulary uses semantic multiword names.

Exact-head completed-run searches retain the existing twenty-page / 1,000-result fail-closed ceiling. Because GitHub's repository workflow-run API does not expose a pull-request-number filter for `pull_request_target`, cancelled target-event candidates are read by supported `cancelled` status and event under the same bounded ceiling, then filtered by linked current-head identity before retention. If that bounded candidate set is exceeded, or if the post-evidence PR identity view changes, the repository becomes explicit incomplete collection evidence rather than silently truncating or preserving stale evidence. This is an availability trade-off, not permission to synthesize success or churn leaf repositories.

A cancelled run with a runner-assigned, step-executing, or non-cancelled matched job remains ordinary terminal evidence and is not reclassified as a pre-runner admission failure. A `pull_request_target` startup failure that cannot be discovered through the supported target-cancellation query also remains incomplete evidence; it is not silently treated as healthy.

## Primary-source traceability

GitHub, Inc. (2026). *REST API endpoints for workflow runs*. GitHub Docs. Retrieved September 2, 2026, from https://docs.github.com/en/rest/actions/workflow-runs

The documented endpoint contract is treated as the authority for request-filter vocabulary; live GitHub run payloads remain the authority for observed run conclusions. The distinction prevents an undocumented observed conclusion such as `startup_failure` from being incorrectly assumed to be a valid REST query-filter value.

## Verification

Only checks produced from the unchanged final `ContextualWisdomLab/.github#1150` head qualify. Queued, pending, cancelled, zero-job startup failures, predecessor checks, or stale reviews are incomplete evidence and must not be transferred to a newer head. The RED/GREEN lineage above documents source intent; hosted 100% statement/branch/docstring and required-workflow evidence must be re-established on the final exact head before ordinary merge.
106 changes: 106 additions & 0 deletions docs/doctoring/actions-queue-health.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# GitHub Actions queue-health evidence

The scheduled `actions-queue-health.yml` workflow reads a fixed allowlist of
CWL repositories once per hour and publishes a JSON report plus a keyboard-
readable HTML report as an artifact. The collector uses only `gh api` reads
through the configured cross-repository `PR_REVIEW_MERGE_TOKEN` or
`OPENCODE_APPROVE_TOKEN`; it fails visibly when neither credential is present.
It does not cancel runs, mutate branches, dispatch workflows, or alter merge
gates, and it never relies on the central repository's scoped `GITHUB_TOKEN`
for sibling-repository reads.

The report schema is `actions.queue_health.v1`. Each observed run records its
repository, pull-request number, head SHA, event, run attempt, concurrency
group (or an explicit unavailable marker), stable workflow identity, queue age,
job state, and runner assignment. When GitHub supplies a positive
`workflow_id`, the report exposes `workflow_identity` as `workflow_id:<id>` and
uses that value for duplicate-lane grouping; `workflow_name` remains
presentation data. Older/offline v1 snapshots that lack `workflow_id` retain a
compatibility fallback of `workflow_name:<name>`. A malformed present
`workflow_id` fails closed instead of being coerced.

A run is `current_head` only when its linked open pull request and head SHA
match. The match compares the open pull request's head SHA against the *linked*
pull-request entry's head SHA carried on the run (`run.pull_requests[].head.sha`),
never against the run-level `head_sha`. `pull_request_target`-triggered runs
report the base-branch commit that was checked out as their run-level
`head_sha`, so comparing against that value would misclassify a genuinely
active, current required-workflow run as obsolete and skip its job evidence.
Stale linked runs are `obsolete`; runs without a pull-request link are
`unlinked`. Queued evidence remains incomplete even when a report is
successfully produced. GitHub's `waiting` job status (paused on an environment
or deployment approval) is also treated as pending evidence, distinct from a
runner-capacity blocker.

Pull-request identity is sampled before and after the bounded active-run
sweeps. The repository snapshot is accepted only when the open pull-request
number/state/head view is unchanged. A push, closure, or other identity change
between those samples becomes repository-scoped incomplete evidence instead of
being allowed to invert current/obsolete classification. A pull-request
response with incomplete head/base identity retains one bounded retry after a
one-second delay.

Queue age for a fetched job is measured from that job's own `created_at`, not
the parent run's, so a later job in an already in-progress run (for example one
gated by `needs:`) that only just became eligible is not measured against the
whole run's age and does not trigger a false capacity-breach alert. Every row
exports both `queue_age_started_at` and `queue_age_source` (`job_created_at` or
`run_created_at`) so consumers can reproduce the reported `queue_age_seconds`.
Requested, pending, and queued runs intentionally use run-level evidence when
GitHub has not supplied job detail.

Two bounded active-status sweeps run in opposite orders and must agree before
the snapshot is accepted. This prevents historical completed runs from
exhausting the bound while rejecting evidence that changes between partitioned
reads. Each status read is capped at one 50-run page, limiting collection to ten
run-list calls per repository; exceeding the cap is reported as incomplete
evidence. Current-head `in_progress` and `waiting` runs make the additional jobs
API read needed to distinguish concrete runner assignment from an environment
or deployment approval wait.

List endpoints use collector-controlled GitHub API pagination with at most 20
explicit page reads; the collector never asks GitHub CLI to download an
unbounded page set and never requests page 21. Pull-request and job lists use
pages of 100 records; workflow-run lists use pages of 50 so a large Actions
queue does not require one oversized response. An incomplete, malformed, or
larger response is recorded as repository-scoped incomplete evidence and the
collector continues with the remaining allowlisted repositories; it never
silently claims that the visible page is the whole queue. The JSON and HTML
reports expose each collection error explicitly.

Every external `gh api` read has a 30-second subprocess timeout, and the
collector job has a 30-minute execution ceiling. A timeout is typed as
incomplete queue evidence rather than success. Repository names reject `.` and
`..` path segments. Offline snapshots also reject duplicate repository entries
before counting runs so repeated input cannot inflate the reported queue.

The default queue-age SLO is 900 seconds. A current-head job that remains
unassigned beyond that limit produces a warning and an explicit manual action
to inspect runner capacity, billing, runner-group policy, environment approval,
and concurrency saturation. The workflow intentionally remains read-only and
fail-closed when GitHub API or runner evidence is unavailable. Paged API reads
are not atomic; changing totals are retained only when the collected records
cover the largest observed total, and the report remains explicitly an
observation rather than a merge decision.

Implementation ownership is intentionally split without duplicate collector
copies: `actions_queue_health_core.py` owns the shared bounded parsing and
reporting primitives, while the executable `actions_queue_health.py` entrypoint
owns stable pull/workflow identity and audit-provenance reconciliation. Tests
load the executable boundary used by the scheduled workflow.

The allowlist is deliberately explicit in
`config/actions_queue_health_repositories.json`; adding a repository requires
review of its governance and data boundary. This first slice does not claim
that a queued run is obsolete or safe to cancel.

## References

GitHub. (n.d.). *REST API endpoints for workflow runs*. Retrieved August 20,
2026, from https://docs.github.com/en/rest/actions/workflow-runs

Internet Engineering Task Force. (2022). *HTTP semantics* (RFC 9110).
https://www.rfc-editor.org/rfc/rfc9110

OWASP Foundation. (n.d.). *Path traversal*. Retrieved August 20, 2026, from
https://owasp.org/www-community/attacks/Path_Traversal
Loading
Loading