@codyswann/lisa 3.11.9 → 3.13.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 (145) hide show
  1. package/all/copy-overwrite/scripts/lisa-command-envelope.mjs +10 -0
  2. package/all/copy-overwrite/scripts/lisa-destructive-guard.mjs +267 -0
  3. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +15 -2
  4. package/all/create-only/scripts/remote-agent-aws-setup.sh +3 -0
  5. package/cdk/create-only/.github/workflows/deploy.yml +2 -1
  6. package/cdk/create-only/vitest.config.local.ts +3 -0
  7. package/dist/cli/doctor-lisa-owned-artifacts.d.ts +11 -2
  8. package/dist/cli/doctor-lisa-owned-artifacts.d.ts.map +1 -1
  9. package/dist/cli/doctor-lisa-owned-artifacts.js +60 -15
  10. package/dist/cli/doctor-lisa-owned-artifacts.js.map +1 -1
  11. package/dist/core/config.d.ts +21 -1
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +1 -0
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/guard-capabilities.d.ts +60 -0
  16. package/dist/core/guard-capabilities.d.ts.map +1 -0
  17. package/dist/core/guard-capabilities.js +102 -0
  18. package/dist/core/guard-capabilities.js.map +1 -0
  19. package/dist/core/lisa-owned-hash-ledger.d.ts +11 -0
  20. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -0
  21. package/dist/core/lisa-owned-hash-ledger.js +127 -0
  22. package/dist/core/lisa-owned-hash-ledger.js.map +1 -0
  23. package/dist/core/lisa-owned-provenance.d.ts +75 -0
  24. package/dist/core/lisa-owned-provenance.d.ts.map +1 -0
  25. package/dist/core/lisa-owned-provenance.js +127 -0
  26. package/dist/core/lisa-owned-provenance.js.map +1 -0
  27. package/dist/core/lisa.d.ts +18 -0
  28. package/dist/core/lisa.d.ts.map +1 -1
  29. package/dist/core/lisa.js +38 -0
  30. package/dist/core/lisa.js.map +1 -1
  31. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  32. package/dist/core/upstream-evidence-manifest.js +66 -53
  33. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  34. package/dist/strategies/copy-overwrite.d.ts +28 -1
  35. package/dist/strategies/copy-overwrite.d.ts.map +1 -1
  36. package/dist/strategies/copy-overwrite.js +53 -3
  37. package/dist/strategies/copy-overwrite.js.map +1 -1
  38. package/dist/strategies/strategy.interface.d.ts +13 -0
  39. package/dist/strategies/strategy.interface.d.ts.map +1 -1
  40. package/expo/create-only/.github/workflows/ci.yml +3 -0
  41. package/expo/create-only/.github/workflows/deploy.yml +2 -1
  42. package/expo/create-only/.github/workflows/maestro-e2e.yml +3 -0
  43. package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +3 -0
  44. package/expo/create-only/.github/workflows/nightly-e2e-health.yml +3 -0
  45. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +3 -0
  46. package/expo/create-only/babel.config.js +3 -0
  47. package/expo/create-only/jest.config.local.ts +3 -0
  48. package/expo/create-only/jest.config.react-native-mock.js +3 -0
  49. package/expo/create-only/jest.setup.local.ts +3 -0
  50. package/expo/create-only/jest.setup.pre.local.js +3 -0
  51. package/expo/create-only/scripts/zap-baseline.sh +3 -0
  52. package/harper-fabric/create-only/.github/workflows/deploy.yml +3 -0
  53. package/harper-fabric/create-only/.github/workflows/zap-baseline.yml +3 -0
  54. package/harper-fabric/create-only/scripts/zap-baseline.sh +3 -0
  55. package/nestjs/create-only/.github/workflows/deploy.yml +3 -0
  56. package/nestjs/create-only/scripts/zap-baseline.sh +3 -0
  57. package/nestjs/create-only/vitest.config.local.ts +3 -0
  58. package/npm-package/create-only/.github/workflows/publish-to-npm.yml +2 -1
  59. package/package.json +3 -1
  60. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa/hooks/block-no-verify.sh +15 -2
  63. package/plugins/lisa/rules/eager/reset-seed-coverage.md +2 -0
  64. package/plugins/lisa-agy/plugin.json +1 -1
  65. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  68. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-copilot/hooks/block-no-verify.sh +15 -2
  72. package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +2 -0
  73. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-cursor/hooks/block-no-verify.sh +15 -2
  75. package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +2 -0
  76. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-expo-agy/plugin.json +1 -1
  79. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  84. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  89. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  94. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  99. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  103. package/plugins/lisa-rails-agy/plugin.json +1 -1
  104. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  109. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  113. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  114. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  116. package/plugins/src/base/hooks/block-no-verify.sh +15 -2
  117. package/plugins/src/base/rules/eager/reset-seed-coverage.md +2 -0
  118. package/rails/create-only/.github/workflows/claude-code-review-response.yml +2 -1
  119. package/rails/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
  120. package/rails/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
  121. package/rails/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
  122. package/rails/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
  123. package/rails/create-only/.github/workflows/deploy.yml +2 -1
  124. package/rails/create-only/.mutant.yml +3 -0
  125. package/rails/create-only/.reek.yml +3 -0
  126. package/rails/create-only/.rubocop_todo.yml +3 -0
  127. package/rails/create-only/mutation.gate.yml +3 -0
  128. package/rails/create-only/rubocop.thresholds.yml +3 -0
  129. package/rails/create-only/spec/rails_helper.rb +2 -0
  130. package/rails/create-only/spec/spec_helper.rb +2 -0
  131. package/scripts/generate-lisa-owned-hash-ledger.mjs +226 -0
  132. package/typescript/create-only/.github/workflows/auto-update-pr-branches-dispatch.yml +2 -1
  133. package/typescript/create-only/.github/workflows/auto-update-pr-branches.yml +2 -1
  134. package/typescript/create-only/.github/workflows/claude-ci-auto-fix.yml +2 -1
  135. package/typescript/create-only/.github/workflows/claude-code-review-response.yml +2 -1
  136. package/typescript/create-only/.github/workflows/claude-deploy-auto-fix.yml +2 -1
  137. package/typescript/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
  138. package/typescript/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
  139. package/typescript/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
  140. package/typescript/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
  141. package/typescript/create-only/.github/workflows/claude.yml +2 -1
  142. package/typescript/create-only/.github/workflows/required-checks-drift.yml +2 -1
  143. package/typescript/create-only/eslint.config.local.ts +3 -0
  144. package/typescript/create-only/jest.config.local.ts +3 -0
  145. package/typescript/create-only/vitest.config.local.ts +3 -0
