@codyswann/lisa 4.35.4 → 4.36.0

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.
Files changed (89) hide show
  1. package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +554 -0
  2. package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +296 -3
  3. package/dist/core/downstream-references.d.ts.map +1 -1
  4. package/dist/core/downstream-references.js +4 -0
  5. package/dist/core/downstream-references.js.map +1 -1
  6. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  7. package/dist/core/lisa-owned-hash-ledger.js +16 -0
  8. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  9. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  10. package/dist/core/third-party-action-pins.d.ts +84 -0
  11. package/dist/core/third-party-action-pins.d.ts.map +1 -0
  12. package/dist/core/third-party-action-pins.js +153 -0
  13. package/dist/core/third-party-action-pins.js.map +1 -0
  14. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  15. package/dist/core/upstream-evidence-manifest.js +15 -7
  16. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  17. package/dist/migrations/ensure-third-party-action-pins.d.ts +123 -0
  18. package/dist/migrations/ensure-third-party-action-pins.d.ts.map +1 -0
  19. package/dist/migrations/ensure-third-party-action-pins.js +238 -0
  20. package/dist/migrations/ensure-third-party-action-pins.js.map +1 -0
  21. package/dist/migrations/index.d.ts +1 -0
  22. package/dist/migrations/index.d.ts.map +1 -1
  23. package/dist/migrations/index.js +3 -0
  24. package/dist/migrations/index.js.map +1 -1
  25. package/package.json +4 -4
  26. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa/.codex-plugin/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  29. package/plugins/lisa/rules/reference/measurement-integrity.md +10 -0
  30. package/plugins/lisa/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  31. package/plugins/lisa-agy/plugin.json +1 -1
  32. package/plugins/lisa-agy/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  33. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  36. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-copilot/rules/reference/measurement-integrity.md +10 -0
  40. package/plugins/lisa-copilot/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  41. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-cursor/rules/measurement-integrity-reference.mdc +10 -0
  43. package/plugins/lisa-cursor/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  44. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-expo-agy/plugin.json +1 -1
  47. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  57. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  67. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails-agy/plugin.json +1 -1
  72. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  77. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  82. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/src/base/rules/reference/measurement-integrity.md +10 -0
  85. package/plugins/src/base/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
  86. package/scripts/check-third-party-action-pins.mjs +111 -14
  87. package/scripts/two-channel-couplings.json +1 -1
  88. package/typescript/copy-overwrite/.prettierignore +1 -0
  89. package/typescript/package-lisa/package.lisa.json +1 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.35.4",
3
+ "version": "4.36.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.35.4",
3
+ "version": "4.36.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.35.4",
3
+ "version": "4.36.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.35.4",
3
+ "version": "4.36.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.35.4",
3
+ "version": "4.36.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -33,6 +33,16 @@ An entire corpus sits outside the query's scope and nothing says so. `gh search
33
33
 
34
34
  **Catch it:** validate the detector against known-true items before believing any count.
35
35
 
36
+ #### A second domain, which is what makes this a category rather than a quirk
37
+
38
+ The same mode, on a filesystem rather than a tracker. A survey of agent worktrees enumerating `.claude/worktrees/` and `.worktrees/` returns a well-formed list — and omits every worktree created outside the repository tree, under the per-user temporary directory. Four such worktrees existed, three of them with uncommitted changes; none appeared.
39
+
40
+ Run it through the membership test and it qualifies on all three counts: the list is **well formed** (no error, no warning, no partial marker); the omission is **invisible from the list itself**, which looks exactly like a complete survey of a machine with fewer worktrees on it; and **a mechanical step would have caught it** — the same one A1 already prescribes, validating the enumeration against a worktree you know exists.
41
+
42
+ This example is here rather than folded into the paragraph above because **a second domain tests whether the category is real or just a description of one tool's behaviour.** A mode observed only through `gh search` could be a fact about GitHub's search semantics; the same shape in a `find` over directories says the failure belongs to *enumeration with an unstated boundary*, whichever substrate is enumerating. When extending this document, a new domain for an existing mode is worth as much as a new mode.
43
+
44
+ **The consequence in that instance was not academic.** Uncommitted work outside the repository tree is invisible to every query anyone would run before deciding what to clean up, and looks identical to abandoned scratch — so a survey feeding a deletion decision under-reports precisely the thing that must not be deleted. See the worktree-and-binding hazards for the separate question of a mitigation whose cost is invisibility; the measurement half is this mode.
45
+
36
46
  ### A2. Truncation — under, enumeration
37
47
 
38
48
  Real members past the first page are dropped with no truncation signal. `gh issue list` and `gh pr list` default to 30 and cap silently at whatever `--limit` is passed: one audit reported **37 items where the answer was 513**, a 14× undercount. A paging API returning **249 of 845 rows with `hasNextPage: false`** is the same mode on another substrate.
@@ -348,7 +348,66 @@ In `@example` blocks, use fenced code blocks and escape `@` as `\@`:
348
348
  */
349
349
  ```
350
350
 
351
- ### Quick Reference for Escaping
351
+ ### Path Examples Must Not Contain the Block Terminator
352
+
353
+ A path written as an example inside a doc comment **ends the comment** if it
354
+ contains `*/` — a glob such as `sandbox-*/`, or any real directory whose
355
+ name ends in an asterisk. Everything below that point is then parsed as source:
356
+ path segments become identifiers, and the separator between them becomes
357
+ division.
358
+
359
+ **Write the wildcard as a placeholder, not a glob:**
360
+
361
+ ```typescript
362
+ // WRONG — the comment ends inside the path, mid-line
363
+ /**
364
+ * Debris lands at `.stryker-tmp/bite-guard-intact/sandbox-*/rails/scripts/foo.sh`
365
+ * and this line is no longer a comment.
366
+ */
367
+
368
+ // RIGHT — a placeholder cannot terminate the block
369
+ /**
370
+ * Debris lands at `.stryker-tmp/bite-guard-intact/sandbox-<id>/rails/scripts/foo.sh`
371
+ * and this line is still a comment.
372
+ */
373
+ ```
374
+
375
+ `<id>`, `<name>`, `<n>` — the same placeholder convention used everywhere else
376
+ in Lisa's docs. Escaping (`*\/`) also works but produces an example nobody can
377
+ copy and paste, which defeats the point of writing one.
378
+
379
+ ### What it looks like when you get it wrong
380
+
381
+ The build goes red immediately — `bun run typecheck`, `bun run lint` and the
382
+ test run all fail — but **none of them mentions a comment**, and every line
383
+ they report sits *below* the real cause. Measured, with the true cause on
384
+ line 89:
385
+
386
+ ```text
387
+ src/configs/repo-scan.ts(90,9): error TS1005: ';' expected.
388
+ src/configs/repo-scan.ts(90,20): error TS1443: Module declaration names may only use ' or " quoted strings.
389
+ src/configs/repo-scan.ts(94,1): error TS1160: Unterminated template literal.
390
+ ```
391
+
392
+ Errors naming identifiers you never wrote — `Cannot find name 'mutation'`,
393
+ `Cannot find name 'src'` — are the giveaway: those are backtick contents from
394
+ inside the comment, now being read as code. **Look upward for a doc comment
395
+ before looking for a missing import.**
396
+
397
+ Lisa's gate runner says this for you. When a gate fails, it scans the files the
398
+ failure named and reports the closed comment by name:
399
+
400
+ ```text
401
+ a doc comment in src/configs/repo-scan.ts ends early, on line 89 — every error
402
+ reported below that line is downstream of it, not a fault of its own
403
+ ↳ src/configs/repo-scan.ts:89 — the block opened on line 87 ends at the
404
+ terminator INSIDE this line, not on line 92
405
+ ```
406
+
407
+ It is a text scan rather than a lint rule on purpose: the file does not parse,
408
+ so ESLint reports `Parsing error` and runs zero rules on it.
409
+
410
+ ## Quick Reference for Escaping
352
411
 
353
412
  | Context | Approach | Example |
354
413
  |---------|----------|---------|
@@ -69,16 +69,41 @@
69
69
  * - the file list comes from `git ls-files`, so the gate sees exactly what a
70
70
  * release would carry.
71
71
  *
72
+ * ## `--warn`: how this arrives in a consumer without breaking it
73
+ *
74
+ * This file also ships to consumers through `all/copy-overwrite/scripts/`,
75
+ * which — unlike `create-only` — reaches every repository that already exists,
76
+ * on its next update. That reach is exactly why it must not arrive blocking.
77
+ *
78
+ * A consumer's seeded workflows carry mutable refs *today*: that is the defect
79
+ * #3588 exists to migrate away. A detector that hard-failed on arrival would
80
+ * redden the entire installed base at once, for a condition those repositories
81
+ * did not introduce and could not have fixed yet, since the migration that
82
+ * fixes it lands in the same update. That is not hypothetical — a guard added
83
+ * to a workflow consumed at `@main` failed closed on a pre-existing consumer
84
+ * misconfiguration and took four repositories' releases down for five hours;
85
+ * the repair was to keep it loud and stop it blocking (#3755, #3757).
86
+ *
87
+ * So `--warn` reports every finding and exits 0, and the shipped consumer
88
+ * script entry uses it. Tightening to blocking is a separate, deliberate
89
+ * decision for once the installed base has had a chance to migrate — one flag
90
+ * on one line — not something to "finish" while consumers are still mutable.
91
+ *
92
+ * `--warn` also softens the empty-scan usage error, because a consumer with no
93
+ * workflow files is an ordinary shape rather than a broken invocation. A
94
+ * misspelled flag stays a hard error in every mode: that one is the caller's.
95
+ *
72
96
  * CLI:
73
- * node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json]
97
+ * node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json] [--warn]
74
98
  *
75
99
  * Exit codes (mirroring the sibling gates in this directory):
76
- * 0 — every third-party reference is a 40-hex SHA carrying a version comment.
100
+ * 0 — every third-party reference is a 40-hex SHA carrying a version comment;
101
+ * or `--warn` was passed and the findings are being reported, not enforced.
77
102
  * 1 — >=1 reference is mutable, or pinned without a version comment.
78
103
  * 2 — operational/usage error: unknown flag, a flag missing its value,
79
104
  * `--root` absent or not a git repository, git unavailable, or zero
80
105
  * workflow files discovered. Finding nothing to check is a broken
81
- * invocation, not conformance.
106
+ * invocation, not conformance — except under `--warn`, see above.
82
107
  *
83
108
  * @module scripts/check-third-party-action-pins
84
109
  */
@@ -134,14 +159,60 @@ const NO_VERSION = "missing-version-comment";
134
159
  * a trailing `# v1.2.3` can be read. Local (`./path`) and container
135
160
  * (`docker://`) references have no owner and never match.
136
161
  */
137
- const USES_RE =
138
- /^\s*(?:-\s*)?uses:\s*(?<owner>[A-Za-z0-9][A-Za-z0-9-]*)\/(?<rest>[A-Za-z0-9_.][A-Za-z0-9_./-]*)@(?<ref>[^\s"'#]+)(?<trailer>.*)$/;
162
+ const USES_RE = /^[ \t]*(?:-[ \t]*)?uses:[ \t]*(?<value>[^\s"'#]+)/;
163
+
164
+ /** A well-formed action owner. Anchored, so matching is linear. */
165
+ const OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
166
+
167
+ /** A well-formed action path below the owner. Anchored, so linear. */
168
+ const ACTION_PATH_RE = /^[A-Za-z0-9_.][A-Za-z0-9_./-]*$/;
169
+
170
+ /**
171
+ * Split a `uses:` value into owner, action path, and ref.
172
+ *
173
+ * The shape is validated with anchored parts rather than described as one
174
+ * pattern. Spelling `owner/repo/path@ref` as adjacent character classes gives
175
+ * the engine several ways to divide the same text, which is super-linear on a
176
+ * long line; splitting first and validating each piece has one.
177
+ *
178
+ * @param {string} value - the raw value of a `uses:` line.
179
+ * @returns {{ owner: string, rest: string, ref: string } | null} parts, or null
180
+ * when this is not an `owner/repo@ref` reference (a local `./path` or a
181
+ * `docker://` image never is).
182
+ */
183
+ function parseUsesValue(value) {
184
+ const at = value.lastIndexOf("@");
185
+ if (at <= 0) return null;
186
+ const action = value.slice(0, at);
187
+ const ref = value.slice(at + 1);
188
+ const slash = action.indexOf("/");
189
+ if (ref === "" || slash <= 0 || action.includes("://")) return null;
190
+ const owner = action.slice(0, slash);
191
+ const rest = action.slice(slash + 1);
192
+ if (!OWNER_RE.test(owner) || !ACTION_PATH_RE.test(rest)) return null;
193
+ return { owner, rest, ref };
194
+ }
139
195
 
140
196
  /** A full commit SHA. Abbreviated SHAs are ambiguous and do not count. */
141
197
  const FULL_SHA_RE = /^[0-9a-f]{40}$/;
142
198
 
143
- /** A trailing `# <version>` comment with at least one non-space character. */
144
- const VERSION_COMMENT_RE = /#\s*(?<version>\S.*?)\s*$/;
199
+ /**
200
+ * Read the version out of a line's trailing `# ...` comment.
201
+ *
202
+ * Deliberately not a regex. Every spelling of "text after a hash to end of
203
+ * line" is either a backtracking shape or an unanchored `.*$` the engine
204
+ * retries from each position, and neither is worth it for a one-character
205
+ * search.
206
+ *
207
+ * @param {string} trailer - everything on the line after the `uses:` value.
208
+ * @returns {string | null} the comment text, or null when there is none.
209
+ */
210
+ function versionComment(trailer) {
211
+ const hash = trailer.indexOf("#");
212
+ if (hash < 0) return null;
213
+ const version = trailer.slice(hash + 1).trim();
214
+ return version === "" ? null : version;
215
+ }
145
216
 
146
217
  /** Max bytes of `git ls-files` output (7k+ tracked paths is ~0.3 MB today). */
147
218
  const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
@@ -182,15 +253,19 @@ export function findActionRefs(content) {
182
253
  for (let index = 0; index < lines.length; index++) {
183
254
  const match = USES_RE.exec(lines[index]);
184
255
  if (match === null) continue;
185
- const { owner, ref, rest, trailer } = match.groups;
186
- const comment = VERSION_COMMENT_RE.exec(trailer);
256
+ const parsed = parseUsesValue(match.groups.value);
257
+ if (parsed === null) continue;
258
+ const { owner, ref, rest } = parsed;
187
259
  refs.push({
188
260
  action: `${owner}/${rest}`,
189
261
  kind: classifyOwner(owner),
190
262
  line: index + 1,
191
263
  owner,
192
264
  ref,
193
- version: comment === null ? null : comment.groups.version,
265
+ // The rest of the line is sliced rather than captured: a trailing `.*`
266
+ // after the value's character class gives the engine two ways to divide
267
+ // the same text, which is the super-linear shape.
268
+ version: versionComment(lines[index].slice(match[0].length)),
194
269
  });
195
270
  }
196
271
  return refs;
@@ -350,10 +425,13 @@ function humanReport(report) {
350
425
  export function parseArgs(argv) {
351
426
  let root = null;
352
427
  let json = false;
428
+ let warn = false;
353
429
  for (let i = 0; i < argv.length; i++) {
354
430
  const arg = argv[i];
355
431
  if (arg === "--json") {
356
432
  json = true;
433
+ } else if (arg === "--warn") {
434
+ warn = true;
357
435
  } else if (arg === "--root") {
358
436
  const next = argv[i + 1];
359
437
  if (next === undefined || next.startsWith("--")) {
@@ -365,7 +443,7 @@ export function parseArgs(argv) {
365
443
  throw new UsageError(`unknown argument: ${arg}`);
366
444
  }
367
445
  }
368
- return { json, root: path.resolve(root ?? REPO_ROOT) };
446
+ return { json, warn, root: path.resolve(root ?? REPO_ROOT) };
369
447
  }
370
448
 
371
449
  /**
@@ -406,14 +484,22 @@ export function main(argv, io = {}) {
406
484
  const out = io.stdout ?? process.stdout;
407
485
  const err = io.stderr ?? process.stderr;
408
486
  let opts;
409
- let scan;
410
487
  try {
411
488
  opts = parseArgs(argv);
412
- scan = collectWorkflows(opts.root);
413
489
  } catch (error) {
490
+ // A misspelled flag is the caller's mistake and stays loud in every mode.
414
491
  err.write(`error: ${error.message}\n`);
415
492
  return 2;
416
493
  }
494
+ let scan;
495
+ try {
496
+ scan = collectWorkflows(opts.root);
497
+ } catch (error) {
498
+ // Under --warn this is a repository with nothing to scan, which for a
499
+ // consumer is an ordinary shape rather than a broken invocation.
500
+ err.write(`error: ${error.message}\n`);
501
+ return opts.warn ? 0 : 2;
502
+ }
417
503
 
418
504
  let checked = 0;
419
505
  let thirdParty = 0;
@@ -445,7 +531,18 @@ export function main(argv, io = {}) {
445
531
  out.write(
446
532
  `${opts.json ? JSON.stringify(report, null, 2) : humanReport(report)}\n`
447
533
  );
448
- return report.findings.length === 0 ? 0 : 1;
534
+ if (report.findings.length === 0) {
535
+ return 0;
536
+ }
537
+ if (opts.warn) {
538
+ out.write(
539
+ "\nReporting only: this check is not blocking here. Nothing above stops " +
540
+ "a build.\nRun `npx @codyswann/lisa@latest .` to pin these " +
541
+ "references, then re-run.\n"
542
+ );
543
+ return 0;
544
+ }
545
+ return 1;
449
546
  }
450
547
 
451
548
  if (invokedAsScript(import.meta.url)) {
@@ -7,7 +7,7 @@
7
7
  "workflows": 22,
8
8
  "steps": 716,
9
9
  "couplings": 34,
10
- "inventory": 71
10
+ "inventory": 72
11
11
  },
12
12
  "counts": {
13
13
  "package-backed": 10,
@@ -73,6 +73,7 @@ scripts/check-npm-publish-landed.mjs
73
73
  scripts/check-release-package-identity.mjs
74
74
  scripts/check-skipped-required-checks.mjs
75
75
  scripts/check-state-classification.mjs
76
+ scripts/check-third-party-action-pins.mjs
76
77
  scripts/check-third-party-review-evidence.mjs
77
78
  scripts/check-threshold-ratchet.mjs
78
79
  scripts/check-verification-coverage.mjs
@@ -26,6 +26,7 @@
26
26
  "test:node": "LISA_TEST_SCRATCH_PREFIXES='[\"node-\"]' lisa-test-run --profile typescript --adapter direct -- node scripts/lisa-test-node.mjs",
27
27
  "check:work-item": "node scripts/lisa-work-item.mjs validate-pr",
28
28
  "check:conflict-markers": "node scripts/check-conflict-markers.mjs",
29
+ "check:third-party-action-pins": "node scripts/check-third-party-action-pins.mjs --warn",
29
30
  "check:work-item:push": "node scripts/lisa-work-item.mjs validate-push",
30
31
  "check:verification": "node scripts/check-verification-coverage.mjs"
31
32
  },