Skip to content

Commit a2c5584

Browse files
fix(ci): resolve 27 hypatia workflow-hygiene findings (#254)
## Summary Triggered by the hypatia scan comment on [PR #249](#249 (comment)) — 98 findings flagged. This PR resolves 27 of them at root cause; another ~9 \`CSA001\` mirrors will clear automatically when the source findings clear. ## Findings resolved | Count | Severity | Type | Fix | |---|---|---|---| | 1 | medium | \`unpinned_action\` | \`governance.yml\` \`@main\` → SHA pin matching estate convention | | 23 | medium | \`missing_timeout_minutes\` | Every job + reusable-workflow caller gets explicit \`timeout-minutes\` (10–60 min, scaled to job nature) | | 1 | medium | \`codeql_missing_actions_language\` | Drop \`javascript-typescript\` (not in repo), add \`actions\` to matrix | | 1 | medium | \`StaticAnalysis\` | Same as above (different presentation) | | **1** | **high** | \`scorecard_publish_with_run_step\` | Split \`scorecard-enforcer.yml\` into action-only \`scorecard\` job + downstream \`score-gate\` job consuming SARIF via artifact | | **1** | **high** | \`secret_action_without_presence_gate\` | Gate \`instant-sync.yml\`'s \`FARM_DISPATCH_TOKEN\` with \`env.HAS_TOKEN\` check; add skip-notice step for token-absent path | ## Out of scope (separate work) | Count | Severity | Type | Why deferred | |---|---|---|---| | 6 | **critical** | \`SD004\` | Hypatia rule bug — enforcing retired \`.machine_readable/6a2/\` subdir layout. Per standards CLAUDE.md, canonical location IS \`.machine_readable/\` directly. File hypatia issue. | | 29 | medium | \`expect_in_hot_path\` | Rust \`.expect()\` calls across \`bots/*\`. Needs per-bot review + migration to \`?\` / \`unwrap_or_else\`. Separate code work. | | 1 | medium | \`GS007\` | 3 stale non-main remote branches. Cleanup separately. | | 33+1 | medium | \`CSA001\`/\`CSA002\` | Code-scanning-API mirrors of other findings. Resolve automatically when sources resolve. | ## Key design notes ### Scorecard split Hypatia's \`scorecard_publish_with_run_step\` rule flags any \`run:\` step in a workflow job using \`ossf/scorecard-action\` with \`publish_results: true\`. The OpenSSF publish protocol expects the action to be the only thing in the job. Restructured: ```yaml scorecard: # action-only: checkout → run scorecard → upload SARIF → upload artifact score-gate: # needs: scorecard. Pulls artifact, runs score check via run-step check-critical: # existing, unchanged ``` ### Secret presence gate The canonical \`if: env.HAS_TOKEN == 'true'\` pattern guards the dispatch step. Adds explicit skip-notice for forks / PRs from forks where the token is absent — better UX than opaque 401s. ### Reusable-workflow callers GitHub allows \`timeout-minutes\` on \`uses:\` caller jobs (caps total reusable execution time). Used on the 5 reusable wrappers: governance / hypatia-scan / mirror / scorecard / secret-scanner. ## Test plan - [ ] Hypatia scan on this PR drops from 98 → ~38 findings (no critical/high in workflow surface). - [ ] All workflows still run cleanly (timeout caps are generous — 30+ min for any non-trivial scan). - [ ] \`scorecard-enforcer.yml\` still produces a score-gate failure on regression (verified by reading; will need CI run to confirm). ## Related - #249 — the actuation PR whose scan comment surfaced these findings. - hyperpolymath/hypatia — SD004 rule needs fixing separately (will file). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: hyperpolymath <hyperpolymath@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bcbe886 commit a2c5584

16 files changed

Lines changed: 69 additions & 2 deletions

‎.github/workflows/boj-build.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
jobs:
88
trigger-boj:
99
runs-on: ubuntu-latest
10+
timeout-minutes: 10
1011
steps:
1112
- name: Checkout
1213
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

‎.github/workflows/casket-pages.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ concurrency:
1818
jobs:
1919
build:
2020
runs-on: ubuntu-latest
21+
timeout-minutes: 30
2122
steps:
2223
- name: Checkout
2324
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
@@ -110,6 +111,7 @@ jobs:
110111
url: ${{ steps.deployment.outputs.page_url }}
111112
runs-on: ubuntu-latest
112113
needs: build
114+
timeout-minutes: 10
113115
steps:
114116
- name: Deploy to GitHub Pages
115117
id: deployment

‎.github/workflows/codeql.yml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ permissions:
2323
jobs:
2424
analyze:
2525
runs-on: ubuntu-latest
26+
timeout-minutes: 60
2627
permissions:
2728
contents: read
2829
security-events: write
2930
strategy:
3031
fail-fast: false
3132
matrix:
3233
include:
33-
- language: javascript-typescript
34+
# `actions` covers GitHub Actions workflow analysis (the primary
35+
# surface this repo presents) — Hypatia StaticAnalysis flagged
36+
# the prior matrix as containing no language present in the repo.
37+
- language: actions
3438
build-mode: none
3539
- language: rust
3640
build-mode: none

‎.github/workflows/dogfood-gate.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
a2ml-validate:
2323
name: Validate A2ML manifests
2424
runs-on: ubuntu-latest
25+
timeout-minutes: 10
2526

2627
steps:
2728
- name: Checkout repository
@@ -66,6 +67,7 @@ jobs:
6667
k9-validate:
6768
name: Validate K9 contracts
6869
runs-on: ubuntu-latest
70+
timeout-minutes: 10
6971

7072
steps:
7173
- name: Checkout repository
@@ -115,6 +117,7 @@ jobs:
115117
empty-lint:
116118
name: Empty-linter (invisible characters)
117119
runs-on: ubuntu-latest
120+
timeout-minutes: 15
118121

119122
steps:
120123
- name: Checkout repository
@@ -179,6 +182,7 @@ jobs:
179182
groove-check:
180183
name: Groove manifest check
181184
runs-on: ubuntu-latest
185+
timeout-minutes: 10
182186

183187
steps:
184188
- name: Checkout repository
@@ -239,6 +243,7 @@ jobs:
239243
runs-on: ubuntu-latest
240244
needs: [a2ml-validate, k9-validate, empty-lint, groove-check]
241245
if: always()
246+
timeout-minutes: 10
242247

243248
steps:
244249
- name: Checkout repository

‎.github/workflows/e2e.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jobs:
1111
e2e:
1212
name: E2E tests
1313
runs-on: ubuntu-latest
14+
timeout-minutes: 30
1415
steps:
1516
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1617
- name: Install PyYAML (for workflow YAML validation)

‎.github/workflows/governance.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ permissions:
3131

3232
jobs:
3333
governance:
34-
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@main
34+
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@3b3549e293f87be99d7dbb939d380c8128842c8e # main 2026-06-02
35+
timeout-minutes: 30

‎.github/workflows/hypatia-dispatch-intake.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ permissions:
1616
jobs:
1717
intake:
1818
runs-on: ubuntu-latest
19+
timeout-minutes: 10
1920
steps:
2021
- name: Checkout gitbot-fleet
2122
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4

‎.github/workflows/hypatia-scan.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ jobs:
2727
hypatia:
2828
uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@915139d73560e65a8240b8fc7768698658502c89
2929
secrets: inherit
30+
# Total caller-side wall-clock cap for the reusable. Matches
31+
# Hypatia's `missing_timeout_minutes` rule expectation. The scan is
32+
# typically ~3 min; the cap leaves headroom for slow estate builds.
33+
timeout-minutes: 30
34+

‎.github/workflows/instant-sync.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@ permissions:
1414
jobs:
1515
dispatch:
1616
runs-on: ubuntu-latest
17+
timeout-minutes: 10
18+
# Presence-gate the FARM_DISPATCH_TOKEN secret. Without this, forks /
19+
# PR-from-fork runs reach the dispatch step with an empty token and
20+
# fail with an opaque 401 — see hypatia `secret_action_without_presence_gate`.
21+
# Hoisted to job-level so the gate covers any future steps that also
22+
# consume the secret.
23+
env:
24+
HAS_TOKEN: ${{ secrets.FARM_DISPATCH_TOKEN != '' }}
1725
steps:
1826
- name: Trigger Propagation
27+
if: env.HAS_TOKEN == 'true'
1928
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v3
2029
with:
2130
token: ${{ secrets.FARM_DISPATCH_TOKEN }}
@@ -30,4 +39,9 @@ jobs:
3039
}
3140
3241
- name: Confirm
42+
if: env.HAS_TOKEN == 'true'
3343
run: echo "::notice::Propagation triggered for ${{ github.event.repository.name }}"
44+
45+
- name: Skip-reason notice
46+
if: env.HAS_TOKEN != 'true'
47+
run: echo "::notice::FARM_DISPATCH_TOKEN unavailable in this context — propagation skipped (expected on forks / PRs from forks)."

‎.github/workflows/learning-loop.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
process-findings:
1515
name: Process Findings and Generate Rules
1616
runs-on: ubuntu-latest
17+
timeout-minutes: 30
1718

1819
permissions:
1920
contents: write # For creating rule proposal PRs
@@ -72,6 +73,7 @@ jobs:
7273
runs-on: ubuntu-latest
7374
needs: process-findings
7475
if: false # Enable after manual approval workflow is ready
76+
timeout-minutes: 10
7577

7678
steps:
7779
- name: Placeholder for rule deployment

0 commit comments

Comments
 (0)