Skip to content

ci(test): guard the mise cache settings and save the rocks cache only on push - #579

Merged
ThomasK33 merged 3 commits into
mainfrom
ci/mise-cache-guard
Nov 12, 2026
Merged

ThomasK33 merged 3 commits into
mainfrom
ci/mise-cache-guard

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Why

#560 keyed mise-action's tool cache on mise.lock, saved it only on a push, and raised mise's download retries. Its independent review found two gaps, which this PR closes.

  1. No test or lint checked those three settings. actionlint and zizmor stayed green when a later edit removed cache_key or MISE_HTTP_RETRIES, or loosened cache_save. A removed cache_key or MISE_HTTP_RETRIES can show up later as a slow or failed Set up mise step. A loosened cache_save shows nowhere: no step gets slow or fails.
  2. The Lua rocks cache still saved on pull requests and the merge queue. After a miss, actions/cache saves in its post step on pull_request and merge_group runs too, after the PR's own scripts ran. GitHub scopes cache entries by ref, so an entry saved by a PR run cannot reach main. No run has shown a bad entry. This change removes the write path by construction, as ci(test): key mise-action's tool cache on mise.lock and raise mise's HTTP retries #560 did for the tool cache.

What changed

  1. tests/unit/workflow_cache_spec.lua (new, 10 tests) reads .github/workflows/test.yml as text. It uses plain Lua string matching, no YAML library and no network. Comment lines do not count, so a setting that sits only in a comment fails. A step that is renamed fails closed (no step is named ...). The spec finds each setting in its place: a key of the step at eight spaces of indent, and an entry of the with: or env: block at ten. A setting that moves to another block fails.
    • Set up mise step: cache_key is under with: and matches mise-v<n>-{{platform}}-{{version}}-${{ hashFiles('mise.lock') }} as a whole (<n> can change). So it hashes only mise.lock (one hashFiles(, no {{file_hash}}, no {{default}}), and no text that changes between runs can be added to it. The step has no restore-keys. cache_save is under with: and is exactly ${{ github.event_name == 'push' }}. MISE_HTTP_RETRIES is under env: and is an integer of at least 6.
    • Rock cache: every step that uses actions/cache (plain, restore or save, with or without a quote around the name) and has a line that contains the text .luarocks (the path as .luarocks, ./.luarocks, ~/.luarocks, an absolute path, or one line of a multi-line path) must be, in order, one actions/cache/restore and one actions/cache/save. So a second step for the tree written with a plain uses: key, or the old single actions/cache@ step, fails. Both steps pin the same 40-character SHA and both use the path .luarocks. The restore key hashes mise.toml, and the restore step has no restore-keys. The save key is the restore step's cache-primary-key output. The save step's if: is exactly github.event_name == 'push' && steps.luarocks-cache.outputs.cache-hit != 'true'.
  2. .github/workflows/test.yml: the Cache Lua test rocks step (actions/cache) becomes Restore Lua test rocks (actions/cache/restore, id: luarocks-cache, same key) before mise run setup, and Save Lua test rocks (actions/cache/save) after it. Both use the SHA that actions/cache had (27d5ce7f..., v5.0.5). The save step runs only on a push to main and only when the restore did not find the exact key. The comment above the restore step says what the old step did: it saved after a miss, on every event.

README.md and DEVELOPMENT.md do not describe the CI cache, so they are unchanged.

Evidence

All numbers are from runs on this head (cc903344) unless a row says otherwise.

Claim Evidence
The guard passes on the head 10 successes, 0 failures
It passes on the first head of this PR (37efd357), whose workflow is the same except for one comment 10 successes, 0 failures
It fails on main before #560 (523ee200) 1 success, 9 failures: 3 of the 4 Set up mise tests (key, cache_save, retries) and all 6 rock-cache tests
It fails on main now (c7e2eb4e, and b43d1c23, 7dbad04b and 4005ab66 give the same) 4 successes, 6 failures: the 6 rock-cache tests. The 4 Set up mise tests pass, as they should
It fails on each of the 6 mutants of #560's review 1 failing test per mutant, listed below. actionlint and zizmor exit 0 on all 6
It fails on a mutant of each other rule 55 mutants in all (m1 to m12, L1 to L12, X1 to X23, Y1 to Y8): 47 red, 4 controls and 4 known gaps green. actionlint and zizmor exit 0 on 43 of the 47 red mutants
A harmless edit stays green Controls m12 (retries "7"), X8 (retries 6 without quotes), X19 (an actions/cache step for another path), X23 (a second cache_save under env: next to the right one): 10 successes each
The workflows are valid actionlint 1.7.12 on all 5 workflows: exit 0 on the head and on main (b43d1c23 and c7e2eb4e). zizmor 1.25.2 --offline on all 5: no findings (25 suppressed), the same as main. mise run format-check: exit 0 (436 files, 0 changed)
Gate mise run clean && mise run all exits 0 in 1318 s: 1847 successes, 0 failures, 0 errors, 33 pending (seed 1794518856). luacheck: 0 warnings, 0 errors in 194 files. The stub claude and GNU timeout first on PATH
Mutants and the test each one turns red

Each mutant is the head's test.yml with one edit that applies exactly once. m1 to m6 are the six mutants of #560's review. m7 to m12 and L1 to L12 are mine from the first round. X1 to X15 came from the independent review of the first head, X16 to X23 are mine, and Y1 to Y8 came from the independent review of the second head.

Mutant Edit Failing test
m1 delete the cache_key: line keys the tool cache on mise.lock only
m2 delete the cache_save: line saves the cache only on a push
m3 cache_save: ${{ github.event_name != 'pull_request' }} saves the cache only on a push
m4 delete the env: block with MISE_HTTP_RETRIES retries a failed download at least 6 times
m5 hashFiles('mise.lock', 'mise.toml') keys the tool cache on mise.lock only
m6 the key without the hash keys the tool cache on mise.lock only
m7 add restore-keys to the step restores from the exact key only (mise step)
m8 MISE_HTTP_RETRIES: "5" retries a failed download at least 6 times
m9 MISE_HTTP_RETRIES: "six" retries a failed download at least 6 times
m10 add -{{file_hash}} to the key keys the tool cache on mise.lock only
m11 {{default}}-${{ hashFiles('mise.lock') }} keys the tool cache on mise.lock only
L1 put the single actions/cache@ step back all 6 rock-cache tests (the steps are not restore then save)
L2 delete the save step's if: saves only on a push, and only after a miss
L3 if: github.event_name != 'pull_request' && ... saves only on a push, and only after a miss
L4 if: github.event_name == 'push' (no miss check) saves only on a push, and only after a miss
L5 the save step's SHA differs in its last digit restores and saves with the same pinned action
L6 the save key hashes mise.lock saves under the key that the restore step tried
L7 the restore key hashes mise.lock restores the key that the rock list in mise.toml decides
L8 the save step before the restore step all 6 rock-cache tests
L9 delete the restore step's id saves under the key that the restore step tried, and saves only on a push, and only after a miss
L10 actions/cache/save@v5 restores and saves with the same pinned action
L11 actions/cache/restore@v5 restores and saves with the same pinned action
L12 delete the save step all 6 rock-cache tests
X1 cache_save only in a comment saves the cache only on a push
X2 the step renamed Setup mise all 4 Set up mise tests (no step is named ...)
X4 if: ... cache-hit == 'false' saves only on a push, and only after a miss
X5 if: always() && ... saves only on a push, and only after a miss
X6 a second actions/cache@ step for ./.luarocks all 6 rock-cache tests
X7 restore-keys on the rock restore restores from the exact key only (rock cache)
X9 the key without {{version}} keys the tool cache on mise.lock only
X11 cache_save: "true" saves the cache only on a push
X12 hashFiles('mise.lock') without ${{ }} keys the tool cache on mise.lock only
X13 MISE_HTTP_RETRIES moved under with: retries a failed download at least 6 times
X14 cache_save moved under env: saves the cache only on a push
X15 cache_key moved under env: keys the tool cache on mise.lock only
X16 a second actions/cache@ step for the absolute path of .luarocks all 6 rock-cache tests
X17 a second actions/cache@ step with .luarocks in a multi-line path all 6 rock-cache tests
X18 a second actions/cache/save@ step for .luarocks all 6 rock-cache tests
X20 the save path ./.luarocks restores and saves the same path
X21 the restore path ./.luarocks restores and saves the same path
X22 `restore-keys: ` (multi-line) on the rock restore
Y1 a second actions/cache@ step for .luarocks with a double-quoted uses: all 6 rock-cache tests
Y2 -${{ github.run_id }} added to the mise key keys the tool cache on mise.lock only
Y5 the restore path written as ".luarocks" restores and saves the same path
Y6 a second actions/cache@ step for ~/.luarocks all 6 rock-cache tests
Y7 MISE_HTTP_RETRIES set in the job's env: instead of the step's retries a failed download at least 6 times
Y8 a second actions/cache@ step for .luarocks with a single-quoted uses: all 6 rock-cache tests

Green on purpose: m12, X8, X19 and X23 are harmless edits. Green because the spec does not check it: X3 (a second cache_save: true key: actionlint rejects a duplicate key, and mise run format-check runs actionlint), X10 (the save step moved after the tests), Y3 (a second jdx/mise-action step with the default cache settings) and Y4 (-${{ github.sha }} added to the rock key). Y5, Y6 and Y7 are harmless edits that the spec fails on purpose.

actionlint exits non-zero on L8 and L9 (an undefined step id) and X3, and zizmor exits 14 on L10 and L11 (an unpinned action). Both exit 0 on every other mutant.

Not covered

  • The spec reads text. It does not show that GitHub restores or saves as the settings say. It pins the if: as an exact string, so a reformatted but equal expression fails it on purpose: edit the spec with the workflow. It does not check that the save step stays right after mise run setup (X10).
  • The spec fails some harmless edits on purpose, so a maintainer edits it with the workflow: the restore path in quotes (Y5), a cache step for ~/.luarocks (Y6), MISE_HTTP_RETRIES set in the job's env: (Y7), and the cache_key value in YAML quotes.
  • The spec finds a step only in block style, and finds uses: only as a plain key. Three spellings of a second .luarocks cache step are not seen: a quoted key ("uses": actions/cache@…), which actionlint reads as uses, a step in YAML flow style (- { uses: actions/cache@…, with: { path: .luarocks, key: … } }), and an owner or repository name in other letter case (Actions/cache@…), which GitHub resolves to the same action. A follow-up PR in this run lets the spec accept an optional quote around the key and match the action name in any letter case.
  • A second jdx/mise-action step with the default cache settings (Y3) would save the tool cache on a pull request, and the spec does not look for it.
  • The rock restore key is checked only for hashFiles('mise.toml'), so text that changes on each run, such as -${{ github.sha }} (Y4), keeps the spec green and the cache never hits.
  • A rock path without the text .luarocks (a glob such as .luaroc*, or a parent folder) is not seen as a rock cache step.
  • A PR that edits mise.toml (a task edit included) changes the rock cache key, misses, and now rebuilds the rocks on every one of its runs, because a PR no longer saves. Before, its first run saved and later runs hit. mise run setup took 64 s on one host and 195 s on another. A PR that leaves mise.toml alone restores main's entry as before.
  • Eviction has the same effect. GitHub removes a cache entry that nobody accessed for 7 days, and removes the least recently accessed entries when the repository passes its cache limit (10 GB by default). When main's entry for a key is gone, every PR run misses and rebuilds until the next push to main saves the key again. Before this PR, the first PR run after an eviction saved the entry for its own later runs. The mise tool cache from ci(test): key mise-action's tool cache on mise.lock and raise mise's HTTP retries #560 behaves the same way: each PR run downloads the tools again until a push to main saves the key.
  • The save step runs right after mise run setup, not at the end of the job, so a later red step does not stop the save. The rock tree is complete at that point, and a failed setup fails the job before the save step.
  • covered-lock still caches the TLC jar with plain actions/cache. check.sh checks the sha256 of a restored jar on every run and exits 2 before TLC runs on a mismatch. I left it alone.
  • The spec fails on a tree made with git archive: .gitattributes export-ignores .github. The failure message says so. A checkout or a git worktree has the file.

Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh

…only on push

A spec reads .github/workflows/test.yml and checks the cache settings of the
Set up mise step: cache_key hashes only mise.lock and the step has no
restore-keys, cache_save is true only on a push, and MISE_HTTP_RETRIES is an
integer of at least 6. actionlint and zizmor accept a workflow that loses any
of them.

actions/cache saves the Lua rocks cache in its post step on every event,
including pull_request and merge_group, after the PR's own scripts ran. The
step is now actions/cache/restore plus actions/cache/save at the same pinned
SHA. The save step runs only on a push, and only after a restore miss. The same
spec checks that split.

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking:
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T21:53:29.560594Z cc90334 Manual request
🔒 Security Review ✅ Completed 2026-11-12T21:53:32.059297Z cc90334 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 37efd357b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 37efd357b0

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…ks cache step

The spec found a setting by its key anywhere in the step, so cache_key or
cache_save moved under env:, or MISE_HTTP_RETRIES moved under with:, kept it
green. It now looks for each setting in its with: or env: block and requires
hashFiles('mise.lock') inside ${{ }}.

The rock cache rules now name the tree in any spelling (./.luarocks, an
absolute path, a line of a multi-line path): a second actions/cache step for
it fails, and so does a restore-keys on the restore step. Both paths must be
.luarocks. The comment above the restore step says that actions/cache saves
only after a miss.

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking:
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 95f33947f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 95f33947f2

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…kflow spec

A double-quoted `uses: "actions/cache@..."` hid a second step that caches
.luarocks from the rock-cache rule, so the spec now strips a leading quote
before it matches the action name.

The mise cache_key must now match mise-v<n>-{{platform}}-{{version}}-
${{ hashFiles('mise.lock') }} as a whole. A key that drops {{version}} or adds
text that changes on each run (${{ github.run_id }}) fails. The comment of
caches_rock_tree now says what the function checks.

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking:
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: cc90334438

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: cc90334438

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit b450eb9 Nov 12, 2026
7 checks passed
@ThomasK33
ThomasK33 deleted the ci/mise-cache-guard branch November 12, 2026 23:03
hareki pushed a commit to hareki/claudecode.nvim that referenced this pull request Nov 14, 2026
…tep it cannot read (coder#591)

## Why

coder#579 added a spec that reads `.github/workflows/test.yml` as text and
checks the cache settings. The independent reviews of its heads found
gaps in how the spec reads the file. Each gap lets a risky edit pass or
fails a harmless one.

1. A step written with a quoted key (`"uses": actions/cache@...`) was
not seen, so a second step that caches `.luarocks` passed.
2. A second `jdx/mise-action` step with the default cache settings would
save the tool cache on a pull request. Nothing failed.
3. The rock restore key was only checked to contain
`hashFiles('mise.toml')`. Text that changes on each run (`-${{
github.sha }}`) kept the spec green and the cache would never hit.
4. The spec failed edits that YAML reads the same: a quoted `cache_key`,
a quoted restore path, a quoted `uses:`.
5. A step in flow style (`- { uses: actions/cache@..., with: { path:
.luarocks, key: ... } }`) was not seen.

## What changed

Only `tests/unit/workflow_cache_spec.lua` (+98, -19). The workflows are
unchanged. The spec still has no YAML parser.

1. `field()` accepts a quote around the key (`"uses":` or `'uses':`).
2. A new test: every `jdx/mise-action` step turns its cache off (`cache:
false` or `cache_save: false`) or saves only on a push.
3. The rock restore key must be `luarocks-${{ runner.os }}-${{
hashFiles('mise.toml') }}` as a whole. A version after the prefix
(`luarocks-v2-`) is allowed, as `v1` is in the mise key.
4. `field()` strips one pair of YAML quotes from a value, and reads `''`
in a single-quoted value as `'`.
5. A new test fails closed. It counts the lines of the file that are not
comment lines and name `actions/cache` or `jdx/mise-action` (a trailing
comment stays in the line), and the steps that have the name as their
`uses:`. The two counts must be equal. So the spec fails for a
flow-style step or list of steps, even with a ` #` inside a quoted
value, and for the name on any other line (a step `name:`, a `run:`
line, a trailing comment). The line count ignores the letter case and
the `uses:` match does not, so an upper-case spelling such as
`Actions/cache@...` fails too. The one exception is a name written with
a YAML escape (`"actions\x2fcache@..."`), which the spec does not decode
(see Not covered).
6. The helper that tells whether a step uses an action requires `@` or
`/` after the action name. A similarly prefixed repository such as
`jdx/mise-action-fork@...` no longer counts as `jdx/mise-action`, so it
cannot satisfy the line count or the check that a `jdx/mise-action` step
exists.

## Evidence

Before is the spec of coder#579 (10 tests, merged as `b450eb91`), after is
this branch (12 tests). Each mutant is the workflow of coder#579 with one
edit that applies exactly once. `m`, `L`, `X` and `Y` are the mutants of
coder#579, `Z1` to `Z12` are the probes of its last review, `N1` to `N20` are
mine (there is no `N6`), `P1` to `P26` are the probes of the review of
this PR (the ones in the tables below), and `Q1` to `Q4` come from a
Codex finding on the second head.

| Item | Mutant | Before | After |
| --- | --- | --- | --- |
| 1 | `Z9` and `N5`: a second `actions/cache@` step for `.luarocks` with
a quoted key `"uses":` or `'uses':` | green | red (6 rock-cache tests) |
| 2 | `Y3`: a second `jdx/mise-action` step with the default cache
settings. `N10`: it sets `cache_save: true`. `N11`: it sets `cache_save`
to `!= 'pull_request'` | green | red (1 test) |
| 2 | Controls `N7` (`cache: false`), `N8` (`cache_save: false`), `N9`
(the push-only expression in quotes) | green | green |
| 3 | `Y4`: `-${{ github.sha }}` on the rock key. `N14`: an extra
segment in it | green | red (1 test) |
| 3 | Controls `N12` (the key in quotes), `N13` (`luarocks-v2-`) | green
| green |
| 4 | `Z1` (quoted `cache_key`), `Y5` (quoted restore path), `Z7`
(quoted `uses:` on the restore step), `N15` (quoted `cache_save`), `N16`
(quoted save `if:`), `N17` (single-quoted mise key with `''`), `N19`
(quoted save path), `N20` (quoted step name) | red | green |
| 4 | Control `N18` (`MISE_HTTP_RETRIES: '6'`) | green | green |
| 5 | `N1`: a flow-style second cache step for `.luarocks`. `N2`: the
same for another path, which fails closed on purpose. `N3`: a flow-style
second `jdx/mise-action` step. `Z6`: `Actions/cache@` | green | red (1
test) |
| 5 | `P23` and `P25`: a flow-style second cache step for `.luarocks`
with a ` #` inside a quoted value. `P24`: the same for a second
`jdx/mise-action` step. `P26`: the same step as `P23` without the `#` |
green | red (1 test) |
| 6 | `Q1`: the setup step uses `jdx/mise-action-fork@...`. `Q2`: a step
`actions/cache-extra@...` for another path. `Q3`: the same step for
`.luarocks` | green | red (1 test, and 2 for `Q1`) |
| 6 | Control `Q4`: the setup step uses `jdx/mise-action/sub@...` |
green | green |
| 5 | `N4`: a flow-style `with:` on the rock restore. It fails the path
and key tests, as it did before, and not the new test | red | red |
| 5 | Harmless edits that the spec fails on purpose: `P3` (a trailing
comment that names `actions/cache` on a `run:` line), `P4` (the name in
a step `name:`), `P5` (the name in a `run:` line), `P13` (`cache: False`
on a second `jdx/mise-action` step) | green | red (1 test) |

Counts over all 102 mutants (the 86 of the first head, `P1`, `P1b`,
`P2`, `P3`, `P4`, `P5`, `P13`, `P14`, `P23` to `P26`, and `Q1` to `Q4`).
Before, 59 are red and 43 are green. After, 73 are red and 29 are green.
51 stay red, 22 turn red, 8 turn green and 21 stay green. Of the 29
green mutants, 8 are harmless edits that used to fail, 13 are controls
(`m12`, `X8`, `X19`, `X23`, `Z2`, `Z3`, `N7`, `N8`, `N9`, `N12`, `N13`,
`N18`, `Q4`), and 8 are gaps (`X3`, `X10`, `Z10`, `Z11`, and the four
YAML escapes `P1`, `P1b`, `P2`, `P14`, see below). Five of the 51 red
mutants (`m2`, `m3`, `X1`, `X11`, `X14`) and the tree before coder#560 now
also fail the new `jdx/mise-action` test next to the test that failed
before. No other test result changed.

| Claim | Evidence |
| --- | --- |
| The spec passes on main (`b450eb91`) with this change | 12 successes,
0 failures |
| It still fails on the trees before coder#579 (`b43d1c23`, `c7e2eb4e`) | 6
successes, 6 failures: the 6 rock-cache tests |
| The workflows are valid | The workflows are identical to main.
`actionlint` 1.7.12 on all 5: exit 0. `zizmor` 1.25.2 `--offline` on all
5: no findings (25 suppressed). On the mutants, `actionlint` exits
non-zero only on `L8`, `L9` and `X3`, and `zizmor` only on `L10` and
`L11`, as for coder#579. `mise run format-check`: exit 0 (441 files, 0
changed) |
| Gate | `mise run clean && mise run all` exits 0 in 540 s: 1904
successes, 0 failures, 0 errors, 35 pending (seed 1794532755). It ran on
the merge of main (`692b460c`) into this branch. `luacheck`: 0 warnings,
0 errors in 198 files. The stub `claude` and GNU `timeout` first on
`PATH` |

## Not covered

- The spec does not see a rock path without the text `.luarocks` (a glob
such as `.luaroc*`, `Z10`, or a parent folder, `Z11`). The save step is
not checked for its place in the job, so moving it after the tests keeps
the spec green (`X10`). A second `cache_save: true` key is caught by
`actionlint` and not by the spec (`X3`).
- The new tests also fail harmless edits, so a maintainer edits the spec
with the workflow. Examples are a flow-style step for another path
(`N2`), the name of an action in a step `name:` (`P4`), in a `run:` line
(`P5`) or in a trailing comment (`P3`), and `cache: False` on a second
`jdx/mise-action` step (`P13`). The action reads `False` as false and
the spec compares the text `false`. A flow-style `with:` on a step the
spec reads fails the path and key tests (`N4`), as it did before.
- A name written with a YAML escape (`"actions\x2fcache@..."`,
`"actions\u002fcache@..."`, `"jdx\x2fmise-action@..."`, or a path
`"\x2eluarocks"`) is not decoded, so the spec does not see it (`P1`,
`P1b`, `P2`, `P14`). The spec guards against accidental edits, and an
escape spelling is not an accident, so it has no reader for it. A
double-quoted `uses:` split by an escaped line break (`"actions/\` and
then `cache@..."`) falls in the same class.
- GitHub ignores the letter case in the owner and the repository of
`uses:`, as the runner log in actions/runner#3731 shows. So
`Actions/cache@...` is a real cache step, and the line-count test
rejects it. The spec does not read it as a rock-cache step.
- The spec reads the rock path as text, so a path that comes from a YAML
alias (`with: *rocks` on a second `actions/cache` step, where the
restore step's `with:` has the anchor `&rocks`) or from an expression
such as `${{ format(...) }}` is not seen. GitHub supports anchors and
aliases. An alias is a deliberate structure, like an escape, not an
accidental edit, so the spec has no reader for it.
- `field()` also accepts a quote on one side of a key (`cache_save":
...`, `cache': false`). YAML reads that as another key (`cache_save"`),
so the action ignores it and uses its default (`true`). The spec reads
it as `cache_save`, so a Set up mise step written this way passes this
spec (the spec on main fails it), and so does a second `jdx/mise-action`
step with `cache': false`. actionlint 1.7.12 exits 0 on both. A
follow-up PR requires a matching pair of quotes.
- The spec fails on a tree made with `git archive`, because
`.gitattributes` export-ignores `.github`. The failure message says so.

---
_Generated with [`xum`](https://github.com/coder/xum) • Model:
`anthropic:claude-sonnet-5-5` • Thinking: `xhigh`_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant