@codyswann/lisa 4.55.1 → 4.55.3

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 (434) hide show
  1. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +49 -13
  2. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +9 -1
  3. package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +10 -5
  4. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +4 -0
  5. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +73 -2
  6. package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +63 -29
  7. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +2 -1
  8. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +2 -1
  9. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +7 -2
  10. package/all/copy-overwrite/scripts/lisa-work-item.mjs +50 -14
  11. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  12. package/dist/core/lisa-owned-hash-ledger.js +36 -0
  13. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  14. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  15. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  16. package/dist/core/upstream-evidence-manifest.js +89 -139
  17. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  18. package/dist/opencode/plugin-templates/block-managed-file-edits.sh +76 -2
  19. package/dist/opencode/plugin-templates/block-no-verify.sh +3 -0
  20. package/dist/opencode/plugin-templates/guard-dedupe.bash +66 -29
  21. package/dist/opencode/plugin-templates/parity-safety-net-heredoc.py +3 -0
  22. package/dist/opencode/plugin-templates/parity-safety-net.sh +5 -1
  23. package/dist/opencode/support-file-resolver.d.ts.map +1 -1
  24. package/dist/opencode/support-file-resolver.js +3 -1
  25. package/dist/opencode/support-file-resolver.js.map +1 -1
  26. package/expo/copy-overwrite/plugins/withAndroidSplashNoClientExit.js +23 -21
  27. package/expo/create-only/.github/required-checks.json +1 -1
  28. package/nestjs/create-only/.github/required-checks.json +1 -1
  29. package/package.json +4 -4
  30. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +18 -10
  33. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +18 -9
  34. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +45 -7
  35. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +18 -7
  36. package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  37. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  38. package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +0 -17
  39. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +3 -34
  40. package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  41. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +46 -6
  42. package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +2 -1
  43. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  44. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +1 -1
  45. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  46. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  47. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  48. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +18 -4
  49. package/plugins/lisa/hooks/block-blind-automerge.sh +13 -5
  50. package/plugins/lisa/hooks/block-direct-issue-create.sh +7 -0
  51. package/plugins/lisa/hooks/block-instruction-file-edits.sh +3 -0
  52. package/plugins/lisa/hooks/block-managed-file-edits.sh +76 -2
  53. package/plugins/lisa/hooks/block-no-verify.sh +3 -0
  54. package/plugins/lisa/hooks/block-shell-json-parsing.sh +3 -0
  55. package/plugins/lisa/hooks/guard-dedupe.bash +66 -29
  56. package/plugins/lisa/hooks/parity-safety-net-heredoc.py +3 -0
  57. package/plugins/lisa/hooks/parity-safety-net.sh +5 -1
  58. package/plugins/lisa/hooks/worktree-binding-guard.mjs +3 -0
  59. package/plugins/lisa/hooks/worktree-binding-guard.sh +5 -1
  60. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  61. package/plugins/lisa/rules/eager/base-rules.md +3 -1
  62. package/plugins/lisa/rules/eager/withdrawn-rulings.md +14 -3
  63. package/plugins/lisa/rules/reference/automation-runbook-contract.md +1 -1
  64. package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +1 -1
  65. package/plugins/lisa/rules/reference/blocker-containment.md +3 -3
  66. package/plugins/lisa/rules/reference/claim-archaeology.md +1 -1
  67. package/plugins/lisa/rules/reference/claim-evidence-mapping.md +1 -1
  68. package/plugins/lisa/rules/reference/claim-time-guards.md +1 -1
  69. package/plugins/lisa/rules/reference/control-reachability.md +1 -1
  70. package/plugins/lisa/rules/reference/convergent-review.md +1 -1
  71. package/plugins/lisa/rules/reference/credential-substrate-precedence.md +1 -1
  72. package/plugins/lisa/rules/reference/dependency-decision-records.md +1 -1
  73. package/plugins/lisa/rules/reference/dependency-internalization-kit.md +1 -1
  74. package/plugins/lisa/rules/reference/dependency-trust-classes.md +1 -1
  75. package/plugins/lisa/rules/reference/deployed-state-readback.md +1 -1
  76. package/plugins/lisa/rules/reference/derived-branch-plan.md +1 -1
  77. package/plugins/lisa/rules/reference/design-source-of-truth.md +1 -1
  78. package/plugins/lisa/rules/reference/design-value-binding.md +1 -1
  79. package/plugins/lisa/rules/reference/documentation-source-paths.md +1 -1
  80. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
  81. package/plugins/lisa/rules/reference/observability-audit.md +1 -1
  82. package/plugins/lisa/rules/reference/prd-definition-of-ready.md +1 -1
  83. package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +1 -1
  84. package/plugins/lisa/rules/reference/pre-flight-autofill.md +1 -1
  85. package/plugins/lisa/rules/reference/promotion-contract.md +1 -1
  86. package/plugins/lisa/rules/reference/readiness-rubric.md +1 -1
  87. package/plugins/lisa/rules/reference/ready-role-filing.md +1 -1
  88. package/plugins/lisa/rules/reference/rejection-detection.md +1 -1
  89. package/plugins/lisa/rules/reference/repo-scope-split.md +9 -10
  90. package/plugins/lisa/rules/reference/reset-seed-coverage.md +1 -1
  91. package/plugins/lisa/rules/reference/usage-accounting.md +1 -1
  92. package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +1 -1
  93. package/plugins/lisa/scripts/linear-state-write-target.mjs +2 -2
  94. package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +24 -11
  95. package/plugins/lisa/scripts/resolve-lifecycle-role.mjs +2 -7
  96. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +18 -10
  97. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +18 -9
  98. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +45 -7
  99. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +18 -7
  100. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  101. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  102. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +0 -17
  103. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +3 -34
  104. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  105. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +46 -6
  106. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +2 -1
  107. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  108. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +1 -1
  109. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  110. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  111. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  112. package/plugins/lisa/skills/lisa-track/SKILL.md +18 -4
  113. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +13 -5
  114. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +7 -0
  115. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +3 -0
  116. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +76 -2
  117. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +3 -0
  118. package/plugins/lisa-agy/hooks/guard-dedupe.bash +66 -29
  119. package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +3 -0
  120. package/plugins/lisa-agy/hooks/parity-safety-net.sh +5 -1
  121. package/plugins/lisa-agy/plugin.json +1 -1
  122. package/plugins/lisa-agy/scripts/linear-state-write-target.mjs +2 -2
  123. package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +24 -11
  124. package/plugins/lisa-agy/scripts/resolve-lifecycle-role.mjs +2 -7
  125. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +18 -10
  126. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +18 -9
  127. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +45 -7
  128. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +18 -7
  129. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  130. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  131. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +0 -17
  132. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +3 -34
  133. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  134. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +46 -6
  135. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +2 -1
  136. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  137. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +1 -1
  138. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  139. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  140. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  141. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +18 -4
  142. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  144. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  145. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  146. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  147. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  148. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +13 -5
  149. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +7 -0
  150. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +3 -0
  151. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +76 -2
  152. package/plugins/lisa-copilot/hooks/block-no-verify.sh +3 -0
  153. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +3 -0
  154. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +66 -29
  155. package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +3 -0
  156. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +5 -1
  157. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +3 -0
  158. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +5 -1
  159. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  160. package/plugins/lisa-copilot/rules/eager/base-rules.md +3 -1
  161. package/plugins/lisa-copilot/rules/eager/withdrawn-rulings.md +14 -3
  162. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +1 -1
  163. package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +1 -1
  164. package/plugins/lisa-copilot/rules/reference/blocker-containment.md +3 -3
  165. package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +1 -1
  166. package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +1 -1
  167. package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +1 -1
  168. package/plugins/lisa-copilot/rules/reference/control-reachability.md +1 -1
  169. package/plugins/lisa-copilot/rules/reference/convergent-review.md +1 -1
  170. package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +1 -1
  171. package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +1 -1
  172. package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +1 -1
  173. package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +1 -1
  174. package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +1 -1
  175. package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +1 -1
  176. package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +1 -1
  177. package/plugins/lisa-copilot/rules/reference/design-value-binding.md +1 -1
  178. package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +1 -1
  179. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  180. package/plugins/lisa-copilot/rules/reference/observability-audit.md +1 -1
  181. package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +1 -1
  182. package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +1 -1
  183. package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +1 -1
  184. package/plugins/lisa-copilot/rules/reference/promotion-contract.md +1 -1
  185. package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +1 -1
  186. package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +1 -1
  187. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +1 -1
  188. package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +9 -10
  189. package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +1 -1
  190. package/plugins/lisa-copilot/rules/reference/usage-accounting.md +1 -1
  191. package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +1 -1
  192. package/plugins/lisa-copilot/scripts/linear-state-write-target.mjs +2 -2
  193. package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +24 -11
  194. package/plugins/lisa-copilot/scripts/resolve-lifecycle-role.mjs +2 -7
  195. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +18 -10
  196. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +18 -9
  197. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +45 -7
  198. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +18 -7
  199. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  200. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  201. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +0 -17
  202. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +3 -34
  203. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  204. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +46 -6
  205. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +2 -1
  206. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  207. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +1 -1
  208. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  209. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  210. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  211. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +18 -4
  212. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  213. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +13 -5
  214. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +7 -0
  215. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +3 -0
  216. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +76 -2
  217. package/plugins/lisa-cursor/hooks/block-no-verify.sh +3 -0
  218. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +3 -0
  219. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +66 -29
  220. package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +3 -0
  221. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +5 -1
  222. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +3 -0
  223. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +5 -1
  224. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  225. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +1 -1
  226. package/plugins/lisa-cursor/rules/base-rules.mdc +3 -1
  227. package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +1 -1
  228. package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +3 -3
  229. package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +1 -1
  230. package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +1 -1
  231. package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +1 -1
  232. package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +1 -1
  233. package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +1 -1
  234. package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +1 -1
  235. package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +1 -1
  236. package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +1 -1
  237. package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +1 -1
  238. package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +1 -1
  239. package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +1 -1
  240. package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +1 -1
  241. package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +1 -1
  242. package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +1 -1
  243. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  244. package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +1 -1
  245. package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +1 -1
  246. package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +1 -1
  247. package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +1 -1
  248. package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +1 -1
  249. package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +1 -1
  250. package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +1 -1
  251. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +1 -1
  252. package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +9 -10
  253. package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +1 -1
  254. package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +1 -1
  255. package/plugins/lisa-cursor/rules/withdrawn-rulings.mdc +14 -3
  256. package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +1 -1
  257. package/plugins/lisa-cursor/scripts/linear-state-write-target.mjs +2 -2
  258. package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +24 -11
  259. package/plugins/lisa-cursor/scripts/resolve-lifecycle-role.mjs +2 -7
  260. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +18 -10
  261. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +18 -9
  262. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +45 -7
  263. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +18 -7
  264. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  265. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  266. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +0 -17
  267. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +3 -34
  268. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  269. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +46 -6
  270. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +2 -1
  271. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  272. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +1 -1
  273. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  274. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  275. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  276. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +18 -4
  277. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  278. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  279. package/plugins/lisa-expo-agy/plugin.json +1 -1
  280. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  281. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  282. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  283. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  284. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  285. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  286. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  287. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  288. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  289. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  290. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  291. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  292. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  293. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  294. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  295. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  296. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  297. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  298. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  299. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  300. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  301. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  302. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  303. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  304. package/plugins/lisa-rails-agy/plugin.json +1 -1
  305. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  306. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  307. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  308. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  309. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  310. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  311. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  312. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  313. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  314. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  315. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  316. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  317. package/plugins/materialized-artifacts.json +11 -0
  318. package/plugins/src/base/hooks/block-blind-automerge.sh +10 -5
  319. package/plugins/src/base/hooks/block-direct-issue-create.sh +4 -0
  320. package/plugins/src/base/hooks/block-managed-file-edits.sh +73 -2
  321. package/plugins/src/base/hooks/guard-dedupe.bash +63 -29
  322. package/plugins/src/base/hooks/parity-safety-net.sh +2 -1
  323. package/plugins/src/base/hooks/worktree-binding-guard.sh +2 -1
  324. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  325. package/plugins/src/base/rules/eager/base-rules.md +3 -1
  326. package/plugins/src/base/rules/eager/withdrawn-rulings.md +14 -3
  327. package/plugins/src/base/rules/reference/automation-runbook-contract.md +1 -1
  328. package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +1 -1
  329. package/plugins/src/base/rules/reference/blocker-containment.md +3 -3
  330. package/plugins/src/base/rules/reference/claim-archaeology.md +1 -1
  331. package/plugins/src/base/rules/reference/claim-evidence-mapping.md +1 -1
  332. package/plugins/src/base/rules/reference/claim-time-guards.md +1 -1
  333. package/plugins/src/base/rules/reference/control-reachability.md +1 -1
  334. package/plugins/src/base/rules/reference/convergent-review.md +1 -1
  335. package/plugins/src/base/rules/reference/credential-substrate-precedence.md +1 -1
  336. package/plugins/src/base/rules/reference/dependency-decision-records.md +1 -1
  337. package/plugins/src/base/rules/reference/dependency-internalization-kit.md +1 -1
  338. package/plugins/src/base/rules/reference/dependency-trust-classes.md +1 -1
  339. package/plugins/src/base/rules/reference/deployed-state-readback.md +1 -1
  340. package/plugins/src/base/rules/reference/derived-branch-plan.md +1 -1
  341. package/plugins/src/base/rules/reference/design-source-of-truth.md +1 -1
  342. package/plugins/src/base/rules/reference/design-value-binding.md +1 -1
  343. package/plugins/src/base/rules/reference/documentation-source-paths.md +1 -1
  344. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  345. package/plugins/src/base/rules/reference/observability-audit.md +1 -1
  346. package/plugins/src/base/rules/reference/prd-definition-of-ready.md +1 -1
  347. package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +1 -1
  348. package/plugins/src/base/rules/reference/pre-flight-autofill.md +1 -1
  349. package/plugins/src/base/rules/reference/promotion-contract.md +1 -1
  350. package/plugins/src/base/rules/reference/readiness-rubric.md +1 -1
  351. package/plugins/src/base/rules/reference/ready-role-filing.md +1 -1
  352. package/plugins/src/base/rules/reference/rejection-detection.md +1 -1
  353. package/plugins/src/base/rules/reference/repo-scope-split.md +9 -10
  354. package/plugins/src/base/rules/reference/reset-seed-coverage.md +1 -1
  355. package/plugins/src/base/rules/reference/usage-accounting.md +1 -1
  356. package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +1 -1
  357. package/plugins/src/base/scripts/linear-state-write-target.mjs +2 -2
  358. package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +24 -11
  359. package/plugins/src/base/scripts/resolve-lifecycle-role.mjs +2 -7
  360. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +18 -10
  361. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +18 -9
  362. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +45 -7
  363. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +18 -7
  364. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  365. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  366. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +0 -17
  367. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +3 -34
  368. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  369. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +46 -6
  370. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +2 -1
  371. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  372. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +1 -1
  373. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  374. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  375. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  376. package/plugins/src/base/skills/lisa-track/SKILL.md +18 -4
  377. package/scripts/build-plugins.sh +4 -0
  378. package/scripts/copy-opencode-plugin-templates.mjs +5 -1
  379. package/scripts/lisa-enforcement-fallback.sh +9 -1
  380. package/scripts/two-channel-couplings.json +5 -33
  381. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +551 -13
  382. package/typescript/create-only/.github/required-checks.json +1 -1
  383. package/plugins/lisa/.codex-plugin/skills/lisa-qa-checklist/SKILL.md +0 -70
  384. package/plugins/lisa/.codex-plugin/skills/lisa-qa-checklist/agents/openai.yaml +0 -4
  385. package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +0 -86
  386. package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/agents/openai.yaml +0 -4
  387. package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +0 -119
  388. package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/agents/openai.yaml +0 -4
  389. package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +0 -94
  390. package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/agents/openai.yaml +0 -4
  391. package/plugins/lisa/commands/qa-checklist.md +0 -6
  392. package/plugins/lisa/commands/qa-clear.md +0 -6
  393. package/plugins/lisa/commands/qa-fail.md +0 -6
  394. package/plugins/lisa/commands/qa-queue.md +0 -6
  395. package/plugins/lisa/skills/lisa-qa-checklist/SKILL.md +0 -70
  396. package/plugins/lisa/skills/lisa-qa-checklist/agents/openai.yaml +0 -4
  397. package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +0 -86
  398. package/plugins/lisa/skills/lisa-qa-clear/agents/openai.yaml +0 -4
  399. package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +0 -119
  400. package/plugins/lisa/skills/lisa-qa-fail/agents/openai.yaml +0 -4
  401. package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +0 -94
  402. package/plugins/lisa/skills/lisa-qa-queue/agents/openai.yaml +0 -4
  403. package/plugins/lisa-agy/commands/lisa/qa-checklist.md +0 -6
  404. package/plugins/lisa-agy/commands/lisa/qa-clear.md +0 -6
  405. package/plugins/lisa-agy/commands/lisa/qa-fail.md +0 -6
  406. package/plugins/lisa-agy/commands/lisa/qa-queue.md +0 -6
  407. package/plugins/lisa-agy/skills/lisa-qa-checklist/SKILL.md +0 -70
  408. package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +0 -86
  409. package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +0 -119
  410. package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +0 -94
  411. package/plugins/lisa-copilot/commands/lisa/qa-checklist.md +0 -6
  412. package/plugins/lisa-copilot/commands/lisa/qa-clear.md +0 -6
  413. package/plugins/lisa-copilot/commands/lisa/qa-fail.md +0 -6
  414. package/plugins/lisa-copilot/commands/lisa/qa-queue.md +0 -6
  415. package/plugins/lisa-copilot/skills/lisa-qa-checklist/SKILL.md +0 -70
  416. package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +0 -86
  417. package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +0 -119
  418. package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +0 -94
  419. package/plugins/lisa-cursor/commands/lisa/qa-checklist.md +0 -6
  420. package/plugins/lisa-cursor/commands/lisa/qa-clear.md +0 -6
  421. package/plugins/lisa-cursor/commands/lisa/qa-fail.md +0 -6
  422. package/plugins/lisa-cursor/commands/lisa/qa-queue.md +0 -6
  423. package/plugins/lisa-cursor/skills/lisa-qa-checklist/SKILL.md +0 -70
  424. package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +0 -86
  425. package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +0 -119
  426. package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +0 -94
  427. package/plugins/src/base/commands/qa-checklist.md +0 -6
  428. package/plugins/src/base/commands/qa-clear.md +0 -6
  429. package/plugins/src/base/commands/qa-fail.md +0 -6
  430. package/plugins/src/base/commands/qa-queue.md +0 -6
  431. package/plugins/src/base/skills/lisa-qa-checklist/SKILL.md +0 -70
  432. package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +0 -86
  433. package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +0 -119
  434. package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +0 -94
@@ -91,24 +91,7 @@ prd_source: "https://notion.so/..." # set when the Issue was generated from a
91
91
 
92
92
  If the caller passes only an identifier, fetch the item via `lisa-linear-access operation: get-issue` (Issue) or `lisa-linear-access operation: get-project` (Project), derive the same fields from the fetched data — including `runtime_behavior_change` (derived from the `## Target Backend Environment` declaration per `derived-branch-plan`, and authoritative over any caller assertion), `build_ready` (the Issue's state is the configured `ready` state) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa-linear-read-issue`) so S15 can classify the item — then run gates.
93
93
 
94
- ## Standalone use, against an item that already exists
95
-
96
- This is a supported entry point, not only an internal step of a caller flow.
97
- Point it at a live issue and it fetches and validates the stored state:
98
-
99
- ```text
100
- Skill(lisa-linear-validate-issue) with ENG-1234
101
- ```
102
-
103
- Use it whenever the issue reached the tracker by some path other than
104
- `lisa-linear-write-issue` — a team's own script, a workflow step, a cron, anything holding the
105
- credentials but no skill runtime. Those paths get neither the pre-write nor the
106
- post-write gate, and their own read-back substitutes for neither: a read-back
107
- proves the tracker stored what was sent, never that what was sent was any good.
108
-
109
- The gate definitions live here on purpose, so every caller picks up a change
110
- automatically. Running this skill by hand is the same gate the write path runs,
111
- not an approximation of it.
94
+ For existing items written through another path, use the [standalone entry point](#standalone-entry-point--validating-an-item-written-by-another-path). A transport read-back does not prove the stored specification passes the gates.
112
95
 
113
96
  ## Gates
114
97
 
@@ -63,20 +63,3 @@ normalize-then-compare. Byte-exact comparison of such text is forbidden.
63
63
  - Never write to Linear. Read-only.
64
64
  - Never short-circuit the validator. Always run the full gate set.
65
65
  - If `get_issue` / `get_project` returns an access error, surface it and exit — don't pretend the item is fine.
66
-
67
- ## Comparison is semantic, never byte-exact
68
-
69
- Re-run the validator against the live issue. Do NOT compare the stored body
70
- against what was sent byte for byte.
71
-
72
- The reason is measured rather than theoretical. Trackers normalize markdown on
73
- write: `-` bullets become `*`, a bare URL is wrapped as `[url](<url>)`, bold
74
- emphasis is re-segmented around inline code spans. All lossless, all
75
- rendering-identical, and all of it makes a byte comparator report failure on a
76
- write that was completely fine. A comparator that cannot tell vendor
77
- normalization from corruption fails on healthy writes and trains its reader to
78
- ignore it, which costs more than the check was ever worth.
79
-
80
- Compare meaning: run `lisa-linear-validate-issue` against the stored item and let the gates
81
- decide. Where a single field must be compared directly, normalize both sides
82
- first.
@@ -33,40 +33,7 @@ Linear's data model maps Epic / Story / Sub-task to **different entity types**.
33
33
 
34
34
  The build lifecycle uses native **workflow states** (`Ready`, `In Progress`, `Blocked`, `On Dev`, `On Stg`, `Done`, plus an optional review state a project may bind), resolved per role from `linear.workflow` — see "Why Linear uses states, not labels" in `config-resolution`. A new **leaf** work unit is created in the configured `ready` state only on explicit `build_ready: true`; omitted or `false` leaves it in the team's default backlog state, and a container is never put in `ready` at all (see the Build-ready control input below).
35
35
 
36
- ## Writing by another path? The gates still apply
37
-
38
- A team that talks to its tracker through its own script is doing a normal
39
- thing — the script usually owns the credential plumbing, and Lisa neither
40
- controls nor wants to control it. What that script does NOT get is either half
41
- of this skill's quality gate, and nothing about the write says so.
42
-
43
- **A read-back is not the missing check.** A bespoke path almost always re-reads
44
- the issue after writing and confirms the tracker stored what was sent. That is
45
- worth doing and it is not this. It proves TRANSPORT: the API accepted the
46
- payload and the field values round-tripped. It cannot fail for the reason these
47
- gates exist, because it never looks at whether what was sent was any good — a
48
- issue with no acceptance criteria, no parent, and a human decision left sitting
49
- in the middle of it round-trips perfectly. That is the dangerous half of the
50
- shape: a failing control gets investigated, a misread one gets trusted.
51
-
52
- So a write by any other path still owes both phases, and both run standalone
53
- against an item that already exists:
54
-
55
- | Phase | Skill | What it costs you |
56
- |---|---|---|
57
- | Pre-write validate | `lisa-linear-validate-issue` | Run it on the draft before you send it |
58
- | Post-write verify | `lisa-linear-verify` | Run it on the live issue after you send it |
59
-
60
- ```text
61
- Skill(lisa-linear-validate-issue) with the draft issue, or with a reference to a live one
62
- Skill(lisa-linear-verify) with ENG-1234
63
- ```
64
-
65
- **These are plugin-resident skills invoked through the Skill tool.** They are
66
- not shell scripts and will not appear in any repository's `scripts/` directory,
67
- including yours. An agent that searches the repo it is standing in, finds
68
- nothing, and concludes the capability is absent has made the one mistake that
69
- turns a local script from the convenient option into the only one.
36
+ For direct API or bespoke script writes, the [bespoke-path contract](#writing-by-a-bespoke-path-your-own-script-direct-api-or-graphql) still requires pre-write validation and post-write verification.
70
37
 
71
38
  ## Phase 1 — Resolve Intent
72
39
 
@@ -334,6 +301,8 @@ person scanning a board and to any path that has not yet been routed through the
334
301
  If the label does not exist in the tracker, create it, or record that it could not be applied and
335
302
  proceed — the marker still holds. Never file the label *instead of* the marker.
336
303
 
304
+ **Every marker reader must consult comments.** Use `classifyReadyCandidate` with labels, body, comments and the configured human-needed label, then `planHumanGateRelease` for any release actions. A historical body marker alone is not an active hold after its matching release; unreadable comments leave a hold in place.
305
+
337
306
  **Write a `reason=` the release can name.** The hold's reason is not decoration: a hold ends when a
338
307
  `[lisa-human-gate-release]` comment repeating that same `reason=` is recorded on the item, and the
339
308
  next intake sweep then takes the marker label off and puts the item back in the build-ready role on
@@ -15,7 +15,7 @@ The PRD URL can be a **Notion page URL**, a **Confluence page URL**, a **Linear
15
15
 
16
16
  - `notion.so` / `notion.site` → Notion. Fetch with `mcp__claude_ai_Notion__notion-fetch` (`include_discussions: true`) and `mcp__claude_ai_Notion__notion-get-comments`.
17
17
  - Atlassian Confluence host (e.g. `*.atlassian.net/wiki/...`) → Confluence. Through `lisa-atlassian-access`, fetch the page, its descendants (for child epic pages), and its footer + inline comments including nested replies. State the operation, not a vendor tool name — the access skill owns substrate selection (`integration-access-layer`).
18
- - `linear.app` host → Linear. Fetch with `lisa-linear-access operation: get-project` (capture description, labels, state, attached resources), `lisa-linear-access operation: list-documents({projectId})` + `lisa-linear-access operation: get-document` per attached document, `lisa-linear-access operation: list-issues({project})` for sub-issues that act as child epics / user stories, and `lisa-linear-access operation: list-comments({issueId})` per sub-issue for decisions and engineering notes. Comments do not exist on the project itself in the MCP surface — sub-issue comments are the substitute.
18
+ - `linear.app` host → Linear. Fetch with `lisa-linear-access operation: get-project` (capture description, labels, state, attached resources), `lisa-linear-access operation: list-documents({projectId})` + `lisa-linear-access operation: get-document` per attached document, `lisa-linear-access operation: list-issues({project})` for sub-issues that act as child epics / user stories, and `lisa-linear-access operation: list-comments({issueId})` per sub-issue for decisions and engineering notes. Also fetch all project comments with `lisa-linear-access operation: list-comments project_id:<ID>`; the access layer pages through GraphQL even when the MCP lacks project comments. An incomplete comment read is an access failure, not an empty history.
19
19
  - `github.com` host → GitHub Issues. Fetch with the `gh` CLI (no GitHub MCP — Lisa uses the CLI exclusively for GitHub):
20
20
  - `gh issue view <number> --repo <org>/<repo> --json number,title,body,labels,milestone,assignees,author,createdAt,comments,url` for the PRD body and comments.
21
21
  - `gh api graphql` with the `subIssues` field for native sub-issue children (these stand in for child epic pages — see `lisa-github-read-issue` Phase 3 for the exact query).
@@ -41,7 +41,7 @@ Per-ticket gates (`lisa-jira-validate-ticket`) prove each created ticket is well
41
41
  2. Fetch the PRD using the vendor-appropriate tool surface:
42
42
  - **Notion**: `notion-fetch` with `include_discussions: true`. Capture: title, body, child Epic pages, all comment threads.
43
43
  - **Confluence**: `getConfluencePage` (capture title, body, labels), `getConfluencePageFooterComments` + `getConfluencePageInlineComments` (capture all comments; walk replies via `getConfluenceCommentChildren` for any thread with children).
44
- - **Linear**: `get_project` (capture name, description, labels, state, attached resources). Capture sub-issues via `list_issues({project})` and per-issue comments via `list_comments({issueId})`.
44
+ - **Linear**: `get_project` (capture name, description, labels, state, attached resources). Capture all project comments via `lisa-linear-access operation: list-comments project_id:<ID>`, plus sub-issues via `list_issues({project})` and per-issue comments via `list_comments({issueId})`. Include both comment sources in destination URL discovery; refuse to report `NO_TICKETS_FOUND` from an incomplete read.
45
45
  - **GitHub**: `gh issue view --json` for the source PRD issue (title, body, labels, comments). Capture sub-issues via the GraphQL `subIssues` traversal.
46
46
  3. If the PRD has child Epic sub-pages / sub-issues (a multi-epic PRD), fetch each in parallel:
47
47
  - **Notion**: `notion-fetch` per child page with `include_discussions: true`.
@@ -189,7 +189,7 @@ In addition to the lifecycle roles above, the build lifecycle defines the **`hum
189
189
  - The blocks repair-intake **itself writes** are the auto-recoverable kind — it files a build-ready fix ticket and moves the item `blocked` *blocked by that ticket*, expecting the next cycle to self-heal. Those are **not** `human_needed`; if such an item arrives carrying a `human_needed` marker **this skill applied on an earlier cycle**, repair-intake **clears** it (the block is no longer waiting on a human).
190
190
  - **Never remove a `human_needed` marker this skill did not apply.** "Stale" is a judgment about the block's kind, not about who applied the marker or when — so without this rule an operator's deliberate hold, applied *after* correcting a wrong transition, is indistinguishable from a leftover the sweep is designed to clear, and gets swept. Establish provenance from the label event's actor (`rejection-detection` **Automation-reversal memory** reads the same surfaces); if provenance is not readable, **leave the marker in place**. Removing a human's hold is unrecoverable within the loop; leaving a stale one costs a cycle and is visible.
191
191
  - **The one exception is a hold that has recorded its own discharge.** A `[lisa-human-gate-release]` comment naming the hold's `reason=` is not a guess about provenance — it is the hold's stated void condition, recorded on the item by the person who answered it. Clearing the marker there is not overriding a human's judgment; it is *enacting* it. That is the whole of the exception: no other reading of "this looks stale" reopens the question above, and an item whose release cannot be read stays held. See "Release the holds that have been answered" below (#3852).
192
- - The marker is consulted **before **any** repair transition**, not only before Class C. Class C's hard stop is the strictest reading of it, but a marker that is honoured on one classification path and ignored on the other three is not a guard — and Class A, dependency clearing, is exactly the path an operator reverting a wrongly-cleared blocker is trying to protect. Match it robustly (hyphen/underscore, case-insensitive, label set and note prose) wherever it is read.
192
+ - The marker is consulted **before **any** repair transition**, not only before Class C. Class C's hard stop is the strictest reading of it, but a marker that is honoured on one classification path and ignored on the other three is not a guard — and Class A, dependency clearing, is exactly the path an operator reverting a wrongly-cleared blocker is trying to protect. Read all comments and consult `classifyReadyCandidate` / `planHumanGateRelease` before treating the marker as active; a matching release discharges the historical body marker. Use the shared helpers for robust label and declaration matching, and leave unreadable releases held.
193
193
  - The blocks the **vendor agent** writes when repair-intake re-dispatches it (its pre-flight gate) carry `human_needed` already — the agent owns that marker. repair-intake leaves it in place.
194
194
 
195
195
  Resolve with the standard role-read pattern (local overrides global, default fallback):
@@ -621,6 +621,41 @@ the item open. Recording "the fix reached dev and production but skipped staging
621
621
  branch back-fill, not outstanding delivery" and then closing anyway is this defect exactly — the
622
622
  condition observed, filed under the wrong heading, and overridden.
623
623
 
624
+ ### Resolve the shipped blocker-edge module
625
+
626
+ The helper ships in the plugin and npm package. Only the runtime-supplied plugin roots
627
+ and installed package are trusted here; do not search arbitrary checkout-local plugin folders.
628
+ Write the evaluated edge inputs to a JSON array file, then call `blocker_edge_decisions`.
629
+ An absent module, unreadable input or import failure stops this repair without tracker writes.
630
+
631
+ ```bash
632
+ resolve_blocker_guard() {
633
+ local candidate
634
+ for candidate in \
635
+ "${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/scripts/blocker-edge-resolution.mjs}" \
636
+ "${PLUGIN_ROOT:+$PLUGIN_ROOT/scripts/blocker-edge-resolution.mjs}" \
637
+ node_modules/@codyswann/lisa/plugins/lisa/scripts/blocker-edge-resolution.mjs; do
638
+ [ -n "$candidate" ] && [ -f "$candidate" ] && { printf '%s\n' "$candidate"; return; }
639
+ done
640
+ echo "Error: could not locate blocker-edge-resolution.mjs; refusing repair." >&2
641
+ return 1
642
+ }
643
+
644
+ blocker_edge_decisions() { # JSON array file -> decision object
645
+ local guard
646
+ guard=$(resolve_blocker_guard) || return 1
647
+ node --input-type=module - "$guard" "$1" <<'LISA_BLOCKER_NODE'
648
+ import { readFileSync } from "node:fs";
649
+ import { resolve } from "node:path";
650
+ import { pathToFileURL } from "node:url";
651
+ const { resolveBlockerEdges } = await import(pathToFileURL(resolve(process.argv[2])).href);
652
+ const edges = JSON.parse(readFileSync(process.argv[3], "utf8"));
653
+ if (!Array.isArray(edges)) throw new Error("Expected an array of blocker edges");
654
+ console.log(JSON.stringify(resolveBlockerEdges(edges)));
655
+ LISA_BLOCKER_NODE
656
+ }
657
+ ```
658
+
624
659
  ### Build `blocked` → re-evaluate, unblock if cleared
625
660
 
626
661
  1. Read the block reason and classify the blocker (see Blocker classification & clearing). An item
@@ -629,8 +664,11 @@ condition observed, filed under the wrong heading, and overridden.
629
664
  Re-check **every** class present — do not stop at "no `is blocked by` links, therefore nothing
630
665
  to do." A self-block has zero dependencies by definition, yet is fully re-checkable.
631
666
  2. **Dependency cleared** — decide each parsed `is blocked by` edge with
632
- `scripts/blocker-edge-resolution.mjs`, feeding it the `blocker-containment` verdict plus the
633
- blocker's state and closure reason. It returns `dissolve` / `keep` / `escalate` per edge.
667
+ the shipped plugin helper `scripts/blocker-edge-resolution.mjs`, feeding it the
668
+ `blocker-containment` verdict plus the blocker's state and closure reason. This is a module,
669
+ not a CLI and not a required host `scripts/` file. Resolve a trusted plugin/package path and
670
+ invoke `resolveBlockerEdges` as shown above; never create a second resolver in the host.
671
+ It returns `dissolve` / `keep` / `escalate` per edge.
634
672
  **Dissolve the edge in the SAME write that records the ruling, then read the relation back and
635
673
  confirm it is gone.** Only when every edge dissolves → move `blocked → claimed`, then run the
636
674
  same agent-dispatch + post-agent `claimed → done` sequence as the stalled-`claimed` path above
@@ -1033,7 +1071,7 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
1033
1071
  `intake_mode=build`, every non-PRD issue is normalized as a build ticket. If `intake_mode=both`,
1034
1072
  classify PRDs first and normalize all remaining issues as build tickets.
1035
1073
  2a. **Ask whether a person is holding it, before anything else.** Call
1036
- `planLabelNormalization({ labels, body, humanNeededLabel, lifecycleLabels, readyLabel })` from
1074
+ `planLabelNormalization({ labels, body, comments, humanNeededLabel, lifecycleLabels, readyLabel })` from
1037
1075
  `scripts/intake-blocker-reprobe.mjs` for every candidate and apply exactly the verdict it
1038
1076
  returns. Do **not** re-implement the test here and do **not** decide held-ness from labels
1039
1077
  alone: the vendor writers stamp the `[lisa-human-gate]` marker into the body of a deliberate
@@ -1187,9 +1225,11 @@ layer, and test ancestry against the remote.
1187
1225
  remote, ancestry query errored) → **still blocking**, with the rule's reason key in the run
1188
1226
  record.
1189
1227
  - **Ships no code** — the blocker is closed as completed, positively declares
1190
- `Target Backend Environment: None — no runtime behavior change`, and has no merged PR →
1228
+ `## Target Backend Environment` reading `None — no runtime behavior change: <kind>`
1229
+ with kind `doc-only`, `config-only`, or `type-only`, and has no linked PR and no merge commit →
1191
1230
  **cleared** under the rule's carve-out. This is a positive determination, never an absence:
1192
- "no PR found" is `no-pr` and stays blocking.
1231
+ "no PR found" is `no-pr` and stays blocking. A missing kind does not qualify; any linked
1232
+ open or unmerged PR stays blocking under the normal containment test.
1193
1233
  - **Human override** — an explicit human statement on this item that the dependency is satisfied
1194
1234
  clears it and outranks a failed containment check. Name it in the run record.
1195
1235
  - **Human hold** — the override runs in both directions. `human_needed` is checked here as well as
@@ -53,7 +53,8 @@ first-attempt work.
53
53
  # bundle.json: { "labels": [...], "comments": [<bodies, oldest first>], "role": "<current>" }
54
54
  ```
55
55
 
56
- Branch on `action`, never on the label being there:
56
+ Branch on `outcome`, never on the label being there. The resolver's separate
57
+ `action` field is `keep` / `clear` / `none`; it is not the liveness verdict:
57
58
 
58
59
  | `outcome` | Meaning | What triage does |
59
60
  |---|---|---|
@@ -743,7 +743,8 @@ export function createCoordinationRecord(cfg, key, projectId, note) {
743
743
  "--output",
744
744
  "json",
745
745
  ],
746
- env
746
+ env,
747
+ "creating a coordination record in Bitwarden"
747
748
  );
748
749
  const created = JSON.parse(raw);
749
750
  return {
@@ -357,7 +357,7 @@ jq -e '.linear.workspace' .lisa.config.json >/dev/null
357
357
  [ "$(jq -r '.tracker // empty' .lisa.config.json)" = "linear" ] && jq -e '.linear.teamKey' .lisa.config.json >/dev/null
358
358
  ```
359
359
 
360
- Confirm what was scaffolded is present: `list-workflow-states` for every build role when Linear is the tracker, `list_project_labels` for `prd-*` (including the terminal `prd-verified`) when Linear is the PRD source. Do NOT expect a `status:*` namespace — it is not part of this model. Report success with the resolved workspace, team key (if any), which namespaces were scaffolded (created vs. already existed), any non-default overrides, and whether `tracker` / `source` were set. Direct the user to `/lisa:intake` to test.
360
+ Confirm what was scaffolded is present: `list-workflow-states` for every build role when Linear is the tracker, `list-project-labels` for `prd-*` (including the terminal `prd-verified`) when Linear is the PRD source. Do NOT expect a `status:*` namespace — it is not part of this model. Report success with the resolved workspace, team key (if any), which namespaces were scaffolded (created vs. already existed), any non-default overrides, and whether `tracker` / `source` were set. Direct the user to `/lisa:intake` to test.
361
361
 
362
362
  ## Idempotency
363
363
 
@@ -70,7 +70,7 @@ const BLOCKING = new Set(["missing", "invalid"]);
70
70
  /**
71
71
  * Whether an unpinnable entry is nonetheless usable on this machine.
72
72
  *
73
- * Presence-only, and deliberately not a skip. Dropping the entry entirely would
73
+ * Executability is required, even when no version floor is declared. Dropping the entry entirely would
74
74
  * report clean for a tool that is neither pinned nor installed, which is the
75
75
  * vacuous green this module refuses everywhere else. So the probe still decides
76
76
  * — it just gets asked, which on this branch it previously never was.
@@ -83,7 +83,12 @@ const BLOCKING = new Set(["missing", "invalid"]);
83
83
  * @returns {boolean} Whether the tool is present and good enough to use.
84
84
  */
85
85
  function usableWithoutPin(step, minVersions) {
86
- if (!step.unpinnedForPlatform || !step.found?.present) return false;
86
+ if (
87
+ !step.unpinnedForPlatform ||
88
+ !step.found?.present ||
89
+ step.found.executable !== true
90
+ )
91
+ return false;
87
92
  const floor = minVersions[step.name];
88
93
  return !floor || compareVersions(step.found.version ?? "0", floor) >= 0;
89
94
  }
@@ -120,7 +120,7 @@ export function readRemoteEnvConfig(cwd = process.cwd()) {
120
120
  * on which quirky binaries happen to exist on the machine running the tests.
121
121
  * @param {string} name Executable name.
122
122
  * @param {Function} [exec] Command runner, for tests.
123
- * @returns {{version: string|null, present: boolean}} Probe result.
123
+ * @returns {{version: string|null, present: boolean, executable: boolean}} Probe result.
124
124
  */
125
125
  export function probe(name, exec = boundedChildOutput) {
126
126
  try {
@@ -128,12 +128,19 @@ export function probe(name, exec = boundedChildOutput) {
128
128
  encoding: "utf8",
129
129
  stdio: ["ignore", "pipe", "pipe"],
130
130
  });
131
- return { present: true, version: extractVersion(out) };
131
+ return { present: true, executable: true, version: extractVersion(out) };
132
132
  } catch (err) {
133
- if (err.code === "ENOENT") return { present: false, version: null };
134
133
  if (err.code === "ETIMEDOUT") throw err;
134
+ // A numeric exit status proves the child started, even when --version is
135
+ // unsupported. EACCES and other spawn failures carry no exit status: a
136
+ // file being discoverable does not mean the agent can execute it.
137
+ const executable = typeof err.status === "number";
135
138
  const output = `${err.stdout ?? ""}${err.stderr ?? ""}`;
136
- return { present: true, version: extractVersion(output) };
139
+ return {
140
+ present: err.code !== "ENOENT",
141
+ executable,
142
+ version: executable ? extractVersion(output) : null,
143
+ };
137
144
  }
138
145
  }
139
146
 
@@ -56,7 +56,7 @@ export function extractVersion(output) {
56
56
  /**
57
57
  * Classify one required tool against what the environment actually provides.
58
58
  * @param {object} tool Manifest entry.
59
- * @param {{version: string|null, present: boolean}} found Probe result.
59
+ * @param {{version: string|null, present: boolean, executable: boolean}} found Probe result.
60
60
  * @returns {{name: string, action: string, reason: string}} The decision.
61
61
  */
62
62
  function planRequired(tool, found) {
@@ -70,6 +70,13 @@ function planRequired(tool, found) {
70
70
  `Either add an install entry or pin a different image.`,
71
71
  };
72
72
  }
73
+ if (found.executable !== true) {
74
+ return {
75
+ name: tool.name,
76
+ action: "missing",
77
+ reason: `${tool.name} was found but cannot be executed. Repair its permissions or replace the binary before using it.`,
78
+ };
79
+ }
73
80
  if (
74
81
  tool.minVersion &&
75
82
  compareVersions(found.version ?? "0", tool.minVersion) < 0
@@ -97,7 +104,7 @@ function planRequired(tool, found) {
97
104
  * and maintenance the same script, and it is the cheap path when a container
98
105
  * resumes from cache rather than being built fresh.
99
106
  * @param {object} tool Manifest entry.
100
- * @param {{version: string|null, present: boolean}} found Probe result.
107
+ * @param {{version: string|null, present: boolean, executable: boolean}} found Probe result.
101
108
  * @returns {{name: string, action: string, reason: string}} The decision.
102
109
  */
103
110
  function planInstallable(tool, found, pinIsFloor = false) {
@@ -108,7 +115,11 @@ function planInstallable(tool, found, pinIsFloor = false) {
108
115
  reason: `${tool.name} has no pinned version. An unpinned install is not reproducible.`,
109
116
  };
110
117
  }
111
- if (found.present && found.version === tool.version) {
118
+ if (
119
+ found.present &&
120
+ found.executable === true &&
121
+ found.version === tool.version
122
+ ) {
112
123
  return {
113
124
  name: tool.name,
114
125
  action: "skip",
@@ -125,6 +136,7 @@ function planInstallable(tool, found, pinIsFloor = false) {
125
136
  if (
126
137
  pinIsFloor &&
127
138
  found.present &&
139
+ found.executable === true &&
128
140
  found.version &&
129
141
  compareVersions(found.version, tool.version) > 0
130
142
  ) {
@@ -286,10 +298,10 @@ export function appliesToSurface(tool, surface) {
286
298
  * asking about availability rather than provisioning has the answer instead of
287
299
  * having to infer one from the other.
288
300
  * @param {{require?: object[], install?: object[]}} tools Manifest.
289
- * @param {(name: string) => {version: string|null, present: boolean}} probe Version probe.
301
+ * @param {(name: string) => {version: string|null, present: boolean, executable: boolean}} probe Version probe.
290
302
  * @param {string} [surface] Surface being provisioned.
291
303
  * @param {string} [platform] Platform key to resolve install entries against.
292
- * @returns {Array<{name: string, action: string, reason: string, tool?: object, found?: {version: string|null, present: boolean}, unpinnedForPlatform?: boolean}>} Ordered decisions.
304
+ * @returns {Array<{name: string, action: string, reason: string, tool?: object, found?: {version: string|null, present: boolean, executable: boolean}, unpinnedForPlatform?: boolean}>} Ordered decisions.
293
305
  */
294
306
  export function planToolchain(
295
307
  tools,
@@ -24,7 +24,7 @@ This flow must return exactly one canonical `(tracker_provider, work_item_ref)`
24
24
 
25
25
  ### Explicit reference
26
26
 
27
- Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body for the hold marker: an item carrying `[lisa-human-gate]` is held and Phase 3 refuses to claim it, whatever role or labels it carries. This live read is mandatory even if caller context already includes ticket text.
27
+ Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body, labels and all comments; Phase 3 uses the shared hold classifier so a historical `[lisa-human-gate]` marker with a matching release is not mistaken for an active hold. This live read is mandatory even if caller context already includes ticket text.
28
28
 
29
29
  ### File or plain text
30
30
 
@@ -44,9 +44,23 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
44
44
 
45
45
  ## Phase 3 — Claim and bind
46
46
 
47
- The work item is **held** when either of two independent signals is present: the caller declared a `human_gate`, or the resolved item's body contains the literal `[lisa-human-gate]` marker. Test the marker as a plain substring and never key the parse on a `reason=` value — markers are written both with and without one, and a parser that requires the key misses the keyless ones while appearing to work.
47
+ The work item is **held** when the caller declared a `human_gate`, independently of any item history,
48
+ or `classifyReadyCandidate({ labels, body, comments, humanNeededLabel })` from the shipped
49
+ `scripts/intake-blocker-reprobe.mjs` returns `claimable: false`. Pass the complete live comment
50
+ history so a matching `[lisa-human-gate-release]` discharges the historical body marker. Never
51
+ reimplement heldness as a substring test or as the negation of `planHumanGateRelease().released`:
52
+ that planner also returns `released: false` for an item that was never held. Missing comments fail
53
+ closed for a hold; never infer release from a missing label.
48
54
 
49
- A held item stops the flow here, whatever the resolution outcome and whatever roles or labels the item carries. The marker is the authority: an item can be held while sitting in the build-ready role with no blocked or human-needed label, because a human stamping a hold on live work is the most likely way a gate is ever applied, and the vendor claim contracts reject a closed item or an active blocker but not a marked one. Do not invoke `lisa-tracker-claim`, do not write the worktree binding, and do not begin durable project work. Claiming a held leaf binds a live lane to it and makes it look already-attended, which suppresses exactly the human attention it was filed to attract. Return the structured result below with `claim_outcome: held-by-gate` and `binding_outcome: skipped-human-gate`, name which signal held it in `held_by` and carry the reason verbatim in `gate_reason` alongside the canonical reference, then stop. A marker written without a `reason=` key yields `gate_reason: null` — a missing reason narrows what the result can say, never whether the item is held.
55
+ A held item stops the flow here, whatever roles or labels it carries. Do not invoke
56
+ `lisa-tracker-claim`, write the worktree binding, or begin durable project work. Return
57
+ `claim_outcome: held-by-gate` and `binding_outcome: skipped-human-gate`, name the signal in `held_by`
58
+ and carry its reason verbatim in `gate_reason` (null for a keyless hold), then stop.
59
+
60
+ For a discharged item, call
61
+ `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
62
+ and apply only the returned actions before claiming. Keep the body marker as history; a release
63
+ never overrides a new caller-declared hold or another eligibility check.
50
64
 
51
65
  Otherwise:
52
66
 
@@ -78,5 +92,5 @@ Otherwise:
78
92
  - Branch setup may call `node scripts/lisa-work-item.mjs attach-branch` after the feature branch exists.
79
93
  - Keep the binding across ordinary interruptions and blocked outcomes so resumed work remains attributable.
80
94
  - Clear it only after true terminal completion — merged, deployed/verified where required, tracker evidence/backlink complete, and the work item terminal — by running `node scripts/lisa-work-item.mjs clear` and verifying no current binding remains.
81
- - A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human decision, but not by a human edit: the person records the decision as a comment on the leaf beginning `[lisa-human-gate-release]` and repeating the hold's `reason=` verbatim, and the next intake sweep takes the human-needed marker off and puts the leaf back in the build-ready role on its own (`planHumanGateRelease` in `scripts/intake-blocker-reprobe.mjs`). A later invocation then resolves it on the ordinary path. **Do not tell anyone to delete the marker from the description, and do not delete it here.** The only description write available is a whole-body replacement, so clearing one line means rewriting the entire record — which is why, before this, the rational move was always to leave a hold in place and answered holds only accumulated (CodySwannGT/lisa#3852). The hold stays in the body as history; the release is recorded beside it.
95
+ - A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human decision, but not by a human edit: the person records the decision as a comment on the leaf beginning `[lisa-human-gate-release]` and, for a keyed hold, repeating its `reason=` verbatim. For a keyless hold, the comment begins `[lisa-human-gate-release]` without a `reason=` field. The next intake sweep takes the human-needed marker off and puts the leaf back in the build-ready role on its own (`planHumanGateRelease` in `scripts/intake-blocker-reprobe.mjs`). A later invocation then resolves it on the ordinary path. **Do not tell anyone to delete the marker from the description, and do not delete it here.** The only description write available is a whole-body replacement, so clearing one line means rewriting the entire record — which is why, before this, the rational move was always to leave a hold in place and answered holds only accumulated (CodySwannGT/lisa#3852). The hold stays in the body as history; the release is recorded beside it.
82
96
  - A tracker outage, invalid item, failed claim, or failed binding blocks durable work. Never continue untracked and never ask a Git hook to create the item.
@@ -169,6 +169,9 @@ for guard in block-no-verify parity-safety-net block-shell-json-parsing \
169
169
  block-managed-file-edits block-blind-automerge worktree-binding-guard; do
170
170
  if [ -f "$SRC_DIR/base/hooks/$guard.sh" ]; then
171
171
  materialize "$SRC_DIR/base/hooks/$guard.sh" "$HOST_GUARD_DIR/$guard.sh"
172
+ # Both active channels must hash identical code, including the ownership
173
+ # banner; fan-out below derives the other plugins from this same copy.
174
+ materialize "$SRC_DIR/base/hooks/$guard.sh" "$PLUGINS_DIR/lisa/hooks/$guard.sh"
172
175
  chmod +x "$HOST_GUARD_DIR/$guard.sh"
173
176
  fi
174
177
  done
@@ -196,6 +199,7 @@ for companion in parity-safety-net-heredoc.py worktree-binding-guard.mjs \
196
199
  guard-dedupe.bash; do
197
200
  if [ -f "$SRC_DIR/base/hooks/$companion" ]; then
198
201
  materialize "$SRC_DIR/base/hooks/$companion" "$HOST_GUARD_DIR/$companion"
202
+ materialize "$SRC_DIR/base/hooks/$companion" "$PLUGINS_DIR/lisa/hooks/$companion"
199
203
  fi
200
204
  done
201
205
  # The Sonar hook wrapper, which ships alongside the guards but is not one.
@@ -12,6 +12,8 @@
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
14
 
15
+ import { materialize } from "./materialize-copy-overwrite.mjs";
16
+
15
17
  const repoRoot = path.resolve(import.meta.dirname, "..");
16
18
  const sourceDir = path.join(repoRoot, "src", "opencode", "plugin-templates");
17
19
  const destDir = path.join(repoRoot, "dist", "opencode", "plugin-templates");
@@ -40,7 +42,9 @@ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
40
42
  }
41
43
 
42
44
  for (const filename of canonicalSupportFiles) {
43
- fs.copyFileSync(
45
+ // build:dist precedes build:plugins. Stamp fresh authoring sources rather
46
+ // than copying possibly stale generated hooks, preserving channel parity.
47
+ materialize(
44
48
  path.join(canonicalHookDir, filename),
45
49
  path.join(destDir, filename)
46
50
  );
@@ -853,8 +853,16 @@ if [ -n "$notice_temp_base" ] && [ "$notice_state_trusted" -eq 1 ]; then
853
853
  if [ ! -e "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ]; then
854
854
  (umask 077 && mkdir "$guard_memo_dir") 2>/dev/null || true
855
855
  fi
856
+ # Unlike shared temp ancestors, memo leaves may not be writable by another
857
+ # account even with the sticky bit: it could pre-create the session child.
858
+ guard_memo_mode=""
859
+ guard_memo_mode="$(stat -c '%a' "$guard_memo_dir" 2>/dev/null)" ||
860
+ guard_memo_mode="$(stat -f '%p' "$guard_memo_dir" 2>/dev/null)" ||
861
+ guard_memo_mode=""
862
+ case "$guard_memo_mode" in "" | *[!0-7]*) guard_memo_mode=777 ;; esac
856
863
  if [ -d "$guard_memo_dir" ] && [ ! -L "$guard_memo_dir" ] && \
857
- [ -O "$guard_memo_dir" ]; then
864
+ [ -O "$guard_memo_dir" ] &&
865
+ [ $((8#$guard_memo_mode & 8#0022)) -eq 0 ]; then
858
866
  export LISA_GUARD_MEMO_DIR="$guard_memo_dir"
859
867
  export LISA_GUARD_MEMO_UID="$notice_uid"
860
868
  fi
@@ -69,10 +69,6 @@
69
69
  "decision": "not-needed",
70
70
  "reason": "Unreachable outside this repository, and ratified above on that ground: the step tests for two paths that co-exist only here, and every consumer takes the else arm and runs the prover from the PACKAGE channel, whose vintage is their declared dependency version. There is no delivered artifact at this path for a consumer to hold a stale copy of — which is why the detection is `unchecked` rather than a measured negative."
71
71
  },
72
- "quality.yml::scripts/check-skipped-required-checks.mjs": {
73
- "decision": "warranted",
74
- "reason": "Verdict-bearing required gate: it compares skip tokens against the ruleset's required contexts, and the step states that its absence is a failure precisely because a skipped required check reads as satisfied. A superseded copy comparing against a stale notion of the required set is the same false green arriving by the other door. The artifact exports no contract version yet; recorded as an open obligation rather than exempted."
75
- },
76
72
  "quality.yml::scripts/check-state-classification.mjs": {
77
73
  "decision": "warranted",
78
74
  "reason": "Verdict-bearing required gate whose subject is classification itself — it decides which states a change is allowed to leave behind. A copy behind a classification change enforces the superseded taxonomy and names states that no longer mean what it thinks. The artifact exports no contract version yet; recorded as an open obligation rather than exempted."
@@ -99,13 +95,13 @@
99
95
  },
100
96
  "inspected": {
101
97
  "workflows": 23,
102
- "steps": 777,
103
- "couplings": 34,
98
+ "steps": 778,
99
+ "couplings": 33,
104
100
  "inventory": 78
105
101
  },
106
102
  "counts": {
107
103
  "package-backed": 10,
108
- "apply-lagged": 22,
104
+ "apply-lagged": 21,
109
105
  "never-delivered": 0,
110
106
  "undelivered": 2
111
107
  },
@@ -113,13 +109,13 @@
113
109
  "version-handshake": 4,
114
110
  "capability-probe": 0,
115
111
  "existence-only": 4,
116
- "no-probe": 14,
112
+ "no-probe": 13,
117
113
  "unchecked": 2
118
114
  },
119
115
  "decisionCounts": {
120
116
  "handshake": 4,
121
117
  "not-needed": 11,
122
- "warranted": 9
118
+ "warranted": 8
123
119
  },
124
120
  "couplings": [
125
121
  {
@@ -484,30 +480,6 @@
484
480
  "lanes": [],
485
481
  "detail": "`quality.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/check-learnings-budget.ts` from the CALLER's tree, and Lisa ships no such path in any delivery lane — no apply and no bump ever produces it. Either the consumer authors it, or whatever the step does where the path is missing runs instead — and that branch is the part this scan does not read. The read sits behind an existence test, which is ALL this scan examined of the step's control flow: it did not read the test's other branch, any fallback, any explicit `exit`, or any verification of the step's output. Whether an absent path skips quietly, falls back, or fails loudly is therefore NOT MEASURED — read the step before concluding it proves nothing. The shape worth ruling out is the silent one, where a skip posts no context and an absent required context is not a red one. The step's text does carry `else-branch`, `exit-nonzero`, `error-annotation`, `failure-suppression` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `unchecked`. Lisa delivers nothing at this path, so there is no artifact to ask whether it could answer a version. The staleness half is UNKNOWN here — not current, and not probe-less. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
486
482
  },
487
- {
488
- "key": "quality.yml::scripts/check-skipped-required-checks.mjs",
489
- "workflow": "quality.yml",
490
- "path": "scripts/check-skipped-required-checks.mjs",
491
- "channel": "apply",
492
- "verdict": "apply-lagged",
493
- "remedy": "run-lisa-apply",
494
- "guarded": false,
495
- "handling": [
496
- "exit-zero",
497
- "exit-nonzero",
498
- "error-annotation"
499
- ],
500
- "detection": "no-probe",
501
- "stalenessSignals": [
502
- "workflow-requests-version"
503
- ],
504
- "decision": "warranted",
505
- "reason": "Verdict-bearing required gate: it compares skip tokens against the ruleset's required contexts, and the step states that its absence is a failure precisely because a skipped required check reads as satisfied. A superseded copy comparing against a stale notion of the required set is the same false green arriving by the other door. The artifact exports no contract version yet; recorded as an open obligation rather than exempted.",
506
- "lanes": [
507
- "typescript/copy-overwrite"
508
- ],
509
- "detail": "`quality.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/check-skipped-required-checks.mjs` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (typescript/copy-overwrite). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. No existence test guards the read, which is ALL this scan examined of the step's control flow: it did not read anything that could absorb the failure. An absent path most likely fails the job loudly — the better of the two failures, because it is visible — but that is inferred from the missing guard, NOT MEASURED. The step's text does carry `exit-zero`, `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `no-probe`. Neither an existence test nor a version probe bears on this read, so nothing here distinguishes a current artifact from an absent or a superseded one. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
510
- },
511
483
  {
512
484
  "key": "quality.yml::scripts/check-state-classification.mjs",
513
485
  "workflow": "quality.yml",