@@ -25,6 +25,7 @@ import * as fs from "node:fs";
25
25
  import * as path from "node:path";
26
26
  import { fileURLToPath, pathToFileURL } from "node:url";
27
27
 
28
+ import { destructiveDenial } from "./lisa-destructive-guard.mjs";
28
29
  import { validateAgainstSchema } from "./lisa-schema-validate.mjs";
29
30
 
30
31
  /** Pinned envelope schema version. */
@@ -126,6 +127,15 @@ export function validateEnvelope(envelope) {
126
127
  if (envelope.mode === "declared-noop" && envelope.status !== "no-op") {
127
128
  errors.push('mode "declared-noop" requires status "no-op"');
128
129
  }
130
+ // The production boundary lives here, at the one interface every reset, seed,
131
+ // and verify adapter must pass through, rather than in each adapter. A
132
+ // destructive run against a production-resolved (or unresolvable) environment
133
+ // has no representable success envelope, so it can never exit 0 — reporting
134
+ // the refusal stays available, which is the only outcome that should be.
135
+ const denial = destructiveDenial(envelope);
136
+ if (denial && SUCCESS_STATUSES.includes(envelope.status)) {
137
+ errors.push(`${denial.code}: ${denial.message}`);
138
+ }
129
139
  return { valid: errors.length === 0, errors };
130
140
  }
