@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
@@ -113,6 +113,27 @@ set -euo pipefail
113
113
 
114
114
  input="$(cat)"
115
115
 
116
+ # Evaluate once per tool call when this guard is registered on both channels.
117
+ #
118
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
119
+ # manifest, and where both are live the harness runs this guard twice for one
120
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
121
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
122
+ # call; a differing vintage, a refusal, and a host with one channel all
123
+ # evaluate exactly as before. Nothing is de-registered by it
124
+ # (CodySwannGT/lisa#3814).
125
+ #
126
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
127
+ # older channel copy that predates it is unaffected.
128
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
129
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
130
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
131
+ # shellcheck source=guard-dedupe.bash
132
+ . "$lisa_guard_dedupe_lib"
133
+ trap 'lisa_guard_dedupe_record $?' EXIT
134
+ lisa_guard_dedupe block-direct-issue-create "$input"
135
+ fi
136
+
116
137
  # Probe both interpreters before use and announce a missing one rather than
117
138
  # swallowing it. Under `set -e` an absent jq aborts with 127, and Claude Code
118
139
  # treats any non-2 exit as a NON-BLOCKING hook error — so the guard would
@@ -329,10 +350,66 @@ EOF
329
350
  esac
330
351
  }
331
352
 
353
+ # The branch-specific half of the remediation, printed only for the branch that
354
+ # fired. Every refusal this guard issues has to name an inverse the author can
355
+ # actually perform — a refusal with none is a state change with no inverse, and
356
+ # on a file-contents refusal it made a path permanently unnameable to any
357
+ # command the guard did not recognise as a reader (CodySwannGT/lisa#3683).
358
+ #
359
+ # NEITHER PARAGRAPH IS AN ESCAPE HATCH, and that is deliberate. `3594` records
360
+ # that a PreToolUse guard whose escape the guarded agent can set is not a
361
+ # control, so nothing here is settable: the only inverses named are edits to
362
+ # the refused command itself, and performing either one leaves a genuine
363
+ # undeclared creation refused by the checks above. Re-quoting an unlexable
364
+ # creation hands it to the parsed path, which refuses it; naming a document to
365
+ # a reader files nothing, which is the whole point. The one true escape stays
366
+ # what it was — an ambient variable a human exports before the session, which a
367
+ # tool-call shell cannot reach because its exports do not survive into this
368
+ # hook's environment.
369
+ remedy_hint() {
370
+ case "${1:-}" in
371
+ unparseable)
372
+ cat <<'EOF'
373
+
374
+ THIS COMMAND DID NOT LEX, so it was judged by pattern rather than by parse. An
375
+ unbalanced quote is the cause, and an apostrophe in ordinary prose is the
376
+ measured one. The pattern cannot separate a sentence about filing from a real
377
+ filing hidden behind a trailing `#` comment, so it refuses both.
378
+
379
+ THE INVERSE IS EXECUTABLE: re-quote the command so it lexes, and run it again.
380
+ It is not a bypass — a re-quoted creation is handed to the full parser, which
381
+ refuses it unless it declares readiness the ordinary way.
382
+
383
+ A command whose every segment runs a known READER is not refused on this branch
384
+ at all, so `cat`, `grep`, `ls`, `cp`, `mv`, `git` and `echo` never reach it.
385
+ EOF
386
+ ;;
387
+ file)
388
+ cat <<'EOF'
389
+
390
+ THIS REFUSAL CAME FROM A FILE THIS COMMAND RUNS. A path is opened only when the
391
+ command puts it in a COMMAND position; a program that takes it as data — `cat`,
392
+ `grep`, `ls`, `cp`, `mv`, `git`, a test runner — never reaches this branch. Two
393
+ inverses, both executable:
394
+
395
+ - The file WRITES a payload locally and cannot transmit it (no HTTP client,
396
+ no CLI, no process spawn): it is read as data, not as a filing. Drop the
397
+ transmitting primitive, or slice the payload out of an existing file
398
+ instead of restating it here.
399
+ - The file produces no local artifact either — it is a DOCUMENT that quotes a
400
+ creation rather than a program that performs one. Name it to a reader
401
+ rather than to an interpreter; rewording its contents is not the remedy and
402
+ does not converge.
403
+ EOF
404
+ ;;
405
+ esac
406
+ }
407
+
332
408
  refuse() {
333
409
  local signature="$1"
334
410
  local roles="$2"
335
411
  local target="$3"
412
+ local remedy="${4:-}"
336
413
  if [ -n "$target" ]; then
337
414
  refuse_cross_repo "$signature" "$roles" "$target"
338
415
  fi
@@ -403,6 +480,8 @@ WHERE THE DECLARATION IS READ FROM: argv, the request payload — inline, in a
403
480
  this command runs. Moving the create into a file no longer moves it out of
404
481
  sight, so the declaration can live wherever the create does.
405
482
 
483
+ $(remedy_hint "$remedy")
484
+
406
485
  OPERATOR ESCAPE: a human can export \`LISA_ALLOW_DIRECT_ISSUE_CREATE=1\` in the
407
486
  environment before starting the session. It is deliberately not reachable by
408
487
  setting it inline on this command — an inline assignment is refused.
@@ -782,6 +861,84 @@ TRACKER_ENDPOINT = re.compile(
782
861
  re.IGNORECASE,
783
862
  )
784
863
 
864
+ # ---------------------------------------------------------------------------
865
+ # A PAYLOAD HELD AS DATA IS NOT A SUBMISSION
866
+ #
867
+ # The conjunction above — an endpoint AND a creation verb in one file — cannot
868
+ # tell a payload being WRITTEN AS TEST DATA from one about to be SUBMITTED.
869
+ # Measured (CodySwannGT/lisa#3943): a helper that assembles test fixtures, and
870
+ # holds a mutation body as a string constant it writes to a fixture file, was
871
+ # refused as "a tracker creation inside <path>". It submits nothing. The same
872
+ # helper rewritten to SLICE the payload out of an existing file was allowed —
873
+ # so the discriminator was the literal, not the behaviour.
874
+ #
875
+ # The behaviour that separates them is EGRESS. A file can only file an issue if
876
+ # it can hand its bytes to another host or another process. A file with no such
877
+ # primitive anywhere in it cannot submit whatever its constants spell, so the
878
+ # conjunction is reading data.
879
+ #
880
+ # BOTH HALVES ARE REQUIRED, and the second one is why this is not a hole. The
881
+ # exemption needs POSITIVE evidence that the file produces a local artifact
882
+ # (`LOCAL_WRITE`) as well as the ABSENCE of egress, so it is bounded to the
883
+ # measured population — a fixture writer — rather than granted to any file that
884
+ # happens to use a transport nobody enumerated. Egress is over-matched on
885
+ # purpose: every miss in `PAYLOAD_EGRESS` widens the exemption, so the list
886
+ # reaches for `.post(`-shaped calls and process spawning as well as named HTTP
887
+ # clients, and a file doing both is refused. That is the guard's usual
888
+ # direction of failure — a false refusal is reported, a false allow is silent.
889
+ #
890
+ # RESIDUAL, stated rather than hidden: a submitting file that ALSO writes a
891
+ # local artifact and reaches the network through a primitive no pattern here
892
+ # names is allowed. Nothing about `text_declares_readiness`, the nested shell
893
+ # scan, the unparseable arm, or the argv path is weakened — this reads only the
894
+ # coarse conjunction, which is the only arm that ever inferred a submission
895
+ # from contents alone.
896
+ PAYLOAD_EGRESS = re.compile(
897
+ # Clients and trackers, by name.
898
+ r"\bcurl\b|\bwget\b|\bhttpie\b|\bgh\b|\bjira\b|\bacli\b"
899
+ r"|requests\.|httpx|aiohttp|pycurl|urllib|urlopen|http\.client|\bsocket\b"
900
+ r"|https?connection|httpurlconnection|httpclient|httprequest|webclient"
901
+ r"|okhttp|net::http|restclient|faraday|httparty|open-uri|\blwp\b|http::tiny"
902
+ r"|curl_init|curl_exec|guzzle|fsockopen|file_get_contents"
903
+ r"|\bfetch\s*\(|axios|xmlhttprequest|node-fetch|undici|superagent"
904
+ r"|http\.post|http\.newrequest"
905
+ # Handing the payload to another process, which can carry it anywhere.
906
+ r"|subprocess|os\.system|popen|child_process|execsync|spawnsync"
907
+ r"|\bspawn\s*\(|\bexec\s*\(|\bsystem\s*\(|bun\.spawn|deno\.command"
908
+ # The shape a send takes in almost any language, whatever the client is
909
+ # called. Over-matching here costs an exemption, never a refusal.
910
+ r"|\.post\s*\(|\.put\s*\(|\.patch\s*\(|\.request\s*\(|\.send\s*\("
911
+ r"|\.execute\s*\(|\.mutate\s*\(|\.do\s*\(",
912
+ re.IGNORECASE,
913
+ )
914
+ # Producing a local artifact: the positive half of the exemption.
915
+ LOCAL_WRITE = re.compile(
916
+ r"\.write\s*\(|\.writelines\s*\(|write_text\s*\(|write_bytes\s*\("
917
+ r"|writefilesync|writefile\s*\(|createwritestream|outputstream"
918
+ r"|ioutil\.writefile|\btee\b"
919
+ # A shell redirect. Anchored on a separator so `=>` and `->` are not read
920
+ # as one, and required to be followed by something path-shaped.
921
+ r"|(?:^|[\s;&|(])>>?\s*[\"']?[\w./$~-]",
922
+ re.IGNORECASE | re.MULTILINE,
923
+ )
924
+
925
+
926
+ def payload_is_inert(text):
927
+ """Whether this file HOLDS a creation payload rather than submitting one.
928
+
929
+ Args:
930
+ text: The file's contents.
931
+
932
+ Returns:
933
+ True when the file writes a local artifact and carries no primitive
934
+ capable of transmitting anything.
935
+ """
936
+ return (
937
+ LOCAL_WRITE.search(text) is not None
938
+ and PAYLOAD_EGRESS.search(text) is None
939
+ )
940
+
941
+
785
942
  # ---------------------------------------------------------------------------
786
943
  # DECLARING BUILD-READY WHEN THE ROLE IS A STATE AND NOT A LABEL
787
944
  #
@@ -1814,11 +1971,23 @@ def is_assignment_word(token):
1814
1971
  # harm #3705 records, because it can block the verification of a fix while
1815
1972
  # saying nothing about why.
1816
1973
  #
1974
+ # RELOCATION AND ECHOING ARE READS TOO, and leaving them off is what turned a
1975
+ # false positive into a property of the repository (CodySwannGT/lisa#3683). A
1976
+ # file that QUOTES a creation was already reachable by `grep`, `cat` and `ls`
1977
+ # once this list existed — but not by `cp` or `mv`, so the file could be read
1978
+ # and never moved, by any agent and by CI, for as long as the literal stayed in
1979
+ # it. The measured case shows why "reword it" is not the inverse: the refusal
1980
+ # recurred AFTER the content had been reworded, on a different file quoting the
1981
+ # same command. None of these four executes an operand: `cp` and `mv` copy
1982
+ # bytes, `echo` and `printf` write them to stdout. They are also the vocabulary
1983
+ # `unparseable_reads_only` answers with when the text could not be lexed at all.
1984
+ #
1817
1985
  # RESIDUAL, stated rather than hidden: a reader NOT on this list is still
1818
1986
  # followed and can still over-refuse. The set of read-only tools is unbounded,
1819
1987
  # so this closes the measured population and not the class. Add names here as
1820
1988
  # they are measured; do not invert the default to close it by fiat, or the
1821
1989
  # executed-script reach that #3484 bought is lost.
1990
+ #
1822
1991
  # The copy/move/link family is measured, not inferred: `cp <a file that files>`
1823
1992
  # was refused as "an unparseable command that reads as a tracker creation
1824
1993
  # inside <path>" (CodySwannGT/lisa#3683, trip 5, reproduced in this guard's own
@@ -1834,11 +2003,12 @@ def is_assignment_word(token):
1834
2003
  # standing instruction to add on measurement rather than by sweep.
1835
2004
  READ_ONLY_PROGRAMS = {
1836
2005
  "awk", "bat", "cat", "cksum", "cmp", "column", "comm", "cp", "cut",
1837
- "diff", "du", "file", "fold", "git", "grep", "head", "hexdump",
1838
- "install", "jest", "jq", "less", "ln", "ls", "md5", "md5sum", "more",
1839
- "mv", "nl", "od", "pytest", "rg", "sed", "sha1sum", "sha256sum",
1840
- "shellcheck", "shfmt", "sort", "stat", "strings", "tail", "tee",
1841
- "uniq", "vitest", "wc", "xxd", "yamllint",
2006
+ "diff", "du", "echo", "file", "fold", "git", "grep", "head",
2007
+ "hexdump", "install", "jest", "jq", "less", "ln", "ls", "md5",
2008
+ "md5sum", "more", "mv", "nl", "od", "printf", "pytest", "rg",
2009
+ "sed", "sha1sum", "sha256sum", "shellcheck", "shfmt", "sort",
2010
+ "stat", "strings", "tail", "tee", "uniq", "vitest", "wc", "xxd",
2011
+ "yamllint",
1842
2012
  }
1843
2013
 
1844
2014
 
@@ -1879,6 +2049,67 @@ def executing_command(argv):
1879
2049
  return (None, None, [])
1880
2050
 
1881
2051
 
2052
+ # Operators that end a command in raw shell text. Used ONLY where `shlex` has
2053
+ # already refused the text, so a quote-aware split is not available — and
2054
+ # OVER-splitting is the safe direction here, because an extra segment can only
2055
+ # add a command word to check, never remove one.
2056
+ RAW_SEGMENT_SPLIT = re.compile(r"&&|\|\||[;|&\n]")
2057
+
2058
+
2059
+ def raw_command_words(text):
2060
+ """The program each segment of unlexable text puts in command position.
2061
+
2062
+ Answered on a whitespace split because the reason this path exists is that
2063
+ `shlex` refused the text. It still routes through `executing_command`, so a
2064
+ leading assignment and a wrapper — `nice`, `env`, `sudo`, `timeout` — are
2065
+ stepped over here exactly as they are on the parsed path. Reading the
2066
+ wrapper as the command is the bypass this guard's own comments record.
2067
+
2068
+ Args:
2069
+ text: The raw command string.
2070
+
2071
+ Returns:
2072
+ One entry per non-empty segment: the resolved program name, or None
2073
+ when the segment names none.
2074
+ """
2075
+ words = []
2076
+ for chunk in RAW_SEGMENT_SPLIT.split(text):
2077
+ tokens = chunk.split()
2078
+ if not tokens:
2079
+ continue
2080
+ words.append(executing_command(tokens)[1])
2081
+ return words
2082
+
2083
+
2084
+ def unparseable_reads_only(text):
2085
+ """Whether unlexable text only READS, and therefore files nothing.
2086
+
2087
+ `UNPARSEABLE_CREATION` matches raw text, so it cannot tell a filing from a
2088
+ sentence about one. `echo the guard's <creation> behaviour` fails to lex
2089
+ for the apostrophe alone, and was refused as a tracker creation — a command
2090
+ that files nothing, told that "this filing declares no readiness", with no
2091
+ printed remedy that applied to any part of it (CodySwannGT/lisa#3683).
2092
+
2093
+ The fallback is NOT removable and is not narrowed here. `<creation> #'` is
2094
+ a real filing that bash runs and `shlex` rejects, so "I could not parse it"
2095
+ must keep meaning refuse. What this asks instead is the COMMAND POSITION
2096
+ question the parsed path already asks and this arm skipped: only a command
2097
+ position can run anything, and a reader takes its operands as data.
2098
+
2099
+ Fails closed on everything else, which is the same asymmetry
2100
+ `READ_ONLY_PROGRAMS` documents: an unresolvable command word, an
2101
+ unrecognised program, or a text with no segments at all is refused.
2102
+
2103
+ Args:
2104
+ text: The raw command string that would not lex.
2105
+
2106
+ Returns:
2107
+ True when every segment's command word is a known reader.
2108
+ """
2109
+ words = raw_command_words(text)
2110
+ return bool(words) and all(word in READ_ONLY_PROGRAMS for word in words)
2111
+
2112
+
1882
2113
  def executed_operand(argv):
1883
2114
  """The path this command EXECUTES, or None.
1884
2115
 
@@ -2021,7 +2252,9 @@ def file_creation(text, depth):
2021
2252
  `node wrapper.mjs`, a Python client, or anything else that speaks HTTP
2022
2253
  directly — it needs a tracker endpoint AND a creation verb in the same
2023
2254
  file, which is what keeps a changelog that merely mentions `issueCreate`
2024
- from reading as a creation.
2255
+ from reading as a creation. It also needs the file to be capable of
2256
+ SENDING what it spells: a helper that writes the same payload into a test
2257
+ fixture submits nothing. See `payload_is_inert`.
2025
2258
 
2026
2259
  Args:
2027
2260
  text: The file's contents.
@@ -2041,6 +2274,9 @@ def file_creation(text, depth):
2041
2274
  GRAPHQL_CREATE.search(text)
2042
2275
  and TRACKER_ENDPOINT.search(text)
2043
2276
  and not text_declares_readiness(text)
2277
+ # A payload the file WRITES rather than SENDS is data. See
2278
+ # `payload_is_inert` for why the absence of egress alone is not enough.
2279
+ and not payload_is_inert(text)
2044
2280
  ):
2045
2281
  return "a tracker creation", [ready_role], None
2046
2282
  return None
@@ -2095,6 +2331,12 @@ def scan(text, depth, from_file=False):
2095
2331
  # reachable by declaring inline instead, which this change makes work.
2096
2332
  if text_declares_readiness(text):
2097
2333
  return None
2334
+ # The command-position arm of the same reasoning `executed_operand`
2335
+ # applies. A text every one of whose segments runs a known READER
2336
+ # cannot file anything, whatever its prose says — see
2337
+ # `unparseable_reads_only` for why this does not narrow the fallback.
2338
+ if unparseable_reads_only(text):
2339
+ return None
2098
2340
  if UNPARSEABLE_CREATION.search(text):
2099
2341
  return (
2100
2342
  "an unparseable command that reads as a tracker creation",
@@ -2199,6 +2441,30 @@ def scan(text, depth, from_file=False):
2199
2441
  # a human one retry and a false negative costs the entire control.
2200
2442
  inline_override = (OVERRIDE_NAME + "=") in command
2201
2443
 
2444
+
2445
+ def remedy_for(signature):
2446
+ """Which extra remediation paragraph this refusal earns.
2447
+
2448
+ A guard that prints every remedy it knows prints one that does not apply,
2449
+ and a remedy that cannot be performed is the failure this ticket is about
2450
+ (CodySwannGT/lisa#3683): the file-payload paragraph told an author to drop
2451
+ a transmitting primitive that a DOCUMENT does not have, and the
2452
+ unparseable arm printed declaration advice for a command that files
2453
+ nothing. Both are now keyed off which branch actually fired.
2454
+
2455
+ Args:
2456
+ signature: The refusal signature.
2457
+
2458
+ Returns:
2459
+ A key the shell half maps to a paragraph, or an empty string.
2460
+ """
2461
+ if signature.startswith("an unparseable command"):
2462
+ return "unparseable"
2463
+ if " inside " in signature:
2464
+ return "file"
2465
+ return ""
2466
+
2467
+
2202
2468
  found = scan(command, 0)
2203
2469
  if found is not None:
2204
2470
  signature, roles, target = found
@@ -2209,6 +2475,7 @@ if found is not None:
2209
2475
  print("signature=%s" % signature)
2210
2476
  print("roles=%s" % ", ".join(role for role in roles if role))
2211
2477
  print("target=%s" % (target or ""))
2478
+ print("remedy=%s" % remedy_for(signature))
2212
2479
  sys.exit(0)
2213
2480
 
2214
2481
  print("ALLOW")
@@ -2239,11 +2506,28 @@ if [ -n "$structured_call" ]; then
2239
2506
  exit 0
2240
2507
  fi
2241
2508
 
2509
+ # A PACKED label string counts. Exact equality against the flattened value
2510
+ # list reads `labels: ["status:ready"]` and nothing else, but the same
2511
+ # compliant filing spelled `labels: "status:ready,type:Bug"` — the shape
2512
+ # `gh issue create --label` takes, and the shape several MCP servers pass
2513
+ # through verbatim — carries no value equal to the role, so the guard
2514
+ # refused a filing that had declared exactly what it demanded. A false
2515
+ # positive in a guard costs more than a miss: it teaches the operator that
2516
+ # the guard is wrong, and the next refusal is argued with rather than
2517
+ # obeyed.
2518
+ #
2519
+ # Split on the DELIMITERS a packed list uses (comma, semicolon, newline) and
2520
+ # trimmed, never on a `contains` match. `contains` would accept a body that
2521
+ # merely mentions the role in prose — "do not mark this status:ready" — and
2522
+ # that is a fail-open on the one question this path exists to answer.
2242
2523
  structured_declaration="$(
2243
2524
  printf '%s' "$input" |
2244
2525
  jq -r --arg role "$ready_role" '
2245
2526
  [(.tool_input // {}) | .. | strings] as $values
2246
- | if ($values | index($role)) then "role"
2527
+ | ($values + ($values
2528
+ | map(splits("[,;\n]"))
2529
+ | map(sub("^\\s+"; "") | sub("\\s+$"; "")))) as $atoms
2530
+ | if ($atoms | index($role)) then "role"
2247
2531
  elif ($values | map(select(contains("[lisa-human-gate]"))) | length) > 0 then "gate"
2248
2532
  else "" end
2249
2533
  ' 2>/dev/null || printf 'UNREADABLE'
@@ -2296,7 +2580,8 @@ case "$verdict" in
2296
2580
  refuse \
2297
2581
  "$(verdict_field signature)" \
2298
2582
  "$(verdict_field roles)" \
2299
- "$(verdict_field target)"
2583
+ "$(verdict_field target)" \
2584
+ "$(verdict_field remedy)"
2300
2585
  ;;
2301
2586
  esac
2302
2587
 
@@ -31,6 +31,27 @@ set -euo pipefail
31
31
 
32
32
  input="$(cat)"
33
33
 
34
+ # Evaluate once per tool call when this guard is registered on both channels.
35
+ #
36
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
37
+ # manifest, and where both are live the harness runs this guard twice for one
38
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
39
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
40
+ # call; a differing vintage, a refusal, and a host with one channel all
41
+ # evaluate exactly as before. Nothing is de-registered by it
42
+ # (CodySwannGT/lisa#3814).
43
+ #
44
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
45
+ # older channel copy that predates it is unaffected.
46
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
47
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
48
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
49
+ # shellcheck source=guard-dedupe.bash
50
+ . "$lisa_guard_dedupe_lib"
51
+ trap 'lisa_guard_dedupe_record $?' EXIT
52
+ lisa_guard_dedupe block-instruction-file-edits "$input"
53
+ fi
54
+
34
55
  command -v jq >/dev/null 2>&1 || exit 0
35
56
 
36
57
  # The operator's override. Checked before anything else so it is always the
@@ -70,25 +70,57 @@
70
70
  # propagate any further either — a path a followed script merely NAMES is data
71
71
  # one file further out, not a third hop.
72
72
  #
73
- # ## Every agent surface reaches this guard
73
+ # ## Every agent surface reaches this guard, and Codex reaches it twice over
74
74
  #
75
- # There is no parity gap left to record. A note here used to record one for
76
- # three surfaces; every one of those ports now exists, and the note outlived
77
- # them. It is deleted rather than trimmed, because a stale gap note is
78
- # worse than none: it OVERSTATES the gap, and a reader who trusts it goes
79
- # looking for three missing ports, finds two present, and cannot tell which
80
- # third is real without redoing the measurement from scratch. AGENTS.md asks
81
- # for a gap to be documented instead of silently dropped — that only works
82
- # while the document is true.
75
+ # There is no parity gap left to record. A note here used to record one, and it
76
+ # was wrong by the time anyone read it through two wordings. The first named
77
+ # three surfaces; every one of those ports then shipped and the note stayed
78
+ # unedited. The second named Codex alone, and Codex had never been the gap
79
+ # either: the note and the check that read it were both looking in
80
+ # `src/codex/scripts/`, and `src/codex/hooks-installer.ts` says in its own
81
+ # opening remark that the linked-script layout that directory serves is RETIRED
82
+ # (CodySwannGT/lisa#3750).
83
+ #
84
+ # The note is deleted rather than trimmed, because a stale gap note is worse
85
+ # than none: it OVERSTATES the gap, and a reader who trusts it goes looking for
86
+ # three missing ports, finds two present, and cannot tell which third is real
87
+ # without redoing the measurement from scratch. AGENTS.md asks for a gap to be
88
+ # documented instead of silently dropped — that only works while the document
89
+ # is true.
90
+ #
91
+ # The deeper lesson is not "the note was stale". It is that a note asserting
92
+ # ABSENCE and a check deriving presence from ONE path shape agree with each
93
+ # other for free — the check read the same retired directory the note did, so
94
+ # it returned a clean tick on a claim that was false.
83
95
  #
84
96
  # Where each surface picks it up, so the next reader measures instead of
85
97
  # guessing: Claude and Copilot from `.claude-plugin/plugin.json`, Cursor from
86
- # `hooks/hooks.json`, Codex from `.codex-plugin/hooks.json` AND from the
87
- # enforcement-fallback dispatcher, Antigravity from `hooks.json` via the
88
- # `.agy.sh` adapter beside this file, and OpenCode from
89
- # `src/opencode/plugin-templates/lisa-block-managed-file-edits.ts`.
90
- # `tests/unit/hooks/managed-file-guard-parity-note.test.ts` fails if this
91
- # paragraph goes stale in either direction.
98
+ # `hooks/hooks.json`, Antigravity from `hooks.json` via the `.agy.sh` adapter
99
+ # beside this file, OpenCode from
100
+ # `src/opencode/plugin-templates/lisa-block-managed-file-edits.ts`, and Codex
101
+ # from TWO channels rather than one:
102
+ #
103
+ # `plugins/lisa/.codex-plugin/hooks.json` registers this guard directly, on
104
+ # the Codex plugin channel.
105
+ #
106
+ # `scripts/lisa-enforcement-fallback.sh` names this guard in its roster, and
107
+ # `src/codex/enforcement-fallback-installer.ts` registers that dispatcher on
108
+ # `PreToolUse` for `Bash|Edit|Write|apply_patch` — which is how Codex's
109
+ # `apply_patch` writes are reached at all.
110
+ #
111
+ # Both Codex channels are measured rather than inferred — see
112
+ # `tests/unit/codex/block-managed-file-edits-codex.test.ts`, which drives the
113
+ # registered command as a subprocess in a synthetic host project: a Codex
114
+ # `apply_patch` or `Bash` redirect at a copy-overwrite template exits 2; an
115
+ # ordinary edit to a host-owned file exits 0.
116
+ #
117
+ # Two checks hold this paragraph to the tree.
118
+ # `tests/unit/hooks/managed-file-guard-parity-note.test.ts` fails if it goes
119
+ # stale in either direction — a port that ships without the note being
120
+ # narrowed, and a port REMOVED without it being restored.
121
+ # `check:guard-parity-notes` refuses an absence claim written back in, and now
122
+ # resolves Codex from those two registration channels as well as from a path
123
+ # shape, so it can no longer agree with a false note for free.
92
124
  #
93
125
  # Exemptions (allowed):
94
126
  # - `LISA_ALLOW_MANAGED_FILE_WRITE` set — the operator's explicit override,
@@ -107,6 +139,27 @@ set -euo pipefail
107
139
 
108
140
  input="$(cat)"
109
141
 
142
+ # Evaluate once per tool call when this guard is registered on both channels.
143
+ #
144
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
145
+ # manifest, and where both are live the harness runs this guard twice for one
146
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
147
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
148
+ # call; a differing vintage, a refusal, and a host with one channel all
149
+ # evaluate exactly as before. Nothing is de-registered by it
150
+ # (CodySwannGT/lisa#3814).
151
+ #
152
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
153
+ # older channel copy that predates it is unaffected.
154
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
155
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
156
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
157
+ # shellcheck source=guard-dedupe.bash
158
+ . "$lisa_guard_dedupe_lib"
159
+ trap 'lisa_guard_dedupe_record $?' EXIT
160
+ lisa_guard_dedupe block-managed-file-edits "$input"
161
+ fi
162
+
110
163
  command -v jq >/dev/null 2>&1 || exit 0
111
164
 
112
165
  # The operator's override, checked first so it is always the cheapest way out.
@@ -547,6 +600,16 @@ SOURCE_BUILTINS = {"source", "."}
547
600
  FOLLOW_WRAPPERS = {
548
601
  "builtin": (frozenset(), 0),
549
602
  "command": (frozenset(), 0),
603
+ # `env bash edit.sh` runs `bash edit.sh`. Its absence here was the one
604
+ # wrapper on this list a caller reaches for without thinking about
605
+ # wrappers at all, because `env VAR=value <cmd>` is how a one-off
606
+ # environment override is spelled. Those `VAR=value` operands need no
607
+ # positional count: the walk below re-enters its assignment skip on the
608
+ # next iteration and steps over as many as are present.
609
+ "env": (
610
+ frozenset({"-C", "--chdir", "-P", "-S", "--split-string", "-u", "--unset"}),
611
+ 0,
612
+ ),
550
613
  "exec": (frozenset({"-a"}), 0),
551
614
  "nice": (frozenset({"-n", "--adjustment"}), 0),
552
615
  "nohup": (frozenset(), 0),
@@ -91,6 +91,27 @@ set -euo pipefail
91
91
 
92
92
  input="$(cat)"
93
93
 
94
+ # Evaluate once per tool call when this guard is registered on both channels.
95
+ #
96
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
97
+ # manifest, and where both are live the harness runs this guard twice for one
98
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
99
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
100
+ # call; a differing vintage, a refusal, and a host with one channel all
101
+ # evaluate exactly as before. Nothing is de-registered by it
102
+ # (CodySwannGT/lisa#3814).
103
+ #
104
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
105
+ # older channel copy that predates it is unaffected.
106
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
107
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
108
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
109
+ # shellcheck source=guard-dedupe.bash
110
+ . "$lisa_guard_dedupe_lib"
111
+ trap 'lisa_guard_dedupe_record $?' EXIT
112
+ lisa_guard_dedupe block-no-verify "$input"
113
+ fi
114
+
94
115
  # Both interpreters must be probed BEFORE they are used, and a missing one must
95
116
  # be announced rather than swallowed.
96
117
  #
@@ -24,6 +24,27 @@ set -euo pipefail
24
24
 
25
25
  input="$(cat)"
26
26
 
27
+ # Evaluate once per tool call when this guard is registered on both channels.
28
+ #
29
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
30
+ # manifest, and where both are live the harness runs this guard twice for one
31
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
32
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
33
+ # call; a differing vintage, a refusal, and a host with one channel all
34
+ # evaluate exactly as before. Nothing is de-registered by it
35
+ # (CodySwannGT/lisa#3814).
36
+ #
37
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
38
+ # older channel copy that predates it is unaffected.
39
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
40
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
41
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
42
+ # shellcheck source=guard-dedupe.bash
43
+ . "$lisa_guard_dedupe_lib"
44
+ trap 'lisa_guard_dedupe_record $?' EXIT
45
+ lisa_guard_dedupe block-shell-json-parsing "$input"
46
+ fi
47
+
27
48
  # Probe the interpreters before the first use. Under `set -euo pipefail` an
28
49
  # absent jq does not merely skip the parse — the assignment below dies with
29
50
  # 127, and 127 is not a refusal, so the guard vanishes and the command runs.