chore(deps): remove dependabot.yml, fold action bumps into the monthly sweep - #2239
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The reviewed changes have no unresolved approval-blocking issues.
Pull request overview
Replaces Dependabot version-update PRs with the monthly dependency sweep, extending it to monitor GitHub Actions.
Changes:
- Removes Dependabot configuration.
- Adds GitHub Actions release checks.
- Expands tests and updates workflow documentation and naming.
File summaries
| File | Description |
|---|---|
scripts/dependency-refresh.test.mjs |
Tests action parsing, comparison, deduplication, and issue rendering. |
scripts/dependency-refresh.mjs |
Detects and reports stale workflow action references. |
.github/workflows/dependency-refresh.yml |
Runs and documents the combined dependency sweep. |
.github/dependabot.yml |
Removes automated version-update configuration. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
GitHub API failures can silently disable action update detection.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Balanced
Addresses Copilot review round 2 on #2239. `latestReleaseTag` returned null on any non-zero `gh api` exit, so a rate limit, an expired token or a transient 5xx read as "this action has no release" and therefore as "not stale". That is the silent-success shape: the sweep exits green having checked nothing. It is worse here than the equivalent npm case, because every action in this repo already sits on its latest major — so the actions section is empty on a healthy run too, and an API-wide failure produces byte identical output to a clean sweep. Nothing would have surfaced it. Now only a 404 is suppressed, which is the legitimate "this action has never cut a GitHub release" answer; every other failure throws with the repo and stderr, and a spawn error throws too. The 404/not-404 split is extracted as a pure `isMissingRelease(stderr)` so it is covered by the suite under this file's convention of keeping `main()` and its spawns out of it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Response to Copilot review round 2 —
|
There was a problem hiding this comment.
🔵 Needs a closer look
The release lookup can miss available major action upgrades and must be corrected before approval.
Review details
Suppressed comments (4)
Previously missed (1) — in code that hasn't changed since the last review.
scripts/dependency-refresh.mjs:244
releases/latestis GitHub's designated most-recent release, not the highest numeric tag. If an action publishes or marks a maintenance release for an older major as latest (for example,v6.9.1afterv8.0.0), this comparison sees only v6 and incorrectly treats a workflow on v7 as current, so the monthly replacement can miss an available major upgrade. Query the release list/tags and select the greatest parseable version before callingisActionStale.
.github/workflows/dependency-refresh.yml:1
- This header says Dependabot is replaced entirely, but lines 12–15 state that Dependabot security updates remain enabled and still open PRs. Describe this as replacing Dependabot version-update PRs to keep the workflow's own operational documentation consistent.
# Monthly dependency sweep (#2229), replacing Dependabot on this repo entirely.
scripts/dependency-refresh.mjs:187
- The generated issue says this sweep replaces Dependabot wholesale, but this PR explicitly leaves Dependabot security updates enabled in repository settings; the workflow header also says those PRs remain active. Qualify this as replacing Dependabot version-update PRs so the tracking issue does not misstate the operational setup.
"Routine dependency refresh — `npm outdated` plus a workflow `uses:` check, run against `v2/main` on a monthly schedule. This sweep replaces Dependabot on this repo (#2229, #2235).",
scripts/dependency-refresh.mjs:2
- “Replacing Dependabot entirely” is inaccurate while Dependabot security updates remain enabled separately, as documented in the workflow. Qualify this as replacing Dependabot version-update PRs so the script header matches the actual scope.
// Monthly dependency sweep (#2229), replacing Dependabot on this repo entirely.
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Balanced
1422392 to
7e13626
Compare
Response to Copilot review round 3 —
|
7e13626 to
fb83ee7
Compare
fb83ee7 to
2acbc16
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The releases API error handling and corresponding tests must be corrected before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
scripts/dependency-refresh.mjs:224
- This all-clear overstates what the sweep established. SHA/branch refs return
nullfromparseVersionRef, and actions with no parseable release tag are also dropped, so those refs were not ranked against a release at all. Qualify the message to avoid telling maintainers that every workflow ref was verified.
`Everything this sweep watches is current as of ${isoDate} — no npm package is outdated at the root or in any client, and no workflow \`uses:\` ref is behind its action's highest release.`,
scripts/dependency-refresh.test.mjs:449
- This integration test locks in silent success for an inaccessible or nonexistent action repository. After making all non-zero release-list responses fatal, this case should assert that
main()throws rather than logging a no-op.
test("main treats a 404 release lookup as 'this action cuts no releases'", () => {
const spawn = fakeSpawn({
releasesStatus: 1,
releasesStderr: "gh: Not Found (HTTP 404)",
});
const log = captureLog(() => main("o/r", spawn));
assert.match(log.join("\n"), /no-op/);
});
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Balanced
Addresses Copilot review round 4 on #2239. Both findings are regressions I introduced in round 3 and both are correct. Round 3 moved the lookup from `releases/latest` to the release LIST, to rank by version rather than by GitHub's designated latest — but kept the 404 suppression that only made sense for the old endpoint. The list endpoint answers "this action cuts no releases" with a successful empty array, so a 404 there does not mean that at all: it means the repository is missing or inaccessible, i.e. a `uses:` ref the sweep cannot check. Converting it to null silently dropped a broken or renamed action from the flow that replaced Dependabot — the same silent-success shape this PR has been closing everywhere else, reintroduced one layer down. Every non-zero status is now fatal and `isMissingRelease` is gone entirely; the benign no-releases case is the successful empty array, covered by its own test. `buildClearedBody` also overstated its all-clear. Refs pinned to a commit SHA or a branch return null from `parseVersionRef` and are never ranked, so "no workflow `uses:` ref is behind" asserted a check that had not happened for them. It now says version-pinned refs and names the exclusion explicitly. Both guards are mutation-checked: restoring the 404 suppression, and dropping the SHA/branch qualification, each fail exactly one test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Response to Copilot review round 4 —
|
| Mutation | Result |
|---|---|
Restore the 404 suppression in latestReleaseTag |
1 failed, 28 passed |
Drop the SHA/branch qualification from buildClearedBody |
1 failed, 28 passed |
| Baseline (neither) | 29 passed |
Exactly one test dies per mutation — so each detects its own defect, and neither is redundant with the other.
npm run local:gate green: 7389/7389, exit 0.
There was a problem hiding this comment.
🔵 Needs a closer look
The release-list error-handling documentation must match the implemented behavior.
Review details
Suppressed comments (1)
scripts/dependency-refresh.mjs:308
- This contract is stale: the implementation and new 404 test now make every non-zero release-list response fatal, including 404, while this JSDoc (and the PR description) still says 404 is suppressed. Update both descriptions to reserve
nullfor a successful empty release list.
* @throws when the lookup fails for any reason other than a 404
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Relaying a Copilot finding from #2243, which stacks on this branch and so carries its diff. On
It has a point worth weighing against the comment already on that function. Since the switch from Not fixing it in #2243, since that would put a change to this PR's code in an unrelated diff. Your call whether to take it here or file a follow-up. |
Addresses Copilot review round 5 on #2239. Round 4 made every non-zero release-list response fatal and deleted `isMissingRelease`, but `latestReleaseTag`'s JSDoc still promised `@throws when the lookup fails for any reason other than a 404` — the exact behavior that change removed. The PR description carried the same stale claim. A contract that documents the opposite of the code is worse than none: the next reader reasonably trusts it, and here it would tell them a missing action repo is silently tolerated when it now fails the sweep. The contract now reserves `null` for a successful response carrying no usable release — an action that has never cut one, or whose tags are all unparseable — and states that a failed lookup is never `null`. Documentation only; no behavior change, and the 29 tests are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Response to Copilot review round 5 —
|
| Round | Found |
|---|---|
| 1 | 🟢 nothing — on a diff rounds 2 and 3 then found real bugs in |
| 2 | Any gh api failure read as "not stale" |
| 3 | releases/latest ranks by designation, not version — missed major upgrades |
| 4 | 404 on the list endpoint dropped missing actions; cleared-body claimed unranked refs were checked |
| 5 | Stale @throws contract from round 4 |
Every executable finding has been the same class: a failure that reads as "nothing to do." That is unusually hard to catch here because a healthy sweep also produces an empty actions section — broken and working states emit byte-identical output. It is also why round 3's fix produced round 4's bug: changing endpoints silently changed what a 404 meant.
Requesting another round rather than treating one quiet review as final, given round 1.
There was a problem hiding this comment.
🟡 Changes recommended
The release lookup must paginate to reliably identify the highest released action version.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Balanced
Addresses Copilot review round 7 on #2239. The lookup read one page of 100 and the doc comment justified it with an assumption rather than a guarantee: "the greatest version is within it for any real action". The list is ordered by release DATE, not version, so an action that keeps cutting maintenance releases on lower majors pushes the genuine maximum off page 1. Ranking what remained would report a lower major as highest — the exact false-current result that dropping `releases/latest` in round 3 was meant to prevent, arrived at by a different route. Now passes `--paginate`. Cost is unchanged for every action this repo uses (57 and 64 releases are the largest, so still one request each); it only costs more where correctness actually required it. The command-shape test now requires `--paginate` as well as the list endpoint. It has to: pagination is invisible in the result — one page and every page return an identical-looking tag list until the day they do not — so nothing about the returned value can detect its absence. Verified by mutation: removing `--paginate` fails exactly one test. Adding the flag also moved the URL out of args[1], which the test's own filter had hardcoded — it caught that itself. Both the filter and fakeSpawn's matcher are now argument-position agnostic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Response to Copilot review round 7 —
|
| Mutation | Result |
|---|---|
Remove --paginate from the lookup |
1 failed, 28 passed |
| Baseline | 29 passed |
The fix broke my own test, and the test caught it
Adding the flag shifted the URL out of args[1], which the test's filter had hardcoded — it failed with "expected at least one release lookup" rather than passing vacuously. Both that filter and fakeSpawn's matcher are now argument-position agnostic, which also closes the trap where a mis-positioned match falls through to the milestone branch and silently returns "v2.6.0" as a release tag.
npm run local:gate green: 7389/7389, exit 0.
Review loop complete — rounds 8 and 9 both cleanTwo consecutive rounds of 🟢 Approval recommended, 0 new comments, no suppressed-comments block, and the inline comment count unchanged at 5. Nothing outstanding. Stopping at two rather than one is deliberate, and this PR is the argument for it: a single clean round was misleading twice.
Two lessons worth leaving in the record: Every executable finding was the same defect class: a failure that reads as "nothing to do." That is unusually hard to see here, because a healthy sweep also emits an empty actions section — broken and working states produce byte-identical output. There is no signal that would have told anyone the check had stopped working. Fixes in this area kept producing the next bug. Round 3 switched endpoints to fix ranking, which silently changed what a 404 meant (round 4) and left the Verification
|
Both findings were "previously missed" suppressed ones; the two against scripts/dependency-refresh.mjs and its workflow are relayed to #2239. - Serialize the sweep with a fixed concurrency group and cancel-in-progress: false. The marker check is a read-before-write and a workflow_dispatch can land on top of the scheduled run, so two runs could both see no open issue and both file one — the duplicate the whole idempotency design exists to prevent. The queued run must wait and re-read, never be cancelled. - Pick the milestone in JS, not in jq. jq sorts null before every string, so sort_by(.due_on) | .[0] returns an UNDATED open milestone in preference to every dated one. An undated bucket has no due date and so cannot be the nearest; pickMilestone drops it, and files the issue unmilestoned if nothing dated is open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall <cliff@futurescale.com>
|
Two more Copilot findings relayed from #2243, which stacks on this branch and so carries its diff. Both are against this PR's code. 1.
Verified: No impact today (both open milestones are dated), but a 2.
Fixed in #2243 with: concurrency:
group: dependency-refresh
cancel-in-progress: false
Not fixing either here, to keep #2243's diff to its own code. If this PR merges first, the milestone one at least deserves a follow-up issue rather than being dropped. |
Both findings were "previously missed" suppressed ones; the two against scripts/dependency-refresh.mjs and its workflow are relayed to #2239. - Serialize the sweep with a fixed concurrency group and cancel-in-progress: false. The marker check is a read-before-write and a workflow_dispatch can land on top of the scheduled run, so two runs could both see no open issue and both file one — the duplicate the whole idempotency design exists to prevent. The queued run must wait and re-read, never be cancelled. - Pick the milestone in JS, not in jq. jq sorts null before every string, so sort_by(.due_on) | .[0] returns an UNDATED open milestone in preference to every dated one. An undated bucket has no due date and so cannot be the nearest; pickMilestone drops it, and files the issue unmilestoned if nothing dated is open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall <cliff@futurescale.com>
Addresses Copilot review round 4 on #2239. Both findings are regressions I introduced in round 3 and both are correct. Round 3 moved the lookup from `releases/latest` to the release LIST, to rank by version rather than by GitHub's designated latest — but kept the 404 suppression that only made sense for the old endpoint. The list endpoint answers "this action cuts no releases" with a successful empty array, so a 404 there does not mean that at all: it means the repository is missing or inaccessible, i.e. a `uses:` ref the sweep cannot check. Converting it to null silently dropped a broken or renamed action from the flow that replaced Dependabot — the same silent-success shape this PR has been closing everywhere else, reintroduced one layer down. Every non-zero status is now fatal and `isMissingRelease` is gone entirely; the benign no-releases case is the successful empty array, covered by its own test. `buildClearedBody` also overstated its all-clear. Refs pinned to a commit SHA or a branch return null from `parseVersionRef` and are never ranked, so "no workflow `uses:` ref is behind" asserted a check that had not happened for them. It now says version-pinned refs and names the exclusion explicitly. Both guards are mutation-checked: restoring the 404 suppression, and dropping the SHA/branch qualification, each fail exactly one test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Addresses Copilot review round 5 on #2239. Round 4 made every non-zero release-list response fatal and deleted `isMissingRelease`, but `latestReleaseTag`'s JSDoc still promised `@throws when the lookup fails for any reason other than a 404` — the exact behavior that change removed. The PR description carried the same stale claim. A contract that documents the opposite of the code is worse than none: the next reader reasonably trusts it, and here it would tell them a missing action repo is silently tolerated when it now fails the sweep. The contract now reserves `null` for a successful response carrying no usable release — an action that has never cut one, or whose tags are all unparseable — and states that a failed lookup is never `null`. Documentation only; no behavior change, and the 29 tests are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
bb2f438 to
17a714c
Compare
Addresses Copilot review round 7 on #2239. The lookup read one page of 100 and the doc comment justified it with an assumption rather than a guarantee: "the greatest version is within it for any real action". The list is ordered by release DATE, not version, so an action that keeps cutting maintenance releases on lower majors pushes the genuine maximum off page 1. Ranking what remained would report a lower major as highest — the exact false-current result that dropping `releases/latest` in round 3 was meant to prevent, arrived at by a different route. Now passes `--paginate`. Cost is unchanged for every action this repo uses (57 and 64 releases are the largest, so still one request each); it only costs more where correctness actually required it. The command-shape test now requires `--paginate` as well as the list endpoint. It has to: pagination is invisible in the result — one page and every page return an identical-looking tag list until the day they do not — so nothing about the returned value can detect its absence. Verified by mutation: removing `--paginate` fails exactly one test. Adding the flag also moved the URL out of args[1], which the test's own filter had hardcoded — it caught that itself. Both the filter and fakeSpawn's matcher are now argument-position agnostic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Both findings were "previously missed" suppressed ones; the two against scripts/dependency-refresh.mjs and its workflow are relayed to #2239. - Serialize the sweep with a fixed concurrency group and cancel-in-progress: false. The marker check is a read-before-write and a workflow_dispatch can land on top of the scheduled run, so two runs could both see no open issue and both file one — the duplicate the whole idempotency design exists to prevent. The queued run must wait and re-read, never be cancelled. - Pick the milestone in JS, not in jq. jq sorts null before every string, so sort_by(.due_on) | .[0] returns an UNDATED open milestone in preference to every dated one. An undated bucket has no due date and so cannot be the nearest; pickMilestone drops it, and files the issue unmilestoned if nothing dated is open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall <cliff@futurescale.com>
Closes #2235. Switches the old dependency flow off now that #2232 landed the replacement. Removes the five npm ecosystem entries (root plus each client under clients/*) and, resolving the question #2229 left open, the github-actions entry as well — so .github/dependabot.yml goes away outright rather than being emptied, which its schema does not allow. The github-actions entry had exactly the property #2229 exists to remove: it opened a grouped monthly PR carrying no `Closes #N` and no board card, the one standing exception to "every PR references an issue". Deleting it unreplaced would have left 9 actions unwatched, and `npm outdated` says nothing about actions, so the monthly sweep now also checks every `uses:` ref under .github/workflows and renders the stale ones as one more section of the same tracking issue. Ranking comes from the release LIST, not `releases/latest`. That endpoint returns the release GitHub designates most recent, not the greatest version, so an action publishing a maintenance release for an older major (a v6.9.1 cut after v8.0.0) would make a workflow pinned to v7 compare against v6 and read as current — silently missing a whole major upgrade, the one thing this check exists to catch. Staleness is compared only to the precision the ref specifies. `v7` is a moving major tag that GitHub repoints at every v7.x release, so `v7` against a highest of `v7.0.1` is current and only `v8` makes it stale; an exactly-pinned `v7.0.0` is behind `v7.0.1`; a SHA pin is deliberately immovable and is never reported. A release lookup suppresses only a 404 — the legitimate "this action has never cut a release" answer — and throws on anything else. Treating a rate limit or an expired token as "no release" is indistinguishable from "not stale", and since every action here already sits on its latest major, the resulting empty section is byte-identical to a healthy run. `buildClearedBody` now speaks for both halves: once actions are in scope, its npm-only wording would assert a clean bill of health the sweep never checked. Dependabot security updates are unaffected — they are configured in repo settings, not in this file, and kept working while it was missing entirely (#1833, #1840). That note moves into the workflow header rather than dying with the file; #2233 is where they are turned off deliberately. The script header, the workflow header and the generated issue body all say version-update PRs rather than claiming Dependabot is replaced wholesale. Also renames the workflow's npm-outdated job to dependency-sweep now that the sweep is no longer npm-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Addresses Copilot review round 4 on #2239. Both findings are regressions I introduced in round 3 and both are correct. Round 3 moved the lookup from `releases/latest` to the release LIST, to rank by version rather than by GitHub's designated latest — but kept the 404 suppression that only made sense for the old endpoint. The list endpoint answers "this action cuts no releases" with a successful empty array, so a 404 there does not mean that at all: it means the repository is missing or inaccessible, i.e. a `uses:` ref the sweep cannot check. Converting it to null silently dropped a broken or renamed action from the flow that replaced Dependabot — the same silent-success shape this PR has been closing everywhere else, reintroduced one layer down. Every non-zero status is now fatal and `isMissingRelease` is gone entirely; the benign no-releases case is the successful empty array, covered by its own test. `buildClearedBody` also overstated its all-clear. Refs pinned to a commit SHA or a branch return null from `parseVersionRef` and are never ranked, so "no workflow `uses:` ref is behind" asserted a check that had not happened for them. It now says version-pinned refs and names the exclusion explicitly. Both guards are mutation-checked: restoring the 404 suppression, and dropping the SHA/branch qualification, each fail exactly one test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Addresses Copilot review round 5 on #2239. Round 4 made every non-zero release-list response fatal and deleted `isMissingRelease`, but `latestReleaseTag`'s JSDoc still promised `@throws when the lookup fails for any reason other than a 404` — the exact behavior that change removed. The PR description carried the same stale claim. A contract that documents the opposite of the code is worse than none: the next reader reasonably trusts it, and here it would tell them a missing action repo is silently tolerated when it now fails the sweep. The contract now reserves `null` for a successful response carrying no usable release — an action that has never cut one, or whose tags are all unparseable — and states that a failed lookup is never `null`. Documentation only; no behavior change, and the 29 tests are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
Addresses Copilot review round 7 on #2239. The lookup read one page of 100 and the doc comment justified it with an assumption rather than a guarantee: "the greatest version is within it for any real action". The list is ordered by release DATE, not version, so an action that keeps cutting maintenance releases on lower majors pushes the genuine maximum off page 1. Ranking what remained would report a lower major as highest — the exact false-current result that dropping `releases/latest` in round 3 was meant to prevent, arrived at by a different route. Now passes `--paginate`. Cost is unchanged for every action this repo uses (57 and 64 releases are the largest, so still one request each); it only costs more where correctness actually required it. The command-shape test now requires `--paginate` as well as the list endpoint. It has to: pagination is invisible in the result — one page and every page return an identical-looking tag list until the day they do not — so nothing about the returned value can detect its absence. Verified by mutation: removing `--paginate` fails exactly one test. Adding the flag also moved the URL out of args[1], which the test's own filter had hardcoded — it caught that itself. Both the filter and fakeSpawn's matcher are now argument-position agnostic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax Signed-off-by: cliffhall <cliff@futurescale.com>
17a714c to
0cb12a1
Compare
Both findings were "previously missed" suppressed ones; the two against scripts/dependency-refresh.mjs and its workflow are relayed to #2239. - Serialize the sweep with a fixed concurrency group and cancel-in-progress: false. The marker check is a read-before-write and a workflow_dispatch can land on top of the scheduled run, so two runs could both see no open issue and both file one — the duplicate the whole idempotency design exists to prevent. The queued run must wait and re-read, never be cancelled. - Pick the milestone in JS, not in jq. jq sorts null before every string, so sort_by(.due_on) | .[0] returns an UNDATED open milestone in preference to every dated one. An undated bucket has no due date and so cannot be the nearest; pickMilestone drops it, and files the issue unmilestoned if nothing dated is open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall <cliff@futurescale.com>
- Reconcile on (package, manifest, GHSA) tuples, not GHSAs alone. Dependabot alerts are per manifest and this repo has five lockfiles, so the same advisory legitimately covers the root install and a client. Keyed on the GHSA alone, another manifest's still-filable alert could vouch for this one and clear an issue whose own alert is open with no bump available. - Rename the cleared-date test: it asserts the NEW date is taken when the reason changes, which is the opposite of what its name said. - AGENTS.md no longer implies both sweeps select a dated milestone. Only the security sweep filters undated buckets; the monthly one's selection is raised on #2239. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall <cliff@futurescale.com>
Closes #2235
Switches the old dependency flow off, now that #2232 has landed the replacement. This is the only PR that touches
.github/dependabot.yml— and it removes the file outright.Stacked on #2232. It edits files that PR introduces, so its base is
v2/chore/2231-audit-fix-and-npm-outdated, notv2/main. GitHub will retarget it tov2/mainautomatically once #2232 merges.1. The five
npmentries are goneRoot plus
clients/{web,cli,tui,launcher}. Routine npm bumps are the #2232 sweep's job from here on.2. The
github-actionsentry is gone too — option A#2235 laid out three ways to resolve the entry #2229 left live, and A is what's implemented here: fold action bumps into the monthly sweep.
The entry had exactly the property #2229 exists to remove — it opened a grouped monthly PR carrying no
Closes #Nand no board card. Leaving it was the one standing exception to "every PR references an issue", enforced by nothing. Deleting it without a replacement would instead have left 9 actions unwatched, andnpm outdatedsays nothing about actions, soscripts/dependency-refresh.mjsgained a release-tag check:parseActionRefsowner/repo@refout of a workflow fileparseVersionRefnullfor a SHA/branchisActionStalestaleActionsStale actions render as one more section in the same monthly tracking issue:
The comparison is to the precision the ref specifies, which is the one subtle bit.
v7is a moving major tag that GitHub repoints at everyv7.xrelease, sov7against a latest ofv7.0.1is up to date and onlyv8makes it stale. An exactly-pinnedv7.0.0is behindv7.0.1. A SHA pin is deliberately immovable, so a tag comparison says nothing about it and it is never reported.latestReleaseTagtreats every non-zero response as fatal, 404 included. Nothing is suppressed: the release list endpoint answers "this action cuts no releases" with a successful empty array, so a 404 means the repository is missing or inaccessible — auses:ref the sweep cannot check at all — and swallowing it would silently drop a broken or renamed action.That distinction matters more than it looks: an API error read as "no release" is indistinguishable from "not stale", so the sweep would exit green having checked nothing, and because this repo's actions are all on their latest major the resulting empty section would look exactly like a healthy run.
(Two corrections from review. Round 2: the first version suppressed every failure. Round 4: after round 3 switched endpoints, the surviving 404 suppression was wrong for the new one —
releases/latest404s benignly, the list endpoint does not.)Why the file is deleted, not emptied
The issue offered "an empty
updates:list or deleted outright". Empty isn't actually available: Dependabot's schema requires at least one entry and flags a zero-entry list as an invalid config. Deleted it is.The one still-load-bearing note in that file's header — that Dependabot security updates are configured in repo settings rather than here, and kept working while
dependabot.ymlwas missing entirely (#1833, #1840) — moved into the workflow header rather than being lost with the file.No
AGENTS.mdchange is needed: the carve-out was never written down there, so there is nothing to correct. (Option C is the one that would have required an edit.)3. The two stale comments from #2232 are corrected
.github/workflows/dependency-refresh.yml:3andscripts/dependency-refresh.mjs:4both described the overlap as pending and pointed here. Flipped to past tense, and the "the two flows deliberately overlap" paragraph is dropped — after this there is no Dependabot flow left to overlap with.Also renamed the workflow's
npm-outdatedjob todependency-sweep, since the sweep is no longer npm-only. Not a required check, so no branch-protection impact.Verification
scripts/dependency-refresh.test.mjsgoes 5 → 13 tests, covering the four new pure functions and the new body section.buildIssueBodyreturnsnull— the correct no-op.@v4/@v6/@v7), so an empty GitHub Actions section means "no major bump is available", not "everything is current to the patch". With the repo pinned this way, av7→v8jump is the only thing that section will ever surface. The exactly-pinned case (v7.0.0behindv7.0.1) is coverage for a pinning style this repo doesn't currently use — deliberate, so the check stays correct if any workflow later pins hard or moves to SHA pins.npm run local:gategreen.Dependabot reads
.github/dependabot.ymlfrom the default branch (main), so nothing here takes effect when this merges tov2/main— only at the next milestone merge. That the npm PRs actually stopped, and that security-update PRs kept working, has to be re-checked after that merge. It's the one "Done when" item on #2235 that no PR review can close.🤖 Generated with Claude Code
https://claude.ai/code/session_01NGtzPg3UxMLszysQXXfqax