@codyswann/lisa 4.42.6 → 4.43.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 (186) hide show
  1. package/README.md +4 -0
  2. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +7 -0
  3. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +12 -2
  4. package/all/copy-overwrite/scripts/lisa-cross-worktree-guard.mjs +4 -0
  5. package/all/copy-overwrite/scripts/lisa-environment-prepare.mjs +2 -0
  6. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +31 -3
  7. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +4 -0
  8. package/all/copy-overwrite/scripts/lisa-work-item.mjs +13 -0
  9. package/dist/cli/doctor-readiness-delivery.d.ts.map +1 -1
  10. package/dist/cli/doctor-readiness-delivery.js +13 -6
  11. package/dist/cli/doctor-readiness-delivery.js.map +1 -1
  12. package/dist/cli/doctor-readiness-deploy-outcome.d.ts +9 -0
  13. package/dist/cli/doctor-readiness-deploy-outcome.d.ts.map +1 -0
  14. package/dist/cli/doctor-readiness-deploy-outcome.js +87 -0
  15. package/dist/cli/doctor-readiness-deploy-outcome.js.map +1 -0
  16. package/dist/cli/doctor-readiness-workflows.d.ts +8 -0
  17. package/dist/cli/doctor-readiness-workflows.d.ts.map +1 -1
  18. package/dist/cli/doctor-readiness-workflows.js +1 -0
  19. package/dist/cli/doctor-readiness-workflows.js.map +1 -1
  20. package/dist/configs/vitest/base.d.ts +1 -1
  21. package/dist/configs/vitest/base.d.ts.map +1 -1
  22. package/dist/configs/vitest/base.js +1 -0
  23. package/dist/configs/vitest/base.js.map +1 -1
  24. package/dist/configs/vitest/cdk.d.ts +2 -2
  25. package/dist/configs/vitest/cdk.d.ts.map +1 -1
  26. package/dist/configs/vitest/cdk.js +5 -2
  27. package/dist/configs/vitest/cdk.js.map +1 -1
  28. package/dist/configs/vitest/coverage-reports-directory.d.ts +47 -0
  29. package/dist/configs/vitest/coverage-reports-directory.d.ts.map +1 -0
  30. package/dist/configs/vitest/coverage-reports-directory.js +131 -0
  31. package/dist/configs/vitest/coverage-reports-directory.js.map +1 -0
  32. package/dist/configs/vitest/harper-fabric.d.ts +2 -2
  33. package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
  34. package/dist/configs/vitest/harper-fabric.js +5 -2
  35. package/dist/configs/vitest/harper-fabric.js.map +1 -1
  36. package/dist/configs/vitest/nestjs.d.ts +2 -2
  37. package/dist/configs/vitest/nestjs.d.ts.map +1 -1
  38. package/dist/configs/vitest/nestjs.js +5 -2
  39. package/dist/configs/vitest/nestjs.js.map +1 -1
  40. package/dist/configs/vitest/phaser.d.ts +2 -2
  41. package/dist/configs/vitest/phaser.d.ts.map +1 -1
  42. package/dist/configs/vitest/phaser.js +5 -2
  43. package/dist/configs/vitest/phaser.js.map +1 -1
  44. package/dist/configs/vitest/typescript.d.ts +2 -2
  45. package/dist/configs/vitest/typescript.d.ts.map +1 -1
  46. package/dist/configs/vitest/typescript.js +5 -2
  47. package/dist/configs/vitest/typescript.js.map +1 -1
  48. package/dist/core/deploy-release-dependence.d.ts +72 -0
  49. package/dist/core/deploy-release-dependence.d.ts.map +1 -0
  50. package/dist/core/deploy-release-dependence.js +236 -0
  51. package/dist/core/deploy-release-dependence.js.map +1 -0
  52. package/dist/core/github-actions-condition.d.ts +78 -0
  53. package/dist/core/github-actions-condition.d.ts.map +1 -0
  54. package/dist/core/github-actions-condition.js +307 -0
  55. package/dist/core/github-actions-condition.js.map +1 -0
  56. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  57. package/dist/core/lisa-owned-hash-ledger.js +24 -0
  58. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  59. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  60. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  61. package/dist/core/upstream-evidence-manifest.js +45 -24
  62. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  63. package/dist/migrations/deploy-outcome-guard-edit.d.ts +11 -0
  64. package/dist/migrations/deploy-outcome-guard-edit.d.ts.map +1 -0
  65. package/dist/migrations/deploy-outcome-guard-edit.js +184 -0
  66. package/dist/migrations/deploy-outcome-guard-edit.js.map +1 -0
  67. package/dist/migrations/deploy-outcome-guard-yaml.d.ts +91 -0
  68. package/dist/migrations/deploy-outcome-guard-yaml.d.ts.map +1 -0
  69. package/dist/migrations/deploy-outcome-guard-yaml.js +180 -0
  70. package/dist/migrations/deploy-outcome-guard-yaml.js.map +1 -0
  71. package/dist/migrations/ensure-deploy-outcome-guard.d.ts +41 -0
  72. package/dist/migrations/ensure-deploy-outcome-guard.d.ts.map +1 -0
  73. package/dist/migrations/ensure-deploy-outcome-guard.js +238 -0
  74. package/dist/migrations/ensure-deploy-outcome-guard.js.map +1 -0
  75. package/dist/migrations/index.d.ts +1 -0
  76. package/dist/migrations/index.d.ts.map +1 -1
  77. package/dist/migrations/index.js +3 -0
  78. package/dist/migrations/index.js.map +1 -1
  79. package/expo/copy-overwrite/scripts/bdd/baseline.mjs +26 -7
  80. package/package.json +5 -4
  81. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  84. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  85. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  86. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  87. package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  88. package/plugins/lisa/hooks/threshold-ratchet.mjs +6 -0
  89. package/plugins/lisa/hooks/worktree-binding-guard.mjs +31 -3
  90. package/plugins/lisa/scripts/design-variable-ids.mjs +2 -0
  91. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  92. package/plugins/lisa/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  93. package/plugins/lisa/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  94. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  95. package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  96. package/plugins/lisa-agy/plugin.json +1 -1
  97. package/plugins/lisa-agy/scripts/design-variable-ids.mjs +2 -0
  98. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  99. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  100. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  101. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  102. package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  103. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  106. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +6 -0
  110. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +31 -3
  111. package/plugins/lisa-copilot/scripts/design-variable-ids.mjs +2 -0
  112. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  113. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  114. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  115. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  116. package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  117. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +6 -0
  119. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +31 -3
  120. package/plugins/lisa-cursor/scripts/design-variable-ids.mjs +2 -0
  121. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  122. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  123. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  124. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  125. package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  126. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  127. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  128. package/plugins/lisa-expo-agy/plugin.json +1 -1
  129. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  131. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  133. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  134. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  138. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  139. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  141. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  143. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  144. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  146. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  147. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  148. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  149. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  151. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  152. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  153. package/plugins/lisa-rails-agy/plugin.json +1 -1
  154. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  156. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  157. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  158. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  159. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  160. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  161. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  162. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  163. package/plugins/lisa-wiki/scripts/ingest-git.mjs +2 -0
  164. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  165. package/plugins/lisa-wiki-agy/scripts/ingest-git.mjs +2 -0
  166. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  167. package/plugins/lisa-wiki-copilot/scripts/ingest-git.mjs +2 -0
  168. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  169. package/plugins/lisa-wiki-cursor/scripts/ingest-git.mjs +2 -0
  170. package/plugins/src/base/hooks/threshold-ratchet.mjs +6 -0
  171. package/plugins/src/base/hooks/worktree-binding-guard.mjs +31 -3
  172. package/plugins/src/base/scripts/design-variable-ids.mjs +2 -0
  173. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
  174. package/plugins/src/base/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
  175. package/plugins/src/base/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
  176. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
  177. package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
  178. package/plugins/src/wiki/scripts/ingest-git.mjs +2 -0
  179. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +6 -0
  180. package/scripts/check-merge-driver-registration.mjs +2 -0
  181. package/scripts/check-probe-absence-direction.mjs +611 -0
  182. package/scripts/check-test-case-executed.mjs +184 -0
  183. package/scripts/install-generated-artifact-merge-driver.mjs +7 -1
  184. package/scripts/lib/shell-guard-refusal-coverage.mjs +18 -19
  185. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +6 -0
  186. package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +4 -0
