@codyswann/lisa 4.71.7 → 4.71.8

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 (99) hide show
  1. package/all/copy-overwrite/scripts/check-threshold-ratchet.mjs +571 -0
  2. package/all/copy-overwrite/scripts/check-workflow-load-failures.mjs +31 -9
  3. package/all/copy-overwrite/scripts/lib/automation-provenance-contract.mjs +19 -0
  4. package/all/copy-overwrite/scripts/lib/automation-provenance-files.mjs +49 -0
  5. package/all/copy-overwrite/scripts/lib/automation-provenance-local.mjs +54 -38
  6. package/all/copy-overwrite/scripts/lib/npm-update-allocate.mjs +8 -2
  7. package/all/copy-overwrite/scripts/lib/npm-update-bun.mjs +189 -0
  8. package/all/copy-overwrite/scripts/lib/npm-update-cancel-origin.mjs +7 -3
  9. package/all/copy-overwrite/scripts/lib/npm-update-cancellation-proof.mjs +6 -1
  10. package/all/copy-overwrite/scripts/lib/npm-update-contract.mjs +26 -5
  11. package/all/copy-overwrite/scripts/lib/npm-update-gate.mjs +8 -5
  12. package/all/copy-overwrite/scripts/lib/npm-update-github.mjs +2 -2
  13. package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +3 -4
  14. package/all/copy-overwrite/scripts/lib/npm-update-hook-installation.mjs +68 -1
  15. package/all/copy-overwrite/scripts/lib/npm-update-hosted-gate.mjs +13 -80
  16. package/all/copy-overwrite/scripts/lib/npm-update-leaf-contract.mjs +6 -5
  17. package/all/copy-overwrite/scripts/lib/npm-update-prepare.mjs +35 -7
  18. package/all/copy-overwrite/scripts/lib/npm-update-publish.mjs +1 -1
  19. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +43 -16
  20. package/all/copy-overwrite/scripts/threshold-ratchet-compare.mjs +521 -0
  21. package/all/copy-overwrite/scripts/threshold-ratchet-families.mjs +546 -0
  22. package/dist/configs/repo-scan.js +1 -1
  23. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  24. package/dist/core/lisa-owned-hash-ledger.js +60 -0
  25. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  26. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  27. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  28. package/dist/core/upstream-evidence-manifest.js +44 -31
  29. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  30. package/package.json +4 -4
  31. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +3 -3
  34. package/plugins/lisa/hooks/threshold-ratchet.mjs +1 -1
  35. package/plugins/lisa-agy/plugin.json +1 -1
  36. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  39. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +3 -3
  43. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +1 -1
  44. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +3 -3
  46. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +1 -1
  47. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-expo-agy/plugin.json +1 -1
  50. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  55. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  60. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  65. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  69. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  70. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  74. package/plugins/lisa-rails-agy/plugin.json +1 -1
  75. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  79. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  80. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  84. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  85. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  87. package/plugins/materialized-artifacts.json +3 -0
  88. package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +3 -3
  89. package/plugins/src/base/hooks/threshold-ratchet.mjs +1 -1
  90. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +1 -1
  91. package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +3 -3
  92. package/scripts/build-plugins.sh +2 -1
  93. package/scripts/generate-scratch-supervisor-profile.mjs +1 -1
  94. package/scripts/two-channel-couplings.json +9 -7
  95. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +1 -1
  96. package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +3 -3
  97. /package/{rails → all}/copy-contents/scripts/lisa-mutation.sh +0 -0
  98. /package/{rails → all}/copy-overwrite/scripts/lisa-clean-git-env.sh +0 -0
  99. /package/{rails → all}/copy-overwrite/scripts/lisa-scratch-run.sh +0 -0
@@ -2,7 +2,7 @@
2
2
  // Do not edit directly — durable changes belong upstream in Lisa.
3
3
 
4
4
  /** Original manager readback preserves installed bytes; dispatch qualification is a separate obligation. */
5
- import { lstatSync, realpathSync } from "node:fs";
5
+ import { existsSync, lstatSync, realpathSync, readFileSync } from "node:fs";
6
6
  import { join, resolve } from "node:path";
7
7
  import { keys, required } from "./npm-update-contract.mjs";
8
8
  import { readBytes } from "./npm-update-process.mjs";
