Skip to content

ci: hygiene aggregate annotations name the failed gate but not why it failed #6009

Description

@kyle-sexton

Problem

Each hygiene gate step in lint and lint-2 runs with continue-on-error: true, and scripts/aggregate-hygiene-results.sh fails the job at the end. For a failed gate it emits only:

::error title=<check> failed::Hygiene check finished with outcome: failure

The gate step's own annotation is the generic "Process completed with exit code 1." with no step name. The reason (which file, which rule, what command fixes it) is printed only in the gate step's log, so a reader has to open the full job log and search for it.

Evidence

Run 37094085450, job lint-2 (111120385079), on #5992:

gh api repos/melodic-software/claude-code-plugins/check-runs/111120385079/annotations returns six annotations: three "Process completed with exit code 1." and three "Hygiene check finished with outcome: failure" titled spoke-plugin-root failed, plugin-options-docs failed, shell-portability-lint failed. The reasons are only in the 3,976-line log:

  • line 802: plugins/harness-config/skills/audit/reference/audit-checklist.md: has 2 occurrence(s) of ${CLAUDE_PLUGIN_ROOT}, scripts/spoke-plugin-root-baseline.txt allows ...
  • line 1234: STALE options docs in 1 plugin(s): harness-ops / Run: python scripts/sync-plugin-options-docs.py
  • line 3714: PORTABILITY: plugins/harness-ops/hooks/session-event-log.test.sh:345: \\b -> ...

Each of those lines would have been enough to fix the failure without opening the log.

Impact

Every hygiene failure costs a log download and search (gh run view --log plus grep, or scrolling the web log) before the fix can start. Agents pay this on every red run, and the PR checks page shows nothing actionable.

Proposed fix options

  1. Capture each gate's output and put its tail in the aggregate annotation. Each gate step tees stdout and stderr to $RUNNER_TEMP/hygiene/<id>.log; on failure the aggregator adds the last N lines (say 10) to the ::error message and writes all failing tails to $GITHUB_STEP_SUMMARY. One change in the aggregator plus a tee in each step, or a small wrapper the steps call.
  2. Have each gate script emit its own ::error file=...,line=...:: annotations. Best signal (inline on the diff), but it means editing about 35 scripts and the composite actions in ci-workflows.
  3. Fold this into the single gate runner proposed in the companion draft ("ci: add one local entry point that runs the lint and lint-2 hygiene gates against the diff"): the runner already holds each gate's output and can emit the annotation itself.

Recommendation: option 1 now, which is small and self-contained, and option 3 if the gate runner lands. Basis: the annotation and log lines above.

Acceptance criteria

  • For a failed hygiene gate, the check run's annotations include at least the gate's last non-empty output lines, so the three failures in run 37094085450 would each show their reason line above in the annotation.
  • The job summary lists every failing gate with its output tail.
  • scripts/aggregate-hygiene-results.sh --self-test covers the new behaviour.

Activity

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

    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.status: readyTriaged, unblocked, and fully specified; eligible to pick up.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions