Skip to content

main is CI-dead since #830: tests.yml startup_failure (jobs=0) and Actions lockfile verify red #837

Closed

Description

@hyperpolymath

The finding

Two faults appeared on main together with #830 (chore(deps): bump the actions group across 1 directory with 4 updates, head 0aa89724).

1. tests.yml is startup_failure — zero jobs

2026-09-22T14:22  startup_failure  push          0aa89724   <- #830
2026-09-22T14:21  startup_failure  pull_request  b3e4057a
2026-09-22T14:20  failure          push          0e121c94   <- last run that ran
2026-09-22T14:00  startup_failure  pull_request  2e0e7bba
2026-09-22T13:51  failure          pull_request  c22ce6c1
2026-09-22T13:20  failure          push          3bf9674a

Run 35739874762 on 0aa89724 has jobs = 0. A 0-job run is a startup death,
not a verdict — the workflow was never parsed into jobs, so nothing it would have
checked was checked, and the run does not report a failure anyone would read as
one.

The only diff between the last run that started (0e121c94) and the first that
did not (0aa89724) is the Dependabot pin bumps across five workflows — for
tests.yml a pure step-level tag bump, taiki-e/install-action@v2.87.15 →
v2.87.16. A step-level tag bump should not startup-kill a workflow, so the
cause is not obvious and is not yet established. The check-runs API returns 404
for annotations on this run.

Note this fault masks #836 rather than replacing it: the test tier was already
dead by needs:-skip before the workflow stopped starting at all.

2. governance / Actions lockfile verify is red on main

The same Dependabot commit changed uses: refs without regenerating
.github/workflows/actions.lock. A hyperpolymath/standards reusable does
verify the lockfile, so it is now inconsistent with the tree.

This also corrects an earlier note in the #120 planning that said "no hypatia
workflow enforces actions.lock"
— one does, via a standards reusable.

Acceptance criteria

  1. Root cause of the startup_failure named and evidenced, not guessed. Bisect
    by dispatching tests.yml on a branch that reverts only the tests.yml hunk of
    chore(deps): bump the actions group across 1 directory with 4 updates #830, and confirm jobs > 0. State which change did it.
  2. tests.yml reaches jobs > 0 on a push to main. Verify by the job count
    in the run, never by a green tick — a startup death has no red tick to see.
  3. .github/workflows/actions.lock regenerated in generate mode only. Never
    fix/rewrite mode: it has previously de-pinned 24 SHAs and invented invalid
    uses: refs that themselves cause startup death. git diff the lock before
    staging and confirm the change is purely additive.
  4. governance / Actions lockfile verify green on main, evidenced from
    /commits/<sha>/check-runs, not from gh run list.
  5. The lockfile must be transitively closed — a dangling edge to an
    unresolvable ref is itself a jobs=0 killer. Absent is harmless; unresolvable
    is fatal.
  6. A regression guard: any Dependabot commit touching a uses: ref must either
    update actions.lock in the same commit or fail. Prove it with a mutant —
    hand-edit one uses: tag without touching the lock and confirm red.

Sequencing

(1)-(2) before (3)-(4). Regenerating the lock while the workflow cannot start
leaves you unable to tell which change fixed what.

Found while preparing #835. Related: #836.

