Commit d73c93e
## Summary
\`rust-ci-reusable.yml\` lines 109 and 149 used a bare \`if:\`
expression at job level:
\`\`\`yaml
if: hashFiles(format('{0}/Cargo.toml', inputs.working_directory)) != ''
\`\`\`
GH Actions silently fails to parse the entire CALLER workflow when this
exact shape (`hashFiles(format(inputs.X))` bare at job-level) is reached
in a reusable. Caller manifests as 0s-duration "completed failure" with
the workflow name reported as the path (`.github/workflows/rust-ci.yml`)
instead of the YAML `name:` field — GitHub's "This run likely failed
because of a workflow file issue" diagnostic.
## Root-cause investigation
Run on panic-attack#95 (experiment branch, 5 commits), tracking the
parsed workflow `name` field via API:
| commit | caller pin | caller shape | reusable | parsed name | result |
|---|---|---|---|---|---|
| `1d0a8b9` | `@cc5a372` | original thin wrapper |
`rust-ci-reusable.yml` | path-fallback | ❌ fail |
| `d022c8f` | `@main` | original thin wrapper | `rust-ci-reusable.yml` |
path-fallback | ❌ fail |
| `02c6753` | `@main` | governance.yml shape | `rust-ci-reusable.yml` |
path-fallback | ❌ fail |
| `6250e19` | `@main` | governance.yml shape |
**`governance-reusable.yml`** | `'Rust CI'` ✓ | parses (fails at
runtime, expected) |
| `cb23224` | `@main` | governance.yml verbatim clone |
`governance-reusable.yml` | `'Rust CI'` ✓ | parses |
Falsifies #322 hypotheses 1+2+3:
- Hypothesis 1 (caller structure) — falsified: changing the caller's
YAML to match governance.yml verbatim did NOT fix.
- Hypothesis 2 (SHA resolution) — falsified: `@main` floating ref ALSO
failed.
- Hypothesis 3 (workflow_id cache) — falsified: a fresh \`workflow_id\`
287101574 (created via filename rename) ALSO failed.
Root cause: hypothesis 4 (new) — when the caller's \`uses:\` resolves to
a reusable whose first job has a bare \`if: hashFiles(format(...))\` at
job level, GH's expression evaluator rejects the resolution at
workflow-parse time. The audit + coverage jobs (lines 187, 215) were
always wrapped in \`\${{ }}\` and worked; the check + test jobs were
not.
## The fix
Two-line `if:` wrapping. Diff:
\`\`\`diff
- if: hashFiles(format('{0}/Cargo.toml', inputs.working_directory)) !=
''
+ if: \${{ hashFiles(format('{0}/Cargo.toml', inputs.working_directory))
!= '' }}
\`\`\`
## Estate impact
Once this merges, every caller of \`rust-ci-reusable.yml\` should
immediately start parsing on its next push — no caller-side action
needed for callers using \`@main\` floating refs. SHA-pinned callers
will need to bump the pin to a commit at-or-after this merge.
## Follow-up
- 43+ panic-attack-tracked callers using SHA-pinned rust-ci-reusable.yml
— bumping their pins to the new commit could be a fan-out, but a no-op
SHA bump is sufficient once \`@main\` resolution is confirmed clean.
- Audit other reusables (\`governance-reusable.yml\`,
\`secret-scanner-reusable.yml\`, etc.) for the same anti-pattern via
grep: \`^\\s*if: [^$]\` followed by \`hashFiles\\|format\\|inputs\\.\` —
none found in governance-reusable on inspection but worth a sweep.
## Refs
- Closes #322
- Investigation PR (panic-attack side): hyperpolymath/panic-attack#95
- Original symptom report PRs: panic-attack#84 (orphan-SHA hypothesis,
falsified), panic-attack#92 (SHA-bump experiment, falsified)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3f34549 commit d73c93e
1 file changed
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
109 | | - | |
| 109 | + | |
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
| 149 | + | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
| |||
0 commit comments