Skip to content

Commit 2774722

Browse files
committed
fix(taskless): close two holes the rules could not see through
From review of #187. Both findings verified before acting, and both real. `ci-uses-workspace-cli` matched `npx @taskless/cli` and nothing else, so the invocation CLAUDE.md names by name, `pnpm dlx @taskless/cli@latest`, went straight through the rule written to forbid it. `yarn dlx` and a global install did too. A rule that permits the exact string its own repository documents as the thing to avoid is worse than no rule, because it reads as coverage. Widening it to the bare package name overcorrected, and the dogfooding caught that within one run: it fired on `pnpm --filter @taskless/cli build:nightly` in `release-cli-nightly.yml`, which is the WORKSPACE package addressed as a filter. `pnpm exec @taskless/cli` is the same story, running the local binary. What separates a published build is being FETCHED, so the rule matches the fetch verbs: `npx`, `dlx`, and `-g`. Fixtures now carry `pnpm dlx`, `yarn dlx` and the two-step global install on the invalid side, and `pnpm exec` and `--filter` on the valid side, so neither the hole nor the overcorrection can come back. Measured: `pnpm dlx` and `npx` both exit 1, the clean tree exits 0. `pr-workflow-ready-for-review` anchored on a `types:` node, so it only fired on an explicit list that omitted `ready_for_review`. A `pull_request:` with no `types:` at all falls through to GitHub's default set, which omits `ready_for_review` just as surely — and the rule said nothing, because there was no node to anchor to. That is the #103 gap the rule exists to prevent, reachable by writing less rather than more. It now also matches a `pull_request:` with no `types:` descendant. Nothing on `main` hits it today (all three triggers declare `types:`), so this was latent; a probe workflow with a bare `pull_request:` is now reported where it previously was not. Severity stays `warning`, unchanged.
1 parent fa9a4cd commit 2774722

4 files changed

Lines changed: 80 additions & 21 deletions

File tree

.taskless/rules/sg/ci-uses-workspace-cli/.tests/ci-uses-workspace-cli-20260826-test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ valid:
1818
run: |
1919
pnpm build
2020
pnpm cli check
21+
- |
22+
steps:
23+
- name: pnpm exec runs the LOCAL binary, not a published build
24+
run: pnpm exec @taskless/cli check
25+
- |
26+
steps:
27+
- name: --filter addresses the workspace package, not a published one
28+
run: pnpm --filter @taskless/cli build:nightly
2129
invalid:
2230
- |
2331
steps:
@@ -33,3 +41,17 @@ invalid:
3341
run: |
3442
pnpm install
3543
npx @taskless/cli check
44+
- |
45+
steps:
46+
- name: The invocation CLAUDE.md names by name
47+
run: pnpm dlx @taskless/cli@latest check
48+
- |
49+
steps:
50+
- name: Another runner, same published build
51+
run: yarn dlx @taskless/cli check
52+
- |
53+
steps:
54+
- name: Global install names the package too
55+
run: |
56+
npm i -g @taskless/cli
57+
taskless check

.taskless/rules/sg/ci-uses-workspace-cli/ci-uses-workspace-cli.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,26 @@ note: |
1717
rather than written down: the comment it replaces asked a reader not to
1818
"fix" the invocation back, which is a request, not a boundary.
1919
20+
Matched on the FETCH VERBS, not on `npx` alone and not on the package
21+
name alone. `npx` is one of several ways to reach a published build:
22+
`pnpm dlx @taskless/cli@latest` is the invocation CLAUDE.md names by
23+
name, `yarn dlx` is the same thing, and `npm i -g` reaches it in two
24+
steps whose install line still names the package. Anchoring to `npx`
25+
let every one of those through, including the exact string this
26+
repository documents as the thing `pnpm cli` replaces.
27+
28+
Anchoring to the bare package name is too wide in the other direction,
29+
and measurably so: it fires on `pnpm --filter @taskless/cli
30+
build:nightly` in `release-cli-nightly.yml`, which is the WORKSPACE
31+
package addressed as a filter, not a published install. `pnpm exec
32+
@taskless/cli` is out for the same reason — it runs the local binary.
33+
What distinguishes a published build is being FETCHED, so the verbs are
34+
what the rule matches.
35+
2036
`@taskless/cli-nightly` is deliberately not matched. The nightly IS a
2137
published artifact by design, and the release workflows reference it on
22-
purpose.
38+
purpose. The pattern ends before a `-` so the nightly's name cannot
39+
match.
2340
2441
Only a `run:` value matches, never a comment: `npx @taskless/cli` appears
2542
in prose in `validate.yml` and `release-cli-nightly.yml`, explaining this
@@ -43,6 +60,6 @@ rule:
4360
# block_scalar under `|` or `>`. Both spellings are ordinary in
4461
# these workflows, so both have to match.
4562
- kind: flow_node
46-
regex: 'npx\s+@taskless/cli($|[@\s])'
63+
regex: '(npx|dlx|-g)\s+@taskless/cli($|[@\s])'
4764
- kind: block_scalar
48-
regex: 'npx\s+@taskless/cli($|[@\s])'
65+
regex: '(npx|dlx|-g)\s+@taskless/cli($|[@\s])'

.taskless/rules/sg/pr-workflow-ready-for-review/.tests/pr-workflow-ready-for-review-20260824-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ invalid:
2525
on:
2626
pull_request:
2727
types: [opened, reopened, edited, closed]
28+
- |
29+
on:
30+
pull_request:

.taskless/rules/sg/pr-workflow-ready-for-review/pr-workflow-ready-for-review.yml

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,38 @@ files:
2020
- ".github/workflows/*.yml"
2121
- ".github/workflows/*.yaml"
2222
rule:
23-
kind: block_mapping_pair
24-
has:
25-
field: key
26-
kind: flow_node
27-
regex: '^types$'
28-
not:
29-
has:
30-
field: value
31-
stopBy: end
32-
kind: flow_node
33-
regex: 'ready_for_review'
34-
inside:
35-
stopBy: end
36-
kind: block_mapping_pair
37-
has:
38-
field: key
39-
kind: flow_node
40-
regex: '^pull_request$'
23+
any:
24+
# An explicit `types:` that omits `ready_for_review`.
25+
- kind: block_mapping_pair
26+
has:
27+
field: key
28+
kind: flow_node
29+
regex: '^types$'
30+
not:
31+
has:
32+
field: value
33+
stopBy: end
34+
kind: flow_node
35+
regex: 'ready_for_review'
36+
inside:
37+
stopBy: end
38+
kind: block_mapping_pair
39+
has:
40+
field: key
41+
kind: flow_node
42+
regex: '^pull_request$'
43+
# NO `types:` at all. GitHub then applies its default set — `opened`,
44+
# `synchronize`, `reopened` — which omits `ready_for_review` exactly as
45+
# an incomplete explicit list does. Anchoring only on a `types:` node
46+
# cannot see this: there is no node to anchor to.
47+
- kind: block_mapping_pair
48+
has:
49+
field: key
50+
kind: flow_node
51+
regex: '^pull_request$'
52+
not:
53+
has:
54+
field: value
55+
stopBy: end
56+
kind: flow_node
57+
regex: '^types$'

0 commit comments

Comments
 (0)