Repository navigation
[Phase 2] Add scheduled-run reliability analysis to the fleet health daily check #725
Description
Story
As a fleet reliability owner,
I want extend the daily Actions Fleet Monitor to measure and report per-workflow scheduled-run reliability -- expected scheduled ticks vs. actual schedule-triggered runs in the window,
so that a cron that silently never fires becomes visible and distinguishable from a healthy one, so 'successful but never ran' stops hiding stalled backlogs.
Acceptance Criteria
- For each workflow that has a schedule trigger, fleet_monitor.sh/fleet_report.sh compute the number of expected scheduled ticks within the lookback window and the number of actual runs whose triggering event is
schedule, plus a reliability/hit-rate metric (and/or a missed-tick count). - The per-workflow runs query captures the run's
eventfield so schedule-triggered runs are isolated from workflow_dispatch / push / repository_dispatch runs. - The workflow's cron expression(s) are obtained per workflow (parsed from the workflow file YAML, since the actions/workflows API does not expose cron) and expanded to the count of expected ticks over the window; multi-cron workflows and the default-branch-only scheduling caveat are handled or explicitly documented.
- The daily fleet report (both the GITHUB_STEP_SUMMARY and the issue-body report) gains a schedule-reliability section, and workflows whose reliability falls below a threshold are surfaced in a style consistent with the existing high-failure tracking.
- Best-effort delay is not falsely reported as a miss: a tolerance window is applied (a run that fired late but within tolerance counts as present), and the existing caveat that GitHub caps run queries at 1000 results per window is respected/noted.
- bats coverage is added (in tests/fleet_report.bats or a new bats file appended to the lint.yml bats list) for the cron-to-expected-ticks expansion and the reliability classification, driven by fixture run data.
Tasks / Subtasks
- Capture the run
eventin the per-workflow runs query in fleet_monitor.sh and thread it through the metrics (AC: test issue from agent #1, Go-live improvements for PR review agent #2) - Add a cron-parse + expected-tick helper that reads schedule.cron from the workflow file and counts expected ticks over LOOKBACK_DAYS (AC: test issue from agent #1, Add @claude delegation, auto-merge, and rebase handling #3)
- Fetch workflow file content (or read locally for this repo) to extract cron; the workflows API does not return cron
- Handle multiple cron entries per workflow; document day-of-week/day-of-month interaction handling
- Compute reliability (actual schedule runs vs expected, with tolerance) and a classification/threshold (AC: test issue from agent #1, feat: add Copilot engine support via REVIEW_ENGINE toggle #5)
- Render a schedule-reliability section in fleet_report.sh generate_report (step summary + issue body) (AC: Optimize review: small-PR and incremental fast paths #4)
- Add bats tests with fixture run data covering tick expansion and classification; append the test file to the lint.yml bats list if new (AC: feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6)
Dev Notes
- Entry points: fleet_monitor.sh fetches per-workflow runs (~lines 100-120) selecting run_number/conclusion/created_at/html_url/duration_s -- it does NOT currently request
.event; add it there. Metrics are emitted as a 12-field TSV (~lines 154-160) consumed by fleet_report.sh generate_report; extend the row or emit a parallel schedule-metrics file rather than breaking the existing 12-field contract relied on by the high-failure JSON export (~lines 227-240). - The actions/workflows API (used at ~line 85) returns id/path/state but NOT the cron schedule -- cron must be parsed from the workflow YAML. For org-wide scanning that means fetching file content per workflow; keep it bounded and tolerate parse failures (record as unknown rather than failing the monitor, consistent with the existing ERROR-sentinel pattern).
- Scheduled events are best-effort by design (the whole point of Improving reliability of scheduled GitHub Actions runs (cron is best-effort) #656): a late-but-present run must NOT be scored as a miss. Apply a tolerance window and only flag workflows where actual scheduled runs fall materially below expected. Respect the documented 1000-results-per-window cap (comment near line 98).
- 'Fleet health daily check' = the Actions Fleet Monitor (actions-fleet-monitor.yml, daily) driven by scripts/fleet_monitor.sh + scripts/fleet_report.sh -- that is the surface the owner asked to incorporate this into, not daily-pr-review-health.yml.
- Testing standard: this repo uses bats with an explicit allow-list in lint.yml; tests/fleet_report.bats already exists and is the natural home for report-rendering and math assertions. python3 is available in CI (lint.yml installs pyyaml/jsonschema) if a small parser helper is cleaner than pure bash/awk.
Project Structure Notes
Changes are confined to scripts/fleet_monitor.sh, scripts/fleet_report.sh, and tests (tests/fleet_report.bats or a new bats file added to lint.yml's bats list). actions-fleet-monitor.yml itself likely needs no change since it just runs fleet_monitor.sh; confirm no new step is required to surface the section.
References
- scripts/fleet_monitor.sh#per-workflow-runs-query
- scripts/fleet_monitor.sh#metrics-tsv
- scripts/fleet_monitor.sh#high-failure-json-export
- scripts/fleet_report.sh#generate_report
- .github/workflows/actions-fleet-monitor.yml
- tests/fleet_report.bats
- .github/workflows/lint.yml#bats
Likely target surface
scripts/fleet_monitor.shscripts/fleet_report.shtests/fleet_report.bats.github/workflows/lint.yml
Story prepared by the BMAD Scrum Master (Bob) for epic #722. Status: ready-for-dev.
Activity
don-petry commentedon Jul 14, 2026
Dev-Lead Implementation Plan
Issue: #725 — [Phase 2] Add scheduled-run reliability analysis to the fleet health daily check
Scope
Extend the Actions Fleet Monitor to measure per-workflow scheduled-run reliability (expected cron ticks vs. actual
schedule-triggered runs, with tolerance) and surface stalled crons in the daily report — without breaking the existing 12-field metrics TSV contract (a parallel schedule-metrics file is used instead).Implementation Checklist
scripts/fleet_report.sh: add pure helperscount_cron_ticks,extract_crons,workflow_expected_ticks,classify_schedule_reliability,generate_schedule_reliability_report; extendlabel_to_iconwith STALLED/STABLE.scripts/fleet_monitor.sh: capture.eventin the per-workflow runs query; compute window epochs; per workflow fetch file content, parse cron, count expected ticks, count actualscheduleruns, classify, and write a parallelschedule_metrics_file; render the section in both the step summary and the issue-body report; clean up temp file.Tests to Write (tests/fleet_report.bats — already in lint.yml bats list)
count_cron_ticks: daily, hourly,*/15, and no-fire-in-window casesextract_crons: single cron, multi-cron, no-schedule,on:-as-boolean YAML gotchaworkflow_expected_ticks: multi-cron sum and no-schedule → 0classify_schedule_reliability: STABLE / STALLED / DEGRADED / within-tolerance / hit-rate capgenerate_schedule_reliability_report: renders STALLED row + caveat note; empty file → no outputTest Command
scripts/run-bats.sh tests/fleet_report.batsandbash .dev-lead/scripts/dev-lead-lint.shdon-petry commentedon Jul 14, 2026
Dev-Lead: Implementation Complete
Plan Execution
scripts/fleet_report.sh: added pure helperscount_cron_ticks,extract_crons,workflow_expected_ticks,classify_schedule_reliability,generate_schedule_reliability_report; extendedlabel_to_iconwith STALLED/STABLE and addedSCHEDULE_TOLERANCE_TICKS/SCHEDULE_RELIABILITY_THRESHOLDtunables.scripts/fleet_monitor.sh: captured.eventin the per-workflow runs query; computed[CUTOFF, now)window epochs; per workflow fetch file content (raw), parse cron, count expected ticks, count actualschedule-event runs, classify, and write a parallelschedule_metrics_file(12-field TSV contract untouched); rendered the section in both the step summary and the issue-body report; cleaned up the temp file.tests/fleet_report.bats(already on thelint.ymlbats list — no workflow change needed).Test Results
Files Changed
scripts/fleet_report.sh: pure, sourced, testable schedule-reliability helpers + report renderer (+225 lines).scripts/fleet_monitor.sh:.eventcapture, cron fetch/parse per workflow, parallel schedule-metrics file, section wiring, cleanup (+53 lines).tests/fleet_report.bats: 23 new tests covering cron→tick expansion, cron extraction, multi-cron summing, reliability classification, and section rendering (+224 lines).Notes
.github/workflows/actions-fleet-monitor.ymlneeds no change — it just runsfleet_monitor.sh, and the new section is appended to both the step summary and the issue body.schedulerun is counted regardless of conclusion (incl. in-progress), and a tolerance of 1 tick absorbs boundary/late ticks. Consequence:STALLEDonly triggers when actual==0 and expected ≥ 2, so a single daily tick missed in a 1-day window is intentionally not flagged (indistinguishable from a delayed-past-window run). Sub-daily crons (hourly,*/15) and low-frequency crons over a longerLOOKBACK_DAYSare flagged as designed. Thresholds are tunable viaSCHEDULE_TOLERANCE_TICKS/SCHEDULE_RELIABILITY_THRESHOLD.contentsAPI read per workflow (unavoidable — the workflows API does not expose cron). Bounded by workflow count; read/parse failures are non-fatal::warning::s consistent with the ERROR-sentinel pattern.feat: implement issue #725 — [Phase 2] Add scheduled-run reliability …