131
141
 
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lisa-destructive-guard — the executable arm of `production-fails-closed`.
4
+ *
5
+ * The `reset-seed-coverage` contract already says the right thing: production
6
+ * is refused "with no override, escape hatch, or environment variable that
7
+ * changes the answer", `--dry-run` is mandatory before anything destructive,
8
+ * and a caller-supplied `--stage` is a *request* to be checked against
9
+ * server-resolved identity rather than the source of truth. Until this module
10
+ * existed, all three were prose — an agent reading a rule and choosing to stop.
11
+ * Measured conformance for that rung in this codebase was approximately zero,
12
+ * including by the agent that authored the rule. For a destructive,
13
+ * irreversible operation that is the wrong rung, so the decision moves here.
14
+ *
15
+ * Three properties make this a control rather than a suggestion:
16
+ *
17
+ * 1. **No override exists to be found.** There is no `force` parameter, no
18
+ * `allowProduction` field, and no environment-variable read anywhere in this
19
+ * file — a test greps the shipped source for the latter, so the claim cannot
20
+ * rot. A caller cannot pass the wrong thing because there is no right thing
21
+ * to pass. The tests assert the absence structurally, not behaviourally.
22
+ * 2. **Ambiguity resolves to refusal.** An environment identity that cannot be
23
+ * read is treated exactly like production. "I could not tell" must never be
24
+ * the cheaper answer than "it is production".
25
+ * 3. **A dry run is not a pass.** `dryRun: true` does not soften a production
26
+ * denial. Enumerating production state is still reaching into production,
27
+ * and allowing it would hand every caller a one-flag override.
28
+ *
29
+ * What this module deliberately does NOT claim: it cannot verify that the
30
+ * `environment` an adapter reports is the environment it actually connected to.
31
+ * An adapter that resolves its stage from a caller-supplied string and reports
32
+ * `"dev"` while pointed at production defeats every in-process check by
33
+ * construction. That hole is why the contract says platform enforcement beats
34
+ * process enforcement, and why the durable answer is a capability that is not
35
+ * deployed to production at all — see `docs/decisions/`. This module closes the
36
+ * narrower hole Lisa can close: an honest adapter can no longer report a
37
+ * successful destructive production run, in any repo, by any path.
38
+ * @module scripts/lisa-destructive-guard
39
+ */
40
+
41
+ /**
42
+ * Capabilities that mutate persistent state by definition.
43
+ *
44
+ * Membership is by capability name because a destructive adapter that fails
45
+ * before it deletes anything still reports zeroes, and a guard that keyed only
46
+ * on the counts would wave it through on the attempt that mattered.
47
+ */
48
+ export const DESTRUCTIVE_CAPABILITIES = Object.freeze([
49
+ "reset",
50
+ "reseed",
51
+ "reset-seed",
52
+ "seed",
53
+ "teardown",
54
+ "truncate",
55
+ "purge",
56
+ "drop",
57
+ "restore",
58
+ "migrate-down",
59
+ ]);
60
+
61
+ /**
62
+ * Substring marking an environment segment as production.
63
+ *
64
+ * Deliberately a substring rather than an exact match, so `preprod`,
65
+ * `prod-blue`, and `acme-prod-1` all classify as production. That
66
+ * over-includes: a genuine pre-production environment is refused too. For a
67
+ * control over irreversible operations, over-refusal is the correct direction
68
+ * to be wrong in, and a project that needs the distinction names its
69
+ * environments so the word does not appear.
70
+ */
71
+ const PRODUCTION_SUBSTRING = "prod";
72
+
73
+ /** Whole segments that mark production without containing the substring. */
74
+ const PRODUCTION_SEGMENTS = Object.freeze(["prd", "live"]);
75
+
76
+ /**
77
+ * Values that carry no environment identity.
78
+ *
79
+ * Each of these is something a resolver returns when it failed, and every one
80
+ * of them classifies as `unresolved`, which the guard then treats exactly as it
81
+ * treats production.
82
+ */
83
+ const UNRESOLVED_SENTINELS = new Set([
84
+ "",
85
+ "unknown",
86
+ "unresolved",
87
+ "undefined",
88
+ "null",
89
+ "n/a",
90
+ "na",
91
+ "none",
92
+ "-",
93
+ ]);
94
+
95
+ /** Flags that explicitly opt out of the mandatory dry run. */
96
+ const EXECUTE_FLAGS = Object.freeze(["--no-dry-run", "--execute"]);
97
+
98
+ /**
99
+ * Normalize an environment identity for comparison.
100
+ * @param {unknown} value - Candidate environment identity
101
+ * @returns {string} Lowercased, trimmed text, or "" when there is no text
102
+ */
103
+ function normalize(value) {
104
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
105
+ }
106
+
107
+ /**
108
+ * Classify a server-resolved environment identity.
109
+ *
110
+ * Returns `unresolved` rather than guessing whenever the value is not text, is
111
+ * blank, or is one of the sentinels a failed resolver produces. Callers must
112
+ * treat `unresolved` as production; this function does not make that decision
113
+ * for them so that the two cases stay separately reportable.
114
+ * @param {unknown} value - The environment identity to classify
115
+ * @returns {"production"|"non-production"|"unresolved"} The classification
116
+ */
117
+ export function classifyEnvironment(value) {
118
+ const text = normalize(value);
119
+ if (typeof value !== "string" || UNRESOLVED_SENTINELS.has(text)) {
120
+ return "unresolved";
121
+ }
122
+ const segments = text.split(/[^a-z0-9]+/u).filter(Boolean);
123
+ if (segments.length === 0) {
124
+ return "unresolved";
125
+ }
126
+ const production = segments.some(
127
+ segment =>
128
+ segment.includes(PRODUCTION_SUBSTRING) ||
129
+ PRODUCTION_SEGMENTS.includes(segment)
130
+ );
131
+ return production ? "production" : "non-production";
132
+ }
133
+
134
+ /**
135
+ * Whether a run mutates persistent state.
136
+ *
137
+ * True when the capability is destructive by name, and also when a run of any
138
+ * capability reports rows deleted or created — a read-only adapter that mutated
139
+ * is still a mutation, and the guard should not depend on it being named
140
+ * honestly.
141
+ * @param {object} fields - An envelope or envelope-shaped object
142
+ * @returns {boolean} True when the run is destructive
143
+ */
144
+ export function isDestructive(fields) {
145
+ const capability = normalize(fields?.capability);
146
+ if (DESTRUCTIVE_CAPABILITIES.includes(capability)) {
147
+ return true;
148
+ }
149
+ const summary = fields?.summary ?? {};
150
+ return Number(summary.deleted) > 0 || Number(summary.created) > 0;
151
+ }
152
+
153
+ /**
154
+ * Build the denial for a resolved environment, or null when it is permitted.
155
+ * @param {unknown} environment - Server-resolved environment identity
156
+ * @returns {{code: string, message: string}|null} The denial, if any
157
+ */
158
+ function denyByEnvironment(environment) {
159
+ const classification = classifyEnvironment(environment);
160
+ if (classification === "unresolved") {
161
+ return {
162
+ code: "unresolved-environment",
163
+ message:
164
+ "the server-resolved environment could not be read, which fails closed exactly as production does — resolve the environment from deployment identity before any destructive operation",
165
+ };
166
+ }
167
+ if (classification === "production") {
168
+ return {
169
+ code: "production-forbidden",
170
+ message: `production is refused by the reset-seed contract with no override, and "${normalize(environment)}" classifies as production — a dry run is not an exemption`,
171
+ };
172
+ }
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * The denial implied by an envelope, or null when the envelope is permitted.
178
+ *
179
+ * This is the check the command envelope itself applies, so a destructive
180
+ * production run cannot be *reported* as a success by any adapter in any repo.
181
+ * @param {object} fields - An envelope or envelope-shaped object
182
+ * @returns {{code: string, message: string}|null} The denial, if any
183
+ */
184
+ export function destructiveDenial(fields) {
185
+ return isDestructive(fields) ? denyByEnvironment(fields?.environment) : null;
186
+ }
187
+
188
+ /**
189
+ * Decide whether a destructive invocation may proceed.
190
+ *
191
+ * Adapter-time counterpart of {@link destructiveDenial}, adding the two checks
192
+ * that need the caller's request alongside the resolved identity. The requested
193
+ * stage is never trusted: it is compared against server-resolved identity, and
194
+ * a production request is refused even when the resolver disagrees, because the
195
+ * disagreement itself means one of the two is wrong.
196
+ * @param {object} request - capability, summary, resolvedEnvironment, requestedStage, dryRun
197
+ * @returns {{allowed: boolean, denial: ({code: string, message: string}|null)}} The decision
198
+ */
199
+ export function assertDestructiveAllowed(request) {
200
+ if (!isDestructive(request)) {
201
+ return { allowed: true, denial: null };
202
+ }
203
+ const resolved = request?.resolvedEnvironment;
204
+ const environmentDenial = denyByEnvironment(resolved);
205
+ if (environmentDenial) {
206
+ return { allowed: false, denial: environmentDenial };
207
+ }
208
+ const requested = request?.requestedStage;
209
+ if (requested === undefined || requested === null) {
210
+ return { allowed: true, denial: null };
211
+ }
212
+ if (classifyEnvironment(requested) === "production") {
213
+ return {
214
+ allowed: false,
215
+ denial: {
216
+ code: "production-requested",
217
+ message: `the caller requested "${normalize(requested)}", which classifies as production; a requested stage never authorizes a destructive operation`,
218
+ },
219
+ };
220
+ }
221
+ if (normalize(requested) !== normalize(resolved)) {
222
+ return {
223
+ allowed: false,
224
+ denial: {
225
+ code: "stage-mismatch",
226
+ message: `the caller requested "${normalize(requested)}" but deployment identity resolved to "${normalize(resolved)}"; a stage is a request to be checked, never the source of truth`,
227
+ },
228
+ };
229
+ }
230
+ return { allowed: true, denial: null };
231
+ }
232
+
233
+ /**
234
+ * Read one `--flag value` / `--flag=value` option out of argv.
235
+ * @param {readonly string[]} argv - Arguments after the script name
236
+ * @param {string} flag - The long flag to read, including leading dashes
237
+ * @returns {string|null} The value, or null when the flag is absent or empty
238
+ */
239
+ function readOption(argv, flag) {
240
+ const inline = argv.find(entry => entry.startsWith(`${flag}=`));
241
+ if (inline) {
242
+ return inline.slice(flag.length + 1) || null;
243
+ }
244
+ const index = argv.indexOf(flag);
245
+ return index === -1 ? null : (argv[index + 1] ?? null);
246
+ }
247
+
248
+ /**
249
+ * Parse the arguments every destructive adapter accepts.
250
+ *
251
+ * `--dry-run` is the default rather than an opt-in: an adapter that forgets to
252
+ * parse anything, or is handed arguments it does not understand, enumerates
253
+ * instead of mutating. Mutating requires an explicit `--no-dry-run` or
254
+ * `--execute`, and passing `--dry-run` alongside either one keeps the dry run,
255
+ * so a stray flag can never silently upgrade a rehearsal into a real run.
256
+ * @param {readonly string[]} argv - Arguments after the script name
257
+ * @returns {{dryRun: boolean, requestedStage: (string|null), idempotencyKey: (string|null)}} Parsed arguments
258
+ */
259
+ export function parseDestructiveArgs(argv) {
260
+ const args = [...(argv ?? [])];
261
+ const optedOut = EXECUTE_FLAGS.some(flag => args.includes(flag));
262
+ return {
263
+ dryRun: args.includes("--dry-run") || !optedOut,
264
+ requestedStage: readOption(args, "--stage"),
265
+ idempotencyKey: readOption(args, "--idempotency-key"),
266
+ };
267
+ }
@@ -5,9 +5,22 @@
5
5
  # check. See feedback_never_no_verify in user memory.
6
6
  #
7
7
  # Blocked bypass vectors:
8
- # 1. the --no-verify long flag (any subcommand, any position, incl. subshells);
8
+ # 1. --no-verify, and any prefix abbreviation git would accept for it;
9
9
  # 2. HUSKY=0 / HUSKY_SKIP_HOOKS=... — disables husky-managed git hooks;
10
- # 3. core.hooksPath pointed at /dev/null or set empty disables ALL git hooks.
10
+ # 3. core.hooksPath pointed anywhere but an allowlisted in-repo hooks dir;
11
+ # 4. --config-env=core.hooksPath=VAR, in both the one- and two-token spellings;
12
+ # 5. GIT_CONFIG_KEY_<n>=core.hooksPath env-var-style command-scope config.
13
+ #
14
+ # The line below is what lets `lisa apply` tell a downstream copy of this guard
15
+ # that is BEHIND from one that is AHEAD. Byte comparison cannot: both look like
16
+ # "differs from mine", and guessing "behind" is how a fork's stronger guard gets
17
+ # silently replaced by a weaker upstream one. Refresh compares the two declared
18
+ # sets, and refuses to overwrite a copy declaring anything this one does not.
19
+ #
20
+ # Add a name here in the same commit that closes a vector. A hardening that
21
+ # forgets to is invisible to refresh, and shows up as an unexplained diff at
22
+ # review time instead of a named capability.
23
+ # lisa-guard-capabilities: no-verify-abbrev, husky-env, hookspath-allowlist, config-env, git-config-key
11
24
  #
12
25
  # Shell-token matching avoids false positives from issue bodies, heredocs, and
13
26
  # commit-message prose while still catching quoted real argv values such as
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env bash
2
+ # Seeded by Lisa on first setup — this file is YOURS.
3
+ # Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
4
+
2
5
  # Installed by Lisa. The authoritative implementation is shipped by the
3
6
  # lisa-setup-remote-aws skill and the @codyswann/lisa package.
4
7
 
@@ -1,4 +1,5 @@
1
- # This file is created by Lisa (create-only edit freely, Lisa won't overwrite it).
1
+ # Seeded by Lisa on first setup this file is YOURS.
2
+ # Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
2
3
 
3
4
  name: 🚀 Release and Deploy
4
5
 
@@ -1,3 +1,6 @@
1
+ // Seeded by Lisa on first setup — this file is YOURS.
2
+ // Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
3
+
1
4
  /**
2
5
  * Vitest Configuration - Project-Local Customizations
3
6
  *
@@ -1,3 +1,4 @@
1
+ import type { HashLedger } from "../core/lisa-owned-provenance.js";
1
2
  /** One doctor check result, structurally identical to `DoctorCheck`. */
2
3
  interface ArtifactCheck {
3
4
  name: string;
@@ -5,14 +6,22 @@ interface ArtifactCheck {
5
6
  detail: string;
6
7
  }
7
8
  /**
8
- * Report Lisa-owned enforcement artifacts the project has an outdated copy of.
9
+ * Report Lisa-owned enforcement artifacts that differ from what Lisa ships.
10
+ *
11
+ * Two different findings, deliberately not merged. An *outdated* copy is fixed
12
+ * by running apply. A *modified* copy is one apply will now refuse to touch, so
13
+ * telling the operator to run apply would send them round a loop that never
14
+ * terminates and make the protection look like a bug. Both still warn — a guard
15
+ * nobody can account for is worth a line either way, and staying silent about a
16
+ * downstream edit would let a project quietly swap a guard for a stub.
9
17
  *
10
18
  * Only artifacts the project already has are considered: a missing one means
11
19
  * the stack does not apply here, not that something drifted.
12
20
  * @param targetPath - Project path to inspect
13
21
  * @param lisaRoot - Installed Lisa package root (injected by tests)
22
+ * @param ledger - Known-good hashes, defaulting to Lisa's shipping history
14
23
  * @returns Doctor check result
15
24
  */
16
- export declare function checkLisaOwnedArtifacts(targetPath: string, lisaRoot?: string): Promise<ArtifactCheck>;
25
+ export declare function checkLisaOwnedArtifacts(targetPath: string, lisaRoot?: string, ledger?: HashLedger): Promise<ArtifactCheck>;
17
26
  export {};
18
27
  //# sourceMappingURL=doctor-lisa-owned-artifacts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-lisa-owned-artifacts.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"AA0CA,wEAAwE;AACxE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAkHD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,aAAa,CAAC,CAiDxB"}
1
+ {"version":3,"file":"doctor-lisa-owned-artifacts.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAoBnE,wEAAwE;AACxE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAkHD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAA0B,EACpC,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,aAAa,CAAC,CA+CxB"}
@@ -20,6 +20,7 @@ import * as path from "node:path";
20
20
  import { fileURLToPath } from "node:url";
21
21
  import * as fse from "fs-extra";
22
22
  import { PROJECT_TYPE_ORDER } from "../core/config.js";
23
+ import { classifyHostCopy, mayRefreshLisaOwned, } from "../core/lisa-owned-provenance.js";
23
24
  import { isLisaOwnedTemplate } from "../core/lisa-owned-templates.js";
24
25
  import { isLisaSourceRepo } from "../core/self-apply.js";
25
26
  import { listFilesRecursive } from "../utils/file-operations.js";
@@ -112,15 +113,23 @@ function reExportsShippedTemplate(installed, installedPath, sources) {
112
113
  return [...installed.toString("utf8").matchAll(RELATIVE_SPECIFIER)].some(match => shipped.has(path.resolve(directory, match[1] ?? "")));
113
114
  }
114
115
  /**
115
- * Report Lisa-owned enforcement artifacts the project has an outdated copy of.
116
+ * Report Lisa-owned enforcement artifacts that differ from what Lisa ships.
117
+ *
118
+ * Two different findings, deliberately not merged. An *outdated* copy is fixed
119
+ * by running apply. A *modified* copy is one apply will now refuse to touch, so
120
+ * telling the operator to run apply would send them round a loop that never
121
+ * terminates and make the protection look like a bug. Both still warn — a guard
122
+ * nobody can account for is worth a line either way, and staying silent about a
123
+ * downstream edit would let a project quietly swap a guard for a stub.
116
124
  *
117
125
  * Only artifacts the project already has are considered: a missing one means
118
126
  * the stack does not apply here, not that something drifted.
119
127
  * @param targetPath - Project path to inspect
120
128
  * @param lisaRoot - Installed Lisa package root (injected by tests)
129
+ * @param ledger - Known-good hashes, defaulting to Lisa's shipping history
121
130
  * @returns Doctor check result
122
131
  */
123
- export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisaRoot()) {
132
+ export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisaRoot(), ledger) {
124
133
  const shipped = await shippedArtifacts(lisaRoot);
125
134
  if (shipped.size === 0) {
126
135
  return {
@@ -145,24 +154,60 @@ export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisa
145
154
  return undefined;
146
155
  if (await matchesAnyShipped(installed, sources))
147
156
  return undefined;
148
- return selfHost &&
149
- reExportsShippedTemplate(installed, installedPath, sources)
150
- ? undefined
151
- : destination;
157
+ if (selfHost &&
158
+ reExportsShippedTemplate(installed, installedPath, sources))
159
+ return undefined;
160
+ const outdated = await isProvablyStale(installed, destination, sources, ledger);
161
+ return [destination, outdated ? "stale" : "modified"];
152
162
  }));
153
- const stale = results
154
- .filter((item) => item !== undefined)
163
+ return summarise(results.filter((item) => item !== undefined));
164
+ }
165
+ /**
166
+ * Turn the per-artifact findings into one doctor line.
167
+ * @param findings - Destination paths paired with what was found
168
+ * @returns Doctor check result
169
+ */
170
+ function summarise(findings) {
171
+ const pick = (wanted) => findings
172
+ .filter(([, finding]) => finding === wanted)
173
+ .map(([destination]) => destination)
155
174
  .sort((left, right) => left.localeCompare(right));
156
- return stale.length === 0
157
- ? {
175
+ const stale = pick("stale");
176
+ const modified = pick("modified");
177
+ if (stale.length === 0 && modified.length === 0) {
178
+ return {
158
179
  name: CHECK_NAME,
159
180
  status: "ok",
160
181
  detail: "Enforcement guards match the installed Lisa version",
161
- }
162
- : {
163
- name: CHECK_NAME,
164
- status: "warn",
165
- detail: `Outdated Lisa-owned guards (run \`npx lisa apply .\` to refresh): ${stale.join(", ")}`,
166
182
  };
183
+ }
184
+ const parts = [
185
+ stale.length > 0
186
+ ? `Outdated Lisa-owned guards (run \`npx lisa apply .\` to refresh): ${stale.join(", ")}`
187
+ : "",
188
+ modified.length > 0
189
+ ? `Lisa-owned guards edited in this project, so apply will keep yours rather than overwrite them: ${modified.join(", ")}`
190
+ : "",
191
+ ].filter(part => part.length > 0);
192
+ return { name: CHECK_NAME, status: "warn", detail: parts.join(". ") };
193
+ }
194
+ /**
195
+ * Whether a differing installed guard is genuinely behind Lisa's.
196
+ *
197
+ * Doctor and apply have to agree, or the operator gets contradictory
198
+ * instructions: doctor telling somebody to run `lisa apply .` to fix a file
199
+ * apply will then deliberately refuse to touch is a loop with no exit, and it
200
+ * reads as a broken tool rather than as the protection it is. So a copy apply
201
+ * would preserve is not reported here either — it is not outdated, and calling
202
+ * it outdated invites the operator to discard their own hardening.
203
+ * @param installed - Bytes currently installed in the project
204
+ * @param destination - Repo-relative destination path
205
+ * @param sources - Absolute paths of the shipped variants
206
+ * @param ledger - Known-good hashes, defaulting to Lisa's shipping history
207
+ * @returns True when every shipped variant considers the installed copy stale
208
+ */
209
+ async function isProvablyStale(installed, destination, sources, ledger) {
210
+ const shipped = await Promise.all(sources.map(async (source) => readFile(source)));
211
+ return shipped.every(candidate => mayRefreshLisaOwned(classifyHostCopy(destination, installed, candidate, ledger)));
167
212
  }
168
213
  //# sourceMappingURL=doctor-lisa-owned-artifacts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-lisa-owned-artifacts.js","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor-lisa-owned-artifacts.js","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"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"}
@@ -158,8 +158,26 @@ export interface FileOperationResult {
158
158
  * let template changes — including fixes to the enforcement guards —
159
159
  * go undelivered while the summary read exactly like a clean no-op.
160
160
  */
161
- | "stale" | "overwritten" | "appended" | "merged" | "created";
161
+ | "stale"
162
+ /**
163
+ * A Lisa-owned artifact whose installed copy could not be proved to be
164
+ * behind Lisa's, so it was preserved rather than overwritten.
165
+ *
166
+ * Emphatically not "stale" — that word says the file is out of date, and
167
+ * here the likeliest reason it differs is that it is *ahead*. Filing the two
168
+ * under one label would reproduce the original defect in the reporting: an
169
+ * operator reading "out of date" about a guard they deliberately hardened
170
+ * would fix the wrong thing.
171
+ */
172
+ | "host-ahead" | "overwritten" | "appended" | "merged" | "created";
162
173
  readonly linesAdded?: number;
174
+ /**
175
+ * Operator-readable explanation of a non-obvious outcome, shown verbatim.
176
+ *
177
+ * Carries the "why" for `host-ahead`, where the path alone tells an operator
178
+ * nothing actionable and the whole point is to say what would have been lost.
179
+ */
180
+ readonly note?: string;
163
181
  }
164
182
  /**
165
183
  * Counters for operation summary
@@ -169,6 +187,8 @@ export interface OperationCounters {
169
187
  skipped: number;
170
188
  /** Managed files left out of date because this apply could not overwrite them. */
171
189
  stale: number;
190
+ /** Lisa-owned artifacts preserved because the installed copy may be ahead. */
191
+ hostAhead: number;
172
192
  overwritten: number;
173
193
  appended: number;
174
194
  merged: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
@@ -139,6 +139,7 @@ export function createInitialCounters() {
139
139
  copied: 0,
140
140
  skipped: 0,
141
141
  stale: 0,
142
+ hostAhead: 0,
142
143
  overwritten: 0,
143
144
  appended: 0,
144
145
  merged: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The capability marker a Lisa-owned guard uses to declare what it defends
3
+ * against, so refresh can compare two copies by *what they stop* rather than by
4
+ * bytes.
5
+ *
6
+ * A byte comparison answers "are these the same?" and nothing else. It cannot
7
+ * answer the only question refresh actually needs answered — "is the host's copy
8
+ * behind mine, or ahead of it?" — and guessing "behind" is how a host's stronger
9
+ * guard gets silently replaced by a weaker upstream one.
10
+ *
11
+ * The marker is one comment line in the guard's header:
12
+ *
13
+ * ```
14
+ * # lisa-guard-capabilities: no-verify-abbrev, hookspath-allowlist, config-env
15
+ * ```
16
+ *
17
+ * Deliberately a *set*, not a version number. A monotonic `lisa-guard-version: N`
18
+ * is easier to compare but lies the moment two hosts harden different vectors
19
+ * independently — which is exactly what happened: `propswapllc/frontend` closed
20
+ * the `GIT_CONFIG_KEY_<n>` bypass while `tunnl-infrastructure` closed a different
21
+ * one. Both would have bumped to the same number and one would have silently
22
+ * won. Two sets stay two sets, and set difference names precisely what each copy
23
+ * has that the other does not.
24
+ *
25
+ * Only the header window is scanned. A guard whose *body* greps for the marker
26
+ * string, or a test fixture that quotes one, must not be read as declaring it.
27
+ * @module core/guard-capabilities
28
+ */
29
+ /** A guard's declared capability set, normalised for comparison. */
30
+ export type CapabilitySet = ReadonlySet<string>;
31
+ /**
32
+ * Read the capabilities a guard's header declares.
33
+ *
34
+ * Absence is not an error and not a failure signal — it means the artifact has
35
+ * not been enrolled, and every caller treats that as "no declaration made"
36
+ * rather than "declares nothing dangerous". That is what lets the marker roll
37
+ * out one guard at a time with no flag day.
38
+ * Scanned line by line with plain string operations rather than a regular
39
+ * expression. A header is untrusted input — it arrives from whatever repository
40
+ * the "host" copy came from — and a linear scan cannot be made to backtrack.
41
+ * @param bytes - Full contents of the guard file
42
+ * @returns The declared capabilities, lower-cased and de-duplicated
43
+ */
44
+ export declare function readGuardCapabilities(bytes: Buffer): CapabilitySet;
45
+ /**
46
+ * The capabilities the host declares that Lisa's copy does not.
47
+ *
48
+ * A non-empty result is the whole signal: the host copy defends something
49
+ * upstream does not, so overwriting it removes protection. This is set
50
+ * *difference*, not a strict-superset test, on purpose — a host that hardened
51
+ * vector A while upstream hardened vector B is not a superset of upstream, yet
52
+ * overwriting it still deletes A. Asking only "is the host a superset?" would
53
+ * clobber precisely the divergent-hardening case the set representation exists
54
+ * to handle.
55
+ * @param host - Capabilities declared by the installed copy
56
+ * @param lisa - Capabilities declared by the packaged copy
57
+ * @returns Capabilities present on the host and absent upstream, sorted
58
+ */
59
+ export declare function capabilitiesOnlyOnHost(host: CapabilitySet, lisa: CapabilitySet): readonly string[];
60
+ //# sourceMappingURL=guard-capabilities.d.ts.map