@codyswann/lisa 4.55.2 → 4.55.4

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 (395) 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 +92 -80
  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 +38 -15
  33. package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +15 -1
  34. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +37 -13
  35. package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  36. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +56 -7
  37. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +36 -11
  38. package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  39. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  40. package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +0 -17
  41. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +18 -35
  42. package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  43. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +66 -12
  44. package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +2 -1
  45. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  46. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +1 -1
  47. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  48. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  49. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  50. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +32 -4
  51. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  52. package/plugins/lisa/hooks/block-blind-automerge.sh +13 -5
  53. package/plugins/lisa/hooks/block-direct-issue-create.sh +7 -0
  54. package/plugins/lisa/hooks/block-instruction-file-edits.sh +3 -0
  55. package/plugins/lisa/hooks/block-managed-file-edits.sh +76 -2
  56. package/plugins/lisa/hooks/block-no-verify.sh +3 -0
  57. package/plugins/lisa/hooks/block-shell-json-parsing.sh +3 -0
  58. package/plugins/lisa/hooks/guard-dedupe.bash +66 -29
  59. package/plugins/lisa/hooks/parity-safety-net-heredoc.py +3 -0
  60. package/plugins/lisa/hooks/parity-safety-net.sh +5 -1
  61. package/plugins/lisa/hooks/worktree-binding-guard.mjs +3 -0
  62. package/plugins/lisa/hooks/worktree-binding-guard.sh +5 -1
  63. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  64. package/plugins/lisa/rules/eager/base-rules.md +3 -1
  65. package/plugins/lisa/rules/eager/withdrawn-rulings.md +14 -3
  66. package/plugins/lisa/rules/reference/automation-runbook-contract.md +1 -1
  67. package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +1 -1
  68. package/plugins/lisa/rules/reference/blocker-containment.md +3 -3
  69. package/plugins/lisa/rules/reference/claim-archaeology.md +1 -1
  70. package/plugins/lisa/rules/reference/claim-evidence-mapping.md +1 -1
  71. package/plugins/lisa/rules/reference/claim-time-guards.md +1 -1
  72. package/plugins/lisa/rules/reference/control-reachability.md +1 -1
  73. package/plugins/lisa/rules/reference/convergent-review.md +1 -1
  74. package/plugins/lisa/rules/reference/credential-substrate-precedence.md +1 -1
  75. package/plugins/lisa/rules/reference/dependency-decision-records.md +1 -1
  76. package/plugins/lisa/rules/reference/dependency-internalization-kit.md +1 -1
  77. package/plugins/lisa/rules/reference/dependency-trust-classes.md +1 -1
  78. package/plugins/lisa/rules/reference/deployed-state-readback.md +1 -1
  79. package/plugins/lisa/rules/reference/derived-branch-plan.md +1 -1
  80. package/plugins/lisa/rules/reference/design-source-of-truth.md +1 -1
  81. package/plugins/lisa/rules/reference/design-value-binding.md +1 -1
  82. package/plugins/lisa/rules/reference/documentation-source-paths.md +1 -1
  83. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
  84. package/plugins/lisa/rules/reference/observability-audit.md +1 -1
  85. package/plugins/lisa/rules/reference/prd-definition-of-ready.md +1 -1
  86. package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +1 -1
  87. package/plugins/lisa/rules/reference/pre-flight-autofill.md +1 -1
  88. package/plugins/lisa/rules/reference/promotion-contract.md +1 -1
  89. package/plugins/lisa/rules/reference/readiness-rubric.md +1 -1
  90. package/plugins/lisa/rules/reference/ready-role-filing.md +37 -1
  91. package/plugins/lisa/rules/reference/rejection-detection.md +1 -1
  92. package/plugins/lisa/rules/reference/repo-scope-split.md +9 -10
  93. package/plugins/lisa/rules/reference/reset-seed-coverage.md +1 -1
  94. package/plugins/lisa/rules/reference/usage-accounting.md +1 -1
  95. package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +1 -1
  96. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +85 -17
  97. package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +5 -2
  98. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +38 -15
  99. package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +15 -1
  100. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +37 -13
  101. package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  102. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +56 -7
  103. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +36 -11
  104. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  105. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  106. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +0 -17
  107. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +18 -35
  108. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  109. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +66 -12
  110. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +2 -1
  111. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  112. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +1 -1
  113. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  114. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  115. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  116. package/plugins/lisa/skills/lisa-track/SKILL.md +32 -4
  117. package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  118. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +13 -5
  119. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +7 -0
  120. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +3 -0
  121. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +76 -2
  122. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +3 -0
  123. package/plugins/lisa-agy/hooks/guard-dedupe.bash +66 -29
  124. package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +3 -0
  125. package/plugins/lisa-agy/hooks/parity-safety-net.sh +5 -1
  126. package/plugins/lisa-agy/plugin.json +1 -1
  127. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +85 -17
  128. package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +5 -2
  129. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +38 -15
  130. package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +15 -1
  131. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +37 -13
  132. package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  133. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +56 -7
  134. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +36 -11
  135. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  136. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  137. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +0 -17
  138. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +18 -35
  139. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  140. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +66 -12
  141. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +2 -1
  142. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  143. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +1 -1
  144. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  145. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  146. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  147. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +32 -4
  148. package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  149. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  151. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  152. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  153. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  154. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +13 -5
  156. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +7 -0
  157. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +3 -0
  158. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +76 -2
  159. package/plugins/lisa-copilot/hooks/block-no-verify.sh +3 -0
  160. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +3 -0
  161. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +66 -29
  162. package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +3 -0
  163. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +5 -1
  164. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +3 -0
  165. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +5 -1
  166. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  167. package/plugins/lisa-copilot/rules/eager/base-rules.md +3 -1
  168. package/plugins/lisa-copilot/rules/eager/withdrawn-rulings.md +14 -3
  169. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +1 -1
  170. package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +1 -1
  171. package/plugins/lisa-copilot/rules/reference/blocker-containment.md +3 -3
  172. package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +1 -1
  173. package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +1 -1
  174. package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +1 -1
  175. package/plugins/lisa-copilot/rules/reference/control-reachability.md +1 -1
  176. package/plugins/lisa-copilot/rules/reference/convergent-review.md +1 -1
  177. package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +1 -1
  178. package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +1 -1
  179. package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +1 -1
  180. package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +1 -1
  181. package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +1 -1
  182. package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +1 -1
  183. package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +1 -1
  184. package/plugins/lisa-copilot/rules/reference/design-value-binding.md +1 -1
  185. package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +1 -1
  186. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  187. package/plugins/lisa-copilot/rules/reference/observability-audit.md +1 -1
  188. package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +1 -1
  189. package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +1 -1
  190. package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +1 -1
  191. package/plugins/lisa-copilot/rules/reference/promotion-contract.md +1 -1
  192. package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +1 -1
  193. package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +37 -1
  194. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +1 -1
  195. package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +9 -10
  196. package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +1 -1
  197. package/plugins/lisa-copilot/rules/reference/usage-accounting.md +1 -1
  198. package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +1 -1
  199. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +85 -17
  200. package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +5 -2
  201. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +38 -15
  202. package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +15 -1
  203. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +37 -13
  204. package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  205. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +56 -7
  206. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +36 -11
  207. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  208. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  209. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +0 -17
  210. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +18 -35
  211. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  212. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +66 -12
  213. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +2 -1
  214. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  215. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +1 -1
  216. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  217. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  218. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  219. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +32 -4
  220. package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  221. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  222. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +13 -5
  223. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +7 -0
  224. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +3 -0
  225. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +76 -2
  226. package/plugins/lisa-cursor/hooks/block-no-verify.sh +3 -0
  227. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +3 -0
  228. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +66 -29
  229. package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +3 -0
  230. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +5 -1
  231. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +3 -0
  232. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +5 -1
  233. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  234. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +1 -1
  235. package/plugins/lisa-cursor/rules/base-rules.mdc +3 -1
  236. package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +1 -1
  237. package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +3 -3
  238. package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +1 -1
  239. package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +1 -1
  240. package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +1 -1
  241. package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +1 -1
  242. package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +1 -1
  243. package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +1 -1
  244. package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +1 -1
  245. package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +1 -1
  246. package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +1 -1
  247. package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +1 -1
  248. package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +1 -1
  249. package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +1 -1
  250. package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +1 -1
  251. package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +1 -1
  252. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  253. package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +1 -1
  254. package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +1 -1
  255. package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +1 -1
  256. package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +1 -1
  257. package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +1 -1
  258. package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +1 -1
  259. package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +37 -1
  260. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +1 -1
  261. package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +9 -10
  262. package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +1 -1
  263. package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +1 -1
  264. package/plugins/lisa-cursor/rules/withdrawn-rulings.mdc +14 -3
  265. package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +1 -1
  266. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +85 -17
  267. package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +5 -2
  268. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +38 -15
  269. package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +15 -1
  270. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +37 -13
  271. package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  272. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +56 -7
  273. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +36 -11
  274. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  275. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  276. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +0 -17
  277. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +18 -35
  278. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  279. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +66 -12
  280. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +2 -1
  281. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  282. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +1 -1
  283. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  284. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  285. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  286. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +32 -4
  287. package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  288. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  289. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  290. package/plugins/lisa-expo-agy/plugin.json +1 -1
  291. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  292. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  293. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  294. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  295. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  296. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  297. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  298. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  299. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  300. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  301. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  302. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  303. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  304. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  305. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  306. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  307. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  308. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  309. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  310. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  311. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  312. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  313. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  314. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  315. package/plugins/lisa-rails-agy/plugin.json +1 -1
  316. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  317. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  318. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  319. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  320. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  321. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  322. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  323. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  324. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  325. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  326. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  327. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  328. package/plugins/materialized-artifacts.json +11 -0
  329. package/plugins/src/base/hooks/block-blind-automerge.sh +10 -5
  330. package/plugins/src/base/hooks/block-direct-issue-create.sh +4 -0
  331. package/plugins/src/base/hooks/block-managed-file-edits.sh +73 -2
  332. package/plugins/src/base/hooks/guard-dedupe.bash +63 -29
  333. package/plugins/src/base/hooks/parity-safety-net.sh +2 -1
  334. package/plugins/src/base/hooks/worktree-binding-guard.sh +2 -1
  335. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  336. package/plugins/src/base/rules/eager/base-rules.md +3 -1
  337. package/plugins/src/base/rules/eager/withdrawn-rulings.md +14 -3
  338. package/plugins/src/base/rules/reference/automation-runbook-contract.md +1 -1
  339. package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +1 -1
  340. package/plugins/src/base/rules/reference/blocker-containment.md +3 -3
  341. package/plugins/src/base/rules/reference/claim-archaeology.md +1 -1
  342. package/plugins/src/base/rules/reference/claim-evidence-mapping.md +1 -1
  343. package/plugins/src/base/rules/reference/claim-time-guards.md +1 -1
  344. package/plugins/src/base/rules/reference/control-reachability.md +1 -1
  345. package/plugins/src/base/rules/reference/convergent-review.md +1 -1
  346. package/plugins/src/base/rules/reference/credential-substrate-precedence.md +1 -1
  347. package/plugins/src/base/rules/reference/dependency-decision-records.md +1 -1
  348. package/plugins/src/base/rules/reference/dependency-internalization-kit.md +1 -1
  349. package/plugins/src/base/rules/reference/dependency-trust-classes.md +1 -1
  350. package/plugins/src/base/rules/reference/deployed-state-readback.md +1 -1
  351. package/plugins/src/base/rules/reference/derived-branch-plan.md +1 -1
  352. package/plugins/src/base/rules/reference/design-source-of-truth.md +1 -1
  353. package/plugins/src/base/rules/reference/design-value-binding.md +1 -1
  354. package/plugins/src/base/rules/reference/documentation-source-paths.md +1 -1
  355. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  356. package/plugins/src/base/rules/reference/observability-audit.md +1 -1
  357. package/plugins/src/base/rules/reference/prd-definition-of-ready.md +1 -1
  358. package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +1 -1
  359. package/plugins/src/base/rules/reference/pre-flight-autofill.md +1 -1
  360. package/plugins/src/base/rules/reference/promotion-contract.md +1 -1
  361. package/plugins/src/base/rules/reference/readiness-rubric.md +1 -1
  362. package/plugins/src/base/rules/reference/ready-role-filing.md +37 -1
  363. package/plugins/src/base/rules/reference/rejection-detection.md +1 -1
  364. package/plugins/src/base/rules/reference/repo-scope-split.md +9 -10
  365. package/plugins/src/base/rules/reference/reset-seed-coverage.md +1 -1
  366. package/plugins/src/base/rules/reference/usage-accounting.md +1 -1
  367. package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +1 -1
  368. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +85 -17
  369. package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +5 -2
  370. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +38 -15
  371. package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +15 -1
  372. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +37 -13
  373. package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +15 -1
  374. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +56 -7
  375. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +36 -11
  376. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  377. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  378. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +0 -17
  379. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +18 -35
  380. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  381. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +66 -12
  382. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +2 -1
  383. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  384. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +1 -1
  385. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  386. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  387. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  388. package/plugins/src/base/skills/lisa-track/SKILL.md +32 -4
  389. package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +15 -1
  390. package/scripts/build-plugins.sh +4 -0
  391. package/scripts/copy-opencode-plugin-templates.mjs +5 -1
  392. package/scripts/lisa-enforcement-fallback.sh +9 -1
  393. package/scripts/two-channel-couplings.json +5 -33
  394. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +551 -13
  395. package/typescript/create-only/.github/required-checks.json +1 -1
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
3
+ # Do not edit directly — durable changes belong upstream in Lisa.
4
+
2
5
  # PreToolUse hook: refuse agent writes to files Lisa overwrites on every apply.
