@codyswann/lisa 3.11.9 → 3.12.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.
- package/all/copy-overwrite/scripts/lisa-command-envelope.mjs +10 -0
- package/all/copy-overwrite/scripts/lisa-destructive-guard.mjs +267 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +7 -2
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/rules/eager/reset-seed-coverage.md +2 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +2 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +2 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/eager/reset-seed-coverage.md +2 -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
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-evidence-manifest.d.ts","sourceRoot":"","sources":["../../src/core/upstream-evidence-manifest.ts"],"names":[],"mappings":"AACA,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"upstream-evidence-manifest.d.ts","sourceRoot":"","sources":["../../src/core/upstream-evidence-manifest.ts"],"names":[],"mappings":"AACA,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8yEpE,CAAC;AAEL,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAyiOjE,CAAC;AAEL,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAk6I/D,CAAC"}
|
|
@@ -4,7 +4,8 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
4
4
|
"all/copy-contents/.gitattributes": "9d3831007e681186a3673e1037ef3fc82980cab2fc04e27c0868e562912c9e9f",
|
|
5
5
|
"all/copy-contents/gitignore": "8104ccd32d7e6137cae706511c5037d11d6b6045b8e2a9bb7b3a48a81c053cfa",
|
|
6
6
|
"all/copy-overwrite/scripts/check-state-classification.mjs": "ac539349c86a7b5b0083ec0e6aaf0eb4c3e40fbd4dc31511c6460a2bad7af9a5",
|
|
7
|
-
"all/copy-overwrite/scripts/lisa-command-envelope.mjs": "
|
|
7
|
+
"all/copy-overwrite/scripts/lisa-command-envelope.mjs": "6b198f088cd3cd862d4357fbb069b1ad5d3afeb7be594f2bf1ff603b092a8354",
|
|
8
|
+
"all/copy-overwrite/scripts/lisa-destructive-guard.mjs": "4fb431a3c58f443e303d613934d023d9e95ddae5c42710bb7009f7c080c82a64",
|
|
8
9
|
"all/copy-overwrite/scripts/lisa-enforcement-fallback.sh": "3049c2f1051c7c02dd60b62355964651e1565ebe6bdcb5e37634cd686cf1deb1",
|
|
9
10
|
"all/copy-overwrite/scripts/lisa-floor-collisions.mjs": "2d11968f6852ab745cba939de03c6d6cb5f413975d2cfc8fc447bdd0b218e91a",
|
|
10
11
|
"all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh": "22184f1dd1b96e818741bd4ae659446299535d7ce594817155edebac4172a7ba",
|
|
@@ -389,7 +390,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
389
390
|
"plugins/src/base/rules/eager/rejection-detection.md": "e0a8e74f9b4eec378f7cc62072153b1a968af0e5e7a1af9473ab476d41c30211",
|
|
390
391
|
"plugins/src/base/rules/eager/repo-scope-split.md": "a1702d6d0e9d80abc118b7c32a97ba9a988b195d86363b2d7a35e71752d9bd2e",
|
|
391
392
|
"plugins/src/base/rules/eager/report-actionability.md": "7c4d5b2bea93b7cd5bb8694fa7c06f4ca8f94a19eb17832f1ccc5354b0207163",
|
|
392
|
-
"plugins/src/base/rules/eager/reset-seed-coverage.md": "
|
|
393
|
+
"plugins/src/base/rules/eager/reset-seed-coverage.md": "ff47c2813439e03e5cd559d7bf32cc4c7ae83544fc5616db6b9ee0e5675b6ee4",
|
|
393
394
|
"plugins/src/base/rules/eager/security-audit-handling.md": "1f6effe92be66736a0c9fab634c5fdd6ad1e789865faa67bb783c67ddb4ad490",
|
|
394
395
|
"plugins/src/base/rules/eager/session-status-updates.md": "bbb42e86b8eb68334b8b9c0bc70beb4895a54015e097bf745fc92156b3e24766",
|
|
395
396
|
"plugins/src/base/rules/eager/settled-decisions.md": "f5339c81b8d29f628830e6147314cbb941b9d9e6d87c3834d8fe7adc4b46ef4c",
|
|
@@ -1328,6 +1329,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
|
|
|
1328
1329
|
"all/copy-contents/gitignore": true,
|
|
1329
1330
|
"all/copy-overwrite/scripts/check-state-classification.mjs": true,
|
|
1330
1331
|
"all/copy-overwrite/scripts/lisa-command-envelope.mjs": true,
|
|
1332
|
+
"all/copy-overwrite/scripts/lisa-destructive-guard.mjs": true,
|
|
1331
1333
|
"all/copy-overwrite/scripts/lisa-enforcement-fallback.sh": true,
|
|
1332
1334
|
"all/copy-overwrite/scripts/lisa-floor-collisions.mjs": true,
|
|
1333
1335
|
"all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh": true,
|
|
@@ -1407,6 +1409,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
|
|
|
1407
1409
|
"cdk/package-lisa/package.lisa.json": true,
|
|
1408
1410
|
"commitlint.config.cjs": true,
|
|
1409
1411
|
"docs/bdd-coverage-schema.md": true,
|
|
1412
|
+
"docs/design/reset-production-absence.md": true,
|
|
1410
1413
|
"docs/design/uat-acceptance-verification-gate.md": true,
|
|
1411
1414
|
"docs/kane-cli-integration.md": true,
|
|
1412
1415
|
"docs/kane-cli-pilot.example.json": true,
|
|
@@ -7717,6 +7720,8 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
|
|
|
7717
7720
|
"tests/unit/scripts/command-envelope.test.ts": true,
|
|
7718
7721
|
"tests/unit/scripts/cross-pollinate.test.ts": true,
|
|
7719
7722
|
"tests/unit/scripts/cursor-artifact-helpers.ts": true,
|
|
7723
|
+
"tests/unit/scripts/destructive-production-guard.test.ts": true,
|
|
7724
|
+
"tests/unit/scripts/destructive-production-unreachable.test.ts": true,
|
|
7720
7725
|
"tests/unit/scripts/detect-stale-workflow-inputs-helpers.ts": true,
|
|
7721
7726
|
"tests/unit/scripts/detect-stale-workflow-inputs.test.ts": true,
|
|
7722
7727
|
"tests/unit/scripts/e2e-coverage.test.ts": true,
|