@@ -0,0 +1,11 @@
1
+ import { type DependenceJob } from "../core/deploy-release-dependence.js";
2
+ /** The guard step's `name:`, identical in every shipped deploy workflow. */
3
+ export declare const GUARD_STEP_NAME = "\uD83D\uDEA8 Confirm the release shipped";
4
+ /**
5
+ * Rewrite one job's condition so a failed release no longer skips it.
6
+ * @param job - The deploy job as parsed
7
+ * @param release - The upstream release job's id
8
+ * @returns The new condition text, or null when it must be left to a human
9
+ */
10
+ export declare function rewrittenCondition(job: DependenceJob, release: string): string | null;
11
+ //# sourceMappingURL=deploy-outcome-guard-edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-outcome-guard-edit.d.ts","sourceRoot":"","sources":["../../src/migrations/deploy-outcome-guard-edit.ts"],"names":[],"mappings":"AAgDA,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,sCAAsC,CAAC;AAE9C,4EAA4E;AAC5E,eAAO,MAAM,eAAe,6CAAmC,CAAC;AAqGhE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CASf"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * The text surgery behind `ensure-deploy-outcome-guard` (CodySwannGT/lisa#3740).
3
+ *
4
+ * Separated from the migration class so the editing rules can be driven
5
+ * directly by tests: what this decides is which host workflows it is willing to
6
+ * touch, and that judgement is the risky half of the migration, not the file
7
+ * I/O around it.
8
+ *
9
+ * ## Two edits, or neither
10
+ *
11
+ * Repairing this defect means suppressing GitHub's implicit `success()` so the
12
+ * deploy job RUNS when the release failed, and adding a first step that fails
13
+ * it loudly. Doing only the first would be **worse than the bug**: a deploy job
14
+ * that runs on a failed release with nothing checking the release result would
15
+ * attempt to ship. So the two edits are produced together or the file is left
16
+ * alone, and a caller cannot obtain one without the other.
17
+ *
18
+ * ## The guard body is read, never written
19
+ *
20
+ * The step this inserts is lifted verbatim out of Lisa's own shipped deploy
21
+ * template. Hardcoding it here would create a fourth copy of a body that
22
+ * `tests/integration/deploy-outcome-guard.test.ts` exists to keep identical
23
+ * across three — and a copy that test cannot see, which is the shape this
24
+ * repository keeps paying for. If the template cannot be read, the migration
25
+ * declines rather than writing something it made up.
26
+ *
27
+ * ## What it refuses
28
+ *
29
+ * A condition it cannot rewrite without guessing is declined, and the doctor
30
+ * finding reports it instead. Two rewrites are accepted and no others:
31
+ *
32
+ * 1. A condition that never mentions the release result — safe to wrap, since
33
+ * `!cancelled() && (original)` cannot change what the original decides.
34
+ * 2. A flat top-level `&&` chain containing `needs.<release>.result ==
35
+ * 'success'` — the conjunct is dropped and `!cancelled()` prepended.
36
+ *
37
+ * Anything else, including that conjunct nested inside an `||`, is left to a
38
+ * human. Dropping a term from inside a disjunction changes what the condition
39
+ * means in ways this cannot check.
40
+ *
41
+ * ## The self-check
42
+ *
43
+ * Every rewrite is fed back through the same analysis that found the defect,
44
+ * and a rewrite that does not come back `independent` is discarded. A repair
45
+ * that does not repair is refused rather than written.
46
+ * @module migrations/deploy-outcome-guard-edit
47
+ */
48
+ import { tokenize } from "../core/github-actions-condition.js";
49
+ import { releaseDependence, } from "../core/deploy-release-dependence.js";
50
+ /** The guard step's `name:`, identical in every shipped deploy workflow. */
51
+ export const GUARD_STEP_NAME = "🚨 Confirm the release shipped";
52
+ /** The status function that suppresses GitHub's implicit `success()`. */
53
+ const SUPPRESSOR = "!cancelled()";
54
+ /** Tokens that take no space before them when a condition is re-rendered. */
55
+ const NO_SPACE_BEFORE = new Set([")", ",", "=="]);
56
+ /** Tokens that take no space after them when a condition is re-rendered. */
57
+ const NO_SPACE_AFTER = new Set(["(", "!", "=="]);
58
+ /**
59
+ * Strip a `${{ }}` wrapper and surrounding whitespace from a condition.
60
+ * @param condition - The raw `if:` text
61
+ * @returns The bare expression
62
+ */
63
+ function bareExpression(condition) {
64
+ return condition
65
+ .trim()
66
+ .replace(/^\$\{\{/, "")
67
+ .replace(/\}\}$/, "")
68
+ .trim();
69
+ }
70
+ /**
71
+ * Re-render a token run as expression text.
72
+ * @param tokens - The tokens, in order
73
+ * @returns Readable expression text
74
+ */
75
+ function render(tokens) {
76
+ return tokens
77
+ .reduce((text, token, index) => {
78
+ const previous = tokens[index - 1];
79
+ const glued = index === 0 ||
80
+ NO_SPACE_BEFORE.has(token) ||
81
+ (previous !== undefined && NO_SPACE_AFTER.has(previous));
82
+ return glued ? text + token : `${text} ${token}`;
83
+ }, "")
84
+ .replace(/ == /g, " == ")
85
+ .trim();
86
+ }
87
+ /**
88
+ * Split a condition into its top-level `&&` conjuncts.
89
+ * @param tokens - The tokenized condition
90
+ * @returns The conjuncts, or null when the expression is not a flat `&&` chain
91
+ */
92
+ function topLevelConjuncts(tokens) {
93
+ const split = tokens.reduce((state, token) => {
94
+ const depth = token === "("
95
+ ? state.depth + 1
96
+ : token === ")"
97
+ ? state.depth - 1
98
+ : state.depth;
99
+ if (state.depth === 0 && token === "||") {
100
+ return { ...state, depth, flat: false };
101
+ }
102
+ if (state.depth === 0 && token === "&&") {
103
+ return {
104
+ depth,
105
+ parts: [...state.parts, state.current],
106
+ current: [],
107
+ flat: state.flat,
108
+ };
109
+ }
110
+ return { ...state, depth, current: [...state.current, token] };
111
+ }, { depth: 0, parts: [], current: [], flat: true });
112
+ if (!split.flat)
113
+ return null;
114
+ return [...split.parts, split.current].filter(part => part.length > 0);
115
+ }
116
+ /**
117
+ * Whether a conjunct is exactly `needs.<release>.result == 'success'`.
118
+ * @param conjunct - One top-level conjunct's tokens
119
+ * @param release - The upstream release job's id
120
+ * @returns True when the conjunct is the hand-written success gate
121
+ */
122
+ function isReleaseSuccessGate(conjunct, release) {
123
+ return (conjunct.length === 3 &&
124
+ conjunct[0] === `needs.${release}.result` &&
125
+ conjunct[1] === "==" &&
126
+ conjunct[2] === "'success'");
127
+ }
128
+ /**
129
+ * Rewrite one job's condition so a failed release no longer skips it.
130
+ * @param job - The deploy job as parsed
131
+ * @param release - The upstream release job's id
132
+ * @returns The new condition text, or null when it must be left to a human
133
+ */
134
+ export function rewrittenCondition(job, release) {
135
+ const rewritten = proposedCondition(job, release);
136
+ if (rewritten === null)
137
+ return null;
138
+ // The self-check: a rewrite that does not actually make the job independent
139
+ // of the release result is discarded rather than written.
140
+ return releaseDependence({ ...job, ifCondition: rewritten }, release).kind ===
141
+ "independent"
142
+ ? rewritten
143
+ : null;
144
+ }
145
+ /**
146
+ * Propose a rewrite, before the self-check decides whether to keep it.
147
+ * @param job - The deploy job as parsed
148
+ * @param release - The upstream release job's id
149
+ * @returns The candidate condition, or null when no rule applies
150
+ */
151
+ function proposedCondition(job, release) {
152
+ const original = bareExpression(job.ifCondition);
153
+ if (original === "")
154
+ return SUPPRESSOR;
155
+ const gate = `needs.${release}.result`;
156
+ if (!original.includes(gate)) {
157
+ return `${SUPPRESSOR} && (${original})`;
158
+ }
159
+ const conjuncts = topLevelConjuncts(tryTokenize(original) ?? []);
160
+ if (conjuncts === null || conjuncts.length === 0)
161
+ return null;
162
+ const kept = conjuncts.filter(conjunct => !isReleaseSuccessGate(conjunct, release));
163
+ // Every remaining conjunct must be free of the release result: one nested
164
+ // inside parentheses is exactly the case this refuses to reason about.
165
+ if (kept.length === conjuncts.length)
166
+ return null;
167
+ if (kept.some(conjunct => conjunct.some(token => token === gate)))
168
+ return null;
169
+ return [SUPPRESSOR, ...kept.map(render)].join(" && ");
170
+ }
171
+ /**
172
+ * Tokenize, returning null instead of throwing on an unsupported expression.
173
+ * @param expression - The bare expression text
174
+ * @returns The tokens, or null when the expression cannot be read
175
+ */
176
+ function tryTokenize(expression) {
177
+ try {
178
+ return tokenize(expression);
179
+ }
180
+ catch {
181
+ return null;
182
+ }
183
+ }
184
+ //# sourceMappingURL=deploy-outcome-guard-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-outcome-guard-edit.js","sourceRoot":"","sources":["../../src/migrations/deploy-outcome-guard-edit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Line-level editing of a host's `deploy.yml` for `ensure-deploy-outcome-guard`
3
+ * (CodySwannGT/lisa#3740).
4
+ *
5
+ * The file being edited is one the host OWNS — Lisa seeded it once and has
6
+ * never overwritten it since — so the discipline here is the opposite of a
7
+ * template copy: touch the two constructs that carry the defect and leave every
8
+ * other byte, including comments and formatting, exactly as the host left it.
9
+ * That is why this works on lines rather than by re-serializing a parsed
10
+ * document; a YAML round-trip would silently reformat a file somebody reviewed.
11
+ *
12
+ * Every locator returns null rather than a best guess. A workflow whose shape
13
+ * this cannot resolve unambiguously is not edited, and the doctor finding
14
+ * reports it instead — a migration that half-recognises a file and edits it
15
+ * anyway is worse than one that declines.
16
+ * @module migrations/deploy-outcome-guard-yaml
17
+ */
18
+ /** Where one job's block sits in the file, and how its keys are indented. */
19
+ export interface JobBlock {
20
+ /** Index of the `<job-id>:` line. */
21
+ readonly start: number;
22
+ /** Index one past the job's last line. */
23
+ readonly end: number;
24
+ /** Column the job's own keys (`if:`, `steps:`) begin at. */
25
+ readonly keyIndent: number;
26
+ }
27
+ /**
28
+ * Locate one job's block by its id.
29
+ * @param lines - The workflow file's lines
30
+ * @param jobId - The job's key in the `jobs:` map
31
+ * @returns Where the job sits, or null when it cannot be located exactly once
32
+ */
33
+ export declare function locateJob(lines: readonly string[], jobId: string): JobBlock | null;
34
+ /** A span of lines to replace, and the indentation its replacement takes. */
35
+ export interface KeySpan {
36
+ /** Index of the key's first line. */
37
+ readonly start: number;
38
+ /** Index one past the key's last line. */
39
+ readonly end: number;
40
+ }
41
+ /**
42
+ * Locate one top-level key of a job, including any continuation lines.
43
+ * @param lines - The workflow file's lines
44
+ * @param block - The job's block
45
+ * @param key - The key name, e.g. `if` or `steps`
46
+ * @returns The key's span, or null when the job does not declare it
47
+ */
48
+ export declare function locateKey(lines: readonly string[], block: JobBlock, key: string): KeySpan | null;
49
+ /**
50
+ * Locate the first entry of a job's `steps:` list.
51
+ * @param lines - The workflow file's lines
52
+ * @param block - The job's block
53
+ * @returns The first step's line index and indentation, or null when absent
54
+ */
55
+ export declare function locateFirstStep(lines: readonly string[], block: JobBlock): {
56
+ readonly index: number;
57
+ readonly indent: number;
58
+ } | null;
59
+ /**
60
+ * Extract the guard step from a shipped Lisa deploy template.
61
+ *
62
+ * Read rather than reproduced: the body is kept byte-identical across the
63
+ * shipped templates by a test, and a copy inside this module would be a fourth
64
+ * one that test cannot see.
65
+ * @param template - The template workflow's source
66
+ * @param stepName - The guard step's `name:` value
67
+ * @returns The step's lines, de-indented to column zero, or null when absent
68
+ */
69
+ export declare function extractGuardStep(template: string, stepName: string): readonly string[] | null;
70
+ /**
71
+ * Render the replacement `if:` block for a job.
72
+ * @param condition - The rewritten condition text
73
+ * @param keyIndent - Column the job's keys begin at
74
+ * @returns The lines to write in place of the job's old `if:`
75
+ */
76
+ export declare function renderCondition(condition: string, keyIndent: number): readonly string[];
77
+ /**
78
+ * Apply both edits to one workflow's source, or return null.
79
+ *
80
+ * The two are produced together on purpose. Suppressing the implicit
81
+ * `success()` without inserting the guard would leave a deploy job that RUNS on
82
+ * a failed release with nothing checking the release result — it would attempt
83
+ * to ship, which is worse than the silence this repairs.
84
+ * @param source - The host workflow's source
85
+ * @param jobId - The deploy job to repair
86
+ * @param condition - The rewritten condition text
87
+ * @param guardStep - The guard step's lines, de-indented to column zero
88
+ * @returns The edited source, or null when the file could not be edited safely
89
+ */
90
+ export declare function applyGuardEdits(source: string, jobId: string, condition: string, guardStep: readonly string[]): string | null;
91
+ //# sourceMappingURL=deploy-outcome-guard-yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-outcome-guard-yaml.d.ts","sourceRoot":"","sources":["../../src/migrations/deploy-outcome-guard-yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,MAAM,GACZ,QAAQ,GAAG,IAAI,CAiBjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,OAAO;IACtB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,GACV,OAAO,GAAG,IAAI,CAahB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,QAAQ,GACd;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAS5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,SAAS,MAAM,EAAE,GAAG,IAAI,CAc1B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,SAAS,MAAM,EAAE,CAkBnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,MAAM,GAAG,IAAI,CA8Bf"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Line-level editing of a host's `deploy.yml` for `ensure-deploy-outcome-guard`
3
+ * (CodySwannGT/lisa#3740).
4
+ *
5
+ * The file being edited is one the host OWNS — Lisa seeded it once and has
6
+ * never overwritten it since — so the discipline here is the opposite of a
7
+ * template copy: touch the two constructs that carry the defect and leave every
8
+ * other byte, including comments and formatting, exactly as the host left it.
9
+ * That is why this works on lines rather than by re-serializing a parsed
10
+ * document; a YAML round-trip would silently reformat a file somebody reviewed.
11
+ *
12
+ * Every locator returns null rather than a best guess. A workflow whose shape
13
+ * this cannot resolve unambiguously is not edited, and the doctor finding
14
+ * reports it instead — a migration that half-recognises a file and edits it
15
+ * anyway is worse than one that declines.
16
+ * @module migrations/deploy-outcome-guard-yaml
17
+ */
18
+ /**
19
+ * Indentation width of one line, or null when the line carries no content.
20
+ * @param line - The raw line
21
+ * @returns Number of leading spaces, or null for a blank or comment-only line
22
+ */
23
+ function indentOf(line) {
24
+ if (line.trim() === "")
25
+ return null;
26
+ return line.length - line.trimStart().length;
27
+ }
28
+ /**
29
+ * Locate one job's block by its id.
30
+ * @param lines - The workflow file's lines
31
+ * @param jobId - The job's key in the `jobs:` map
32
+ * @returns Where the job sits, or null when it cannot be located exactly once
33
+ */
34
+ export function locateJob(lines, jobId) {
35
+ const pattern = new RegExp(`^(\\s+)${jobId}:\\s*$`);
36
+ const starts = lines.flatMap((line, index) => pattern.test(line) ? [index] : []);
37
+ if (starts.length !== 1)
38
+ return null;
39
+ const start = starts[0] ?? 0;
40
+ const jobIndent = indentOf(lines[start] ?? "") ?? 0;
41
+ const body = lines
42
+ .slice(start + 1)
43
+ .findIndex(line => (indentOf(line) ?? jobIndent + 1) <= jobIndent);
44
+ const end = body === -1 ? lines.length : start + 1 + body;
45
+ const keyIndent = lines
46
+ .slice(start + 1, end)
47
+ .map(indentOf)
48
+ .find((indent) => indent !== null);
49
+ return keyIndent === undefined ? null : { start, end, keyIndent };
50
+ }
51
+ /**
52
+ * Locate one top-level key of a job, including any continuation lines.
53
+ * @param lines - The workflow file's lines
54
+ * @param block - The job's block
55
+ * @param key - The key name, e.g. `if` or `steps`
56
+ * @returns The key's span, or null when the job does not declare it
57
+ */
58
+ export function locateKey(lines, block, key) {
59
+ const pattern = new RegExp(`^\\s{${block.keyIndent}}${key}:`);
60
+ const found = lines
61
+ .slice(block.start, block.end)
62
+ .findIndex(line => pattern.test(line));
63
+ if (found === -1)
64
+ return null;
65
+ const start = block.start + found;
66
+ const rest = lines
67
+ .slice(start + 1, block.end)
68
+ .findIndex(line => (indentOf(line) ?? block.keyIndent + 1) <= block.keyIndent);
69
+ return { start, end: rest === -1 ? block.end : start + 1 + rest };
70
+ }
71
+ /**
72
+ * Locate the first entry of a job's `steps:` list.
73
+ * @param lines - The workflow file's lines
74
+ * @param block - The job's block
75
+ * @returns The first step's line index and indentation, or null when absent
76
+ */
77
+ export function locateFirstStep(lines, block) {
78
+ const steps = locateKey(lines, block, "steps");
79
+ if (steps === null)
80
+ return null;
81
+ const offset = lines
82
+ .slice(steps.start + 1, steps.end)
83
+ .findIndex(line => line.trimStart().startsWith("- "));
84
+ if (offset === -1)
85
+ return null;
86
+ const index = steps.start + 1 + offset;
87
+ return { index, indent: indentOf(lines[index] ?? "") ?? 0 };
88
+ }
89
+ /**
90
+ * Extract the guard step from a shipped Lisa deploy template.
91
+ *
92
+ * Read rather than reproduced: the body is kept byte-identical across the
93
+ * shipped templates by a test, and a copy inside this module would be a fourth
94
+ * one that test cannot see.
95
+ * @param template - The template workflow's source
96
+ * @param stepName - The guard step's `name:` value
97
+ * @returns The step's lines, de-indented to column zero, or null when absent
98
+ */
99
+ export function extractGuardStep(template, stepName) {
100
+ const lines = template.split("\n");
101
+ const start = lines.findIndex(line => line.trimStart().startsWith(`- name: ${stepName}`));
102
+ if (start === -1)
103
+ return null;
104
+ const indent = indentOf(lines[start] ?? "") ?? 0;
105
+ const after = lines
106
+ .slice(start + 1)
107
+ .findIndex(line => (indentOf(line) ?? indent + 1) <= indent);
108
+ const end = after === -1 ? lines.length : start + 1 + after;
109
+ return lines
110
+ .slice(start, end)
111
+ .map(line => (line.trim() === "" ? "" : line.slice(indent)));
112
+ }
113
+ /**
114
+ * Render the replacement `if:` block for a job.
115
+ * @param condition - The rewritten condition text
116
+ * @param keyIndent - Column the job's keys begin at
117
+ * @returns The lines to write in place of the job's old `if:`
118
+ */
119
+ export function renderCondition(condition, keyIndent) {
120
+ const pad = " ".repeat(keyIndent);
121
+ const inner = " ".repeat(keyIndent + 2);
122
+ return [
123
+ `${pad}# \`!cancelled()\` suppresses the implicit \`success()\` GitHub would`,
124
+ `${pad}# otherwise AND onto this condition. Without it a failed release`,
125
+ `${pad}# SKIPPED this job, and a skipped job renders as neutral and counts`,
126
+ `${pad}# as a satisfied required check — so a deploy that never happened`,
127
+ `${pad}# left no signal at all. The release result is read by the guard STEP`,
128
+ `${pad}# below, which fails this job loudly instead (#3467, applied by`,
129
+ `${pad}# Lisa's ensure-deploy-outcome-guard migration for #3740).`,
130
+ `${pad}if: >-`,
131
+ ...condition
132
+ .split(" && ")
133
+ .map((part, index, parts) => index === parts.length - 1 ? `${inner}${part}` : `${inner}${part} &&`),
134
+ ];
135
+ }
136
+ /**
137
+ * Apply both edits to one workflow's source, or return null.
138
+ *
139
+ * The two are produced together on purpose. Suppressing the implicit
140
+ * `success()` without inserting the guard would leave a deploy job that RUNS on
141
+ * a failed release with nothing checking the release result — it would attempt
142
+ * to ship, which is worse than the silence this repairs.
143
+ * @param source - The host workflow's source
144
+ * @param jobId - The deploy job to repair
145
+ * @param condition - The rewritten condition text
146
+ * @param guardStep - The guard step's lines, de-indented to column zero
147
+ * @returns The edited source, or null when the file could not be edited safely
148
+ */
149
+ export function applyGuardEdits(source, jobId, condition, guardStep) {
150
+ const lines = source.split("\n");
151
+ const block = locateJob(lines, jobId);
152
+ if (block === null)
153
+ return null;
154
+ const step = locateFirstStep(lines, block);
155
+ if (step === null)
156
+ return null;
157
+ const existing = locateKey(lines, block, "if");
158
+ const guardLines = guardStep.map(line => line === "" ? "" : `${" ".repeat(step.indent)}${line}`);
159
+ const conditionLines = renderCondition(condition, block.keyIndent);
160
+ const declared = existing ?? {
161
+ start: block.start + 1,
162
+ end: block.start + 1,
163
+ };
164
+ const withGuard = [
165
+ ...lines.slice(0, step.index),
166
+ ...guardLines,
167
+ ...lines.slice(step.index),
168
+ ];
169
+ // Inserting the guard shifts every index at or after it. `steps:` normally
170
+ // follows `if:`, so the span is usually untouched — but YAML mapping keys
171
+ // carry no required order, and a host that wrote them the other way round
172
+ // would otherwise have its file corrupted rather than declined.
173
+ const shift = step.index <= declared.start ? guardLines.length : 0;
174
+ return [
175
+ ...withGuard.slice(0, declared.start + shift),
176
+ ...conditionLines,
177
+ ...withGuard.slice(declared.end + shift),
178
+ ].join("\n");
179
+ }
180
+ //# sourceMappingURL=deploy-outcome-guard-yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-outcome-guard-yaml.js","sourceRoot":"","sources":["../../src/migrations/deploy-outcome-guard-yaml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { Migration, MigrationContext, MigrationResult } from "./migration.interface.js";
2
+ /**
3
+ * Bring an already-seeded deploy workflow up to the #3467 outcome contract.
4
+ */
5
+ export declare class EnsureDeployOutcomeGuardMigration implements Migration {
6
+ readonly name = "ensure-deploy-outcome-guard";
7
+ readonly description = "Make an already-seeded deploy job fail loudly instead of skipping silently when its release does not succeed";
8
+ /**
9
+ * Whether any host deploy workflow can be repaired.
10
+ * @param ctx - Migration context
11
+ * @returns True when at least one workflow would change
12
+ */
13
+ applies(ctx: MigrationContext): Promise<boolean>;
14
+ /**
15
+ * Rewrite every repairable deploy workflow.
16
+ * @param ctx - Migration context
17
+ * @returns Applied or no-op result
18
+ */
19
+ apply(ctx: MigrationContext): Promise<MigrationResult>;
20
+ /**
21
+ * Work out which host workflows this run would rewrite.
22
+ * @param ctx - Migration context
23
+ * @returns One entry per workflow that can be repaired
24
+ */
25
+ private plan;
26
+ /**
27
+ * Plan the repair of one workflow.
28
+ * @param ctx - Migration context
29
+ * @param workflow - The parsed workflow
30
+ * @param guardStep - The guard step's lines
31
+ * @returns The planned edit, or null when nothing here can be repaired
32
+ */
33
+ private planOne;
34
+ /**
35
+ * Read the guard step out of Lisa's own shipped deploy template.
36
+ * @param lisaDir - Lisa installation directory
37
+ * @returns The step's lines, or null when the template cannot be read
38
+ */
39
+ private readGuardStep;
40
+ }
41
+ //# sourceMappingURL=ensure-deploy-outcome-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-deploy-outcome-guard.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-deploy-outcome-guard.ts"],"names":[],"mappings":"AAgEA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAiJlC;;GAEG;AACH,qBAAa,iCAAkC,YAAW,SAAS;IACjE,QAAQ,CAAC,IAAI,iCAAiC;IAC9C,QAAQ,CAAC,WAAW,kHAC6F;IAEjH;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAItD;;;;OAIG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAoB5D;;;;OAIG;YACW,IAAI;IAelB;;;;;;OAMG;YACW,OAAO;IAsBrB;;;;OAIG;YACW,aAAa;CAa5B"}