@codyswann/lisa 4.54.13 → 4.55.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 (578) hide show
  1. package/all/copy-contents/.gitattributes +8 -0
  2. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +78 -9
  3. package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +17 -1
  4. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +194 -7
  5. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +31 -0
  6. package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +81 -2
  7. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +293 -8
  8. package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +21 -0
  9. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +78 -15
  10. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +21 -0
  11. package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +21 -0
  12. package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +340 -0
  13. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +29 -1
  14. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +183 -1
  15. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +21 -0
  16. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +21 -1
  17. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +100 -5
  18. package/all/copy-overwrite/scripts/lisa-work-item.mjs +1085 -80
  19. package/all/create-only/.github/workflows/continuous-gates.yml +7 -0
  20. package/cdk/create-only/.github/workflows/ci.yml +7 -0
  21. package/cdk/create-only/.github/workflows/deploy.yml +21 -0
  22. package/dist/cli/apply.d.ts.map +1 -1
  23. package/dist/cli/apply.js +9 -1
  24. package/dist/cli/apply.js.map +1 -1
  25. package/dist/cli/doctor-apply-deletions.d.ts +61 -0
  26. package/dist/cli/doctor-apply-deletions.d.ts.map +1 -0
  27. package/dist/cli/doctor-apply-deletions.js +137 -0
  28. package/dist/cli/doctor-apply-deletions.js.map +1 -0
  29. package/dist/cli/doctor-cdk-preset-adoption.d.ts +15 -0
  30. package/dist/cli/doctor-cdk-preset-adoption.d.ts.map +1 -0
  31. package/dist/cli/doctor-cdk-preset-adoption.js +316 -0
  32. package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -0
  33. package/dist/cli/doctor-config-shadowing.d.ts +22 -0
  34. package/dist/cli/doctor-config-shadowing.d.ts.map +1 -0
  35. package/dist/cli/doctor-config-shadowing.js +123 -0
  36. package/dist/cli/doctor-config-shadowing.js.map +1 -0
  37. package/dist/cli/doctor-rails-deploy-intent.d.ts +15 -0
  38. package/dist/cli/doctor-rails-deploy-intent.d.ts.map +1 -0
  39. package/dist/cli/doctor-rails-deploy-intent.js +197 -0
  40. package/dist/cli/doctor-rails-deploy-intent.js.map +1 -0
  41. package/dist/cli/doctor-seeded-artifacts.d.ts +12 -0
  42. package/dist/cli/doctor-seeded-artifacts.d.ts.map +1 -0
  43. package/dist/cli/doctor-seeded-artifacts.js +54 -0
  44. package/dist/cli/doctor-seeded-artifacts.js.map +1 -0
  45. package/dist/cli/doctor-stale-banner-scan.d.ts +44 -0
  46. package/dist/cli/doctor-stale-banner-scan.d.ts.map +1 -0
  47. package/dist/cli/doctor-stale-banner-scan.js +152 -0
  48. package/dist/cli/doctor-stale-banner-scan.js.map +1 -0
  49. package/dist/cli/doctor-stale-managed-banner.d.ts +19 -0
  50. package/dist/cli/doctor-stale-managed-banner.d.ts.map +1 -0
  51. package/dist/cli/doctor-stale-managed-banner.js +198 -0
  52. package/dist/cli/doctor-stale-managed-banner.js.map +1 -0
  53. package/dist/cli/doctor.d.ts.map +1 -1
  54. package/dist/cli/doctor.js +16 -0
  55. package/dist/cli/doctor.js.map +1 -1
  56. package/dist/cli/ui-detected-stacks.d.ts.map +1 -1
  57. package/dist/cli/ui-detected-stacks.js +3 -2
  58. package/dist/cli/ui-detected-stacks.js.map +1 -1
  59. package/dist/cli/update-check.d.ts.map +1 -1
  60. package/dist/cli/update-check.js +14 -0
  61. package/dist/cli/update-check.js.map +1 -1
  62. package/dist/cli/worktree-liveness.d.ts +5 -0
  63. package/dist/cli/worktree-liveness.d.ts.map +1 -1
  64. package/dist/cli/worktree-liveness.js +7 -6
  65. package/dist/cli/worktree-liveness.js.map +1 -1
  66. package/dist/cli/worktree-ownership.d.ts +13 -0
  67. package/dist/cli/worktree-ownership.d.ts.map +1 -1
  68. package/dist/cli/worktree-ownership.js +14 -0
  69. package/dist/cli/worktree-ownership.js.map +1 -1
  70. package/dist/configs/eslint/base.d.ts +34 -0
  71. package/dist/configs/eslint/base.d.ts.map +1 -1
  72. package/dist/configs/eslint/base.js +34 -0
  73. package/dist/configs/eslint/base.js.map +1 -1
  74. package/dist/core/apply-receipt.d.ts +65 -0
  75. package/dist/core/apply-receipt.d.ts.map +1 -1
  76. package/dist/core/apply-receipt.js +62 -2
  77. package/dist/core/apply-receipt.js.map +1 -1
  78. package/dist/core/cdk-preset-adoption.d.ts +136 -0
  79. package/dist/core/cdk-preset-adoption.d.ts.map +1 -0
  80. package/dist/core/cdk-preset-adoption.js +135 -0
  81. package/dist/core/cdk-preset-adoption.js.map +1 -0
  82. package/dist/core/config-shadowing.d.ts +36 -0
  83. package/dist/core/config-shadowing.d.ts.map +1 -1
  84. package/dist/core/config-shadowing.js +38 -0
  85. package/dist/core/config-shadowing.js.map +1 -1
  86. package/dist/core/config.d.ts +11 -0
  87. package/dist/core/config.d.ts.map +1 -1
  88. package/dist/core/config.js.map +1 -1
  89. package/dist/core/learnings-merge-driver.d.ts +10 -0
  90. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  91. package/dist/core/learnings-merge-driver.js +18 -0
  92. package/dist/core/learnings-merge-driver.js.map +1 -1
  93. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  94. package/dist/core/lisa-owned-hash-ledger.js +136 -0
  95. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  96. package/dist/core/lisa.d.ts.map +1 -1
  97. package/dist/core/lisa.js +9 -1
  98. package/dist/core/lisa.js.map +1 -1
  99. package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -5
  100. package/dist/core/nightly-e2e-guard-behavior-certificate.js +7 -7
  101. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  102. package/dist/core/ownership-header.d.ts +74 -0
  103. package/dist/core/ownership-header.d.ts.map +1 -0
  104. package/dist/core/ownership-header.js +76 -0
  105. package/dist/core/ownership-header.js.map +1 -0
  106. package/dist/core/rails-deploy-production-intent.d.ts +36 -0
  107. package/dist/core/rails-deploy-production-intent.d.ts.map +1 -0
  108. package/dist/core/rails-deploy-production-intent.js +79 -0
  109. package/dist/core/rails-deploy-production-intent.js.map +1 -0
  110. package/dist/core/stale-managed-banner.d.ts +78 -0
  111. package/dist/core/stale-managed-banner.d.ts.map +1 -0
  112. package/dist/core/stale-managed-banner.js +118 -0
  113. package/dist/core/stale-managed-banner.js.map +1 -0
  114. package/dist/core/two-channel-delivery-scan.d.ts +33 -1
  115. package/dist/core/two-channel-delivery-scan.d.ts.map +1 -1
  116. package/dist/core/two-channel-delivery-scan.js +133 -6
  117. package/dist/core/two-channel-delivery-scan.js.map +1 -1
  118. package/dist/core/two-channel-delivery.d.ts +101 -4
  119. package/dist/core/two-channel-delivery.d.ts.map +1 -1
  120. package/dist/core/two-channel-delivery.js +121 -13
  121. package/dist/core/two-channel-delivery.js.map +1 -1
  122. package/dist/core/two-channel-staleness.d.ts +137 -0
  123. package/dist/core/two-channel-staleness.d.ts.map +1 -0
  124. package/dist/core/two-channel-staleness.js +268 -0
  125. package/dist/core/two-channel-staleness.js.map +1 -0
  126. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  127. package/dist/core/upstream-evidence-manifest.js +265 -121
  128. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  129. package/dist/core/workflow-deletion-ownership.d.ts +6 -24
  130. package/dist/core/workflow-deletion-ownership.d.ts.map +1 -1
  131. package/dist/core/workflow-deletion-ownership.js +22 -42
  132. package/dist/core/workflow-deletion-ownership.js.map +1 -1
  133. package/dist/detection/detectors/cdk.d.ts.map +1 -1
  134. package/dist/detection/detectors/cdk.js +2 -1
  135. package/dist/detection/detectors/cdk.js.map +1 -1
  136. package/dist/health/template-inspection.d.ts.map +1 -1
  137. package/dist/health/template-inspection.js +2 -1
  138. package/dist/health/template-inspection.js.map +1 -1
  139. package/dist/migrations/ensure-lisa-postinstall.d.ts +16 -0
  140. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  141. package/dist/migrations/ensure-lisa-postinstall.js +16 -0
  142. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  143. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +2 -2
  144. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -1
  145. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +98 -34
  146. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -1
  147. package/dist/migrations/ensure-playwright-dedicated-caller.d.ts.map +1 -1
  148. package/dist/migrations/ensure-playwright-dedicated-caller.js +1 -0
  149. package/dist/migrations/ensure-playwright-dedicated-caller.js.map +1 -1
  150. package/dist/opencode/hooks-installer.d.ts.map +1 -1
  151. package/dist/opencode/hooks-installer.js +1 -0
  152. package/dist/opencode/hooks-installer.js.map +1 -1
  153. package/dist/opencode/plugin-templates/block-managed-file-edits.sh +78 -15
  154. package/dist/opencode/plugin-templates/block-no-verify.sh +21 -0
  155. package/dist/opencode/plugin-templates/guard-dedupe.bash +337 -0
  156. package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +242 -55
  157. package/dist/opencode/plugin-templates/parity-safety-net.sh +29 -1
  158. package/dist/strategies/package-lisa.d.ts.map +1 -1
  159. package/dist/strategies/package-lisa.js +2 -1
  160. package/dist/strategies/package-lisa.js.map +1 -1
  161. package/dist/utils/path-utils.d.ts +29 -0
  162. package/dist/utils/path-utils.d.ts.map +1 -1
  163. package/dist/utils/path-utils.js +41 -0
  164. package/dist/utils/path-utils.js.map +1 -1
  165. package/expo/create-only/.github/workflows/ci.yml +21 -0
  166. package/expo/create-only/.github/workflows/deploy.yml +28 -0
  167. package/expo/create-only/.github/workflows/maestro-e2e.yml +7 -0
  168. package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +124 -13
  169. package/expo/create-only/.github/workflows/nightly-e2e-health.yml +7 -0
  170. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +7 -0
  171. package/expo/create-only/.github/workflows/nightly-e2e-tracking.yml +8 -0
  172. package/expo/create-only/.github/workflows/playwright-e2e.yml +7 -0
  173. package/harper-fabric/copy-overwrite/.github/workflows/ci.yml +7 -0
  174. package/harper-fabric/create-only/.github/workflows/deploy.yml +14 -0
  175. package/nestjs/create-only/.github/workflows/ci.yml +14 -0
  176. package/nestjs/create-only/.github/workflows/deploy.yml +28 -0
  177. package/package.json +10 -5
  178. package/phaser/copy-overwrite/.github/workflows/ci.yml +7 -0
  179. package/plugins/lisa/.claude-plugin/plugin.json +28 -1
  180. package/plugins/lisa/.codex-plugin/hooks.json +27 -0
  181. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  182. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  183. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +27 -4
  184. package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +1 -1
  185. package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
  186. package/plugins/lisa/.codex-plugin/skills/lisa-github-verify/SKILL.md +14 -0
  187. package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +51 -0
  188. package/plugins/lisa/.codex-plugin/skills/lisa-intake/SKILL.md +1 -1
  189. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +27 -4
  190. package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  191. package/plugins/lisa/.codex-plugin/skills/lisa-jira-verify/SKILL.md +14 -0
  192. package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  193. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +55 -2
  194. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +27 -4
  195. package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +47 -30
  196. package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  197. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  198. package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +14 -0
  199. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +54 -3
  200. package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  201. package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +19 -7
  202. package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +28 -3
  203. package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +28 -3
  204. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +94 -24
  205. package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +32 -2
  206. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +4 -4
  207. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
  208. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  209. package/plugins/lisa/agents/linear-prd-intake.md +8 -8
  210. package/plugins/lisa/hooks/block-blind-automerge.sh +81 -2
  211. package/plugins/lisa/hooks/block-direct-issue-create.agy.sh +64 -16
  212. package/plugins/lisa/hooks/block-direct-issue-create.sh +293 -8
  213. package/plugins/lisa/hooks/block-instruction-file-edits.sh +21 -0
  214. package/plugins/lisa/hooks/block-managed-file-edits.sh +78 -15
  215. package/plugins/lisa/hooks/block-no-verify.sh +21 -0
  216. package/plugins/lisa/hooks/block-shell-json-parsing.sh +21 -0
  217. package/plugins/lisa/hooks/failure-signature-index.mjs +240 -5
  218. package/plugins/lisa/hooks/guard-dedupe.bash +337 -0
  219. package/plugins/lisa/hooks/inject-rules.sh +13 -5
  220. package/plugins/lisa/hooks/operational-hazards.mjs +736 -0
  221. package/plugins/lisa/hooks/operational-hazards.sh +28 -0
  222. package/plugins/lisa/hooks/parity-safety-net.sh +29 -1
  223. package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +102 -21
  224. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +130 -2
  225. package/plugins/lisa/hooks/threshold-ratchet.mjs +63 -13
  226. package/plugins/lisa/hooks/worktree-binding-guard.mjs +183 -1
  227. package/plugins/lisa/hooks/worktree-binding-guard.sh +21 -0
  228. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  229. package/plugins/lisa/rules/eager/operational-hazards.md +50 -0
  230. package/plugins/lisa/rules/eager/tracked-work.md +1 -1
  231. package/plugins/lisa/rules/reference/config-resolution.md +1 -1
  232. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
  233. package/plugins/lisa/rules/reference/operational-hazards.md +115 -0
  234. package/plugins/lisa/rules/reference/work-item-trailer-definition.md +129 -0
  235. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +407 -26
  236. package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +507 -0
  237. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  238. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +27 -4
  239. package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +1 -1
  240. package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
  241. package/plugins/lisa/skills/lisa-github-verify/SKILL.md +14 -0
  242. package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +51 -0
  243. package/plugins/lisa/skills/lisa-intake/SKILL.md +1 -1
  244. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +27 -4
  245. package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  246. package/plugins/lisa/skills/lisa-jira-verify/SKILL.md +14 -0
  247. package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  248. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +55 -2
  249. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +27 -4
  250. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  251. package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  252. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  253. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +14 -0
  254. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +54 -3
  255. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  256. package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +19 -7
  257. package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +28 -3
  258. package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +28 -3
  259. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +94 -24
  260. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +32 -2
  261. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +5 -5
  262. package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
  263. package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  264. package/plugins/lisa-agy/agents/linear-prd-intake.md +8 -8
  265. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +81 -2
  266. package/plugins/lisa-agy/hooks/block-direct-issue-create.agy.sh +64 -16
  267. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +293 -8
  268. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +21 -0
  269. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +78 -15
  270. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +21 -0
  271. package/plugins/lisa-agy/hooks/guard-dedupe.bash +337 -0
  272. package/plugins/lisa-agy/hooks/parity-safety-net.sh +29 -1
  273. package/plugins/lisa-agy/hooks.json +1 -1
  274. package/plugins/lisa-agy/plugin.json +1 -1
  275. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +407 -26
  276. package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +507 -0
  277. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  278. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +27 -4
  279. package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +1 -1
  280. package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
  281. package/plugins/lisa-agy/skills/lisa-github-verify/SKILL.md +14 -0
  282. package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +51 -0
  283. package/plugins/lisa-agy/skills/lisa-intake/SKILL.md +1 -1
  284. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +27 -4
  285. package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  286. package/plugins/lisa-agy/skills/lisa-jira-verify/SKILL.md +14 -0
  287. package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  288. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +55 -2
  289. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +27 -4
  290. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  291. package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  292. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  293. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +14 -0
  294. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +54 -3
  295. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  296. package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +19 -7
  297. package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +28 -3
  298. package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +28 -3
  299. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +94 -24
  300. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +32 -2
  301. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +5 -5
  302. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
  303. package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  304. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  305. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  306. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  307. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  308. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  309. package/plugins/lisa-copilot/.claude-plugin/plugin.json +19 -1
  310. package/plugins/lisa-copilot/agents/linear-prd-intake.agent.md +8 -8
  311. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +81 -2
  312. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +293 -8
  313. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +21 -0
  314. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +78 -15
  315. package/plugins/lisa-copilot/hooks/block-no-verify.sh +21 -0
  316. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +21 -0
  317. package/plugins/lisa-copilot/hooks/failure-signature-index.mjs +240 -5
  318. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +337 -0
  319. package/plugins/lisa-copilot/hooks/inject-rules.sh +13 -5
  320. package/plugins/lisa-copilot/hooks/operational-hazards.mjs +736 -0
  321. package/plugins/lisa-copilot/hooks/operational-hazards.sh +28 -0
  322. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +29 -1
  323. package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +102 -21
  324. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +130 -2
  325. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +63 -13
  326. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +183 -1
  327. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +21 -0
  328. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  329. package/plugins/lisa-copilot/rules/eager/operational-hazards.md +50 -0
  330. package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
  331. package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
  332. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  333. package/plugins/lisa-copilot/rules/reference/operational-hazards.md +115 -0
  334. package/plugins/lisa-copilot/rules/reference/work-item-trailer-definition.md +129 -0
  335. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +407 -26
  336. package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +507 -0
  337. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  338. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +27 -4
  339. package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +1 -1
  340. package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
  341. package/plugins/lisa-copilot/skills/lisa-github-verify/SKILL.md +14 -0
  342. package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +51 -0
  343. package/plugins/lisa-copilot/skills/lisa-intake/SKILL.md +1 -1
  344. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +27 -4
  345. package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  346. package/plugins/lisa-copilot/skills/lisa-jira-verify/SKILL.md +14 -0
  347. package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  348. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +55 -2
  349. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +27 -4
  350. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  351. package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  352. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  353. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +14 -0
  354. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +54 -3
  355. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  356. package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +19 -7
  357. package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +28 -3
  358. package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +28 -3
  359. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +94 -24
  360. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +32 -2
  361. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +5 -5
  362. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
  363. package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  364. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  365. package/plugins/lisa-cursor/agents/linear-prd-intake.md +8 -8
  366. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +81 -2
  367. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +293 -8
  368. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +21 -0
  369. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +78 -15
  370. package/plugins/lisa-cursor/hooks/block-no-verify.sh +21 -0
  371. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +21 -0
  372. package/plugins/lisa-cursor/hooks/failure-signature-index.mjs +240 -5
  373. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +337 -0
  374. package/plugins/lisa-cursor/hooks/hooks.json +10 -0
  375. package/plugins/lisa-cursor/hooks/operational-hazards.mjs +736 -0
  376. package/plugins/lisa-cursor/hooks/operational-hazards.sh +28 -0
  377. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +29 -1
  378. package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +102 -21
  379. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +130 -2
  380. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +63 -13
  381. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +183 -1
  382. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +21 -0
  383. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  384. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
  385. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  386. package/plugins/lisa-cursor/rules/operational-hazards-reference.mdc +120 -0
  387. package/plugins/lisa-cursor/rules/operational-hazards.mdc +55 -0
  388. package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
  389. package/plugins/lisa-cursor/rules/work-item-trailer-definition-reference.mdc +134 -0
  390. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +407 -26
  391. package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +507 -0
  392. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  393. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +27 -4
  394. package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +1 -1
  395. package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
  396. package/plugins/lisa-cursor/skills/lisa-github-verify/SKILL.md +14 -0
  397. package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +51 -0
  398. package/plugins/lisa-cursor/skills/lisa-intake/SKILL.md +1 -1
  399. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +27 -4
  400. package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  401. package/plugins/lisa-cursor/skills/lisa-jira-verify/SKILL.md +14 -0
  402. package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  403. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +55 -2
  404. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +27 -4
  405. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  406. package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  407. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  408. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +14 -0
  409. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +54 -3
  410. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  411. package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +19 -7
  412. package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +28 -3
  413. package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +28 -3
  414. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +94 -24
  415. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +32 -2
  416. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +5 -5
  417. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
  418. package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  419. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  420. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  421. package/plugins/lisa-expo-agy/plugin.json +1 -1
  422. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  423. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  424. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  425. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  426. package/plugins/lisa-harper-fabric/hooks/inject-rules.sh +14 -3
  427. package/plugins/lisa-harper-fabric/rules/eager/harper-fabric.md +31 -0
  428. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  429. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  430. package/plugins/lisa-harper-fabric-copilot/hooks/inject-rules.sh +14 -3
  431. package/plugins/lisa-harper-fabric-copilot/rules/eager/harper-fabric.md +31 -0
  432. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  433. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric-reference.mdc +57 -0
  434. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +32 -53
  435. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  436. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  437. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  438. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  439. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  440. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  441. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  442. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  443. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  444. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  445. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  446. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  447. package/plugins/lisa-phaser/hooks/inject-rules.sh +14 -3
  448. package/plugins/lisa-phaser/rules/eager/phaser.md +44 -0
  449. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  450. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  451. package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +14 -3
  452. package/plugins/lisa-phaser-copilot/rules/eager/phaser.md +44 -0
  453. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  454. package/plugins/lisa-phaser-cursor/rules/phaser-reference.mdc +189 -0
  455. package/plugins/lisa-phaser-cursor/rules/phaser.mdc +45 -185
  456. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  457. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  458. package/plugins/lisa-rails/hooks/inject-rules.sh +14 -4
  459. package/plugins/lisa-rails/rules/eager/rails-conventions.md +23 -0
  460. package/plugins/lisa-rails-agy/plugin.json +1 -1
  461. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  462. package/plugins/lisa-rails-copilot/hooks/inject-rules.sh +14 -4
  463. package/plugins/lisa-rails-copilot/rules/eager/rails-conventions.md +23 -0
  464. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  465. package/plugins/lisa-rails-cursor/rules/rails-conventions-reference.mdc +181 -0
  466. package/plugins/lisa-rails-cursor/rules/rails-conventions.mdc +24 -177
  467. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  468. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  469. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  470. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  471. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  472. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  473. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  474. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  475. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  476. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  477. package/plugins/materialized-artifacts.json +1 -0
  478. package/plugins/src/base/.claude-plugin/plugin.json +27 -0
  479. package/plugins/src/base/agents/linear-prd-intake.md +8 -8
  480. package/plugins/src/base/hooks/block-blind-automerge.sh +81 -2
  481. package/plugins/src/base/hooks/block-direct-issue-create.agy.sh +64 -16
  482. package/plugins/src/base/hooks/block-direct-issue-create.sh +293 -8
  483. package/plugins/src/base/hooks/block-instruction-file-edits.sh +21 -0
  484. package/plugins/src/base/hooks/block-managed-file-edits.sh +78 -15
  485. package/plugins/src/base/hooks/block-no-verify.sh +21 -0
  486. package/plugins/src/base/hooks/block-shell-json-parsing.sh +21 -0
  487. package/plugins/src/base/hooks/failure-signature-index.mjs +240 -5
  488. package/plugins/src/base/hooks/guard-dedupe.bash +337 -0
  489. package/plugins/src/base/hooks/inject-rules.sh +13 -5
  490. package/plugins/src/base/hooks/operational-hazards.mjs +736 -0
  491. package/plugins/src/base/hooks/operational-hazards.sh +28 -0
  492. package/plugins/src/base/hooks/parity-safety-net.sh +29 -1
  493. package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +102 -21
  494. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +130 -2
  495. package/plugins/src/base/hooks/threshold-ratchet.mjs +63 -13
  496. package/plugins/src/base/hooks/worktree-binding-guard.mjs +183 -1
  497. package/plugins/src/base/hooks/worktree-binding-guard.sh +21 -0
  498. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  499. package/plugins/src/base/rules/eager/operational-hazards.md +50 -0
  500. package/plugins/src/base/rules/eager/tracked-work.md +1 -1
  501. package/plugins/src/base/rules/reference/config-resolution.md +1 -1
  502. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  503. package/plugins/src/base/rules/reference/operational-hazards.md +115 -0
  504. package/plugins/src/base/rules/reference/work-item-trailer-definition.md +129 -0
  505. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +407 -26
  506. package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +507 -0
  507. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  508. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +27 -4
  509. package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +1 -1
  510. package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
  511. package/plugins/src/base/skills/lisa-github-verify/SKILL.md +14 -0
  512. package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +51 -0
  513. package/plugins/src/base/skills/lisa-intake/SKILL.md +1 -1
  514. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +27 -4
  515. package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  516. package/plugins/src/base/skills/lisa-jira-verify/SKILL.md +14 -0
  517. package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  518. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +55 -2
  519. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +27 -4
  520. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  521. package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  522. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  523. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +14 -0
  524. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +54 -3
  525. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  526. package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +19 -7
  527. package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +28 -3
  528. package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +28 -3
  529. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +94 -24
  530. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +32 -2
  531. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +5 -5
  532. package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
  533. package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  534. package/plugins/src/harper-fabric/hooks/inject-rules.sh +14 -3
  535. package/plugins/src/harper-fabric/rules/eager/harper-fabric.md +31 -0
  536. package/plugins/src/phaser/hooks/inject-rules.sh +14 -3
  537. package/plugins/src/phaser/rules/eager/phaser.md +44 -0
  538. package/plugins/src/rails/hooks/inject-rules.sh +14 -4
  539. package/plugins/src/rails/rules/eager/rails-conventions.md +23 -0
  540. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  541. package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  542. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  543. package/rails/create-only/.github/workflows/ci.yml +8 -0
  544. package/rails/create-only/.github/workflows/deploy.yml +22 -0
  545. package/scripts/build-plugins.sh +2 -1
  546. package/scripts/check-engine-floor.mjs +361 -0
  547. package/scripts/check-guard-parity-notes.mjs +709 -0
  548. package/scripts/check-template-workflow-refs.mjs +9 -0
  549. package/scripts/check-third-party-action-pins.mjs +17 -1
  550. package/scripts/check-workflow-contract-assertions.mjs +480 -0
  551. package/scripts/copy-opencode-plugin-templates.mjs +1 -0
  552. package/scripts/deployed-guard-advice.mjs +636 -0
  553. package/scripts/generate-agy-plugin-artifacts.mjs +35 -10
  554. package/scripts/generate-export-surface.mjs +105 -19
  555. package/scripts/generate-two-channel-couplings.ts +143 -212
  556. package/scripts/lisa-enforcement-fallback.sh +31 -0
  557. package/scripts/measure-scripts-profile-gap.mts +361 -0
  558. package/scripts/mutation-performance-measure.mjs +21 -5
  559. package/scripts/reconcile-release-tags.mjs +65 -84
  560. package/scripts/two-channel-couplings.json +302 -70
  561. package/scripts/workflow-contract-assertion.sh +47 -0
  562. package/typescript/copy-overwrite/.lintstagedrc.json +1 -0
  563. package/typescript/copy-overwrite/eslint.ignore.config.json +1 -0
  564. package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +527 -53
  565. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +301 -13
  566. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  567. package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  568. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  569. package/typescript/create-only/.github/workflows/ci.yml +7 -0
  570. /package/plugins/lisa-harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  571. /package/plugins/lisa-harper-fabric-copilot/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  572. /package/plugins/lisa-phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  573. /package/plugins/lisa-phaser-copilot/rules/{phaser.md → reference/phaser.md} +0 -0
  574. /package/plugins/lisa-rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  575. /package/plugins/lisa-rails-copilot/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  576. /package/plugins/src/harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  577. /package/plugins/src/phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  578. /package/plugins/src/rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+ # Hook wrapper for the short-lived operational-hazard ledger
3
+ # (CodySwannGT/lisa#3681).
4
+ #
5
+ # Two events, one script:
6
+ # --session-start SessionStart / SubagentStart — deliver every hazard that
7
+ # applies right now, and stamp what this session was born
8
+ # knowing. This is the half a fan-out cannot reach: a
9
+ # session that starts after the warning still gets it.
10
+ # --hook PostToolUse — announce only what was declared or lifted
11
+ # since this session started, once each. This is the only
12
+ # thing that reaches a session already running.
13
+ #
14
+ # Advisory by construction — ALWAYS exit 0. A session that cannot read the
15
+ # ledger must keep working; a broken hook that wedges every tool call would be
16
+ # a far worse defect than the one this closes. Every infrastructure gap (no
17
+ # node, unreadable stdin) is also exit 0, and `bun run check:operational-hazards`
18
+ # is what guarantees the ledger itself still parses.
19
+ set -uo pipefail
20
+
21
+ input="$(cat 2>/dev/null || true)"
22
+
23
+ command -v node >/dev/null 2>&1 || exit 0
24
+
25
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+
27
+ printf '%s' "$input" | node "$script_dir/operational-hazards.mjs" "$@" || true
28
+ exit 0
@@ -118,6 +118,27 @@ trap 'printf "%s\n" "Blocked by safety-net: hook failed while parsing its input;
118
118
 
