Skip to content

V2.10.S10: Automatic shield.io badge updates from PR status #66

Description

@deeprnd

V2.10.S10: Automatic shield.io badge updates from PR status

Status: Backlog
Epic: #[github-epic-issue]
Parent roadmap item: V2.10: Build System Consolidation And Qt Build Validation
Labels: type/story, area/operations, type/feature

Product Outcome

PR badges on the README reflect actual CI lane status in real time — no developer
needs to run refresh-badges.py from their machine. Build, unit test, and
security badges update automatically when a PR workflow completes, and coverage
badges update after coverage jobs finish. The README becomes a live window into
branch health.

User Story

As a reviewer or visitor, I see accurate build, test, security, and coverage
badges on the README that reflect the current PR state without any manual update
from a developer's machine.

Scope

  • In scope: design and implement a GitHub Actions workflow (badge-update.yml
    or a post-run step in existing workflows) that fires when PR workflows complete
    and updates badge SVG URLs between existing marker comments in README.md and
    doc/execution/testing-tickoni.md.
  • In scope: replace the manual contrib/readme/refresh-badges.py workflow
    (exit-code-driven local script) with a CI-driven badge-update approach that
    reads github.event.check_runs or github.event.jobs to determine pass/fail
    state for each badge type.
  • In scope: support badge types currently defined in refresh-badges.py:
    build, unit, integration, quality, security, system, e2e,
    cov-fd, cov-tk. Map each badge type to the corresponding CI workflow or
    job name so status can be determined from PR check-run results.
  • In scope: use shield.io's badge API (img.shields.io) — not cached static SVGs
    — so badges stay fresh on every page load and do not require storing SVG files
    in the repo.
  • In scope: update the badge URLs using the same marker-comment approach already
    in place (<!-- badge:name:start --> / <!-- badge:name:end -->); do not
    change the marker format.
  • In scope: handle the case where a badge type has no corresponding CI job yet
    (e.g. integration is planned but not yet scheduled on every PR) — default
    to unknown lightgrey until coverage exists.
  • In scope: the workflow must commit badge changes back to the PR branch (not
    main) using the peaceiris/actions-gh-pages or a git push to the PR
    branch; on the first successful PR workflow, push badge state to main as a
    baseline.
  • In scope: update doc/execution/build.md and doc/execution/development.md
    to document the new badge-update mechanism and remove references to the manual
    refresh-badges.py as the primary update path.
  • In scope: the manual refresh-badges.py script remains available for
    one-off local experimentation or offline use, but documentation should
    clarify it is no longer the production update path.
  • Out of scope: switching from shield.io to a self-hosted badge server.
  • Out of scope: adding new badge types beyond the existing set (e.g. coverage
    percentage, PR comment counts).
  • Out of scope: changing badge colors, styles, or the marker comment format.

Preconditions And Assumptions

  • V2.10.S6 (CI cutover) has wired existing CI workflows to PR triggers on
    main, so PR check-runs exist for build, test, and security lanes.
  • Shield.io's public badge API (img.shields.io/badge/...) is accepted for
    rendering badge images; no self-hosted badge server is required.
  • GitHub Actions has permission to push commits to PR branches via the
    GITHUB_TOKEN (the default token scope).
  • Existing marker comments (<!-- badge:build:start --> / <!-- badge:build:end -->
    etc.) in README.md and testing-tickoni.md are the insertion points;
    their format is preserved.

Acceptance Criteria

  • Given a PR with all CI lanes passing, when the final PR workflow completes,
    then README.md badge URLs for build, unit, security show
    passing/brightgreen (or equivalent pass state) via shield.io URLs.
  • Given a PR with any CI lane failing, when the PR workflows complete, then
    README.md badge URLs for the failed lane show failing/red.
  • Given a PR where a coverage job has not yet run, when PR workflows complete,
    then the coverage badge shows unknown/lightgrey.
  • Given a badge type with no corresponding CI job (e.g. integration during
    initial wiring), when PR workflows complete, then its badge shows
    unknown/lightgrey — not a stale passing badge.
  • Given a developer who runs refresh-badges.py locally, when they pass an
    exit code, then the badge updates locally but the documentation states
    this is for experimentation, not production use.
  • Given a new contributor reading doc/execution/build.md, when they look
    for "how to update README badges", then they find the badge-update
    workflow is automatic and the manual script is documented as legacy.

Conditional Acceptance

Financial capability and policy

  • N/A - badge update is a documentation surface change; no policy changes.

Audit and replay

  • N/A - badge update touches no audit schema or replay logic.

Runtime topology and tile ownership

  • N/A - badge update touches no tile topology or Firedancer integration.