@@ -114,3 +114,70 @@ export async function originalHookInstallation(cwd, command, authority) {
114
114
  export async function originalPrePush(cwd, command, authority) {
115
115
  return (await originalHookInstallation(cwd, command, authority)).path;
116
116
  }
117
+
118
+ /** Husky 8 declares its real installer in package metadata; dependency install remains script-disabled. */
119
+ function huskyInstaller(cwd) {
120
+ const root = join(cwd, "node_modules/husky");
121
+ const metadata = JSON.parse(
122
+ readBytes(join(root, "package.json"), 65_536, false)
123
+ );
124
+ const bin =
125
+ typeof metadata.bin === "string" ? metadata.bin : metadata.bin?.husky;
126
+ required(
127
+ metadata.name === "husky" &&
128
+ /^8\.\d+\.\d+$/.test(metadata.version) &&
129
+ typeof bin === "string" &&
130
+ /^(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+\.js$/.test(bin),
131
+ "original Husky 8 installer declaration is unsupported"
132
+ );
133
+ const path = join(root, bin);
134
+ required(
135
+ realpathSync(root) === root &&
136
+ realpathSync(path) === path &&
137
+ lstatSync(path).isFile(),
138
+ "original Husky installer is unavailable or aliased"
139
+ );
140
+ return path;
141
+ }
142
+
143
+ /** The original supported manager installs its own wrappers; no synthetic replacement hook is written. */
144
+ export async function installOriginalManager(context, env, nativeStep) {
145
+ const lefthook = existsSync(join(context.cwd, "lefthook.yml"));
146
+ if (lefthook)
147
+ await nativeStep(context, env, "bundle", ["exec", "lefthook", "install"]);
148
+ else {
149
+ required(
150
+ existsSync(join(context.cwd, ".husky/pre-push")),
151
+ "original hook manager installation is unavailable"
152
+ );
153
+ await nativeStep(context, env, process.execPath, [
154
+ huskyInstaller(context.cwd),
155
+ "install",
156
+ ]);
157
+ }
158
+ const git = args => nativeStep(context, env, "/usr/bin/git", args);
159
+ const result = await git(["rev-parse", "--git-path", "hooks"]);
160
+ const text = result.stdout.toString();
161
+ required(
162
+ text.endsWith("\n") && !/[\n\r\0]/.test(text.slice(0, -1)),
163
+ "original hook path is unsupported"
164
+ );
165
+ const directory = resolve(context.cwd, text.slice(0, -1));
166
+ const wrappers = {};
167
+ for (const name of [
168
+ "pre-commit",
169
+ "prepare-commit-msg",
170
+ "commit-msg",
171
+ "pre-push",
172
+ ]) {
173
+ const path = join(directory, name);
174
+ required(
175
+ lstatSync(path).isFile() && !lstatSync(path).isSymbolicLink(),
176
+ "original hook wrapper is missing or aliased"
177
+ );
178
+ wrappers[name] = sha256(readFileSync(path));
179
+ }
180
+ const installation = { manager: lefthook ? "lefthook" : "husky", wrappers };
181
+ await originalHookInstallation(context.cwd, git, installation);
182
+ return installation;
183
+ }
@@ -2,28 +2,21 @@
2
2
  // Do not edit directly — durable changes belong upstream in Lisa.
3
3
 
4
4
  /** Supported disposable Actions Linux gates use original native tools; publisher authority stays in another job. */
5
- import {
6
- existsSync,
7
- lstatSync,
8
- realpathSync,
9
- readFileSync,
10
- mkdirSync,
11
- writeFileSync,
12
- } from "node:fs";
5
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
13
6
  import { join, resolve } from "node:path";
14
7
  import { fileURLToPath } from "node:url";
15
- import { required } from "./npm-update-contract.mjs";
16
- import { readBytes, runProcess, writeJson } from "./npm-update-process.mjs";
8
+ import { required, proposalFileNames } from "./npm-update-contract.mjs";
9
+ import { qualifiedBun, frozenBun } from "./npm-update-bun.mjs";
10
+ import { runProcess, writeJson } from "./npm-update-process.mjs";
17
11
  import { qualifiedControllerGraph } from "./npm-update-helper.mjs";
18
12
  import {
19
13
  controllerTools,
20
14
  controllerSubject,
21
15
  } from "./npm-update-controller-factory.mjs";
22
16
  import { startHookReadBroker } from "./npm-update-hook-provider.mjs";
23
- import { originalHookInstallation } from "./npm-update-hook-installation.mjs";
17
+ import { installOriginalManager } from "./npm-update-hook-installation.mjs";
24
18
  import { withStage } from "./npm-update-invariants.mjs";
25
19
  import {
26
- sha256,
27
20
  PROPOSAL_PREDICATE,
28
21
  RECOVERY_PREDICATE,
29
22
  } from "./github-attestation-verifier.mjs";
@@ -86,6 +79,7 @@ async function installHostedDependencies(context, root, env) {
86
79
  const before = [
87
80
  "package.json",
88
81
  "package-lock.json",
82
+ ...(context.proposal.bunLockSha256 === undefined ? [] : ["bun.lock"]),
89
83
  ...(ruby ? ["Gemfile", "Gemfile.lock"] : []),
90
84
  ].map(name => [name, readFileSync(join(context.cwd, name))]);
91
85
  await nativeStep(context, installed, "npm", [
@@ -94,6 +88,12 @@ async function installHostedDependencies(context, root, env) {
94
88
  "--no-audit",
95
89
  "--no-fund",
96
90
  ]);
91
+ if (context.proposal.bunLockSha256 !== undefined) {
92
+ const bun = await qualifiedBun(installed, { deadline: context.deadline });
93
+ await frozenBun(bun, context.cwd, proposalFileNames(context.proposal), {
94
+ timeout: remainingGateTime(context.deadline),
95
+ });
96
+ }
97
97
  if (ruby)
98
98
  await nativeStep(context, installed, "bundle", [
99
99
  "install",
@@ -111,73 +111,6 @@ async function installHostedDependencies(context, root, env) {
111
111
  return installed;
112
112
  }
113
113
 
114
- /** Husky 8 declares its real installer in package metadata; dependency install remains script-disabled. */
115
- function huskyInstaller(cwd) {
116
- const root = join(cwd, "node_modules/husky");
117
- const metadata = JSON.parse(
118
- readBytes(join(root, "package.json"), 65_536, false)
119
- );
120
- const bin =
121
- typeof metadata.bin === "string" ? metadata.bin : metadata.bin?.husky;
122
- required(
123
- metadata.name === "husky" &&
124
- /^8\.\d+\.\d+$/.test(metadata.version) &&
125
- typeof bin === "string" &&
126
- /^(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+\.js$/.test(bin),
127
- "original Husky 8 installer declaration is unsupported"
128
- );
129
- const path = join(root, bin);
130
- required(
131
- realpathSync(root) === root &&
132
- realpathSync(path) === path &&
133
- lstatSync(path).isFile(),
134
- "original Husky installer is unavailable or aliased"
135
- );
136
- return path;
137
- }
138
-
139
- /** The original supported manager installs its own wrappers; no synthetic replacement hook is written. */
140
- async function installOriginalManager(context, env) {
141
- const lefthook = existsSync(join(context.cwd, "lefthook.yml"));
142
- if (lefthook)
143
- await nativeStep(context, env, "bundle", ["exec", "lefthook", "install"]);
144
- else {
145
- required(
146
- existsSync(join(context.cwd, ".husky/pre-push")),
147
- "original hook manager installation is unavailable"
148
- );
149
- await nativeStep(context, env, process.execPath, [
150
- huskyInstaller(context.cwd),
151
- "install",
152
- ]);
153
- }
154
- const git = args => nativeStep(context, env, "/usr/bin/git", args);
155
- const result = await git(["rev-parse", "--git-path", "hooks"]);
156
- const text = result.stdout.toString();
157
- required(
158
- text.endsWith("\n") && !/[\n\r\0]/.test(text.slice(0, -1)),
159
- "original hook path is unsupported"
160
- );
161
- const directory = resolve(context.cwd, text.slice(0, -1));
162
- const wrappers = {};
163
- for (const name of [
164
- "pre-commit",
165
- "prepare-commit-msg",
166
- "commit-msg",
167
- "pre-push",
168
- ]) {
169
- const path = join(directory, name);
170
- required(
171
- lstatSync(path).isFile() && !lstatSync(path).isSymbolicLink(),
172
- "original hook wrapper is missing or aliased"
173
- );
174
- wrappers[name] = sha256(readFileSync(path));
175
- }
176
- const installation = { manager: lefthook ? "lefthook" : "husky", wrappers };
177
- await originalHookInstallation(context.cwd, git, installation);
178
- return installation;
179
- }
180
-
181
114
  /** Proof paths are the actual fixed slots installed for the unchanged canonical verifier. */
182
115
  async function proofScope(context, env) {
183
116
  const result = await nativeStep(context, env, "/usr/bin/git", [
@@ -276,7 +209,7 @@ export async function createHostedGate(context, root, env) {
276
209
  installHostedDependencies(context, root, env)
277
210
  );
278
211
  const installation = await withStage("gate-hooks", () =>
279
- installOriginalManager(context, installed)
212
+ installOriginalManager(context, installed, nativeStep)
280
213
  );
281
214
  const scope = await withStage("gate-scope", () =>
282
215
  proofScope(context, installed)
@@ -78,6 +78,7 @@ export function buildDraft(proposal, policy, config, evidence) {
78
78
  const versions = proposal.updates
79
79
  .map(update => `${update.name}: ${update.from} -> ${update.to}`)
80
80
  .join("\n");
81
+ const bun = Object.hasOwn(proposal, "bunLockSha256");
81
82
  const body = `## Context / Business Value
82
83
 
83
84
  Keep the selected dependencies current while preserving ordinary review, required checks and runtime verification. This Task owns one exact dependency proposal; its implementation ticket is never reused for another update.
@@ -87,7 +88,7 @@ Keep the selected dependencies current while preserving ordinary review, require
87
88
  Base parent: ${proposal.parent}
88
89
  Proposal key: ${proposal.key}
89
90
  Selection key: ${proposal.selectionKey}
90
- Update exactly package.json and package-lock.json:
91
+ Update exactly package.json and package-lock.json${bun ? " and bun.lock" : ""}:
91
92
  ${versions}
92
93
  Use the trusted four-job npm updater. Preparation performs an installed-tree npm outdated check and npm ci with lifecycle scripts disabled. Allocation precedes the first attributed commit. Ordinary commit and original pre-push gates remain mandatory. Publish only the exact gated unsigned raw Git object with unchanged parent, tree, identities, times and message. Preserve every unrelated dependency, script and existing assignee. Stop on stale base, unavailable authorization, hold, failed gate or mismatching bytes. Lisa is excluded only with verified local full-apply ownership.
93
94
 
@@ -97,8 +98,8 @@ Use the trusted four-job npm updater. Preparation performs an installed-tree npm
97
98
  Scenario: publish one coherent dependency proposal
98
99
  Given parent ${proposal.parent} and the selected versions above
99
100
  When the trusted updater claims this leaf and completes ordinary gates
100
- Then the manifest and lock agree and npm ci preserves both file hashes
101
- And the pull request into main contains exactly the gated commit and two files
101
+ Then the manifest and lock agree and npm ci preserves both file hashes${bun ? " and frozen Bun preserves all three hashes" : ""}
102
+ And the pull request into main contains exactly the gated commit and ${bun ? "three" : "two"} files
102
103
  Scenario: refuse unauthorized or stale publication
103
104
  Given a hold, changed parent, failed gate or mismatching proposal
104
105
  When publication is attempted
@@ -147,7 +148,7 @@ Matches are read completely before allocating this deterministic proposal. No pa
147
148
  ## Validation Journey
148
149
 
149
150
  1. Prepare the selected update from the exact parent with installed npm outdated; record actual exit and versions. [EVIDENCE: cli-output: npm-outdated]
150
- 2. Run script-disabled npm ci against the proposed files and record unchanged hashes. [EVIDENCE: cli-output: npm-ci]
151
+ 2. Run script-disabled npm ci${bun ? " and Bun1.3.8 frozen installation" : ""} against the proposed files and record unchanged hashes. [EVIDENCE: cli-output: npm-ci]
151
152
  3. Record normal commit/pre-push hook exits, all-ref stdin/ranges and the exact raw commit identity. [EVIDENCE: test-run-log: ordinary-gates]
152
153
  4. Read the current-head pull request, required checks and genuine human approval. [EVIDENCE: state-dump: current-head-review]
153
154
  5. Verify ordinary application behavior in ${env}; record actual environment and deployed identity. [EVIDENCE: test-run-log: runtime-verification]
@@ -156,7 +157,7 @@ Matches are read completely before allocating this deterministic proposal. No pa
156
157
  ## Proposal Bytes
157
158
 
158
159
  package.json SHA256: ${proposal.hashes["package.json"]}
159
- package-lock.json SHA256: ${proposal.hashes["package-lock.json"]}
160
+ package-lock.json SHA256: ${proposal.hashes["package-lock.json"]}${bun ? `\nbun.lock SHA256: ${proposal.hashes["bun.lock"]}\nOriginal bun.lock SHA256: ${proposal.bunLockSha256}` : ""}
160
161
 
161
162
  <!-- [lisa-npm-filing] ${Buffer.from(canonicalJson(evidence)).toString("base64")} -->
162
163
  <!-- [lisa-npm-proposal] ${proposal.key} -->
@@ -22,6 +22,7 @@ import {
22
22
  validateHost,
23
23
  validateLock,
24
24
  proposalFrom,
25
+ proposalFileNames,
25
26
  } from "./npm-update-contract.mjs";
26
27
  import {
27
28
  runProcess,
@@ -33,6 +34,7 @@ import { canonicalJson } from "../lisa-automation-provenance.mjs";
33
34
  import { qualifyLisaOwner } from "./npm-update-owner.mjs";
34
35
  export { qualifyLisaOwner } from "./npm-update-owner.mjs";
35
36
  import { sha256 } from "./github-attestation-verifier.mjs";
37
+ import { committedBun, qualifiedBun, frozenBun } from "./npm-update-bun.mjs";
36
38
 
37
39
  const NPM_FLAGS = ["--ignore-scripts", "--no-audit", "--no-fund"];
38
40
 
@@ -71,6 +73,8 @@ export async function baseline(cwd, env, proposal) {
71
73
  "utf8"
72
74
  );
73
75
  }
76
+ const bun = await committedBun(cwd, command, proposal);
77
+ if (bun !== undefined) files["bun.lock"] = bun;
74
78
  if (proposal)
75
79
  required(
76
80
  parent === proposal.parent &&
@@ -146,7 +150,7 @@ async function installUpdates(updates, npm, npmFlags) {
146
150
  }
147
151
  }
148
152
 
149
- /** Build only a two-file proposal in a private HOME, never executing repository scripts. */
153
+ /** Preserve the exact original lock cohort in a private HOME without repository scripts. */
150
154
  async function preparedProposal({
151
155
  app,
152
156
  npm,
@@ -158,29 +162,47 @@ async function preparedProposal({
158
162
  version,
159
163
  outdated,
160
164
  installedBefore,
165
+ bun,
161
166
  }) {
162
167
  await installUpdates(updates, npm, npmFlags);
168
+ const names = Object.keys(original.files).sort();
169
+ if (bun)
170
+ await bun(
171
+ [
172
+ "install",
173
+ "--lockfile-only",
174
+ "--ignore-scripts",
175
+ "--registry",
176
+ "https://registry.npmjs.org/",
177
+ ],
178
+ { cwd: app }
179
+ );
163
180
  const files = Object.fromEntries(
164
- FILES.map(file => [file, readFileSync(join(app, file), "utf8")])
181
+ names.map(file => [file, readFileSync(join(app, file), "utf8")])
165
182
  );
166
183
  const proposal = proposalFrom(
167
184
  policy,
168
185
  original.parent,
169
186
  before,
170
187
  files,
171
- updates
188
+ updates,
189
+ bun ? sha256(original.files["bun.lock"]) : undefined
172
190
  );
173
191
  const hashes = Object.fromEntries(
174
- FILES.map(file => [file, sha256(readFileSync(join(app, file)))])
192
+ proposalFileNames(proposal).map(file => [
193
+ file,
194
+ sha256(readFileSync(join(app, file))),
195
+ ])
175
196
  );
176
197
  await npm(["ci", ...npmFlags]);
198
+ if (bun) await frozenBun(bun, app, names);
177
199
  required(
178
- FILES.every(file => hashes[file] === sha256(readFileSync(join(app, file)))),
200
+ names.every(file => hashes[file] === sha256(readFileSync(join(app, file)))),
179
201
  "npm ci rewrote manifest or lock"
180
202
  );
181
203
  required(
182
204
  readdirSync(app).sort().join("\n") ===
183
- ["node_modules", ...FILES].sort().join("\n"),
205
+ ["node_modules", ...names].sort().join("\n"),
184
206
  "unexpected npm output path or lifecycle effect"
185
207
  );
186
208
  return {
@@ -224,11 +246,16 @@ export async function prepareUpdate({ cwd, policy: input, config }) {
224
246
  );
225
247
  await qualifyLisaOwner(cwd, before, policy, config, engineStrict);
226
248
  validateLock(JSON.parse(original.files["package-lock.json"]), before, []);
227
- for (const file of FILES)
249
+ for (const file of Object.keys(original.files))
228
250
  writeFileSync(join(app, file), original.files[file], { mode: 0o600 });
251
+ const bun =
252
+ original.files["bun.lock"] === undefined
253
+ ? undefined
254
+ : await qualifiedBun(env);
229
255
  const npm = (args, allowed = [0]) =>
230
256
  runNpm(args, { cwd: app, env, timeout: 120_000, allowed });
231
257
  await npm(["ci", ...npmFlags]);
258
+ if (bun) await frozenBun(bun, app, Object.keys(original.files));
232
259
  const installedBefore = installedVersions(app, policy.packages);
233
260
  const outdated = await npm(["outdated", "--json", "--long"], [0, 1]);
234
261
  const detected = JSON.parse(outdated.stdout.toString() || "{}");
@@ -256,6 +283,7 @@ export async function prepareUpdate({ cwd, policy: input, config }) {
256
283
  version,
257
284
  outdated,
258
285
  installedBefore,
286
+ bun,
259
287
  });
260
288
  });
261
289
  required(!existsSync(privatePath), "private npm resources remain");
@@ -127,7 +127,7 @@ export async function publishDestination({
127
127
  };
128
128
  }
129
129
 
130
- /** The publisher consumes signed/gated data and writes only an identical two-file commit. */
130
+ /** The publisher consumes signed/gated data and writes only the identical original lock cohort. */
131
131
  export async function publishProposal({
132
132
  api,
133
133
  proposal,
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "members": {
22
22
  "check-threshold-ratchet.mjs": {
23
- "sha256": "5806cdc3c80757100f0cd24a8cbd3c581f9a381a925f9ebdd1f4d79881e5de2a",
23
+ "sha256": "d4afc90ba8faf10925fff5fb0d6cd0ace22df2eaa0ec609ee969d0ec84092299",
24
24
  "staticImports": [
25
25
  "node:child_process",
26
26
  "node:fs",
@@ -33,17 +33,27 @@
33
33
  "children": []
34
34
  },
35
35
  "lib/automation-provenance-contract.mjs": {
36
- "sha256": "69dba85e2a57e1b89f6dfb9dc98a253d2e5317c9d000eeafc6bbd1649e740bd3",
36
+ "sha256": "b12e64711bf05c357d0cfebfda90540dcac413d34de9c6f2405fe0ad37d137dc",
37
37
  "staticImports": [
38
38
  "lib/github-attestation-verifier.mjs"
39
39
  ],
40
40
  "dynamicImports": [],
41
41
  "children": []
42
42
  },
43
+ "lib/automation-provenance-files.mjs": {
44
+ "sha256": "5c3b2a88669e3e187c123850798e3dfafb3c078b6b25aa00da0ba93db03424c9",
45
+ "staticImports": [
46
+ "lib/github-attestation-verifier.mjs",
47
+ "node:fs"
48
+ ],
49
+ "dynamicImports": [],
50
+ "children": []
51
+ },
43
52
  "lib/automation-provenance-local.mjs": {
44
- "sha256": "78f660ae1567b8254536323e547f1b8d806837fa554d580db84c5d69603cab0a",
53
+ "sha256": "e36147aef8b725147a26322b689e0fcef3c7cb5ae36f32e483a3da59667e3592",
45
54
  "staticImports": [
46
55
  "lib/automation-provenance-contract.mjs",
56
+ "lib/automation-provenance-files.mjs",
47
57
  "lib/bounded-spawn.mjs",
48
58
  "lib/github-attestation-verifier.mjs",
49
59
  "lisa-work-item.mjs",
@@ -176,7 +186,7 @@
176
186
  "children": []
177
187
  },
178
188
  "lib/npm-update-allocate.mjs": {
179
- "sha256": "c35a8c44bf67e4afb2849f2ea41948e190f1b108b35db25b38418478a3227e36",
189
+ "sha256": "85de2576d1c5e954b57f1c9274dbc34d0d24689fbc0964d8f86ec4f91f52b7bc",
180
190
  "staticImports": [
181
191
  "lib/github-attestation-verifier.mjs",
182
192
  "lib/npm-update-contract.mjs",
@@ -221,8 +231,22 @@
221
231
  "dynamicImports": [],
222
232
  "children": []
223
233
  },
234
+ "lib/npm-update-bun.mjs": {
235
+ "sha256": "898cdafe2e119b9c3004ea55ae222d2f33d8b668020cda09e90184474055a676",
236
+ "staticImports": [
237
+ "lib/automation-provenance-contract.mjs",
238
+ "lib/github-attestation-verifier.mjs",
239
+ "lib/npm-update-invariants.mjs",
240
+ "lib/npm-update-process-core.mjs",
241
+ "lib/npm-update-worker-lifecycle.mjs",
242
+ "node:fs",
243
+ "node:path"
244
+ ],
245
+ "dynamicImports": [],
246
+ "children": []
247
+ },
224
248
  "lib/npm-update-cancel-origin.mjs": {
225
- "sha256": "331554c8d2201fb6fe6934d158f5f60d7da11bbb1d4aa938ef9437afe724b5ff",
249
+ "sha256": "635dd82d23042337d68e441b43cec26c171fef9ac57c7e9c5de14af0587d7e43",
226
250
  "staticImports": [
227
251
  "lib/automation-provenance-contract.mjs",
228
252
  "lib/github-attestation-provider.mjs",
@@ -240,7 +264,7 @@
240
264
  "children": []
241
265
  },
242
266
  "lib/npm-update-cancellation-proof.mjs": {
243
- "sha256": "56365dbe20ef540b8a3d59a3ce9e0fa33c9c4efdfb27c2b69e27cbe60b2ba204",
267
+ "sha256": "9262c81836683572d1135112b8cfd0f43abcb050c9f8d84ef7dfb77b8caac59c",
244
268
  "staticImports": [
245
269
  "lib/github-attestation-verifier.mjs",
246
270
  "lib/npm-update-contract.mjs",
@@ -289,8 +313,9 @@
289
313
  "children": []
290
314
  },
291
315
  "lib/npm-update-contract.mjs": {
292
- "sha256": "6905dd9d5747702ab502b66bcb38b6d4dcb30127f02466d29b3905f53c27981d",
316
+ "sha256": "d8e7e94d7b33d0088ea01d32d659f00408487465f7d857a64daa1b09b16fcd8c",
293
317
  "staticImports": [
318
+ "lib/automation-provenance-contract.mjs",
294
319
  "lib/github-attestation-verifier.mjs",
295
320
  "lib/npm-update-invariants.mjs",
296
321
  "lib/npm-update-object.mjs",
@@ -411,7 +436,7 @@
411
436
  "children": []
412
437
  },
413
438
  "lib/npm-update-gate.mjs": {
414
- "sha256": "bb6b6b8ea1e3620ee0b1b620e50331788bc99b44f431057e12a60cd138e56bc4",
439
+ "sha256": "3d53ff811b0cce52fb4b33dbfb48c54e928b58fffa5b8ca67569f1bcd35aea45",
415
440
  "staticImports": [
416
441
  "lib/github-attestation-verifier.mjs",
417
442
  "lib/npm-update-contract.mjs",
@@ -467,7 +492,7 @@
467
492
  "children": []
468
493
  },
469
494
  "lib/npm-update-github.mjs": {
470
- "sha256": "5026c943e0ce87318a713806c1d609022d9818cd51a04b444b95285aa5aeadf8",
495
+ "sha256": "2be34eca5ceb38cc535fac6ca0a655e14993231cd37b1a3e26ea09756042c488",
471
496
  "staticImports": [
472
497
  "lib/github-attestation-verifier.mjs",
473
498
  "lib/npm-update-contract.mjs",
@@ -480,7 +505,7 @@
480
505
  "children": []
481
506
  },
482
507
  "lib/npm-update-helper-inventory.mjs": {
483
- "sha256": "3561c20458d74eff34650d6b4366f4f03ed4e7825ae886ea78394f63f98a18bb",
508
+ "sha256": "647c83dc72c21c4d68e27679e9f3b35b4b541bd8957c779495f78392d099cdd9",
484
509
  "staticImports": [],
485
510
  "dynamicImports": [],
486
511
  "children": []
@@ -506,7 +531,7 @@
506
531
  "children": []
507
532
  },
508
533
  "lib/npm-update-hook-installation.mjs": {
509
- "sha256": "83c4503b12ae53cb894f0d03d31d9cce8c23efec56d27f7a8252120fc5ea6c47",
534
+ "sha256": "af3cf29c76f583a7e18b974d2862f5fc273ac0f13c36bd28c284bcd0d480d045",
510
535
  "staticImports": [
511
536
  "lib/github-attestation-verifier.mjs",
512
537
  "lib/npm-update-contract.mjs",
@@ -559,9 +584,10 @@
559
584
  "children": []
560
585
  },
561
586
  "lib/npm-update-hosted-gate.mjs": {
562
- "sha256": "b6adf8af72067f883c83e11b7465a3f4c219a6e78d98b029b1af489967ccbaa8",
587
+ "sha256": "4d6635ec26435e642dc08de27d21eff033962f1508307e12c78a9c7c4cc12425",
563
588
  "staticImports": [
564
589
  "lib/github-attestation-verifier.mjs",
590
+ "lib/npm-update-bun.mjs",
565
591
  "lib/npm-update-contract.mjs",
566
592
  "lib/npm-update-controller-factory.mjs",
567
593
  "lib/npm-update-helper.mjs",
@@ -611,7 +637,7 @@
611
637
  "children": []
612
638
  },
613
639
  "lib/npm-update-leaf-contract.mjs": {
614
- "sha256": "1686c41dd54accb8e52595cbe66b0780878c2dc9eba3436b574d869bcfe93110",
640
+ "sha256": "9a68e50c354cac20fec847e02668c153f0f3c5783faaa29f51f2b4adf93ba99a",
615
641
  "staticImports": [
616
642
  "lib/npm-update-contract.mjs",
617
643
  "lisa-automation-provenance.mjs",
@@ -703,9 +729,10 @@
703
729
  "children": []
704
730
  },
705
731
  "lib/npm-update-prepare.mjs": {
706
- "sha256": "dcd25565c5139c788f91f695bacf3af5e95a564be66382b9d42af418e19d8aca",
732
+ "sha256": "66520d441ba6413cf189016f35f3babdc3b5d29f962b01cede0846aa924edf26",
707
733
  "staticImports": [
708
734
  "lib/github-attestation-verifier.mjs",
735
+ "lib/npm-update-bun.mjs",
709
736
  "lib/npm-update-contract.mjs",
710
737
  "lib/npm-update-owner.mjs",
711
738
  "lib/npm-update-process.mjs",
@@ -755,7 +782,7 @@
755
782
  "children": []
756
783
  },
757
784
  "lib/npm-update-publish.mjs": {
758
- "sha256": "6dd2cc1e6fcb09c8237798bf08a392035e5a85f0de74fc4ca8777164fa5ce69a",
785
+ "sha256": "781d1abe61cc0ec991f0ca299e8fbbc84cf9b5bf4b541de8954904218b83b007",
759
786
  "staticImports": [
760
787
  "lib/npm-update-authorization.mjs",
761
788
  "lib/npm-update-contract.mjs",
@@ -1147,7 +1174,7 @@
1147
1174
  "children": []
1148
1175
  },
1149
1176
  "threshold-ratchet-compare.mjs": {
1150
- "sha256": "99a97f830b1ee3388e8714602a4a3f188387c0389ac9d2a7b4b898f20f39a7a7",
1177
+ "sha256": "761a0f32ab4bc138b3ed216f0edc9ad28c36783ccd3b62fbd57ef1b7fdc32681",
1151
1178
  "staticImports": [
1152
1179
  "threshold-ratchet-families.mjs"
1153
1180
  ],