119
119
  input="$(cat)"
120
120
 
121
+ # Evaluate once per tool call when this guard is registered on both channels.
122
+ #
123
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
124
+ # manifest, and where both are live the harness runs this guard twice for one
125
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
126
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
127
+ # call; a differing vintage, a refusal, and a host with one channel all
128
+ # evaluate exactly as before. Nothing is de-registered by it
129
+ # (CodySwannGT/lisa#3814).
130
+ #
131
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
132
+ # older channel copy that predates it is unaffected.
133
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
134
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
135
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
136
+ # shellcheck source=guard-dedupe.bash
137
+ . "$lisa_guard_dedupe_lib"
138
+ trap 'lisa_guard_dedupe_record $?' EXIT
139
+ lisa_guard_dedupe parity-safety-net "$input"
140
+ fi
141
+
121
142
  tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
122
143
  if [ "$tool_name" != "Bash" ]; then
123
144
  exit 0
@@ -244,12 +265,19 @@ later with `git apply "$patch"`.
244
265
 
245
266
  If the work is NOT yours -- a concurrent agent dirtied this worktree and your
246
267
  judgement is "none of this is mine" -- that same capture is how you return the
247
- tree to HEAD:
268
+ tree to HEAD. This block is self-contained; paste it as it stands:
248
269
 
270
+ patch="$(mktemp "${TMPDIR:-/tmp}/lisa-preserve-XXXXXX")"
249
271
  git reset
250
272
  git diff --binary HEAD > "$patch"
251
273
  git apply -R "$patch"
252
274
 
275
+ It names the file itself rather than inheriting one from the block above,
276
+ because the two blocks are alternatives and only one of them gets run. A remedy
277
+ printed at the moment an agent is blocked is followed literally; one that
278
+ depends on a variable the reader never set fails on its first line, and the
279
+ guard that printed it stops being believed.
280
+
253
281
  `git reset` clears staged and unmerged entries first, so the capture sees the
254
282
  whole tree as one diff; reversing it leaves the tree at HEAD with "$patch" as
255
283
  the record of what you discarded. Untracked files survive, exactly as they
@@ -7,6 +7,9 @@
7
7
  * for the CLI.
8
8
  */
9
9
  import {
10
+ ALLOW_STATE,
11
+ classifyAllowEntry,
12
+ describeAllowScope,
10
13
  extractAllowEntries,
11
14
  extractK6Constraints,
12
15
  extractLighthouseAssertions,
@@ -221,7 +224,7 @@ function compareAllowList(relPath, base, current) {
221
224
  file: relPath,
222
225
  key: `thresholdRatchet.allow ${entry.file}#${entry.key}`,
223
226
  type: "allow-added",
224
- message: `${relPath}: new threshold exception for ${entry.file} → ${entry.key}. Exceptions are a human decision: land this entry in its own human-approved change first, then make the threshold change.`,
227
+ message: `${relPath}: new threshold exception ${describeAllowScope(entry)}. Exceptions are a human decision: land this entry in its own human-approved change first, then make the threshold change. It needs a reason saying what resolves it and an "until": "YYYY-MM-DD" naming the day it is reviewed, because an exemption that cannot end is a permanent reduction in what the ratchet covers.`,
225
228
  });
226
229
  }
227
230
  }
@@ -311,29 +314,106 @@ export function compareFile(relPath, baselineText, currentText) {
311
314
  }
312
315
 
313
316
  /**
314
- * Drop findings covered by baseline-side allow entries. `allow-added`
315
- * findings are never dropped an exception cannot approve its own creation.
317
+ * Whether an allow entry's file and key cover a finding.
318
+ * @param {{ file: string, key: string }} entry An allow entry
319
+ * @param {Finding} finding A finding from the change
320
+ * @returns {boolean} True when the entry's scope reaches this finding
321
+ */
322
+ function entryCovers(entry, finding) {
323
+ return (
324
+ (finding.file === entry.file || finding.file.endsWith(`/${entry.file}`)) &&
325
+ (entry.key === "*" || entry.key === finding.key)
326
+ );
327
+ }
328
+
329
+ /**
330
+ * Report the allow entries a human has to act on, whether or not this change
331
+ * touched anything they cover.
332
+ *
333
+ * Separate from {@link applyAllowList} because the two answer different
334
+ * questions and only one of them depends on the change under review. An
335
+ * exemption that has outlived its condition is dead weight the moment the
336
+ * condition passes — the way `_thresholdsDivergence` is stale the moment the
337
+ * two floors agree — and saying so is not conditional on anybody happening to
338
+ * touch the gate it covered.
339
+ * @param {Array<{ file: string, key: string, reason?: string, until?: unknown }>} allowEntries
340
+ * The allow list being honoured
341
+ * @param {number} [now] Clock reading, injected so the check is deterministic
342
+ * @returns {string[]} One line per entry that is expired or unevaluable; empty
343
+ * when every entry still names a condition that holds
344
+ */
345
+ export function describeAllowList(allowEntries, now = Date.now()) {
346
+ const lines = [];
347
+ for (const entry of allowEntries) {
348
+ const { state, detail } = classifyAllowEntry(entry, now);
349
+ if (state !== ALLOW_STATE.LIVE) lines.push(detail);
350
+ }
351
+ return lines;
352
+ }
353
+
354
+ /**
355
+ * Drop findings covered by a LIVE baseline-side allow entry.
356
+ *
357
+ * Three ways a finding leaves here, and they are deliberately distinct:
358
+ *
359
+ * allowed a covering entry's condition still holds. Reported, not blocked —
360
+ * a project mid-migration is not red-walled by expiry.
361
+ * expired every covering entry's condition has passed. The weakening the
362
+ * entry used to permit is REFUSED again, and the refusal names the
363
+ * entry, its scope, its reason and the remedy. An expiry that
364
+ * lapsed into "allowed" would be worse than no expiry.
365
+ * blocked nothing covered it, or everything that covered it has expired.
366
+ *
367
+ * An entry whose condition nothing can evaluate counts as covering, so the
368
+ * allow list keeps working for entries written before `until` existed;
369
+ * `describeAllowList` reports those every run instead.
370
+ *
371
+ * `allow-added` findings are never dropped, by any entry in any state — an
372
+ * exception cannot approve its own creation, which is the baseline-side fence
373
+ * and is untouched here.
316
374
  * @param {Finding[]} findings All findings from the change
317
- * @param {Array<{ file: string, key: string }>} allowEntries Baseline
318
- * (already-merged) allow list
319
- * @returns {{ blocked: Finding[], allowed: Finding[] }} Findings that still
320
- * block vs. findings covered by a recorded exception
375
+ * @param {Array<{ file: string, key: string, reason?: string, until?: unknown }>} allowEntries
376
+ * Baseline (already-merged) allow list
377
+ * @param {number} [now] Clock reading, injected so the check is deterministic
378
+ * @returns {{
379
+ * blocked: Finding[],
380
+ * allowed: Array<{ finding: Finding, entry: { file: string, key: string }, message: string }>,
381
+ * expired: Array<{ finding: Finding, entry: { file: string, key: string }, message: string }>,
382
+ * }} Findings that still block, findings a live exemption covers, and the
383
+ * refusals produced by exemptions that have ended
321
384
  */
322
- export function applyAllowList(findings, allowEntries) {
385
+ export function applyAllowList(findings, allowEntries, now = Date.now()) {
386
+ const classified = allowEntries.map(entry => ({
387
+ entry,
388
+ ...classifyAllowEntry(entry, now),
389
+ }));
323
390
  const blocked = [];
324
391
  const allowed = [];
392
+ const expired = [];
325
393
  for (const finding of findings) {
326
- const isAllowed =
327
- finding.type !== "allow-added" &&
328
- allowEntries.some(
329
- e =>
330
- (finding.file === e.file || finding.file.endsWith(`/${e.file}`)) &&
331
- (e.key === "*" || e.key === finding.key)
332
- );
333
- if (isAllowed) allowed.push(finding);
334
- else blocked.push(finding);
394
+ const covering =
395
+ finding.type === "allow-added"
396
+ ? []
397
+ : classified.filter(c => entryCovers(c.entry, finding));
398
+ const live = covering.find(c => c.state !== ALLOW_STATE.EXPIRED);
399
+ if (live) {
400
+ allowed.push({
401
+ finding,
402
+ entry: live.entry,
403
+ message: `allowed by ${live.scope}, which ${live.summary}. It covers: ${finding.message}`,
404
+ });
405
+ continue;
406
+ }
407
+ blocked.push(finding);
408
+ for (const dead of covering) {
409
+ expired.push({
410
+ finding,
411
+ entry: dead.entry,
412
+ message: `${dead.detail} It used to cover: ${finding.message}`,
413
+ });
414
+ }
335
415
  }
336
- return { blocked, allowed };
416
+ return { blocked, allowed, expired };
337
417
  }
338
418
 
339
419
  /**
@@ -351,8 +431,9 @@ export function formatReport(findings) {
351
431
  "Quality thresholds are a one-way ratchet: they may tighten but never",
352
432
  "loosen. Fix the code so it meets the current gate instead of lowering the",
353
433
  "gate. If a human decides an exception is genuinely correct, they record it",
354
- "in .lisa.config.json under thresholdRatchet.allow (with a reason) in a",
355
- "separate human-approved change; this check honors exceptions only after",
356
- "they are merged.",
434
+ "in .lisa.config.json under thresholdRatchet.allow with a reason saying",
435
+ 'what resolves it, and an "until": "YYYY-MM-DD" naming the day it is',
436
+ "reviewed in a separate human-approved change; this check honors an",
437
+ "exception only after it is merged, and only until its until date passes.",
357
438
  ].join("\n");
358
439
  }
@@ -379,8 +379,8 @@ export function extractK6Constraints(conf) {
379
379
  /**
380
380
  * Extract thresholdRatchet.allow entries from parsed .lisa.config.json.
381
381
  * @param {unknown} config Parsed config (may be undefined)
382
- * @returns {Array<{ file: string, key: string, reason?: string }>} The
383
- * well-formed allow entries; malformed entries are dropped
382
+ * @returns {Array<{ file: string, key: string, reason?: string, until?: unknown }>}
383
+ * The well-formed allow entries; malformed entries are dropped
384
384
  */
385
385
  export function extractAllowEntries(config) {
386
386
  const raw = config?.thresholdRatchet?.allow;
@@ -389,3 +389,131 @@ export function extractAllowEntries(config) {
389
389
  e => e && typeof e.file === "string" && typeof e.key === "string"
390
390
  );
391
391
  }
392
+
393
+ /**
394
+ * The three states an allow entry's expiry condition can be in.
395
+ *
396
+ * THE DEFECT THESE EXIST FOR (#3856). An allow entry is the human-approved way
397
+ * for a weakening to pass the ratchet, and it had no expiry, no review and no
398
+ * removal path — nothing read the `reason`, so an exemption granted for a
399
+ * temporary condition (a migration in flight, a suite being rewritten, a
400
+ * dependency waiting upstream) outlived the condition and kept applying,
401
+ * silently, forever. The set of gates actually enforced therefore shrank
402
+ * monotonically while every individual decision that shrank it was correct at
403
+ * the time. A control whose exception list only grows converges on no control.
404
+ *
405
+ * The shape is taken from `_thresholdsDivergence` in `stryker.conf.json`,
406
+ * governed by `src/sync/stryker-thresholds-ownership.ts` — the one exemption
407
+ * surface in this repository that already stops exempting when it goes stale.
408
+ * Its three properties are reproduced here: the entry records its condition in
409
+ * a form something can evaluate, the reason says what resolves it, and once the
410
+ * condition passes the entry stops exempting AND says so, naming the remedy.
411
+ *
412
+ * `unchecked` is deliberately NOT `expired`. An entry written before `until`
413
+ * existed names a condition nothing can evaluate, which is the prose situation
414
+ * the ticket describes — it is reported every run, loudly, and it keeps
415
+ * exempting. Refusing it instead would red-wall every project mid-migration
416
+ * that carries a valid exception, which is the failure the ticket names as
417
+ * worse than the defect.
418
+ * @type {Readonly<Record<string, "live"|"expired"|"unchecked">>}
419
+ */
420
+ export const ALLOW_STATE = Object.freeze({
421
+ LIVE: "live",
422
+ EXPIRED: "expired",
423
+ UNCHECKED: "unchecked",
424
+ });
425
+
426
+ /** A calendar condition, and the only shape `until` may take. */
427
+ const ISO_DAY = /^(\d{4})-(\d{2})-(\d{2})$/u;
428
+
429
+ /** An entry is live through the END of its named day, not up to its start. */
430
+ const DAY_MS = 86_400_000;
431
+
432
+ /**
433
+ * The instant an `until` condition stops holding.
434
+ *
435
+ * Round-tripped through `toISOString` on purpose: `Date.UTC` accepts
436
+ * out-of-range components and slides them, so `2026-02-31` would silently
437
+ * become March 3rd and `0099-01-01` would become 1999. An exemption dated a day
438
+ * that does not exist is a condition nobody can evaluate, and saying so is the
439
+ * honest answer — quietly moving it would hand the entry a different, longer
440
+ * life than the one written down.
441
+ * @param {unknown} until The entry's `until` field
442
+ * @returns {number | undefined} Epoch ms after which the entry has expired, or
443
+ * undefined when `until` is not a calendar day anything can evaluate
444
+ */
445
+ export function allowEntryExpiry(until) {
446
+ if (typeof until !== "string") return undefined;
447
+ const day = until.trim();
448
+ const match = ISO_DAY.exec(day);
449
+ if (!match) return undefined;
450
+ const midnight = Date.UTC(
451
+ Number(match[1]),
452
+ Number(match[2]) - 1,
453
+ Number(match[3])
454
+ );
455
+ if (new Date(midnight).toISOString().slice(0, 10) !== day) return undefined;
456
+ return midnight + DAY_MS;
457
+ }
458
+
459
+ /**
460
+ * Name an allow entry so a report says WHICH exemption it is talking about,
461
+ * and how much it covers.
462
+ *
463
+ * File-wide and key-scoped entries are spelled differently because they are
464
+ * different risks wearing the same shape: `key: "*"` turns off ratchet
465
+ * protection for every threshold in the file, and in a config diff that reads
466
+ * as one more approved exception.
467
+ * @param {{ file: string, key: string }} entry An allow entry, already
468
+ * well-formed — `extractAllowEntries` is the only source of these
469
+ * @returns {string} Operator-readable identification of the entry
470
+ */
471
+ export function describeAllowScope(entry) {
472
+ return entry.key === "*"
473
+ ? `thresholdRatchet.allow entry ${entry.file} → * (file-wide: every key in the file)`
474
+ : `thresholdRatchet.allow entry ${entry.file} → ${entry.key} (key-scoped)`;
475
+ }
476
+
477
+ /**
478
+ * Classify one allow entry's expiry condition, and say what to do about it.
479
+ * @param {{ file: string, key: string, reason?: unknown, until?: unknown }} entry
480
+ * An allow entry
481
+ * @param {number} now Clock reading, injected so the check is deterministic
482
+ * @returns {{ state: "live"|"expired"|"unchecked", scope: string, summary: string, detail: string }}
483
+ * The state, the entry's identification, a short phrase for a one-line
484
+ * mention, and the full sentence naming the entry, its reason and the remedy
485
+ */
486
+ export function classifyAllowEntry(entry, now) {
487
+ const scope = describeAllowScope(entry);
488
+ const because =
489
+ typeof entry.reason === "string" && entry.reason.trim() !== ""
490
+ ? ` Reason on record: ${entry.reason.trim()}`
491
+ : " No reason is recorded.";
492
+ const expiresAt = allowEntryExpiry(entry.until);
493
+ if (expiresAt === undefined) {
494
+ const summary = `names no condition anything can evaluate (until = ${JSON.stringify(entry.until ?? null)})`;
495
+ return {
496
+ state: ALLOW_STATE.UNCHECKED,
497
+ scope,
498
+ summary,
499
+ detail: `${scope} ${summary}, so nothing can ever end it. Give it "until": "YYYY-MM-DD" naming the day the exemption is reviewed, or delete it.${because}`,
500
+ };
501
+ }
502
+ const day = String(entry.until).trim();
503
+ if (now >= expiresAt) {
504
+ const summary = `expired after ${day}`;
505
+ return {
506
+ state: ALLOW_STATE.EXPIRED,
507
+ scope,
508
+ summary,
509
+ detail: `${scope} ${summary} and no longer exempts anything. Delete it, or record a fresh human-approved entry naming the new condition.${because}`,
510
+ };
511
+ }
512
+ const summary = `is live until ${day}`;
513
+ return {
514
+ state: ALLOW_STATE.LIVE,
515
+ scope,
516
+ summary,
517
+ detail: `${scope} ${summary}.`,
518
+ };
519
+ }
@@ -19,10 +19,20 @@
19
19
  * every entry carries a written decision.
20
20
  *
21
21
  * Human override: `.lisa.config.json` → `thresholdRatchet.allow` entries
22
- * ({ file, key, reason }). Honored ONLY from the baseline side (HEAD /
22
+ * ({ file, key, reason, until }). Honored ONLY from the baseline side (HEAD /
23
23
  * merge-base), never from the change under review — an agent cannot grant
24
24
  * itself an exception in the same change that weakens a gate. `key: "*"`
25
- * allows every key in the file.
25
+ * allows every key in the file, and reports as file-wide so it is not mistaken
26
+ * for a key-scoped one.
27
+ *
28
+ * Exemptions END (#3856). `until` is a `YYYY-MM-DD` day the entry is live
29
+ * through; past it the entry stops exempting and the weakening it permitted is
30
+ * refused again, naming the entry, its scope, its reason and the remedy. An
31
+ * entry naming no evaluable condition still exempts — that keeps a project
32
+ * mid-migration off a red wall — but is reported on every run until somebody
33
+ * gives it a condition or deletes it. Modelled on `_thresholdsDivergence` in
34
+ * `stryker.conf.json`, the sibling exemption that already stops exempting when
35
+ * it goes stale; see `threshold-ratchet-families.mjs` `ALLOW_STATE`.
26
36
  *
27
37
  * One exception, and only one: a PROMOTION between deploy-chain branches
28
38
  * (`--base` + `--head`, both named in `deploy.branches`, head upstream of
@@ -46,6 +56,7 @@ import {
46
56
  import {
47
57
  applyAllowList,
48
58
  compareFile,
59
+ describeAllowList,
49
60
  formatReport,
50
61
  } from "./threshold-ratchet-compare.mjs";
51
62
 
@@ -387,6 +398,13 @@ function run(mode, baseRef, onlyFiles, headRef) {
387
398
  );
388
399
  }
389
400
 
401
+ const allow = resolveAllowList(
402
+ root,
403
+ plan.baselineRef,
404
+ mode,
405
+ baseRef,
406
+ headRef
407
+ );
390
408
  const findings = watched.flatMap(f =>
391
409
  compareFile(
392
410
  f,
@@ -394,28 +412,60 @@ function run(mode, baseRef, onlyFiles, headRef) {
394
412
  plan.readCurrent(f)
395
413
  )
396
414
  );
397
- if (findings.length === 0) return 0;
415
+ // The allow-list inventory is printed whether or not the change produced
416
+ // findings. An exemption that has outlived its condition is dead weight the
417
+ // moment the condition passes, and reporting it only when something else has
418
+ // already failed is how a list nobody reads stays a list nobody reads.
419
+ if (findings.length === 0) {
420
+ reportAllowList(allow.entries, []);
421
+ return 0;
422
+ }
423
+ return reportFindings(findings, allow, mode);
424
+ }
398
425
 
399
- const allow = resolveAllowList(
400
- root,
401
- plan.baselineRef,
402
- mode,
403
- baseRef,
404
- headRef
405
- );
426
+ /**
427
+ * Print the allow entries a human has to act on, minus the ones a refusal is
428
+ * about to name in full.
429
+ * @param {object[]} entries The resolved allow list
430
+ * @param {Array<{ message: string }>} expired Refusals already being printed
431
+ * @returns {void}
432
+ */
433
+ function reportAllowList(entries, expired) {
434
+ for (const line of describeAllowList(entries)) {
435
+ if (expired.some(item => item.message.startsWith(line))) continue;
436
+ process.stdout.write(`threshold-ratchet: ${line}\n`);
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Print the verdict for a change that produced findings.
442
+ * @param {object[]} findings Findings from every watched file
443
+ * @param {{ entries: object[], promotion: boolean, note: string | null }} allow
444
+ * The resolved allow list and its provenance
445
+ * @param {"hook"|"staged"|"base"} mode Comparison mode
446
+ * @returns {number} Process exit code (2 for hook mode, 1 otherwise; 0 clean)
447
+ */
448
+ function reportFindings(findings, allow, mode) {
406
449
  const split = partitionCarriedEntries(findings, allow.promotion);
407
- const { blocked, allowed } = applyAllowList(split.rest, allow.entries);
450
+ const { blocked, allowed, expired } = applyAllowList(
451
+ split.rest,
452
+ allow.entries
453
+ );
408
454
  if (allow.note) process.stdout.write(`${allow.note}\n`);
455
+ reportAllowList(allow.entries, expired);
409
456
  for (const finding of split.carried) {
410
457
  process.stdout.write(
411
458
  `threshold-ratchet: carried forward by this promotion, approved upstream — ${finding.message}\n`
412
459
  );
413
460
  }
414
- for (const finding of allowed) {
461
+ for (const item of allowed) {
415
462
  process.stdout.write(
416
- `threshold-ratchet: allowed by .lisa.config.json exception — ${finding.message}\n`
463
+ `threshold-ratchet: ${item.message} (.lisa.config.json exception)\n`
417
464
  );
418
465
  }
466
+ for (const item of expired) {
467
+ process.stderr.write(`threshold-ratchet: refused — ${item.message}\n`);
468
+ }
419
469
  if (blocked.length === 0) return 0;
420
470
  process.stderr.write(`${formatReport(blocked)}\n`);
421
471
  return mode === "hook" ? 2 : 1;