Activity

  1. added
    cicdCI/CD: workflows, actions, lockfiles, pins, runners, release gates
    testingTests, benchmarks, fuzzing, property checks, coverage
    on Sep 22, 2026
  2. hyperpolymath commented on Sep 22, 2026

    @hyperpolymath
    OwnerAuthor

    The startup death is four workflows, not one — and it hides a dead doctest tier

    Measured 2026-09-22 while landing #840. This issue names tests.yml; the same
    fault is live on four workflows on main:

    $ gh run list --repo hyperpolymath/hypatia --branch main --limit 40 \
        --json conclusion,name
    
    CI                                          startup_failure
    Security                                    startup_failure
    CodeQL Security Analysis                    startup_failure
    Tests (E2E / Integration / Stress / Bench)  startup_failure
    

    Reproduced identically on the fix/cargo-testcontainers branch, so it is a
    property of the tree, not of the main ref. This is consistent with the cause
    already identified here — the #830 pin bumps landed across five workflows,
    and this issue only followed one of them.

    The consequence worth naming: the only doctest run in the repo is dead

    Job-name mapping, measured from the workflow files:

    Workflow Supplies State
    rust.yml ("Rust CI") Check, Test, Format, Clippy, Cargo check + clippy + fmt, Cargo test runs
    quality.yml ("Docs & Code Quality") Build AsciiDoc, Validate Documentation, … runs
    ci.yml ("CI") Rust Check & Clippy, Rust Format, Rust Tests, Rust Coverage, Haskell jobs, CI Status startup_failure — none run

    ci.yml:98 is the only cargo test --doc in the entire repository:

    $ command grep -rn -- '--doc' .github/workflows/
    .github/workflows/ci.yml:98:  run: cargo test --workspace --doc
    

    Those doctests exist and pass — cargo test --workspace --doc locally on
    main @ 4fa2e39 reports 4 passed (integration/src/lib.rs,
    ci_simulation/{mod,scenarios,assertions}.rs). They execute nowhere in CI.
    Rust Coverage is likewise never produced.

    This is the same shape as #836 — a tier that exists, passes locally, and runs
    in no pipeline — reached by a different route. #836 is a needs:-skip; this is
    a startup death. Both are invisible in the checks list for the same reason: the
    absence of a check reads like nothing rather than like a failure.

    Suggested addition to the acceptance criteria

    • Whatever cures tests.yml must be verified on all four workflows, each
      evidenced by jobs > 0 on a real run — not by the workflow reappearing in
      the checks list.
    • After the cure, confirm cargo test --workspace --doc actually executes in
      CI and reports a non-zero denominator, so the doctest tier is provably live
      rather than merely present.

    Found while landing #840 (#838). Related: #836, #841.

  3. hyperpolymath commented on Sep 22, 2026

    @hyperpolymath
    OwnerAuthor

    Fixed by #842, squash-merged to main as 450734e
    (verified=true reason=valid).

    Root cause -- two faults, not four

    1. codeql-action pinned to the version its own comment forbids.
    #830 (chore(deps): bump the actions group) moved all 8 sites to
    1c5b675653bb5c22dbe9b12b556ec555138e09fd while leaving the trailing comment
    reading # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100). That SHA is
    v4.38.1, which GitHub refuses at workflow startup estate-wide. Reverted to
    b96794f015dfd88f77b49b1c93e0fa7110f94c63 (genuinely v4.38.0).

    A trailing version comment is not evidence of what is pinned.

    2. An unparseable actions.lock under sha_pinning_required.
    taiki-e/install-action@v2.87.16 was used while the lock pinned only
    v2.87.15, and the lock carried an entry whose full-40-hex key did not resolve
    to itself (codeql-action@1c5b675... claiming commit b96794f...). A
    self-contradicting entry makes the whole lockfile unreadable, which is why
    ci.yml and tests.yml died too although neither uses codeql-action.

    How it was localized

    actionlint reported zero errors and yq parsed all four files, and
    actions.lock already covered 32 of 32 workflows -- so neither the workflow
    files nor the "absent from the lock" cure applied. Comparing each workflow's
    normalized uses: refs against the lock (collapsing
    codeql-action/init@X and /analyze@X to codeql-action@X) implicated
    exactly the four dead workflows and no others.

    ⚠ gh actions-lock --no-fix exits 0 while printing lockfile is unreadable. Branch on the message, never the exit code.

    Verification on main @ 450734e

    All four previously 0-job / 0s startup deaths now produce jobs:

    Workflow before after
    CodeQL Security Analysis 0 jobs 2 jobs
    CI 0 jobs 6 jobs
    Security 0 jobs 14 jobs
    Tests (E2E / Integration / Stress / Bench) 0 jobs 6 jobs

    gh actions-lock --no-fix now scans 32 workflows with zero findings.

    What the cure surfaced

    Reviving tests.yml also repaired the needs: chain: Build Test Images
    passes, so Integration Tests executed and passed -- mix test has now run
    for the first time, which retires the premise behind #836.

    It also made End-to-End Tests run for the first time ever, and it fails --
    docker compose cannot find its configuration file, so the E2E suite is still
    skipped. That job was skipped on every run before the startup death and
    absent after, so it has never once executed. This is a first measurement of
    pre-existing breakage, not a regression from #842. Filed as #843.
    Integration Status is red purely as the rollup of that job.

    🤖 Generated with Claude Code

    https://claude.ai/code/session_0113HQM9LVGkNCzU1WwkJZSV

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cicdCI/CD: workflows, actions, lockfiles, pins, runners, release gatestestingTests, benchmarks, fuzzing, property checks, coverage

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions