@linchpinagency/skills 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ GitHub Copilot, and other compatible coding agents.
12
12
  ![Zero dependencies](https://img.shields.io/badge/Dependencies-0-brightgreen)
13
13
 
14
14
  <!-- x-release-please-start-version -->
15
- ### Latest release: 0.1.11
15
+ ### Latest release: 0.1.12
16
16
  <!-- x-release-please-end -->
17
17
 
18
18
  | Release | Skill standard | Install |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linchpinagency/skills",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Linchpin's library of reusable AI agent skills for WordPress projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: quality-gates
3
- description: Run a Linchpin project's own lint, coding-standards, static-analysis, and test gates before committing or opening a PR — detecting the toolchain from composer.json, package.json, phpcs.xml.dist and lint-staged rather than assuming it. Use when preparing to commit, when asked "is this ready to commit/ship", when CI lint or PHPCS is failing, when a pre-commit hook blocks you, or when a repo is missing the standard lint scripts. Not for writing the commit message — use `commit-and-release`.
4
- version: 1.0.0
3
+ description: Run a Linchpin project's own lint, coding-standards, static-analysis, test and Plugin Check gates before committing or opening a PR — detecting the toolchain from composer.json, package.json, phpcs.xml.dist and lint-staged rather than assuming it. Use when preparing to commit, when asked "is this ready to commit/ship", when CI lint or PHPCS is failing, when a pre-commit hook blocks you, when a plugin has to pass Plugin Check before it ships, or when a repo is missing the standard lint scripts. Not for writing the commit message — use `commit-and-release`.
4
+ version: 1.1.0
5
5
  ---
6
6
 
7
7
  # Quality gates
@@ -22,6 +22,7 @@ is checked but in what counts as a failure.
22
22
  - The user asks whether a change is ready to ship.
23
23
  - CI lint / PHPCS / PHPStan failed and you need to reproduce and fix it locally.
24
24
  - A husky pre-commit hook is blocking a commit.
25
+ - A plugin is heading for WordPress.org, packagist.linchpin.com, or a release.
25
26
  - A repo is missing the house lint scripts and should get them.
26
27
 
27
28
  **Not this skill:** the commit message, branch, or release — [`commit-and-release`](../commit-and-release/SKILL.md).
@@ -47,6 +48,7 @@ the same conventions but expose different gates.
47
48
  | `package.json` → `scripts` | JS/CSS gates (`lint:js`, `lint:css`, `format`, `lint:check`) | Fall back to `eslint`/`prettier` only if configured |
48
49
  | `lint-staged.config.js` + `.husky/` | Pre-commit is already wired — **mirror those exact commands** | Run the scripts directly |
49
50
  | Nested `package.json` (e.g. `themes/*`, `plugins/*`, `src/`) | Gates run **in that workspace**, not the root | Root only |
51
+ | A plugin header (`Plugin Name:`) plus `readme.txt`, or `.github/workflows/plugin-check.yml` | It is a distributed plugin → **Plugin Check applies** | Skip it; Plugin Check is meaningless for a theme or a site repo |
50
52
  | `.linchpin.json` | Project metadata and local environments | Not every repo has one |
51
53
 
52
54
  Full command matrix: [`references/toolchain.md`](references/toolchain.md).
@@ -68,7 +70,13 @@ Full command matrix: [`references/toolchain.md`](references/toolchain.md).
68
70
  5. **Fix, don't silence.** Auto-fixers first (`composer run phpcbf`, `composer run fixer`,
69
71
  `npm run format`), then re-run the gate; hand-fix what remains. → Gate passes with the
70
72
  fix in the code, not in the config.
71
- 6. **Report gaps, then hand off.** State which gates ran, which were skipped and why. If the
73
+ 6. **Plugin Check gate** distributed plugins only. `composer run plugin-check` where the
74
+ repo has it, otherwise the commands in
75
+ [`references/toolchain.md`](references/toolchain.md#plugin-check). It builds the
76
+ distributable and boots WordPress, so it is slower than the rest: run it before opening
77
+ the PR rather than on every commit. → Zero findings, **warnings included**, or each one
78
+ fixed or excluded with a stated reason.
79
+ 7. **Report gaps, then hand off.** State which gates ran, which were skipped and why. If the
72
80
  repo lacks a house script, propose it (see `references/toolchain.md`) and add it **only
73
81
  with approval**. → Then go to [`commit-and-release`](../commit-and-release/SKILL.md).
74
82
 
@@ -133,6 +141,14 @@ registered there hides exactly the bug the sniff exists to catch. Setting the sn
133
141
  dependency change ([`dependency-updates`](../dependency-updates/SKILL.md)), not a fix.
134
142
  - **Never** commit `vendor/`, `node_modules/`, or build output unless the repo already
135
143
  tracks it — check `.gitignore` and `.distignore` first.
144
+ - **Never** read a green "Plugin Check" tick on a PR as a passing Plugin Check.
145
+ `wordpress/plugin-check-action` fails the job on *errors* only, so a plugin carrying
146
+ warnings — the ones WordPress.org review actually raises — shows a passing check. The
147
+ local run is the one that tells the truth, because it treats any finding as a failure.
148
+ - **Never** assume `composer phpcs` covers Plugin Check. Plugin Check ships its own
149
+ `PluginCheck.*` sniffs inside the plugin-check plugin; they are not part of the Linchpin
150
+ standard, so `phpcs.xml.dist` cannot reference them and PHPCS runs green while Plugin
151
+ Check reports findings. Two gates, not one gate twice.
136
152
  - If a tool can't run (not installed, no config, requires Docker that isn't up), **say so
137
153
  explicitly**. A silently skipped gate reads as a passing gate.
138
154
 
@@ -143,5 +159,7 @@ registered there hides exactly the bug the sniff exists to catch. Setting the sn
143
159
  - [ ] JS/CSS gate passed in the owning workspace (or correctly not applicable).
144
160
  - [ ] Tests run for touched, covered code.
145
161
  - [ ] The changed-file annotation count is zero — warnings included, not just errors.
162
+ - [ ] For a distributed plugin: Plugin Check run **locally** with zero findings, warnings
163
+ included — not merely a green badge on the PR.
146
164
  - [ ] No suppressions, config widenings, or `--no-verify` were used to get green.
147
165
  - [ ] Skipped gates and missing house scripts are named in the report.
@@ -58,6 +58,120 @@ it; running the root script may silently skip it.
58
58
  lint-staged rules deliberately skip.
59
59
  - A blocked commit means the gate found something real. Fix the code; never `--no-verify`.
60
60
 
61
+ ## Plugin Check
62
+
63
+ Applies to **distributed plugins only** — anything with a plugin header that ships to
64
+ WordPress.org, packagist.linchpin.com, or a client as a zip. Skip it for themes and site
65
+ repos.
66
+
67
+ ### It is not PHPCS, and PHPCS cannot stand in for it
68
+
69
+ Plugin Check ships its own PHPCS sniffs — the `PluginCheck.*` ruleset — **inside the
70
+ plugin-check plugin**. They are not part of the Linchpin standard, so `phpcs.xml.dist` has
71
+ no way to reference them. `composer run phpcs` therefore runs green on code Plugin Check
72
+ reports on, and no amount of configuring PHPCS closes the gap.
73
+
74
+ It also runs checks PHPCS has no concept of: readme structure, header fields, the stable
75
+ tag, translations, enqueued-asset rules, image sizes in the zip.
76
+
77
+ A worked example. This passes `composer run phpcs` and fails Plugin Check:
78
+
79
+ ```php
80
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery
81
+ $ids = $wpdb->get_col( $wpdb->prepare( $sql, ...$values ) );
82
+ ```
83
+
84
+ The `phpcs:disable` names the `WordPress.DB.*` sniffs, because those are the ones the local
85
+ standard has. `PluginCheck.Security.DirectDB.UnescapedDBParameter` is not silenced, is not
86
+ in the local standard, and fires only under Plugin Check.
87
+
88
+ ### A green badge is not a passing check
89
+
90
+ `wordpress/plugin-check-action` fails the job on Plugin Check **errors** and not on
91
+ **warnings**. A PR can show a passing "Plugin Check" tick while carrying warnings that
92
+ WordPress.org review will raise — the check reports success and uploads the findings to an
93
+ artifact nobody opens.
94
+
95
+ So the local run is the authority, and it should treat **any finding as a failure**. When
96
+ CI and local disagree here, local is right.
97
+
98
+ This is the mirror image of *Green locally, red in CI* in
99
+ [`../SKILL.md`](../SKILL.md#green-locally-red-in-ci). There the shared lint workflow
100
+ annotates warnings a clean local run ignored; here CI ignores warnings a strict local run
101
+ catches. Same underlying cause both times — the two ends disagree about what counts as a
102
+ failure, not about what to check — so neither end is trustworthy on its own without knowing
103
+ which way it leans.
104
+
105
+ To see what CI actually found rather than trusting the tick:
106
+
107
+ ```bash
108
+ gh run view <run-id> --log | grep -A5 'Run Plugin Check'
109
+ gh run download <run-id> -n plugin-check-results # the findings, in full
110
+ ```
111
+
112
+ ### Running it
113
+
114
+ | Intent | Command |
115
+ | --- | --- |
116
+ | The gate | `composer run plugin-check` |
117
+ | What CI saw | `gh run download <run-id> -n plugin-check-results` |
118
+
119
+ When the repo has no script, the check needs a built plugin and a WordPress to run in:
120
+
121
+ ```bash
122
+ # 1. Build the distributable — check what ships, not the source tree, or you
123
+ # get findings from tests/ that never ship and miss what the build breaks.
124
+ composer run build
125
+
126
+ # 2. Boot WordPress with the *built* plugin mapped in, on a port nothing else holds.
127
+ # wp-env defaults to 8888/8889, which any other project of yours already owns.
128
+ mkdir -p build/plugin-check && cd build/plugin-check
129
+ cat > .wp-env.json <<'JSON'
130
+ { "core": null, "plugins": [ "../<slug>" ], "port": 8973, "testsPort": 8974 }
131
+ JSON
132
+ npx wp-env start
133
+
134
+ # 3. Check it, with the same flags as the workflow.
135
+ npx wp-env run cli wp plugin install plugin-check --activate
136
+ npx wp-env run cli wp plugin check <slug> \
137
+ --categories=general,performance,accessibility,security,plugin_repo \
138
+ --include-experimental \
139
+ --format=csv
140
+ ```
141
+
142
+ Keep `--categories` and `--exclude-checks` in step with
143
+ `.github/workflows/plugin-check.yml`. Two sets of flags that drift are two different gates,
144
+ and the PR is judged by the one you did not run.
145
+
146
+ ### A finding you have decided to accept
147
+
148
+ Some findings are architectural and correct — a migration tool that queries `wp_posts`
149
+ directly because the post type it must reach is no longer registered, for instance. Those
150
+ do not get a silent pass:
151
+
152
+ 1. Add the check id to `--exclude-checks` in **both** the local script and the workflow.
153
+ 2. Comment the reason where the exclusion lives, not in a commit message.
154
+ 3. Say so in the PR, because WordPress.org review will ask the same question.
155
+
156
+ `stable_tag_mismatch` / `no_stable_tag` / `trunk_stable_tag` are the standard exclusions on
157
+ a pre-release branch: release-please stamps the version on release, so before one the built
158
+ `readme.txt` legitimately trails the tag.
159
+
160
+ ### The house script
161
+
162
+ ```jsonc
163
+ // composer.json
164
+ "scripts": {
165
+ "plugin-check": "bash scripts/plugin-check.sh"
166
+ }
167
+ ```
168
+
169
+ The script builds, boots wp-env against the built artifact, installs plugin-check, runs it
170
+ with the workflow's flags, and **exits non-zero on any finding**. `linchpin/block-alchemy`
171
+ carries the reference implementation. It needs `@wordpress/env` in `devDependencies` and
172
+ Docker running; both are worth stating in the failure message, because a missing Docker
173
+ looks like a passing gate otherwise.
174
+
61
175
  ## The house script set
62
176
 
63
177
  When a repo is missing these, propose adding them — matching the repo's existing tooling,