3
6
  #
4
7
  # Lisa ships templates in three modes, and only one of them is unsafe to edit:
@@ -566,6 +569,7 @@ EOF
566
569
  import os
567
570
  import re
568
571
  import shlex
572
+ import sys
569
573
 
570
574
  command = os.environ.get("MANAGED_EDIT_COMMAND", "")
571
575
  project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
@@ -576,6 +580,7 @@ project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
576
580
  FOLLOW_MAX_BYTES = 262144
577
581
  FOLLOW_MAX_FILES = 8
578
582
  FOLLOW_MAX_DEPTH = 3
583
+ ENV_SPLIT_UNRESOLVED = 42
579
584
 
580
585
  # Spelled with chr() rather than backslash escapes on purpose. This source is
581
586
  # embedded in a shell heredoc and copied verbatim through several generators
@@ -607,7 +612,7 @@ FOLLOW_WRAPPERS = {
607
612
  # positional count: the walk below re-enters its assignment skip on the
608
613
  # next iteration and steps over as many as are present.
609
614
  "env": (
610
- frozenset({"-C", "--chdir", "-P", "-S", "--split-string", "-u", "--unset"}),
615
+ frozenset({"-C", "--chdir", "-P", "-u", "--unset"}),
611
616
  0,
612
617
  ),
613
618
  "exec": (frozenset({"-a"}), 0),
@@ -741,6 +746,31 @@ def statements(tokens):
741
746
  return grouped
742
747
 
743
748
 
749
+ class UnresolvedEnvSplit(ValueError):
750
+ """Split-string syntax whose executed arguments cannot be proven here."""
751
+
752
+
753
+ def parse_env_split(split_string):
754
+ """Parse only the env -S subset shared with ordinary quoted arguments.
755
+
756
+ GNU env has its own escapes, comments and environment interpolation. Do
757
+ not pretend shlex implements those, or invent environment values in a
758
+ pre-execution guard. The operator can spell the resulting command directly.
759
+
760
+ Args:
761
+ split_string: The literal argument supplied to env's split option.
762
+
763
+ Returns:
764
+ Arguments for the supported subset; otherwise raises UnresolvedEnvSplit.
765
+ """
766
+ if any(marker in split_string for marker in (BACKSLASH, "#", "$", chr(96), chr(11), chr(12))):
767
+ raise UnresolvedEnvSplit("escapes, comments, shell substitution, variable expansion or control whitespace")
768
+ try:
769
+ return shlex.split(split_string, comments=False, posix=True)
770
+ except ValueError as error:
771
+ raise UnresolvedEnvSplit("unbalanced quoting") from error
772
+
773
+
744
774
  def command_word(statement):
745
775
  """The program a statement runs, and the tokens after it.
746
776
 
@@ -757,6 +787,7 @@ def command_word(statement):
757
787
  its command word is itself the executed script.
758
788
  """
759
789
  index = 0
790
+ split_expansions = 0
760
791
  while index < len(statement):
761
792
  token = statement[index]
762
793
  if "=" in token and not token.startswith(("=", "-")):
@@ -772,6 +803,36 @@ def command_word(statement):
772
803
  if option == "--":
773
804
  index += 1
774
805
  break
806
+ # env -S inserts the split string back into env's argument list;
807
+ # it is executable input, not an option value to discard. Keep
808
+ # shell -c's quoted operand intact while expanding the env layer.
809
+ # The known no-operand flags can be combined before S (-vS/-ivS).
810
+ # Stop at S: the rest belongs to its argument, not more flags.
811
+ short_split = re.match(r"^-[iv0]*S(.*)$", option)
812
+ if program == "env" and (
813
+ option == "--split-string"
814
+ or option.startswith("--split-string=")
815
+ or short_split is not None
816
+ ):
817
+ consumed = 1
818
+ if option == "--split-string" or (
819
+ short_split is not None and not short_split.group(1)
820
+ ):
821
+ if index + 1 >= len(statement):
822
+ raise UnresolvedEnvSplit("missing split-string operand")
823
+ split_string = statement[index + 1]
824
+ consumed = 2
825
+ elif option.startswith("--split-string="):
826
+ split_string = option.split("=", 1)[1]
827
+ else:
828
+ split_string = short_split.group(1)
829
+ if split_expansions >= 64:
830
+ raise UnresolvedEnvSplit("nested split-string limit")
831
+ expanded = parse_env_split(split_string)
832
+ statement = [token] + expanded + statement[index + consumed :]
833
+ split_expansions += 1
834
+ index = 0
835
+ break
775
836
  index += 2 if option in separate else 1
776
837
  index += positional
777
838
  return (None, None, [])
@@ -985,7 +1046,11 @@ def collect(text, depth, seen, out):
985
1046
 
986
1047
 
987
1048
  targets = []
988
- collect(command, 0, set(), targets)
1049
+ try:
1050
+ collect(command, 0, set(), targets)
1051
+ except UnresolvedEnvSplit as error:
1052
+ sys.stderr.write(str(error) + NEWLINE)
1053
+ sys.exit(ENV_SPLIT_UNRESOLVED)
989
1054
  for target in targets:
990
1055
  cleaned = target.strip().strip("'\"")
991
1056
  if cleaned:
@@ -1003,6 +1068,15 @@ PY
1003
1068
  else
1004
1069
  analyzer_status=$?
1005
1070
  fi
1071
+ if [ "$analyzer_status" -eq 42 ]; then
1072
+ printf 'Blocked: cannot resolve env --split-string for managed-file protection.\n' >&2
1073
+ printf 'Use an explicit command without split-string escapes, comments or variable expansion.\n' >&2
1074
+ while IFS= read -r analyzer_line; do
1075
+ [ -n "$analyzer_line" ] && printf ' %s\n' "$analyzer_line" >&2
1076
+ done <"$analyzer_stderr"
1077
+ rm -f "$analyzer_stderr"
1078
+ exit 2
1079
+ fi
1006
1080
  if [ "$analyzer_status" -ne 0 ]; then
1007
1081
  printf 'block-managed-file-edits: Bash analyzer failed (exit %s); Bash write protection is NOT active\n' "$analyzer_status" >&2
1008
1082
  while IFS= read -r analyzer_line; do
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
3
+ # Do not edit directly — durable changes belong upstream in Lisa.
4
+
2
5
  # PreToolUse hook for Bash: blocks commands that bypass git's verification hooks.
3
6
  # Bypassing pre-commit/pre-push hooks (which exist for a reason) is blocked in
4
7
  # all of its forms; the fix is to address the underlying issue, not silence the
@@ -1,3 +1,6 @@
1
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ # Do not edit directly — durable changes belong upstream in Lisa.
3
+
1
4
  # shellcheck shell=bash
2
5
  # Evaluate a dual-channel guard once per tool call, without de-registering it.
3
6
  #
@@ -51,12 +54,9 @@
51
54
  # refusal reaching the agent without the message that explains it. On a
52
55
  # refusal every channel evaluates and speaks exactly as it does today.
53
56
  #
54
- # COST ON THE PATH THAT GETS NO BENEFIT. A host with one channel must not pay
55
- # for a mechanism it cannot use. The channel check is builtins only — one small
56
- # file read, no subshell, no fork — and returns before the digest is computed
57
- # unless a second channel has actually been seen. The digest, and the one `stat`
58
- # that feeds it the per-call discriminator, are reached only where a second
59
- # channel is live, which is exactly where they are repaid.
57
+ # State ownership, modes and ancestry are verified before channel discovery.
58
+ # After that validation the channel check uses only builtins, and returns
59
+ # before digesting code or sizing the transcript unless another channel is live.
60
60
 
61
61
  # Key this guard would memoise under, once computed. Empty means "record
62
62
  # nothing on exit".
@@ -74,14 +74,25 @@ lisa_guard_memo_ready=0
74
74
  # Digest tool for the memo key, resolved once. Empty disables the memo.
75
75
  lisa_guard_memo_digest_tool=""
76
76
 
77
- # Whether a directory is a real directory this user owns, and not a symlink —
78
- # builtins only, so this costs no process.
79
- #
80
- # `-O` is the load-bearing test: a memo planted by another user would suppress a
81
- # refusal, so a directory this user does not own is never used. The dispatcher
82
- # validates the whole parent chain before exporting `LISA_GUARD_MEMO_DIR`; this
83
- # re-checks the leaf rather than trusting the environment variable, because an
84
- # exported path is an input like any other.
77
+ # Read ownership and all permission bits without accepting output from a
78
+ # failed platform-specific stat invocation.
79
+ # $1 - directory; sets lisa_guard_memo_owner and lisa_guard_memo_mode
80
+ lisa_guard_memo_stat() {
81
+ local facts=""
82
+ facts="$(stat -c '%u %a' "$1" 2>/dev/null)" ||
83
+ facts="$(stat -f '%u %p' "$1" 2>/dev/null)" || return 1
84
+ lisa_guard_memo_owner="${facts%% *}"
85
+ lisa_guard_memo_mode="${facts#* }"
86
+ case "$lisa_guard_memo_owner:$lisa_guard_memo_mode" in
87
+ *[!0-9:]* | :* | *:) return 1 ;;
88
+ esac
89
+ case "$lisa_guard_memo_mode" in *[!0-7]*) return 1 ;; esac
90
+ lisa_guard_memo_mode=$((8#$lisa_guard_memo_mode & 8#7777))
91
+ }
92
+
93
+ # Memo leaves must be private even when a sticky shared temp ancestor is safe.
94
+ # Ownership alone allows another local account to plant a session/allow file
95
+ # in an owned but group- or world-writable directory.
85
96
  #
86
97
  # $1 - candidate directory
87
98
  lisa_guard_memo_dir_usable() {
@@ -89,23 +100,42 @@ lisa_guard_memo_dir_usable() {
89
100
  [ -d "$1" ] || return 1
90
101
  [ ! -L "$1" ] || return 1
91
102
  [ -O "$1" ] || return 1
103
+ lisa_guard_memo_stat "$1" || return 1
104
+ [ $((lisa_guard_memo_mode & 8#0022)) -eq 0 ]
105
+ }
106
+
107
+ # Resolve parents physically and verify every ancestor. Plugin hooks do not
108
+ # inherit the dispatcher's trust walk, and exported paths are still inputs.
109
+ # $1 - candidate memo base; sets lisa_guard_memo_base only on success
110
+ lisa_guard_memo_trust_base() {
111
+ lisa_guard_memo_dir_usable "$1" || return 1
112
+ local physical=""
113
+ physical="$(cd "$1" 2>/dev/null && pwd -P)" || return 1
114
+ local current="$physical"
115
+ while :; do
116
+ lisa_guard_memo_stat "$current" || return 1
117
+ [ "$lisa_guard_memo_owner" = 0 ] || [ -O "$current" ] || return 1
118
+ if [ $((lisa_guard_memo_mode & 8#0022)) -ne 0 ] &&
119
+ [ $((lisa_guard_memo_mode & 8#1000)) -eq 0 ]; then
120
+ return 1
121
+ fi
122
+ [ "$current" != / ] || break
123
+ current="${current%/*}"
124
+ [ -n "$current" ] || current=/
125
+ done
126
+ lisa_guard_memo_base="$physical"
92
127
  }
93
128
 
94
129
  # Establish the memo base directory.
95
130
  #
96
- # Preferred source is `LISA_GUARD_MEMO_DIR`, exported by the dispatcher, which
97
- # has already resolved `TMPDIR` physically and walked every parent for ownership
98
- # and mode. Guards it spawns therefore pay nothing for that walk. A guard
99
- # reached by the plugin channel has no such parent, so it creates the directory
100
- # itself under `TMPDIR` — private by umask, and used only when this user owns
101
- # it. If neither works the memo is unavailable and every guard evaluates, which
102
- # is today's behaviour.
131
+ # Preferred source is `LISA_GUARD_MEMO_DIR`, exported by the dispatcher. Both
132
+ # this input and the plugin channel's TMPDIR fallback receive the same trust
133
+ # checks. Missing state is created privately; untrusted state disables memo use.
103
134
  lisa_guard_memo_resolve_base() {
104
135
  [ -z "$lisa_guard_memo_base" ] || return 0
105
136
 
106
137
  local candidate="${LISA_GUARD_MEMO_DIR:-}"
107
- if lisa_guard_memo_dir_usable "$candidate"; then
108
- lisa_guard_memo_base="$candidate"
138
+ if lisa_guard_memo_trust_base "$candidate"; then
109
139
  return 0
110
140
  fi
111
141
 
@@ -118,8 +148,7 @@ lisa_guard_memo_resolve_base() {
118
148
  if [ ! -e "$candidate" ] && [ ! -L "$candidate" ]; then
119
149
  (umask 077 && mkdir -p "$candidate") 2>/dev/null || true
120
150
  fi
121
- if lisa_guard_memo_dir_usable "$candidate"; then
122
- lisa_guard_memo_base="$candidate"
151
+ if lisa_guard_memo_trust_base "$candidate"; then
123
152
  return 0
124
153
  fi
125
154
  return 1
@@ -165,6 +194,7 @@ lisa_guard_memo_sweep() {
165
194
  #
166
195
  # $1 - guard name, used only to name the memo file
167
196
  # $2 - the raw tool payload this guard was handed on stdin
197
+ # Remaining arguments - files containing delegated verdict code
168
198
  lisa_guard_dedupe() {
169
199
  local guard_name="${1:-}"
170
200
  local payload="${2:-}"
@@ -194,8 +224,7 @@ lisa_guard_dedupe() {
194
224
  fresh_session=1
195
225
  fi
196
226
  fi
197
- [ -d "$session_dir" ] || return 0
198
- [ ! -L "$session_dir" ] || return 0
227
+ lisa_guard_memo_dir_usable "$session_dir" || return 0
199
228
 
200
229
  # Swept once, by the process that created the session directory, so a
201
230
  # long-lived machine does not accumulate memo state. Off the hot path by
@@ -278,11 +307,18 @@ lisa_guard_dedupe() {
278
307
  case "$transcript_size" in "" | *[!0-9]*) return 0 ;; esac
279
308
 
280
309
  # One command substitution, covering everything the verdict depends on: the
281
- # guard's own bytes, this library's bytes, the payload, and which call it is.
310
+ # guard's own bytes, this library, delegated code, payload, and call identity.
311
+ # Keep literal bytes (including trailing newlines), and propagate every read
312
+ # failure. A successful digest of a partial stream is not proof of identity.
282
313
  local key=""
283
314
  key="$(
315
+ set -o pipefail
284
316
  {
285
- cat "$self_script" "${BASH_SOURCE[0]}" 2>/dev/null
317
+ local source_file=""
318
+ for source_file in "$self_script" "${BASH_SOURCE[0]}" "${@:3}"; do
319
+ cat "$source_file" 2>/dev/null || exit 1
320
+ printf '\0'
321
+ done
286
322
  printf '\n%s\n' "$payload"
287
323
  printf '%s\n' "$transcript_size"
288
324
  } | if [ "$lisa_guard_memo_digest_tool" = "shasum" ]; then
@@ -293,6 +329,7 @@ lisa_guard_dedupe() {
293
329
  )" || key=""
294
330
  key="${key%% *}"
295
331
  case "$key" in "" | *[!0-9a-f]*) return 0 ;; esac
332
+ [ "${#key}" -eq 64 ] || return 0
296
333
 
297
334
  # One memo file per guard per session, rewritten as the call moves on, so the
298
335
  # state directory stays a fixed size however long a session runs. A saturated
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env python3
2
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
3
+ # Do not edit directly — durable changes belong upstream in Lisa.
4
+
2
5
  """Classify the only heredoc forms whose payload is non-executable text.
3
6
 
4
7
  SCOPE — read this before hardening anything here (issue #1993).
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
3
+ # Do not edit directly — durable changes belong upstream in Lisa.
4
+
2
5
  # PreToolUse hook for Bash: a safety net that blocks destructive shell commands
3
6
  # before they run. Lisa-native reimplementation of the upstream
4
7
  # `safety-net@cc-marketplace` plugin's PreToolUse Bash-guard (parity work,
@@ -136,7 +139,8 @@ if [ -r "$lisa_guard_dedupe_lib" ]; then
136
139
  # shellcheck source=guard-dedupe.bash
137
140
  . "$lisa_guard_dedupe_lib"
138
141
  trap 'lisa_guard_dedupe_record $?' EXIT
139
- lisa_guard_dedupe parity-safety-net "$input"
142
+ lisa_guard_dedupe parity-safety-net "$input" \
143
+ "$lisa_guard_hook_dir/parity-safety-net-heredoc.py"
140
144
  fi
141
145
 
142
146
  tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
@@ -1 +1 @@
1
- {"version":3,"file":"support-file-resolver.d.ts","sourceRoot":"","sources":["../../src/opencode/support-file-resolver.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAcR"}
1
+ {"version":3,"file":"support-file-resolver.d.ts","sourceRoot":"","sources":["../../src/opencode/support-file-resolver.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAeR"}
@@ -11,6 +11,8 @@ export function resolveSupportFile(moduleUrl, filename) {
11
11
  const bundled = path.join(moduleDir, "plugin-templates", filename);
12
12
  if (path.basename(path.dirname(moduleDir)) === "dist")
13
13
  return bundled;
14
- return path.resolve(moduleDir, "..", "..", "plugins", "src", "base", "hooks", filename);
14
+ // Generated hooks carry the same ownership banner as the dispatcher copy;
15
+ // raw authoring sources do not, so their literal-byte memo keys would differ.
16
+ return path.resolve(moduleDir, "..", "..", "plugins", "lisa", "hooks", filename);
15
17
  }
16
18
  //# sourceMappingURL=support-file-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"support-file-resolver.js","sourceRoot":"","sources":["../../src/opencode/support-file-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,QAAgB;IAEhB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IACnE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IACtE,OAAO,IAAI,CAAC,OAAO,CACjB,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,QAAQ,CACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"support-file-resolver.js","sourceRoot":"","sources":["../../src/opencode/support-file-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,QAAgB;IAEhB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IACnE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IACtE,0EAA0E;IAC1E,8EAA8E;IAC9E,OAAO,IAAI,CAAC,OAAO,CACjB,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,MAAM,EACN,OAAO,EACP,QAAQ,CACT,CAAC;AACJ,CAAC"}
@@ -152,31 +152,33 @@ const isPriorTag = tag =>
152
152
  * removed, and the tags that were retired.
153
153
  */
154
154
  const stripPriorBlocks = contents => {
155
- const lines = contents.split("\n");
156
- const kept = [];
157
- const retired = [];
158
- let dropping = null;
159
- for (const line of lines) {
160
- if (dropping === null) {
161
- const begin = /@generated begin (\S+)/.exec(line);
162
- if (begin && isPriorTag(begin[1])) {
163
- dropping = begin[1];
164
- retired.push(begin[1]);
165
- continue;
155
+ const result = contents.split("\n").reduce(
156
+ (state, line) => {
157
+ if (state.dropping === null) {
158
+ const begin = /@generated begin (\S+)/.exec(line);
159
+ if (begin && isPriorTag(begin[1])) {
160
+ return {
161
+ ...state,
162
+ dropping: begin[1],
163
+ retired: [...state.retired, begin[1]],
164
+ };
165
+ }
166
+ return { ...state, kept: [...state.kept, line] };
166
167
  }
167
- kept.push(line);
168
- continue;
169
- }
170
- // Inside a doomed block: drop through its own end marker, and only its
171
- // own. Keying the end on the tag we opened with means a nested or
172
- // adjacent block belonging to something else cannot close ours early.
173
- if (line.includes(`@generated end ${dropping}`)) dropping = null;
174
- }
168
+ // Inside a doomed block: drop through its own end marker, and only its
169
+ // own. Keying the end on the tag we opened with means a nested or
170
+ // adjacent block belonging to something else cannot close ours early.
171
+ return line.includes(`@generated end ${state.dropping}`)
172
+ ? { ...state, dropping: null }
173
+ : state;
174
+ },
175
+ { kept: [], retired: [], dropping: null }
176
+ );
175
177
  // An unterminated block would otherwise swallow the rest of the file. Losing
176
178
  // MainActivity to a truncated marker is far worse than leaving a stale block
177
179
  // in place, so this fails closed by keeping the original.
178
- if (dropping !== null) return { contents, retired: [] };
179
- return { contents: kept.join("\n"), retired };
180
+ if (result.dropping !== null) return { contents, retired: [] };
181
+ return { contents: result.kept.join("\n"), retired: result.retired };
180
182
  };
181
183
 
182
184
  const mergeOrThrow = contents => {
@@ -5,7 +5,7 @@
5
5
  "⚠️ `required_contexts` SHIPS EMPTY AND UNSTAMPED ON PURPOSE. Lisa cannot know what YOUR ruleset requires, and an earlier version of this seed shipped a guess that was measured WRONG (#2476): it claimed `🔗 Work-Item Traceability` was required when no ruleset required it, and omitted six contexts that were. Until `ruleset.baseline_fetched_at` carries the date you transcribed the real list, the guard reports NOT CHECKED rather than answering from fiction.",
6
6
  "To arm it: gh api repos/OWNER/NAME/rulesets --jq '.[] | \"\\\\(.id) \\\\(.name)\"' to find the ids, then gh api repos/OWNER/NAME/rulesets/ID --jq '.rules[] | select(.type==\"required_status_checks\") | .parameters.required_status_checks[].context' for the contexts. Paste them into `required_contexts` BYTE FOR BYTE, fill in `ruleset.repo` / `ruleset.ids`, and set `baseline_fetched_at` to today.",
7
7
  "A transcription NEVER EXPIRES. Trust is presence-based: a non-empty, parseable `baseline_fetched_at` means somebody read the list off a live ruleset, and it is believed; an empty or unreadable one means it was never transcribed, and the guard refuses to answer. A ninety-day ceiling used to sit here alongside a scheduled workflow that re-read the live ruleset and re-stamped this file. Both were removed (#3599) because that workflow needed a standing `administration:read` token in every repository. A deadline with no refresh mechanism is an obligation nobody can discharge, and a guard that reliably expires into NOT CHECKED looks healthy right up until it has been quietly inert for a quarter. The consequence is stated plainly: if this list and your live ruleset drift apart, you find out by consequence rather than by check. Re-transcribe when you change your ruleset.",
8
- "Lisa's quality.yml runs the offline arm on every pull request. This seed ships `\"enforcement\": \"warn\"`, which downgrades findings AND the NOT-CHECKED refusal to reports so a fresh install does not go red on arrival. Delete the key once you have transcribed the list — then it blocks.",
8
+ "Lisa's quality.yml runs the OUTCOME arm (`--outcomes`) on every pull request: it fails when a context in `required_contexts` concluded `skipped`, which GitHub counts as satisfied. It also FAILS while `required_contexts` is untranscribed, IN EVERY ENFORCEMENT MODE, because examining no required context is not a pass. This seed ships `\"enforcement\": \"warn\"`, which downgrades the vestigial `skip_jobs` token findings to reports — it does not downgrade either of those. Transcribe the list, then delete the key.",
9
9
  "`skip_jobs` is matched as an exact comma-delimited token and GitHub Actions expression syntax has no string-replace, so write the list with no spaces: `skip_jobs: 'a,b'`, never `skip_jobs: 'a, b'`. A spaced token matches nothing and the job runs — reported as `whitespace_in_skip_token`.",
10
10
  "`_example_required_contexts` below is a STARTING POINT FOR TYPING, never read by the guard. Verify every line against your own ruleset before promoting any of it.",
11
11
  "THE FAMILY: required-and-red is loud; required-and-vacuous is not; advisory-and-stale is invisible. All three are one gate reporting satisfied without proving anything. `required_contexts` + `skip_job_declarations` above cover the SKIPPED variant; `evidence_bearing_checks` below covers the VACUOUS one.",
@@ -5,7 +5,7 @@
5
5
  "⚠️ `required_contexts` SHIPS EMPTY AND UNSTAMPED ON PURPOSE. Lisa cannot know what YOUR ruleset requires, and an earlier version of this seed shipped a guess that was measured WRONG (#2476): it claimed `🔗 Work-Item Traceability` was required when no ruleset required it, and omitted six contexts that were. Until `ruleset.baseline_fetched_at` carries the date you transcribed the real list, the guard reports NOT CHECKED rather than answering from fiction.",
6
6
  "To arm it: gh api repos/OWNER/NAME/rulesets --jq '.[] | \"\\\\(.id) \\\\(.name)\"' to find the ids, then gh api repos/OWNER/NAME/rulesets/ID --jq '.rules[] | select(.type==\"required_status_checks\") | .parameters.required_status_checks[].context' for the contexts. Paste them into `required_contexts` BYTE FOR BYTE, fill in `ruleset.repo` / `ruleset.ids`, and set `baseline_fetched_at` to today.",
7
7
  "A transcription NEVER EXPIRES. Trust is presence-based: a non-empty, parseable `baseline_fetched_at` means somebody read the list off a live ruleset, and it is believed; an empty or unreadable one means it was never transcribed, and the guard refuses to answer. A ninety-day ceiling used to sit here alongside a scheduled workflow that re-read the live ruleset and re-stamped this file. Both were removed (#3599) because that workflow needed a standing `administration:read` token in every repository. A deadline with no refresh mechanism is an obligation nobody can discharge, and a guard that reliably expires into NOT CHECKED looks healthy right up until it has been quietly inert for a quarter. The consequence is stated plainly: if this list and your live ruleset drift apart, you find out by consequence rather than by check. Re-transcribe when you change your ruleset.",
8
- "Lisa's quality.yml runs the offline arm on every pull request. This seed ships `\"enforcement\": \"warn\"`, which downgrades findings AND the NOT-CHECKED refusal to reports so a fresh install does not go red on arrival. Delete the key once you have transcribed the list — then it blocks.",
8
+ "Lisa's quality.yml runs the OUTCOME arm (`--outcomes`) on every pull request: it fails when a context in `required_contexts` concluded `skipped`, which GitHub counts as satisfied. It also FAILS while `required_contexts` is untranscribed, IN EVERY ENFORCEMENT MODE, because examining no required context is not a pass. This seed ships `\"enforcement\": \"warn\"`, which downgrades the vestigial `skip_jobs` token findings to reports — it does not downgrade either of those. Transcribe the list, then delete the key.",
9
9
  "`skip_jobs` is matched as an exact comma-delimited token and GitHub Actions expression syntax has no string-replace, so write the list with no spaces: `skip_jobs: 'a,b'`, never `skip_jobs: 'a, b'`. A spaced token matches nothing and the job runs — reported as `whitespace_in_skip_token`.",
10
10
  "`_example_required_contexts` below is a STARTING POINT FOR TYPING, never read by the guard. Verify every line against your own ruleset before promoting any of it.",
11
11
  "THE FAMILY: required-and-red is loud; required-and-vacuous is not; advisory-and-stale is invisible. All three are one gate reporting satisfied without proving anything. `required_contexts` + `skip_job_declarations` above cover the SKIPPED variant; `evidence_bearing_checks` below covers the VACUOUS one.",
package/package.json CHANGED
@@ -187,7 +187,7 @@
187
187
  "zod-validation-error": "^4.0.0"
188
188
  },
189
189
  "name": "@codyswann/lisa",
190
- "version": "4.55.2",
190
+ "version": "4.55.4",
191
191
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
192
192
  "main": "dist/index.js",
193
193
  "exports": {
@@ -336,7 +336,7 @@
336
336
  "test": "tests"
337
337
  },
338
338
  "types": "./dist/index.d.ts",
339
- "lisaReleaseCommit": "dfa8eee4cbdc81e60bd25f83e68afea9deb8d45a",
340
- "gitHead": "dfa8eee4cbdc81e60bd25f83e68afea9deb8d45a",
341
- "lisaReleaseTag": "v4.55.2"
339
+ "lisaReleaseCommit": "940ce1ffd27231b14136425ecc43d44b11354f00",
340
+ "gitHead": "940ce1ffd27231b14136425ecc43d44b11354f00",
341
+ "lisaReleaseTag": "v4.55.4"
342
342
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.55.2",
3
+ "version": "4.55.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.55.2",
3
+ "version": "4.55.4",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -6,6 +6,20 @@ allowed-tools: ["Skill", "Bash"]
6
6
 
7
7
  # GitHub Build Intake: $ARGUMENTS
8
8
 
9
+ ## Human-gate release authorization
10
+
11
+ A release requires a trusted human author, not just matching comment text. Follow
12
+ `ready-role-filing` — **Human-gate release authorization**: preserve tracker-supplied comment
13
+ author IDs and bot metadata, resolve `trustedHumanActorIds` only from an explicit user instruction
14
+ or existing human-authored trusted project policy, and pass it with structured `comments` to every
15
+ hold classifier, reconciliation, normalization and release planner. Never derive trust from the
16
+ comment body, a display name, the actor's own assertion, or an automation posting on its own behalf.
17
+ Missing policy, missing/unreadable author identity, raw body strings, untrusted actors and known bots
18
+ cannot discharge a hold. Keep the item held and report the missing authorization; do not silently
19
+ replace these inputs with an empty history or an inferred allowlist. Authorized matching releases
20
+ continue through the existing path and never override an independently declared caller hold.
21
+
22
+
9
23
  `$ARGUMENTS` is one of:
10
24
 
11
25
  1. A GitHub `org/repo` token (e.g., `acme/frontend-v2`).
@@ -215,14 +229,21 @@ nothing survives, exit on the denominator-stated summary from `summarizeDryLane`
215
229
  A blocker is a **claim with a timestamp, not a fact** — it goes stale the moment its condition comes
216
230
  true, and nothing re-read one before this phase. For each `$BLOCKED` candidate:
217
231
 
218
- 1. **Human gate first, and it is absolute.** An issue carrying the configured human-needed label
219
- (`github.labels.build.human_needed`, default `human-needed`) or a `[lisa-human-gate]` marker in
220
- its body is **never** auto-selected, whatever any probe says.
232
+ 1. **Read release comments before applying the human gate.** Pass all item comments, labels,
233
+ body, `trustedHumanActorIds` and configured `humanNeededLabel` to `classifyReadyCandidate(...)`. For a discharged hold,
234
+ call `planHumanGateRelease({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
235
+ and retain its plan. Apply human-marker cleanup only as planned; defer any ready-role restoration
236
+ until steps 2–5 have cleared the remaining blockers (map the role to the vendor's state or label).
237
+ A still-active hold is never auto-selected, whatever any probe says. An absent or unreadable
238
+ comment history cannot discharge a hold. A release removes only the human hold: continue the
239
+ remaining blocker checks below before selecting the item.
240
+
221
241
  2. **Extract the stated discharge condition**, then **probe it**. Machine-testable conditions — a
222
242
  version on trunk, a published package, a CI run history, an advisory's patched status — rot
223
243
  fastest and are cheapest to check. A human decision is not machine-testable; leave it.
224
- 3. **Classify with `classifyPreWorkCandidate(...)`** from `scripts/intake-blocker-reprobe.mjs`. A
225
- discharge with no recorded evidence is not a discharge, and neither is a candidate nothing
244
+ 3. **Classify with `classifyPreWorkCandidate(...)`** from `scripts/intake-blocker-reprobe.mjs`.
245
+ Include the structured `comments` and `trustedHumanActorIds` from step 1.
246
+ A discharge with no recorded evidence is not a discharge, and neither is a candidate nothing
226
247
  probed this cycle — the helper refuses both.
227
248
  4. **Record the result on the issue either way** via `formatReprobeNote(...)` as a comment, so the
228
249
  next cycle reads the answer rather than re-deriving it. Keep it idempotent.
@@ -317,7 +338,7 @@ other gate's verdict — however conclusive — may promote an item a person par
317
338
  drift, and a drifted gate fails *silently*, by quietly ceasing to match. Do **not** re-implement
318
339
  the test here, and do **not** key it on `reason=`: markers in the wild carry no `reason=` key at
319
340
  all and sit anywhere in the body, so a structured parse would miss them while appearing to work
320
- on every item that happens to have one. **Pass the item's `comments` alongside its labels and
341
+ on every item that happens to have one. **Pass the item's structured `comments` and `trustedHumanActorIds` alongside its labels and
321
342
  body.** A hold is ended by a release recorded in a comment, so a reader handed no comments cannot
322
343
  see the discharge — it goes on holding an item whose question was answered weeks ago, which is
323
344
  the defect this gate carried from the day it was written (CodySwannGT/lisa#3852). Omitting them
@@ -328,16 +349,17 @@ other gate's verdict — however conclusive — may promote an item a person par
328
349
  and re-rejected every cycle forever and seen by nothing — `lisa-repair-intake` sweeps items that
329
350
  are **not** in the ready role and excludes gated ones outright, so a ready-and-gated item falls
330
351
  outside its filter twice over. Call
331
- `planHumanGateReconciliation({ labels, body, humanNeededLabel, readyLabel, alreadyNotified })`
352
+ `planHumanGateReconciliation({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, readyLabel, alreadyNotified })`
332
353
  and apply exactly the actions it returns: remove `$READY`, add the configured human-needed
333
354
  marker, and post `formatHumanGateNote()` once. The planner is idempotent by state, so an item
334
355
  already out of the lane and already marked yields no second mutation and no second comment. This
335
356
  is the same repair the leaf-only gate already performs for a ready item that must not be
336
357
  dispatched.
337
358
  4. **On `claimable: true` for an item that still carries a hold, RELEASE it — do not just proceed.**
338
- The hold left durable state behind: the item is out of the queue and flagged as needing a person,
339
- and answering the question does not undo either. Call
340
- `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
359
+ This ready-lane candidate can retain a historical body marker and a human-needed label.
360
+ This step reconciles only candidates still in the ready lane; released holds outside it are
361
+ recovered by `lisa-repair-intake` step 2b (or Phase 2.5 when included in this cycle). Call
362
+ `planHumanGateRelease({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
341
363
  and apply exactly the actions it returns: remove the configured human-needed marker, add the
342
364
  configured ready role back, and post `formatHumanGateReleaseNote()` once. It is the exact inverse
343
365
  of step 3's planner and it refuses in both directions — an item still held plans nothing, and an
@@ -369,11 +391,12 @@ GitHub Issues live in one repo by definition, so the scanned repo's issues are u
369
391
  1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report.
370
392
  2. **Cheap path first.** Prefer candidates already carrying the `repo:<current>` label. Keep the Phase 2 scan broad so unlabeled issues are still seen, determined, and stamped.
371
393
  3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
372
- - Carries `repo:<other>` → **skip** (leave it `ready` for that repo's own intake); next candidate.
373
- - **Unlabeled** → determine the target repo(s) from the issue + code surfaces, then **stamp** `repo:<name>` via `gh issue edit <n> --add-label "repo:<name>"` (create the label lazily) so later cycles filter cheaply; re-apply with the now-known repo. (An issue whose work is entirely in the scanned repo is simply labeled `repo:<current>`.)
374
- - **Container visibility is allowed.** A multi-repo Epic / Story / Spike may legitimately carry multiple `repo:<name>` labels for operator visibility. Do not split or claim it here; leave the repo markers intact and fall through to the leaf-only gate, which repairs the stale build-ready label instead of dispatching the container.
375
- - **Multi-repo leaf → split, never claim.** Run the `repo-scope-split` work-time procedure into single-repo siblings, each created **build-ready** (`build_ready: true`) and stamped with its own `repo:<name>`; the current repo's sibling becomes a normal candidate.
376
- - **Single-repo leaf for the current repo** → fall through to 3a (leaf-only gate) and 3b (claim).
394
+ - **Count distinct repository markers first**: collect all `repo:<name>` labels and, on JIRA, recognized repository components; deduplicate by repository. A container may carry multiple `repo:<name>` labels. Do not split or claim it here; send it to the leaf-only gate.
395
+ - **Multi-repo leaf → split, never claim.** More than one repository takes the work-time split before any wrong-repository skip. Each sibling is **build-ready** (`build_ready: true`) and stamped with its own `repo:<name>`; the current repo's sibling becomes a normal candidate.
396
+ - **Exactly one other repository** (`repo:<other>`) → **skip**, leaving the item ready for that repo's intake.
397
+ - **No repository marker** → determine target repo(s) from the ticket and code, stamp `repo:<name>` through the vendor access layer, and re-apply from the count.
398
+ - **Single-repo leaf for the current repo** → fall through to 3a and 3b.
399
+
377
400
  4. Continue until a claimable current-repo leaf is found (claim it; one per cycle) or the candidate set is exhausted — exit cleanly on the denominator-stated summary, naming the current repo alongside the swept lanes.
378
401
 
379
402
  #### 3a. Leaf-only claim gate (repair containers)
@@ -6,6 +6,20 @@ allowed-tools: ["Bash", "Skill", "Read"]
6
6
 
7
7
  # Write GitHub Issue: $ARGUMENTS
8
8
 
9
+ ## Human-gate release authorization
10
+
11
+ A release requires a trusted human author, not just matching comment text. Follow
12
+ `ready-role-filing` — **Human-gate release authorization**: preserve tracker-supplied comment
13
+ author IDs and bot metadata, resolve `trustedHumanActorIds` only from an explicit user instruction
14
+ or existing human-authored trusted project policy, and pass it with structured `comments` to every
15
+ hold classifier, reconciliation, normalization and release planner. Never derive trust from the
16
+ comment body, a display name, the actor's own assertion, or an automation posting on its own behalf.
17
+ Missing policy, missing/unreadable author identity, raw body strings, untrusted actors and known bots
18
+ cannot discharge a hold. Keep the item held and report the missing authorization; do not silently
19
+ replace these inputs with an empty history or an inferred allowlist. Authorized matching releases
20
+ continue through the existing path and never override an independently declared caller hold.
21
+
22
+
9
23
  Create or update a GitHub Issue with all required relationships, metadata, and quality gates. Every section below is mandatory. Thin issues are rejected.
10
24
 
11
25
  This skill is the GitHub counterpart of `lisa-jira-write-ticket`. The two skills share the same gates, description structure, acceptance-criteria format, and verification flow. The data-model translation is documented under "GitHub Issues data model" below — keep that table in sync with `lisa-github-validate-issue` so what one skill writes the other accepts.
@@ -323,7 +337,7 @@ If the label does not exist in the tracker, create it, or record that it could n
323
337
  proceed — the marker still holds. Never file the label *instead of* the marker.
324
338
 
325
339
  **Write a `reason=` the release can name.** The hold's reason is not decoration: a hold ends when a
326
- `[lisa-human-gate-release]` comment repeating that same `reason=` is recorded on the item, and the
340
+ `[lisa-human-gate-release]` comment repeating that same `reason=` is recorded on the item by an authorized human, and the
327
341
  next intake sweep then takes the marker label off and puts the item back in the build-ready role on
328
342
  its own. Matching is per-reason so that a hold declared *after* an earlier release is not born
329
343
  discharged. A keyless hold is legal and is discharged by a keyless release; a hold whose reason is a