Model, tool, adapter, or execution boundary

  • N/A - badge update touches no model, tool, adapter, or execution boundary.

CaseOps API or UI

  • N/A - badge update touches no CaseOps API or UI.

Storage role: Memory, Analytics, Ledger

  • N/A - badge update touches no storage roles.

Metrics, diagnostics, and operations

  • Applies - badge update changes the operational surface for README badge
    management from manual local script to automatic CI-driven workflow.

Security and fail-closed behavior

  • Given the badge-update workflow runs as a GitHub Action, when it pushes
    badge changes, then it uses only contents: write scope and does not
    introduce any external network calls, secrets, or API keys beyond
    the standard GITHUB_TOKEN.

Child Task Issues

V2.10.S10.T1 — Architecture and planning. Survey the badge-update approaches:
(1) a standalone badge-update.yml workflow triggered on workflow_run from
existing PR workflows, (2) a final step in each existing PR workflow that calls
a shared action to update badges, (3) a GitHub App or webhook-based approach.
Document the tradeoff: workflow_run listener is cleanest but requires all PR
workflows to signal completion; inline step adds boilerplate to every workflow;
webhook adds external dependency. Recommend approach and identify risks.

V2.10.S10.T2 — Domain-Driven Design and scaffolding. Design the badge-update
data model: a mapping from badge name to CI workflow/job name to PR check-run
status. Define the update function: read PR check-runs, resolve pass/fail/unknown,
construct shield.io URLs, locate marker comments, replace the img tag between
markers, commit back. Define the CI workflow structure. No production logic yet.

V2.10.S10.T3 — Test-Driven Design. Write a local test script that reads
existing CI workflow names from .github/workflows/, maps them to badge
types, verifies the marker comment format in README.md and testing-tickoni.md,
and confirms the shield.io URL construction logic. Stub the update function.
Tests must pass with hardcoded badge states.

V2.10.S10.T4 — Implementation. Implement the badge-update workflow:
(1) the CI workflow file (recommended: badge-update.yml triggered on
workflow_run with types: [completed] and workflow_run.branches: [main]),
(2) the badge-update script (Python, following the existing
contrib/readme/refresh-badges.py conventions), (3) the CI job logic:
fetch PR check-runs, determine badge state, update markers, commit to PR branch.
Wire the workflow to fire after each PR workflow completes.

V2.10.S10.T5 — Documentation and DevOps sweep. Update doc/execution/build.md
and doc/execution/development.md to document the new badge-update mechanism.
Update contrib/readme/refresh-badges.py comments to note it is a local
experimentation tool, not the production path. Update this story's epic file.

V2.10.S10.T6 — Security audit. Audit the badge-update workflow for:
GITHUB_TOKEN scope minimization (only contents: write), no external API
calls or secret access, no dependency on third-party GitHub Actions beyond
actions/checkout@v7 and standard step runners. Confirm the workflow only
pushes to PR branches or main, never to external repos.

V2.10.S10.T7 — Telemetry and observability audit. Confirm that badge update
does not introduce unexpected logging, metrics, or diagnostic output.

V2.10.S10.T8 — Evidence and quality gate. Run the badge-update workflow against
a test PR. Verify: (1) badges update correctly for passing/failing/unknown states,
(2) badge URLs use shield.io format, (3) markers are preserved, (4) the commit
message is descriptive, (5) no unrelated files are modified.

Evidence Plan

  • Demo or command: open a PR, wait for CI lanes to complete, verify README badges
    reflect actual lane status.
  • Tests: local badge-update script tests with hardcoded PR states.
  • Fixtures or samples: updated README.md badges from a test PR.
  • Audit/replay evidence: N/A - badge update touches no audit schema.
  • Blocked-flow evidence: N/A - badge update is a documentation surface change.

Quality Gate

  • README.md badges accurately reflect CI lane status on a test PR.
  • doc/execution/testing-tickoni.md badges update in sync with README.
  • Unknown states default to lightgrey, not passing or stale values.
  • Badge-update workflow uses only standard GitHub Actions with minimal scope.
  • contrib/readme/refresh-badges.py remains available but documented as
    local experimentation.
  • Documentation updated to reflect the new badge-update mechanism.

Notes And Open Questions

  • Which CI workflow/job maps to which badge type when a single workflow
    produces multiple test results (e.g. tests-short.yml covers unit,
    integration, and quality)?
  • Should coverage badges (cov-fd, cov-tk) update from a separate
    coverage.yml workflow or from the same badge-update trigger?
  • When should the baseline badge state be pushed to main — on the first PR
    merge, or immediately on story completion?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/operationsPayment exceptions, reconciliation breaks, fraud triage, compliance cases, chargebackstype/storySingle implementable deliverable that can be independently verified

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions