@codyswann/lisa 4.29.3 → 4.29.5

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 (69) hide show
  1. package/all/copy-overwrite/scripts/check-release-package-identity.mjs +128 -5
  2. package/dist/cli/version.d.ts +13 -5
  3. package/dist/cli/version.d.ts.map +1 -1
  4. package/dist/cli/version.js +22 -14
  5. package/dist/cli/version.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 +4 -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/upstream-evidence-manifest.d.ts.map +1 -1
  11. package/dist/core/upstream-evidence-manifest.js +6 -3
  12. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  13. package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts +27 -8
  14. package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts.map +1 -1
  15. package/dist/migrations/ensure-nightly-e2e-workflow-pins.js +49 -11
  16. package/dist/migrations/ensure-nightly-e2e-workflow-pins.js.map +1 -1
  17. package/package.json +4 -3
  18. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-agy/plugin.json +1 -1
  21. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  24. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-expo-agy/plugin.json +1 -1
  31. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  51. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-rails-agy/plugin.json +1 -1
  56. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  61. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  66. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  68. package/scripts/check-release-package-identity.mjs +128 -5
  69. package/scripts/two-channel-couplings.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.29.3",
3
+ "version": "4.29.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.29.3",
3
+ "version": "4.29.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.29.3",
3
+ "version": "4.29.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.29.3",
3
+ "version": "4.29.5",
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.29.3",
3
+ "version": "4.29.5",
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.29.3",
3
+ "version": "4.29.5",
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.29.3",
3
+ "version": "4.29.5",
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.29.3",
3
+ "version": "4.29.5",
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"
@@ -17,6 +17,13 @@
17
17
  * Projects without Lisa's nightly certificate still receive the generic
18
18
  * version/commit/tarball checks. When the source certificate exists, its built
19
19
  * and packed counterpart becomes mandatory.
20
+ *
21
+ * `checkout` additionally proves the release commit is reachable from a ref a
22
+ * consumer can resolve. A commit that exists but is referenced by nothing is
23
+ * the one failure with no red signal anywhere: tooling stamps it into consumer
24
+ * workflow pins, and a pinned workflow that cannot be resolved never loads —
25
+ * so it runs zero jobs, fails zero jobs, and its run history is
26
+ * indistinguishable from a healthy one.
20
27
  * @module scripts/check-release-package-identity
21
28
  */
22
29
  import { createHash } from "node:crypto";
@@ -30,10 +37,35 @@ import {
30
37
  import path from "node:path";
31
38
  import process from "node:process";
32
39
 
33
- import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
40
+ import {
41
+ boundedExecFileSync,
42
+ rethrowIfChildTimeout,
43
+ } from "./lib/bounded-spawn.mjs";
34
44
  import { invokedAsScript } from "./lib/invoked-as-script.mjs";
35
45
 
36
46
  const RELEASE_COMMIT_PATTERN = /^[a-f0-9]{40}$/u;
47
+ const RELEASE_TAG_PATTERN = /^[!-~]+$/u;
48
+
49
+ /**
50
+ * Ref namespaces a consumer can actually resolve.
51
+ *
52
+ * Local branches are excluded on purpose. Measuring reachability over all
53
+ * local refs answers "does this commit exist in my clone", not "can a consumer
54
+ * resolve it", and that difference is what made a 235-version outage read as
55
+ * four versions on the first pass.
56
+ */
57
+ const DURABLE_REF_NAMESPACES = Object.freeze(["refs/tags", "refs/remotes"]);
58
+
59
+ /**
60
+ * Ref prefix that does not count as durable reachability.
61
+ *
62
+ * A `refs/tags/backup/*` tag is a byproduct of a history rewrite, not a
63
+ * release artifact. Measured on this repository, a run of 70 published
64
+ * versions was reachable through exactly one such tag and nothing else —
65
+ * alive only until somebody prunes the backup. A publish whose release commit
66
+ * has no anchor but a backup tag is already the failure, one deletion early.
67
+ */
68
+ const BACKUP_TAG_PREFIX = "refs/tags/backup/";
37
69
  const SOURCE_CERTIFICATE = "src/core/nightly-e2e-guard-behavior-certificate.ts";
38
70
  const PACKED_CERTIFICATE =
39
71
  "package/dist/core/nightly-e2e-guard-behavior-certificate.js";
@@ -79,6 +111,43 @@ function sha256(bytes) {
79
111
  return createHash("sha256").update(bytes).digest("hex");
80
112
  }
81
113
 
114
+ /**
115
+ * List the durable refs from which one commit is reachable.
116
+ *
117
+ * Fails closed rather than answering "none" when git cannot be asked: an
118
+ * enumeration that did not run is not evidence of an unreachable commit, and
119
+ * conflating the two would turn a busy machine into a failed release.
120
+ */
121
+ function durableRefsContaining(root, commit) {
122
+ let output;
123
+ try {
124
+ output = String(
125
+ boundedExecFileSync(
126
+ "git",
127
+ [
128
+ "for-each-ref",
129
+ "--format=%(refname)",
130
+ `--contains=${commit}`,
131
+ ...DURABLE_REF_NAMESPACES,
132
+ ],
133
+ { cwd: root, encoding: "utf8", timeout: 120_000 }
134
+ )
135
+ );
136
+ } catch (error) {
137
+ rethrowIfChildTimeout(error);
138
+ mismatch(
139
+ `could not enumerate durable refs containing ${commit} (${error instanceof Error ? error.message : String(error)})`
140
+ );
141
+ }
142
+ return Object.freeze(
143
+ output
144
+ .split(/\r?\n/u)
145
+ .map(line => line.trim())
146
+ .filter(Boolean)
147
+ .filter(ref => !ref.startsWith(BACKUP_TAG_PREFIX))
148
+ );
149
+ }
150
+
82
151
  /** Read one tar member without extracting paths into the checkout. */
83
152
  function tarMember(tarballPath, member) {
84
153
  try {
@@ -137,9 +206,40 @@ export function assertReleaseCommit(releaseCommit) {
137
206
  }
138
207
 
139
208
  /**
140
- * Prove that the checked-out tag and HEAD name the supplied release commit.
209
+ * Require a release tag that a consumer can be pinned at.
210
+ *
211
+ * The one spelling refused outright is a bare commit SHA. Stamping a commit is
212
+ * the defect this check exists to close: the object survives a history rewrite
213
+ * but its reachability does not, and a caller pinned at an orphaned SHA never
214
+ * loads — zero jobs, so zero failures, so nothing red anywhere. Tag NAMING is
215
+ * otherwise left to the calling project, which is free to use its own prefix.
216
+ * @param {string} tag Release tag supplied by the publishing workflow
217
+ * @returns {string} The validated tag
218
+ */
219
+ export function assertReleaseTag(tag) {
220
+ if (typeof tag !== "string" || tag === "") {
221
+ mismatch(
222
+ `release tag must be a non-empty string; got ${JSON.stringify(tag)}`
223
+ );
224
+ }
225
+ if (!RELEASE_TAG_PATTERN.test(tag)) {
226
+ mismatch(
227
+ `release tag must be printable non-whitespace characters; got ${JSON.stringify(tag)}`
228
+ );
229
+ }
230
+ if (RELEASE_COMMIT_PATTERN.test(tag.toLowerCase())) {
231
+ mismatch(
232
+ `release tag ${JSON.stringify(tag)} is a bare commit SHA; a consumer pinned at a commit stops loading the workflow the moment history moves, so publish must stamp a tag ref`
233
+ );
234
+ }
235
+ return tag;
236
+ }
237
+
238
+ /**
239
+ * Prove that the checked-out tag and HEAD name the supplied release commit,
240
+ * and that the commit is reachable from a ref a consumer can resolve.
141
241
  * @param {{root: string, releaseCommit: string, tag: string}} input Identity input
142
- * @returns {{headCommit: string, tagCommit: string, releaseCommit: string}}
242
+ * @returns {{headCommit: string, tagCommit: string, releaseCommit: string, durableRefs: readonly string[]}}
143
243
  */
144
244
  export function assertCheckoutIdentity({ root, releaseCommit, tag }) {
145
245
  const validatedReleaseCommit = assertReleaseCommit(releaseCommit);
@@ -155,25 +255,36 @@ export function assertCheckoutIdentity({ root, releaseCommit, tag }) {
155
255
  `tag ${tag} resolves ${tagCommit}, expected ${validatedReleaseCommit}`
156
256
  );
157
257
  }
258
+ const durableRefs = durableRefsContaining(root, validatedReleaseCommit);
259
+ if (durableRefs.length === 0) {
260
+ mismatch(
261
+ `release commit ${validatedReleaseCommit} is reachable from no durable ref (${DURABLE_REF_NAMESPACES.join(", ")}, excluding ${BACKUP_TAG_PREFIX}*). ` +
262
+ "Publishing it would stamp a pin that resolves to nothing in every consumer that installs this version, and a caller whose pin does not resolve reports no failure at all. " +
263
+ `Push a release tag at ${validatedReleaseCommit} and re-run, or re-cut the release from a commit that is on the published branch.`
264
+ );
265
+ }
158
266
  return Object.freeze({
159
267
  headCommit,
160
268
  tagCommit,
161
269
  releaseCommit: validatedReleaseCommit,
270
+ durableRefs,
162
271
  });
163
272
  }
164
273
 
165
274
  /**
166
275
  * Pack and validate the exact candidate later passed to npm publish.
167
- * @param {{root: string, version: string, releaseCommit: string, packDestination: string}} input Candidate input
168
- * @returns {{version: string, releaseCommit: string, certificateVersion: string|null, tarballPath: string, tarballSha256: string, certificateMemberSha256: string|null}}
276
+ * @param {{root: string, version: string, releaseCommit: string, tag: string, packDestination: string}} input Candidate input
277
+ * @returns {{version: string, releaseCommit: string, releaseTag: string, certificateVersion: string|null, tarballPath: string, tarballSha256: string, certificateMemberSha256: string|null}}
169
278
  */
170
279
  export function packAndValidateReleaseCandidate({
171
280
  root,
172
281
  version,
173
282
  releaseCommit,
283
+ tag,
174
284
  packDestination,
175
285
  }) {
176
286
  const validatedReleaseCommit = assertReleaseCommit(releaseCommit);
287
+ const validatedTag = assertReleaseTag(tag);
177
288
  const packageJson = readJson(path.join(root, "package.json"), "package.json");
178
289
  if (packageJson.version !== version) {
179
290
  mismatch(
@@ -190,6 +301,11 @@ export function packAndValidateReleaseCandidate({
190
301
  `package.json gitHead ${JSON.stringify(packageJson.gitHead)} expected ${validatedReleaseCommit}`
191
302
  );
192
303
  }
304
+ if (packageJson.lisaReleaseTag !== validatedTag) {
305
+ mismatch(
306
+ `package.json lisaReleaseTag ${JSON.stringify(packageJson.lisaReleaseTag)} expected ${validatedTag}`
307
+ );
308
+ }
193
309
 
194
310
  const sourceCertificatePath = path.join(root, SOURCE_CERTIFICATE);
195
311
  const hasCertificate = existsSync(sourceCertificatePath);
@@ -243,6 +359,11 @@ export function packAndValidateReleaseCandidate({
243
359
  `packed gitHead ${JSON.stringify(packedPackage.gitHead)} expected ${validatedReleaseCommit}`
244
360
  );
245
361
  }
362
+ if (packedPackage.lisaReleaseTag !== validatedTag) {
363
+ mismatch(
364
+ `packed lisaReleaseTag ${JSON.stringify(packedPackage.lisaReleaseTag)} expected ${validatedTag}`
365
+ );
366
+ }
246
367
 
247
368
  let packedCertificateVersion = null;
248
369
  let certificateMemberSha256 = null;
@@ -260,6 +381,7 @@ export function packAndValidateReleaseCandidate({
260
381
  return Object.freeze({
261
382
  version,
262
383
  releaseCommit: validatedReleaseCommit,
384
+ releaseTag: validatedTag,
263
385
  certificateVersion: packedCertificateVersion,
264
386
  tarballPath,
265
387
  tarballSha256: sha256(tarballBytes),
@@ -306,6 +428,7 @@ export function runReleasePackageIdentityCli(
306
428
  root,
307
429
  version: requiredOption(options, "--version"),
308
430
  releaseCommit: requiredOption(options, "--release-commit"),
431
+ tag: requiredOption(options, "--tag"),
309
432
  packDestination: requiredOption(options, "--pack-destination"),
310
433
  });
311
434
  writeGithubOutput(result, githubOutput);
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "inspected": {
7
7
  "workflows": 24,
8
- "steps": 648,
8
+ "steps": 680,
9
9
  "couplings": 33,
10
10
  "inventory": 67
11
11
  },