fix(queue-monitor-liveness): drop the failed-audit-step carve-out - #222
Conversation
ci-runner#140 makes queued-job-monitor.yml green-on-success: a successful execution reports success whether or not it detected queue strain, alerting instead by upserting an incident issue in ci-runner rather than failing its own run. Under the old contract, this workflow's MONITOR_AUDIT_STEP carve-out treated a run whose only failed step was "Inspect queued jobs" as healthy (that failure WAS the queue alert). Under the new contract that same failure is genuine breakage - config drift or a GitHub API error - and the carve-out would suppress this watchdog's own alert exactly when both it and the monitor need to fire. Any non-success conclusion of queued-job-monitor.yml is now monitor breakage, full stop. Companion to melodic-software/ci-runner#139/#140. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 16s —— View job PR Review
Verdict: Approve. This is a clean, well-reasoned simplification with no correctness, security, or style issues. What changedThe old health check treated a The new check collapses both branches into one: any CorrectnessLogic is sound. Under the new green-on-success contract, there is no longer a step whose failure is an expected outcome. Collapsing to a single The
Transition-window behavior is acceptable. The PR description is honest: during the window between this merge and ci-runner#140, if the old monitor fails at "Inspect queued jobs" (a real queue alert), this liveness check will now also fire. That's a brief noisy period, not a silent failure — strictly preferable to having this watchdog suppress its alert when both systems are trying to fire. Error message is improved. The new message ( Recovery guidance updated correctly. Adding "and the incident-issue write in ci-runner" to step 3 is accurate — under the new contract, a write failure is now a legitimate failure mode this liveness check covers. SecurityNo security concerns:
CLAUDE.md alignment
No findings. Safe to merge. |
|
Claude finished @kyle-sexton's task in 1m 49s —— View job Security Review
No security vulnerabilities found in this PR. This is the security-relevant analysis of what changed: Attack-surface reduction (net positive) The old code extracted step names from the ci-runner API and interpolated them into the broken_steps="$(gh_read api "repos/${MONITOR_REPOSITORY}/actions/runs/${run_id}/jobs" \
--jq '[.jobs[].steps[] | select(.conclusion == "failure") | .name] | unique | .[]')"
unexpected="$(grep -Fxv "$MONITOR_AUDIT_STEP" <<<"$broken_steps" || true)"
problems+="...(failed: $(tr '\n' ',' <<<"$unexpected" | sed 's/,$//')); ..."Step names are developer-authored strings from workflow YAML files. They were flowing — without sanitization — into an issue body (markdown injection surface). The risk was minimal (org-controlled repo, issue body only, not a code-execution context), but it was the most user-influenced content in the pipeline. The new code removes that entire branch. The only values now interpolated into
Unchanged surfaces (no regression)
Logic correctness note (not a security issue, but relevant to alert contract) The PR description correctly identifies the transition-window behavior: under the old contract, merging this first means a legitimate Verdict: Clean. The simplification removes the most complex data-flow path (step-name extraction and interpolation) and replaces it with values that are structurally impossible to contain injection payloads. |
…ting (#140) Closes #139 ## Summary - `queued-job-monitor.yml` no longer alerts by failing its own scheduled run. A successful execution is green regardless of what it detects; detection upserts a marker-deduped incident issue per target owner in this repository instead (`[Alert] Managed runner queue capacity — <owner>`, with a hidden `<!-- ci-runner:queued-job-monitor:incident:<owner> -->` marker in the body). Matching is marker-only, preserving the fleet precedent's deliberate "a marker survives a retitle" property (`link-check.yml`, `queue-monitor-liveness.yml`, `standards-sync-stuck-automerge-alert.yml` in `ci-workflows`). - **Spoofing hardening (`48b3a6a`):** candidate issues are restricted to ones authored by this workflow's own `GITHUB_TOKEN` identity (`github-actions[bot]`, verified live), and more than one own-authored issue carrying the marker fails the run closed. Mirrors `ci-workflows`#213. - **Data-integrity hardening (`4dc449e`):** `upsertIncident` requires `STUCK_JSON` to be explicit/valid/array-decoding. `findOpenIncident` filters server-side to `labels: 'automated'` (load-bearing for dedup — documented). - **Cross-owner marker-injection — CRITICAL, root-caused (`4c7adff`/`ad6c79c`):** `escapeMarkdownTableCell` HTML-entity-encodes `<`/`>` so no `<!--`/`-->` sequence from untrusted monitored-repo content can survive into the raw body text `findOpenIncident` substring-searches. End-to-end tests prove all three failure modes (adopt-overwrite, false-close, false-ambiguity) are prevented. Independently confirmed by a hard-bounded micro-review. - **Incident body size cap (`c02681d`, from Codex review):** `renderStuckMarkdownTable` caps at 50 rows with a "...and N more, see the workflow run" remainder note (linked via `GITHUB_SERVER_URL`/`GITHUB_RUN_ID`). `boundBodyLength` is a defense-in-depth backstop truncating the assembled body under GitHub's empirically-observed 65536-character write limit while always preserving the trailing marker intact. - Two further Codex findings addressed by rebuttal, not code change (both resolved on the PR with reasoning): the `automated` label is never applied to a monitored repo — `upsertIncident` creates issues exclusively in `homeOwner`/`homeRepo`, derived from `GITHUB_REPOSITORY` (this repo, ci-runner), never a monitored target; and the marker-injection finding was already fixed by the escape-at-source commit above (reply cites the commit, the tests, and the micro-review verdict). - A genuine execution error (bad configuration, GitHub API failure, ambiguous marker match, malformed/missing `STUCK_JSON`) still fails the run. The "Upsert queue capacity incident" step runs only on the default `success()` condition. - Writing the incident issue uses the job's own default `GITHUB_TOKEN` (job-level `issues: write`), separate from the read-only, target-scoped observer App token used to inspect queued jobs. - `docs/queue-monitor.md` updated throughout to describe the current design. **Config-integrity note (documented in-file):** two `CI_RUNNER_MONITOR_TARGETS_JSON` entries sharing an owner would race the incident-issue upsert intra-run (the concurrency group only serializes across scheduled runs, not the matrix within one). **Companion PR:** melodic-software/ci-workflows#222 — **MERGED**. **Environment note:** `ci-runner` is public, so its `ubuntu-24.04` runner is free/unmetered, unaffected by the org's $0 spending cap. This monitor is deliberately kept hosted, off the managed self-hosted fleet, so it still reports when both self-hosted hosts are down. ## Test plan - `node --test .github/scripts/queue-monitor.test.cjs` — 37/37 pass. - `typos`, `npx markdownlint-cli2`, `ec` (editorconfig-checker) run clean against every changed file. - `node --test .github/scripts/*.test.cjs` (full suite): all other suites pass; 5 pre-existing failures in `verify-existing-release.test.cjs` are unrelated Windows-only temp-dir `EPERM` cleanup issues, reproduced identically on `main`. - Full fleet CI green through `ad6c79c`; `c02681d` re-running the same gates now. - All three Codex review threads replied to with evidence and resolved; zero unresolved threads remain. ## Related - Closes #139 - References: medley `docs/topics/cicd-fleet-audit/REPORT.md` (Theme 1 F6 + Status-quo BP4) - Precedent mirrored: `melodic-software/ci-workflows` `queue-monitor-liveness.yml`, `link-check.yml`, `standards-sync-stuck-automerge-alert.yml` / ci-workflows#213 - Companion: melodic-software/ci-workflows#222 (merged) - ci-runner#101 (off-GitHub dead-man's-switch — complementary) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
No related issue: companion to melodic-software/ci-runner#140 contract change
Summary
melodic-software/ci-runner#140makesqueued-job-monitor.ymlgreen-on-success: a successful execution now reportssuccesswhether or not it detected queue strain, alerting instead by upserting a marker-deduped incident issue inci-runnerrather than failing its own scheduled run.This workflow's liveness check hard-coded the old contract: a
MONITOR_AUDIT_STEP("Inspect queued jobs") carve-out treated a monitor run whose only failed step was the audit step ashealthy=true, on the theory that the failure itself WAS the queue alert. Under the new contract there is no longer a step whose failure is an expected, healthy outcome — that same failure shape now means the monitor is genuinely broken (config drift, a GitHub API error, or a failure writing the incident issue), while the watchdog carve-out would suppress its own alert exactly when both the monitor and this watchdog need to fire.MONITOR_AUDIT_STEPenv var and the per-step-failure classification logic (thegh_read api .../jobscall that inspected which steps failed).conclusionis notsuccessis monitor breakage, full stop.Safe to merge under the old
queued-job-monitor.ymlcontract too, ahead ofci-runner#140: today, the monitor's audit-step failure would newly be classified as breakage and this watchdog would fire (correct-if-noisy for the transition window) rather than silently swallowing it as healthy. Per the team lead's merge sequencing, this PR merges first;ci-runner#140re-arms after.Test plan
actionlint .github/workflows/queue-monitor-liveness.yml— cleanzizmor .github/workflows/queue-monitor-liveness.yml— clean, no findingstypos .github/workflows/queue-monitor-liveness.yml— cleanMONITOR_AUDIT_STEP(fully removed, no dangling reference)Related
standards-sync-stuck-automerge-alert.yml), the sibling alert workflow whose author-identity hardeningci-runner#140also mirrors🤖 Generated with Claude Code