@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
@@ -402,6 +402,11 @@ function applyCd(current, target) {
402
402
  * disagreeing. The scripted form is the one neither covers, and it is the only
403
403
  * cell this arm fills.
404
404
  *
405
+ * That reason is a dependency on a runtime Lisa does not ship, and it can
406
+ * expire without anything here failing. It is pinned at
407
+ * {@link VERIFIED_RUNTIME_VERSION} and reported when the runtime moves off it
408
+ * — read that constant before widening this arm.
409
+ *
405
410
  * ## A relative script path is resolved against the cwd the command will have
406
411
  *
407
412
  * A leading `cd` establishes the directory this guard resolves relative tokens
@@ -511,11 +516,28 @@ function readState(sessionId) {
511
516
  }
512
517
  }
513
518
 
519
+ /**
520
+ * Write the session's binding state, MERGING over whatever is already there.
521
+ *
522
+ * Every caller below hands in all three lifecycle fields explicitly, so the
523
+ * merge changes nothing for them — `claimedRoot: null` still clears a claim.
524
+ * What it buys is that a field one writer owns is not silently dropped by
525
+ * another writer that had no opinion about it: `runtimeNoticed` is written by
526
+ * {@link noticeRuntimeDrift} and read by nobody else, and a clobbering write
527
+ * would turn its "reported once" into "reported again after the next
528
+ * acknowledgement", which is the noise the notice is designed not to be.
529
+ * @param sessionId - The session whose state file this is
530
+ * @param state - Fields to write over the recorded state
531
+ * @returns Whether the write succeeded
532
+ */
514
533
  function writeState(sessionId, state) {
515
534
  const file = stateFile(sessionId);
516
535
  try {
517
536
  mkdirSync(dirname(file), { recursive: true });
518
- writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`);
537
+ writeFileSync(
538
+ file,
539
+ `${JSON.stringify({ ...readState(sessionId), ...state }, null, 2)}\n`
540
+ );
519
541
  return true;
520
542
  } catch (error) {
521
543
  say(`could not record the binding (${error.message}); NOT enforcing`);
@@ -695,6 +717,162 @@ function displaced(bound, observed) {
695
717
  ]);
696
718
  }
697
719
 
720
+ /**
721
+ * The runtime version the inline-redirect gap was last verified at.
722
+ *
723
+ * ## What this constant is a pin ON
724
+ *
725
+ * This guard deliberately does NOT refuse an INLINE redirect into another
726
+ * worktree — `cd <other> && git …`, `git -C <other> …`. The reason is good and
727
+ * is written out at {@link scriptReachingForeignTree}: the runtime's own
728
+ * worktree isolation already refuses them, and duplicating a control is how two
729
+ * controls drift into disagreeing about the same command.
730
+ *
731
+ * That reason is a DEPENDENCY on behaviour Lisa does not ship. It was verified
732
+ * refusing at this version, with the refusal wording "this command names git in
733
+ * a form too complex to verify that it stays inside the worktree" and
734
+ * "redirects git to the shared checkout via -C".
735
+ *
736
+ * ## Why a pin rather than a test
737
+ *
738
+ * The runtime's isolation is not reachable from a unit test: it adjudicates
739
+ * inside a live worktree-isolated session, and there is no entry point that
740
+ * takes one hook envelope and returns a verdict the way this guard does. So the
741
+ * dependency cannot be turned red by writing a test, and a claim that cannot go
742
+ * red goes stale silently instead (CodySwannGT/lisa#3944).
743
+ *
744
+ * What CAN be measured is whether the runtime is still the one the claim was
745
+ * checked against. That does not prove the behaviour changed — only that nobody
746
+ * has checked since, which is the honest thing to report and the whole of what
747
+ * this pin does.
748
+ *
749
+ * ## Moving it
750
+ *
751
+ * Re-run both inline forms in a worktree-isolated session. Still refused: move
752
+ * this pin, and say where. Permitted: those two cells are UNCOVERED, and #3944
753
+ * owns the decision about which of them Lisa takes on.
754
+ */
755
+ const VERIFIED_RUNTIME_VERSION = "2.1.261";
756
+
757
+ /** The ticket that owns the inline-redirect dependency and its expiry. */
758
+ const RUNTIME_ASSUMPTION_TICKET = "CodySwannGT/lisa#3944";
759
+
760
+ /**
761
+ * This session's runtime version, or null when this is not that runtime.
762
+ *
763
+ * Three answers, and the difference between the last two is the point:
764
+ * - `null` — not Claude Code. Codex, Cursor and Copilot ship this guard too,
765
+ * and the assumption above is about a control none of them has. A notice that
766
+ * fires on every session of every harness is noise, and noise gets switched
767
+ * off.
768
+ * - `""` — Claude Code, which will not say which version. Read as a DIVERGENCE
769
+ * rather than as a match: the assumption is unconfirmed either way, and
770
+ * treating an unreadable version as "still 2.1.261" is exactly the silent
771
+ * expiry #3944 was filed about.
772
+ * - a version — take it at its word.
773
+ *
774
+ * Both env vars are read because either can be the one a future release keeps.
775
+ * `AI_AGENT` carries `claude-code_2-1-261_agent`; `CLAUDE_CODE_EXECPATH` ends
776
+ * in the version directory the running build was launched from.
777
+ * @param env - Environment to read
778
+ * @returns The version, `""` when unreadable, or null for another runtime
779
+ */
780
+ const isDigit = ch => ch >= "0" && ch <= "9";
781
+
782
+ /** @param value - Segment to test @returns Whether it is all digits */
783
+ const allDigits = value => value.length > 0 && [...value].every(isDigit);
784
+
785
+ /** @param value - Segment to read @returns Its trailing digit run, else `""` */
786
+ const trailingDigits = value => {
787
+ let start = value.length;
788
+ while (start > 0 && isDigit(value[start - 1])) start -= 1;
789
+ return value.slice(start);
790
+ };
791
+
792
+ function runtimeVersion(env) {
793
+ if (!env.CLAUDECODE) return null;
794
+ const tagged = /claude-code[_-](\d+)[-.](\d+)[-.](\d+)/u.exec(
795
+ env.AI_AGENT ?? ""
796
+ );
797
+ if (tagged) return `${tagged[1]}.${tagged[2]}.${tagged[3]}`;
798
+ // Read the version off the final path segment by scanning characters rather
799
+ // than by regex: an unanchored `\d+\.\d+\.\d+` before `$` backtracks
800
+ // super-linearly on a long execpath, and the version Claude Code launches
801
+ // from is always the trailing directory name.
802
+ const segments = (env.CLAUDE_CODE_EXECPATH ?? "").split("/").filter(Boolean);
803
+ const trailing = segments.length ? segments[segments.length - 1] : "";
804
+ const parts = trailing.split(".");
805
+ if (parts.length < 3) return "";
806
+ const [major, minor, patch] = parts.slice(-3);
807
+ const majorDigits = trailingDigits(major);
808
+ if (!majorDigits || !allDigits(minor) || !allDigits(patch)) return "";
809
+ return `${majorDigits}.${minor}.${patch}`;
810
+ }
811
+
812
+ /**
813
+ * The notice text for a runtime the inline-redirect claim was not checked at.
814
+ * @param version - The observed runtime version, `""` when unreadable
815
+ * @returns The lines to report
816
+ */
817
+ function driftNotice(version) {
818
+ return [
819
+ "worktree-binding-guard: the inline-redirect gap is UNVERIFIED here.",
820
+ "",
821
+ "This guard does not refuse `cd <other-worktree> && git …` or",
822
+ "`git -C <other-worktree> …`. Those two cells are covered by the runtime's",
823
+ `own worktree isolation, verified refusing at Claude Code ${VERIFIED_RUNTIME_VERSION}.`,
824
+ version
825
+ ? `This session is running ${version}, so that check does not cover it.`
826
+ : "This session's runtime version could not be read, so nothing covers it.",
827
+ "",
828
+ "This does NOT say the runtime stopped refusing them — only that nobody has",
829
+ "checked since. Run both forms in a worktree-isolated session: still",
830
+ "refused, move VERIFIED_RUNTIME_VERSION in worktree-binding-guard.mjs;",
831
+ `permitted, the two cells are uncovered and ${RUNTIME_ASSUMPTION_TICKET}`,
832
+ "owns what Lisa does about it.",
833
+ ];
834
+ }
835
+
836
+ /**
837
+ * Report, once per session, that the runtime moved off the verified version.
838
+ *
839
+ * ## Why SessionStart and not the guarded call
840
+ *
841
+ * SessionStart is the guard's first evaluation of a session, it fires exactly
842
+ * once per start, and its `additionalContext` is the one channel here that the
843
+ * agent actually reads — the `say()` lines elsewhere in this file are for a
844
+ * human reading a transcript. A notice attached to a guarded tool call would
845
+ * either repeat on every Bash command or need a suppression rule of its own.
846
+ *
847
+ * ## Why it is recorded rather than counted
848
+ *
849
+ * A resume fires SessionStart again for the same session id. The recorded
850
+ * version is what makes the second one silent, and recording the VERSION rather
851
+ * than a flag means a runtime that moves twice inside one resumed session is
852
+ * still reported the second time.
853
+ *
854
+ * Never refuses. The dependency is a reporting gap, not a live displacement,
855
+ * and blocking a session over a version number would be a wall where an
856
+ * observation belongs.
857
+ * @param sessionId - The session being started
858
+ * @param env - Environment to read the runtime version from
859
+ * @returns Nothing; every path through this allows
860
+ */
861
+ function noticeRuntimeDrift(sessionId, env) {
862
+ const version = runtimeVersion(env);
863
+ if (version === null || version === VERIFIED_RUNTIME_VERSION) return;
864
+ if (readState(sessionId)?.runtimeNoticed === version) return;
865
+ writeState(sessionId, { runtimeNoticed: version });
866
+ process.stdout.write(
867
+ `${JSON.stringify({
868
+ hookSpecificOutput: {
869
+ hookEventName: "SessionStart",
870
+ additionalContext: driftNotice(version).join("\n"),
871
+ },
872
+ })}\n`
873
+ );
874
+ }
875
+
698
876
  function evaluate(payload) {
699
877
  const sessionId = payload.session_id;
700
878
  const cwd = payload.cwd;
@@ -707,6 +885,10 @@ function evaluate(payload) {
707
885
 
708
886
  if (payload.hook_event_name === "SessionStart") {
709
887
  recordBaseline(sessionId, observed);
888
+ // After the baseline, never before: `recordBaseline` writes a fresh state
889
+ // for a session it has not seen, and the notice's record has to go on top
890
+ // of that write rather than under it.
891
+ noticeRuntimeDrift(sessionId, process.env);
710
892
  return 0;
711
893
  }
712
894
  if (payload.tool_name === "EnterWorktree") {
@@ -36,6 +36,27 @@ set -uo pipefail
36
36
 
37
37
  input="$(cat)"
38
38
 
39
+ # Evaluate once per tool call when this guard is registered on both channels.
40
+ #
41
+ # Lisa reaches an agent through the repository dispatcher AND the plugin
42
+ # manifest, and where both are live the harness runs this guard twice for one
43
+ # tool call. `guard-dedupe.bash` short-circuits the second run ONLY when a
44
+ # byte-identical copy already ALLOWED this exact payload on this exact tool
45
+ # call; a differing vintage, a refusal, and a host with one channel all
46
+ # evaluate exactly as before. Nothing is de-registered by it
47
+ # (CodySwannGT/lisa#3814).
48
+ #
49
+ # Absent library means no dedupe, which is the pre-existing behaviour, so an
50
+ # older channel copy that predates it is unaffected.
51
+ lisa_guard_hook_dir="${BASH_SOURCE[0]%/*}"
52
+ lisa_guard_dedupe_lib="$lisa_guard_hook_dir/guard-dedupe.bash"
53
+ if [ -r "$lisa_guard_dedupe_lib" ]; then
54
+ # shellcheck source=guard-dedupe.bash
55
+ . "$lisa_guard_dedupe_lib"
56
+ trap 'lisa_guard_dedupe_record $?' EXIT
57
+ lisa_guard_dedupe worktree-binding-guard "$input"
58
+ fi
59
+
39
60
  if ! command -v node >/dev/null 2>&1; then
40
61
  printf 'worktree-binding-guard: node not found; worktree-binding enforcement is NOT active\n' >&2
41
62
  exit 0
@@ -29,6 +29,7 @@ Paths are relative to this file.
29
29
  | `prd-definition-of-ready` | authoring or validating a PRD's requirement atoms | [../reference/prd-definition-of-ready.md](../reference/prd-definition-of-ready.md) |
30
30
  | `prd-lifecycle-rollup` | rolling a PRD up from its generated top-level work | [../reference/prd-lifecycle-rollup.md](../reference/prd-lifecycle-rollup.md) |
31
31
  | `usage-accounting` | writing the managed `## Lisa Usage` section on an artifact | [../reference/usage-accounting.md](../reference/usage-accounting.md) |
32
+ | `work-item-trailer-definition` | reading, counting, or auditing a `Work-Item:` trailer — BEFORE reaching for `%(trailers)` | [../reference/work-item-trailer-definition.md](../reference/work-item-trailer-definition.md) |
32
33
 
33
34
  ## Build-intake claim time
34
35
 
@@ -0,0 +1,50 @@
1
+ # Operational Hazards — Short-Lived, Expiring, and Read by Sessions That Do Not Exist Yet
2
+
3
+ **A broadcast cannot reach a session that starts after it, and the sessions that missed it never
4
+ knew there was something to miss.** The lanes most likely to collide on a port, saturate the
5
+ workers, or re-hit a defect somebody diagnosed an hour ago are precisely the ones that just
6
+ started — so the population a fan-out structurally cannot reach is the population most exposed.
7
+
8
+ Enumerating peers does not fix it: two sessions listing peers at the same moment returned different
9
+ rosters, so "I told everyone" carries an unknown hole.
10
+
11
+ ## Mandatory
12
+
13
+ - **Declare a transient hazard instead of announcing it.** A condition another session will hit —
14
+ a port another lane holds, a saturated resource, a command that dumps credentials, a gate that is
15
+ red for an environmental reason — is not communicated until this has run:
16
+
17
+ ```
18
+ node "$CLAUDE_PLUGIN_ROOT/hooks/operational-hazards.mjs" --declare <id> \
19
+ --hazard "<what will go wrong>" \
20
+ --avoid "<what to do instead>" \
21
+ --until <YYYY-MM-DD>
22
+ ```
23
+
24
+ `--until` is required and has no default. **An entry that never expires is doctrine, and doctrine
25
+ belongs in a rule** — a hazard surface without an expiry becomes permanent scripture, which is
26
+ the failure this exists against. Past its `until`, an entry stops being shown with nobody
27
+ remembering to remove it.
28
+
29
+ - **Lift it when it ends.** A declaration nothing can void is the defect the QA-failure signal and
30
+ the human gate each closed:
31
+
32
+ ```
33
+ node "$CLAUDE_PLUGIN_ROOT/hooks/operational-hazards.mjs" --lift <id> --because "<what resolved it>"
34
+ ```
35
+
36
+ - **Read it where no hook runs:** `node "$CLAUDE_PLUGIN_ROOT/hooks/operational-hazards.mjs" --list`.
37
+
38
+ ## What arrives on its own
39
+
40
+ On Claude Code the ledger is read for you at SessionStart **and SubagentStart** — a subagent
41
+ spawned an hour from now reads it, which no fan-out reaches at all — and re-checked after every
42
+ tool call, so a hazard declared or lifted **while you are running** arrives on your next tool
43
+ result. Two `stat` calls when nothing changed.
44
+
45
+ An entry naming no evaluable expiry still applies and is reported as `unchecked`, loudly. An expiry
46
+ that lapsed into "no longer shown" would restore permanent scripture while every signal read
47
+ healthy.
48
+
49
+ Both ledger tiers, what belongs here versus in a rule, and the surfaces with no hook runtime:
50
+ [reference/operational-hazards.md](../reference/operational-hazards.md).
@@ -4,4 +4,4 @@ Before the first durable project mutation (code, tests, config, docs, committed
4
4
 
5
5
  The mandatory order is: live-validate an explicit ref, or conservatively search and create exactly one valid leaf through `lisa-tracker-write` when no unique match exists; idempotently claim it through `lisa-tracker-claim`; then persist and verify the worktree-local binding with `node scripts/lisa-work-item.mjs link <ref>`. A tracker that answers no, and any claim or binding failure, blocks durable work; a tracker that cannot be reached does not block a commit — the offline checks still run, the skip is loud on stderr, and the required `Work-Item Traceability` check re-runs the live checks with credentials before anything merges.
6
6
 
7
- Carry that canonical ref through the branch, every ordinary commit's `Work-Item:` trailer, the PR, usage/evidence, and `lisa-tracker-sync`. Hooks and CI never create tickets. Keep the binding through interruptions or blocked outcomes; run `node scripts/lisa-work-item.mjs clear` only after merge/deploy/verification, two-way linkage/evidence, and the tracker item have all reached true terminal completion.
7
+ Carry that canonical ref through the branch, every ordinary commit's `Work-Item:` trailer, the PR, usage/evidence, and `lisa-tracker-sync`. Hooks and CI never create tickets. Anything that READS a `Work-Item:` trailer — a gate, an audit, a count, a report — must import `workItemLines` from the shipped `scripts/lisa-work-item.mjs` rather than reach for `git interpret-trailers` or `%(trailers)`, which see only a message's final block and miss 92% of this fleet's trailers; read the `work-item-trailer-definition` rule before writing such a reader. Keep the binding through interruptions or blocked outcomes; run `node scripts/lisa-work-item.mjs clear` only after merge/deploy/verification, two-way linkage/evidence, and the tracker item have all reached true terminal completion.
@@ -1028,7 +1028,7 @@ When `github-to-tracker` is invoked AND `tracker = "github"`, both reads and wri
1028
1028
 
1029
1029
  Never overload one label across both lifecycles.
1030
1030
 
1031
- The same separation applies for Linear self-host (`source = "linear"` AND `tracker = "linear"`), with one asymmetry: project-level **labels** (`prd-*`) drive the PRD lifecycle, because a PRD is a Linear Project and Projects carry their own status object rather than Issue workflow states; issue-level **workflow states** (`linear.workflow`) drive the build lifecycle; the sentinel feedback issue carries the issue-level `prd-intake-feedback` label. So on Linear the two lanes are not merely different vocabularies, they are different *mechanisms* — never move a PRD by state or an Issue by `status:*` label.
1031
+ The same separation applies for Linear self-host (`source = "linear"` AND `tracker = "linear"`), with one asymmetry: project-level **labels** (`prd-*`) drive the PRD lifecycle, because a PRD is a Linear Project and Projects carry their own status object rather than Issue workflow states; issue-level **workflow states** (`linear.workflow`) drive the build lifecycle; clarifying-question comments go on the Project itself via `commentCreate(input: { projectId, body })`, so the PRD lane needs no issue label of its own (`prd-intake-feedback` survives only to recognise the fabricated feedback issues earlier versions created). So on Linear the two lanes are not merely different vocabularies, they are different *mechanisms* — never move a PRD by state or an Issue by `status:*` label.
1032
1032
 
1033
1033
  ## Notion access (substrate ladder)
1034
1034
 
@@ -182,7 +182,7 @@ The env rungs are whatever the project configures for `done` — which is **env-
182
182
 
183
183
  Intermediate-env rollup and terminal native closure are distinct: a parent **rolls up to** an intermediate env (`On Dev`/`On Stg`) as its required leaves reach it, but native closure (next section) fires **only** at the production/terminal `done` value. A parent sitting at `On Stg` is correctly rolled up *and* still open.
184
184
 
185
- **Single-environment collapse (this repo).** Lisa's own deploy has only `main`/`production` (no dev/staging), so `done` is a single value, not a map. For GitHub, the build lifecycle collapses to one chain: `ready → claimed (in-progress) → done`. The rollup terminal state is simply `done`. This is the *collapsed* case of the generic rule, not a different ruleprojects with more environments keep the env-keyed map.
185
+ **Single-environment collapse (this repo).** The collapse is a property of the **resolution**, not of the declaration. `.lisa.config.json` declares `github.labels.build.done` as the generic **env-keyed map** — `dev: status:on-dev`, `staging: status:on-stg`, `production: status:done` — which is exactly the shape `lisa-setup-github` creates by default. What this repo lacks is deploy *branches*: `deploy.branches` maps only `production → main`, so `production` is the sole reachable rung and every merge this repo can make resolves to `status:done`. For GitHub the build lifecycle therefore runs one chain in practice: `ready → claimed (in-progress) → done`, and the rollup terminal state is `status:done`. Do not read the collapse as an absent map: code that reads the `done` role must still resolve the merged PR's base branch through `deploy.branches` and take that env's rung, in this repo as in any other assuming a scalar here would read the wrong rung the moment a deploy branch is added. Projects with more deploy branches differ only in how many rungs are reachable, not in the declared shape.
186
186
 
187
187
  ## Terminal native closure
188
188
 
@@ -0,0 +1,115 @@
1
+ # Operational Hazards — reference
2
+
3
+ Full contract behind the `operational-hazards` slug (CodySwannGT/lisa#3681). The eager rule carries
4
+ the two commands; this is the reasoning, the tiers, and the boundary against doctrine.
5
+
6
+ ## The general property
7
+
8
+ **A detector whose only failure mode is staying quiet cannot be audited.** A fan-out reaches the
9
+ sessions alive at one instant. The sessions that were not alive receive nothing and are told
10
+ nothing, so nobody — the sender included — can name who was missed.
11
+
12
+ Measured on one machine in one hour: one session started 18 minutes after a warning it needed;
13
+ another started 8 minutes after a different one; four more appeared and vanished entirely inside
14
+ the hour. And the lanes most likely to collide are the ones that just started and are standing up
15
+ fresh environments — so the warning that would have prevented the collision cannot reach the
16
+ session that causes it, because that session does not exist yet.
17
+
18
+ **The hole is unmeasurable, not merely growing.** Two sessions on the same machine, enumerating
19
+ peers at the same moment, returned different rosters: one session appeared in one listing and had
20
+ never appeared in four consecutive reads by the other. So "I enumerated the roster and checked the
21
+ list" carries an unknown hole, and a fan-out audited against one session's view cannot be audited
22
+ at all.
23
+
24
+ ## What the existing rule injector cannot do
25
+
26
+ `inject-rules.sh` reads `CLAUDE_PLUGIN_ROOT/rules/eager` — the plugin root, not the project. A
27
+ project cannot register anything there without editing an installed plugin, which the next apply
28
+ overwrites. And it concatenates every `.md` unconditionally: no front matter, no date, no filter.
29
+ Anything written there is injected on every start, permanently, until a human deletes the file.
30
+
31
+ So the injector is the right *reach* and the wrong *store*. This ledger reuses the reach — the same
32
+ two start events, plus the tool-result event — and supplies a project-owned store whose entries
33
+ carry a condition something evaluates.
34
+
35
+ ## Why the expiry is the mechanism, not a convenience
36
+
37
+ **The expiry is what separates a transient hazard from doctrine**, and it is the only reason the
38
+ surface can be trusted with transient content: nobody has to remember to remove anything. A hazard
39
+ surface without an expiry becomes the permanent-scripture problem, and a project that has watched
40
+ that happen once will correctly refuse to use the surface again.
41
+
42
+ The shape is deliberately the threshold-ratchet exemption's (CodySwannGT/lisa#3856) rather than a
43
+ third scheme:
44
+
45
+ - record the condition in a form something can evaluate — `until`, a `YYYY-MM-DD` day or an
46
+ ISO-8601 timestamp, live *through* the day named;
47
+ - never let an unevaluable condition lapse into the permissive answer. An entry naming no evaluable
48
+ expiry — hand-written, or written before `until` existed — **still applies**, and is reported as
49
+ `unchecked` every time it is read.
50
+
51
+ An expiry that lapses into "no longer shown" is worse than no expiry: it restores permanent
52
+ scripture in reverse — the hazard vanishes — while every signal still reads healthy. That is why
53
+ the writer refuses `--declare` without `--until`, and refuses an `until` it cannot parse, while the
54
+ reader tolerates one and says so.
55
+
56
+ ## The inverse
57
+
58
+ A hazard also ends before its expiry. A declaration nothing can void is the defect closed twice
59
+ already — the QA-failure signal (CodySwannGT/lisa#3855) and the human gate (CodySwannGT/lisa#3852)
60
+ — so `--lift` appends a void record naming what resolved it.
61
+
62
+ Matching is **time-ordered**: a lift voids only a declaration that pre-dates it. A hazard
63
+ re-declared after an earlier lift is therefore not born discharged, which is the unsafe direction
64
+ #3852 records as the one this must not introduce. For the same reason the ledger union keeps the
65
+ **newest** declaration for an id, not the first one read.
66
+
67
+ ## The ledger's two tiers
68
+
69
+ - **`.lisa/HAZARDS.jsonl`**, committed — reaches every future reader of this project and merges by
70
+ git's built-in union driver. This is also the tier that answers "does `lisa apply` preserve it":
71
+ it is host-authored content under `.lisa/`, like the withdrawal tombstones beside it, and no
72
+ managed-file reconciliation writes it.
73
+ - **`${LISA_STATE_HOME:-~/.lisa}/operational-hazards.jsonl`** — reaches sibling sessions in OTHER
74
+ worktrees on this machine *now*, which a committed file cannot do until it is merged and pulled.
75
+ For the fleet case above, this is the tier that matters.
76
+
77
+ `--declare` and `--lift` write both. Reads are the union. Both files are append-only JSONL, so
78
+ concurrent writers on their own branches reconstruct exactly under `merge=union`.
79
+
80
+ ## How a running session is reached
81
+
82
+ `--session-start` delivers everything applying and stamps what the session was born knowing.
83
+ `--hook` runs as a PostToolUse hook and announces only what changed since that stamp: a hazard
84
+ declared while this session runs, and a **lift** of a hazard this session was told about. Both
85
+ directions were measured — the provenance of #3681 is a session that was sent a lift for a hold it
86
+ had never received.
87
+
88
+ A missing stamp is deliberately silent. A session whose starting knowledge is unknown would
89
+ otherwise be told every historical hazard, which degrades the mechanism into distrusting
90
+ everything.
91
+
92
+ The common path is cheap: when neither tier's `size:mtime` has moved, `--hook` exits after two
93
+ `stat` calls without parsing anything. It fires on every tool call in every session, so anything
94
+ expensive multiplies.
95
+
96
+ ## Surface parity
97
+
98
+ The hook pair ships into every payload that carries the base hooks, and the injector fires on
99
+ `SubagentStart` as well as `SessionStart` — which makes this better than a broadcast rather than
100
+ merely equivalent, since a fan-out never reaches a subagent at all.
101
+
102
+ Surfaces with no PostToolUse runtime get the start-event half and `--list`. That is the honest
103
+ split: **the durable half — reaching every session that starts later — is closed everywhere the
104
+ hook installs. The already-running half depends on a tool-result event, so where there is none, a
105
+ running session is reached only when it runs `--list`.**
106
+
107
+ ## What deliberately does not belong here
108
+
109
+ Anything that should still be true next month. A hazard is a condition of *this machine, this
110
+ branch, this hour*: a port another lane holds, a resource one run has saturated, a command that
111
+ dumps more than you asked for, a gate red for an environmental reason. If the entry would want
112
+ renewing when it expires, it was never a hazard — promote it through the learnings ladder to a rule
113
+ or an executable control, and let the entry lapse.
114
+
115
+ Not a severity taxonomy, not a notification system, and not a change to how eager rules work.
@@ -0,0 +1,129 @@
1
+ # Work-Item Trailer Definition — What Counts As One, And Which Reader Is Authoritative
2
+
3
+ Read this **before** writing anything that reads, counts, audits, or verifies a
4
+ `Work-Item:` reference in a commit message or a pull-request body. There are two
5
+ plausible definitions of "a `Work-Item:` trailer", they disagree about real
6
+ commits in this fleet, and the obvious tool — git's own trailer parser — is the
7
+ wrong one.
8
+
9
+ ## The two definitions
10
+
11
+ **Definition A — git's.** `git log --format='%(trailers:key=Work-Item)'` and
12
+ `git interpret-trailers --parse` read only the **final contiguous block** of
13
+ `Key: value` lines. Any non-trailer line after a trailer ends that block, and
14
+ everything above it becomes invisible to the parser.
15
+
16
+ **Definition B — Lisa's.** A `Work-Item:` line is any line of the **whole
17
+ message** whose first character begins the ASCII-case-insensitive prefix
18
+ `Work-Item:` at **column zero**, with a non-empty value after it. Position in
19
+ the message carries no information. Repeats of the same reference are accepted.
20
+
21
+ ## Definition B is authoritative. This is settled, not open.
22
+
23
+ Definition A was tried and abandoned in CodySwannGT/lisa#2672: it reported
24
+ `found 0` about messages that plainly contained a trailer, because a blank line
25
+ before the `Co-Authored-By:` attribution block — or a bot appending release
26
+ notes below it — was enough to end the block. Making Definition A work would
27
+ require that nothing ever append to a commit message, which is precisely the
28
+ assumption #2672 recorded as false.
29
+
30
+ The `🤖 Generated with Claude Code` convention puts boilerplate **after** the
31
+ trailer by default, so under Definition A a large share of this repository's own
32
+ commits read as untrailered. **The failure is the common case, not the edge
33
+ case.**
34
+
35
+ ### Measured, not asserted
36
+
37
+ Both definitions run over real history, non-merge commits, counting only commits
38
+ where at least one definition finds a reference:
39
+
40
+ | Range | Commits naming an item | Agree | A finds none, B finds one | Other disagreement |
41
+ |---|---|---|---|---|
42
+ | A single multi-agent branch | 51 | 4 | **47 (92%)** | 0 |
43
+ | Full default-branch history | 1,730 | 1,248 | **477 (27.6%)** | 5 |
44
+
45
+ The five "other" disagreements are commits carrying a bare `Work-Item: #1234`
46
+ above the block as well as the canonical reference inside it; B sees both, A
47
+ sees only the one in the final block.
48
+
49
+ **The undercount is clean, plausible and well-formed**, which is why it survives
50
+ review. `%(trailers)` printing nothing is genuinely convincing evidence until
51
+ you know this rule exists — which is why at least three separate audits have
52
+ shipped confident, specific, wrong findings from it, including an instruction to
53
+ amend a commit whose trailer was fine.
54
+
55
+ ## Do not reimplement it. Import it.
56
+
57
+ `workItemLines(message)` and `soleWorkItem(text, contract, subject)` are
58
+ exported from the shipped `scripts/lisa-work-item.mjs`. They are the definition.
59
+ A second parser is a second answer to "which item is this?", and two answers is
60
+ the state this rule exists to end.
61
+
62
+ ```js
63
+ import { workItemLines } from "./lisa-work-item.mjs";
64
+ ```
65
+
66
+ `workItemLines` returns every value found, in order of appearance, untouched
67
+ beyond trimming. `soleWorkItem` canonicalizes them against the tracker contract
68
+ and refuses a text naming two **different** items.
69
+
70
+ ## What each layer owns, and why the shape filter stays at the call site
71
+
72
+ `workItemLines` deliberately does **not** validate the shape of a value. It
73
+ answers "what does this text say?" — nothing more. Canonicalization,
74
+ repository-membership, and the refusal of the full issue-URL form all live in
75
+ `canonicalizeRef`, above it.
76
+
77
+ That split is load-bearing and must not be converged away. A reader that
78
+ filtered malformed values out during the scan would make a bad value
79
+ **invisible** rather than **refused**: `Work-Item: https://github.com/o/r/issues/7`
80
+ would read as no trailer at all, and the gate would report "no Work-Item trailer
81
+ anywhere in the commit message" about a message containing exactly one, which is
82
+ the #2672 failure re-created one layer down. The reader sees everything; the
83
+ layer above decides what is acceptable.
84
+
85
+ The consequence a caller must respect: **a filter you apply to `workItemLines`'
86
+ output is your filter, not the definition.** State what it drops.
87
+
88
+ ## Column zero is part of the definition
89
+
90
+ The prefix is anchored at column zero, so `# Work-Item: …` in a comment never
91
+ matches, and neither does a unified-diff line — every one of those carries a
92
+ space, `+` or `-` in that column. A reader that tolerates leading whitespace
93
+ matches the **context line of a verbose commit's own diff**. Measured over full
94
+ default-branch history: 2,059 `Work-Item:` lines at column zero, **zero**
95
+ indented ones. There is no real input that needs the looser form and one class
96
+ of false positive that needs the anchor.
97
+
98
+ The prefix is matched with a non-Unicode `/i` regex rather than
99
+ `toLowerCase().startsWith(…)` on purpose: `toLowerCase()` applies full Unicode
100
+ case mapping, so U+212A KELVIN SIGN would fold to `k` and `WorK-Item:` written
101
+ with it would be accepted — a form this has never accepted.
102
+
103
+ ## Testing a trailer reader
104
+
105
+ **A checker exercised only against well-formed messages passes under either
106
+ definition.** That is why three audits shipped with the bug undetected. The only
107
+ input that distinguishes them is a `Work-Item:` line sitting **above** a
108
+ non-trailer line, and any test of a trailer reader must pin one:
109
+
110
+ ```
111
+ feat: something
112
+
113
+ Work-Item: owner/repo#123
114
+
115
+ 🤖 Generated with Claude Code
116
+
117
+ Co-Authored-By: Claude <noreply@anthropic.com>
118
+ ```
119
+
120
+ Under Definition A this message has no work item. Under Definition B — the
121
+ authoritative one — it names exactly one. Assert **present**.
122
+
123
+ ## Related
124
+
125
+ - `tracked-work` (eager) — the obligation to carry the ref on every commit
126
+ - `settled-decisions` — why this is recorded as closed rather than re-litigated
127
+ - CodySwannGT/lisa#2672 — where Definition A was tried and abandoned
128
+ - CodySwannGT/lisa#3747 — where the decision was made discoverable
129
+ - CodySwannGT/lisa#3859 — the same undercount measured on the audit side