@codyswann/lisa 4.55.2 → 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 (367) 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 +86 -75
  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 -6
  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/skills/lisa-github-build-intake/SKILL.md +18 -10
  94. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +18 -9
  95. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +45 -6
  96. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +18 -7
  97. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  98. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  99. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +0 -17
  100. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +3 -34
  101. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  102. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +46 -6
  103. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +2 -1
  104. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  105. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +1 -1
  106. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  107. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  108. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  109. package/plugins/lisa/skills/lisa-track/SKILL.md +18 -4
  110. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +13 -5
  111. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +7 -0
  112. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +3 -0
  113. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +76 -2
  114. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +3 -0
  115. package/plugins/lisa-agy/hooks/guard-dedupe.bash +66 -29
  116. package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +3 -0
  117. package/plugins/lisa-agy/hooks/parity-safety-net.sh +5 -1
  118. package/plugins/lisa-agy/plugin.json +1 -1
  119. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +18 -10
  120. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +18 -9
  121. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +45 -6
  122. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +18 -7
  123. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  124. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  125. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +0 -17
  126. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +3 -34
  127. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  128. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +46 -6
  129. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +2 -1
  130. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  131. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +1 -1
  132. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  133. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  134. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  135. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +18 -4
  136. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  138. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  139. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  141. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +13 -5
  143. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +7 -0
  144. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +3 -0
  145. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +76 -2
  146. package/plugins/lisa-copilot/hooks/block-no-verify.sh +3 -0
  147. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +3 -0
  148. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +66 -29
  149. package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +3 -0
  150. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +5 -1
  151. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +3 -0
  152. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +5 -1
  153. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  154. package/plugins/lisa-copilot/rules/eager/base-rules.md +3 -1
  155. package/plugins/lisa-copilot/rules/eager/withdrawn-rulings.md +14 -3
  156. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +1 -1
  157. package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +1 -1
  158. package/plugins/lisa-copilot/rules/reference/blocker-containment.md +3 -3
  159. package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +1 -1
  160. package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +1 -1
  161. package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +1 -1
  162. package/plugins/lisa-copilot/rules/reference/control-reachability.md +1 -1
  163. package/plugins/lisa-copilot/rules/reference/convergent-review.md +1 -1
  164. package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +1 -1
  165. package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +1 -1
  166. package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +1 -1
  167. package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +1 -1
  168. package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +1 -1
  169. package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +1 -1
  170. package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +1 -1
  171. package/plugins/lisa-copilot/rules/reference/design-value-binding.md +1 -1
  172. package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +1 -1
  173. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  174. package/plugins/lisa-copilot/rules/reference/observability-audit.md +1 -1
  175. package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +1 -1
  176. package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +1 -1
  177. package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +1 -1
  178. package/plugins/lisa-copilot/rules/reference/promotion-contract.md +1 -1
  179. package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +1 -1
  180. package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +1 -1
  181. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +1 -1
  182. package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +9 -10
  183. package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +1 -1
  184. package/plugins/lisa-copilot/rules/reference/usage-accounting.md +1 -1
  185. package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +1 -1
  186. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +18 -10
  187. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +18 -9
  188. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +45 -6
  189. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +18 -7
  190. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  191. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  192. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +0 -17
  193. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +3 -34
  194. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  195. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +46 -6
  196. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +2 -1
  197. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  198. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +1 -1
  199. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  200. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  201. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  202. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +18 -4
  203. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  204. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +13 -5
  205. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +7 -0
  206. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +3 -0
  207. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +76 -2
  208. package/plugins/lisa-cursor/hooks/block-no-verify.sh +3 -0
  209. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +3 -0
  210. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +66 -29
  211. package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +3 -0
  212. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +5 -1
  213. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +3 -0
  214. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +5 -1
  215. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  216. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +1 -1
  217. package/plugins/lisa-cursor/rules/base-rules.mdc +3 -1
  218. package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +1 -1
  219. package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +3 -3
  220. package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +1 -1
  221. package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +1 -1
  222. package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +1 -1
  223. package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +1 -1
  224. package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +1 -1
  225. package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +1 -1
  226. package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +1 -1
  227. package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +1 -1
  228. package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +1 -1
  229. package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +1 -1
  230. package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +1 -1
  231. package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +1 -1
  232. package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +1 -1
  233. package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +1 -1
  234. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  235. package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +1 -1
  236. package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +1 -1
  237. package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +1 -1
  238. package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +1 -1
  239. package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +1 -1
  240. package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +1 -1
  241. package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +1 -1
  242. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +1 -1
  243. package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +9 -10
  244. package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +1 -1
  245. package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +1 -1
  246. package/plugins/lisa-cursor/rules/withdrawn-rulings.mdc +14 -3
  247. package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +1 -1
  248. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +18 -10
  249. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +18 -9
  250. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +45 -6
  251. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +18 -7
  252. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  253. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  254. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +0 -17
  255. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +3 -34
  256. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  257. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +46 -6
  258. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +2 -1
  259. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  260. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +1 -1
  261. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  262. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  263. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  264. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +18 -4
  265. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  266. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  267. package/plugins/lisa-expo-agy/plugin.json +1 -1
  268. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  269. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  270. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  271. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  272. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  273. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  274. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  275. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  276. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  277. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  278. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  279. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  280. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  281. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  282. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  283. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  284. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  285. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  286. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  287. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  288. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  289. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  290. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  291. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  292. package/plugins/lisa-rails-agy/plugin.json +1 -1
  293. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  294. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  295. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  296. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  297. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  298. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  299. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  300. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  301. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  302. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  303. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  304. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  305. package/plugins/materialized-artifacts.json +11 -0
  306. package/plugins/src/base/hooks/block-blind-automerge.sh +10 -5
  307. package/plugins/src/base/hooks/block-direct-issue-create.sh +4 -0
  308. package/plugins/src/base/hooks/block-managed-file-edits.sh +73 -2
  309. package/plugins/src/base/hooks/guard-dedupe.bash +63 -29
  310. package/plugins/src/base/hooks/parity-safety-net.sh +2 -1
  311. package/plugins/src/base/hooks/worktree-binding-guard.sh +2 -1
  312. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  313. package/plugins/src/base/rules/eager/base-rules.md +3 -1
  314. package/plugins/src/base/rules/eager/withdrawn-rulings.md +14 -3
  315. package/plugins/src/base/rules/reference/automation-runbook-contract.md +1 -1
  316. package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +1 -1
  317. package/plugins/src/base/rules/reference/blocker-containment.md +3 -3
  318. package/plugins/src/base/rules/reference/claim-archaeology.md +1 -1
  319. package/plugins/src/base/rules/reference/claim-evidence-mapping.md +1 -1
  320. package/plugins/src/base/rules/reference/claim-time-guards.md +1 -1
  321. package/plugins/src/base/rules/reference/control-reachability.md +1 -1
  322. package/plugins/src/base/rules/reference/convergent-review.md +1 -1
  323. package/plugins/src/base/rules/reference/credential-substrate-precedence.md +1 -1
  324. package/plugins/src/base/rules/reference/dependency-decision-records.md +1 -1
  325. package/plugins/src/base/rules/reference/dependency-internalization-kit.md +1 -1
  326. package/plugins/src/base/rules/reference/dependency-trust-classes.md +1 -1
  327. package/plugins/src/base/rules/reference/deployed-state-readback.md +1 -1
  328. package/plugins/src/base/rules/reference/derived-branch-plan.md +1 -1
  329. package/plugins/src/base/rules/reference/design-source-of-truth.md +1 -1
  330. package/plugins/src/base/rules/reference/design-value-binding.md +1 -1
  331. package/plugins/src/base/rules/reference/documentation-source-paths.md +1 -1
  332. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  333. package/plugins/src/base/rules/reference/observability-audit.md +1 -1
  334. package/plugins/src/base/rules/reference/prd-definition-of-ready.md +1 -1
  335. package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +1 -1
  336. package/plugins/src/base/rules/reference/pre-flight-autofill.md +1 -1
  337. package/plugins/src/base/rules/reference/promotion-contract.md +1 -1
  338. package/plugins/src/base/rules/reference/readiness-rubric.md +1 -1
  339. package/plugins/src/base/rules/reference/ready-role-filing.md +1 -1
  340. package/plugins/src/base/rules/reference/rejection-detection.md +1 -1
  341. package/plugins/src/base/rules/reference/repo-scope-split.md +9 -10
  342. package/plugins/src/base/rules/reference/reset-seed-coverage.md +1 -1
  343. package/plugins/src/base/rules/reference/usage-accounting.md +1 -1
  344. package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +1 -1
  345. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +18 -10
  346. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +18 -9
  347. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +45 -6
  348. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +18 -7
  349. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +12 -2
  350. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -18
  351. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +0 -17
  352. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +3 -34
  353. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  354. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +46 -6
  355. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +2 -1
  356. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
  357. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +1 -1
  358. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
  359. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
  360. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
  361. package/plugins/src/base/skills/lisa-track/SKILL.md +18 -4
  362. package/scripts/build-plugins.sh +4 -0
  363. package/scripts/copy-opencode-plugin-templates.mjs +5 -1
  364. package/scripts/lisa-enforcement-fallback.sh +9 -1
  365. package/scripts/two-channel-couplings.json +5 -33
  366. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +551 -13
  367. package/typescript/create-only/.github/required-checks.json +1 -1
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.55.2",
3
+ "version": "4.55.3",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -215,9 +215,15 @@ nothing survives, exit on the denominator-stated summary from `summarizeDryLane`
215
215
  A blocker is a **claim with a timestamp, not a fact** — it goes stale the moment its condition comes
216
216
  true, and nothing re-read one before this phase. For each `$BLOCKED` candidate:
217
217
 
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.
218
+ 1. **Read release comments before applying the human gate.** Pass all item comments, labels,
219
+ body and configured `humanNeededLabel` to `classifyReadyCandidate(...)`. For a discharged hold,
220
+ call `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
221
+ and retain its plan. Apply human-marker cleanup only as planned; defer any ready-role restoration
222
+ until steps 2–5 have cleared the remaining blockers (map the role to the vendor's state or label).
223
+ A still-active hold is never auto-selected, whatever any probe says. An absent or unreadable
224
+ comment history cannot discharge a hold. A release removes only the human hold: continue the
225
+ remaining blocker checks below before selecting the item.
226
+
221
227
  2. **Extract the stated discharge condition**, then **probe it**. Machine-testable conditions — a
222
228
  version on trunk, a published package, a CI run history, an advisory's patched status — rot
223
229
  fastest and are cheapest to check. A human decision is not machine-testable; leave it.
@@ -335,8 +341,9 @@ other gate's verdict — however conclusive — may promote an item a person par
335
341
  is the same repair the leaf-only gate already performs for a ready item that must not be
336
342
  dispatched.
337
343
  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
344
+ This ready-lane candidate can retain a historical body marker and a human-needed label.
345
+ This step reconciles only candidates still in the ready lane; released holds outside it are
346
+ recovered by `lisa-repair-intake` step 2b (or Phase 2.5 when included in this cycle). Call
340
347
  `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
341
348
  and apply exactly the actions it returns: remove the configured human-needed marker, add the
342
349
  configured ready role back, and post `formatHumanGateReleaseNote()` once. It is the exact inverse
@@ -369,11 +376,12 @@ GitHub Issues live in one repo by definition, so the scanned repo's issues are u
369
376
  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
377
  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
378
  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).
379
+ - **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.
380
+ - **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.
381
+ - **Exactly one other repository** (`repo:<other>`) → **skip**, leaving the item ready for that repo's intake.
382
+ - **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.
383
+ - **Single-repo leaf for the current repo** → fall through to 3a and 3b.
384
+
377
385
  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
386
 
379
387
  #### 3a. Leaf-only claim gate (repair containers)
@@ -189,9 +189,15 @@ swept (see `automation-runbook-contract`).
189
189
  A blocker is a **claim with a timestamp, not a fact** — it goes stale the moment its condition comes
190
190
  true, and nothing re-read one before this phase. For each pre-work candidate outside `$READY`:
191
191
 
192
- 1. **Human gate first, and it is absolute.** A ticket carrying the configured human-needed label
193
- (`jira.labels.human_needed`, default `Human Needed`) or a `[lisa-human-gate]` marker in its
194
- description is **never** auto-selected, whatever any probe says.
192
+ 1. **Read release comments before applying the human gate.** Pass all item comments, labels,
193
+ body and configured `humanNeededLabel` to `classifyReadyCandidate(...)`. For a discharged hold,
194
+ call `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
195
+ and retain its plan. Apply human-marker cleanup only as planned; defer any ready-role restoration
196
+ until steps 2–5 have cleared the remaining blockers (map the role to the vendor's state or label).
197
+ A still-active hold is never auto-selected, whatever any probe says. An absent or unreadable
198
+ comment history cannot discharge a hold. A release removes only the human hold: continue the
199
+ remaining blocker checks below before selecting the item.
200
+
195
201
  2. **Extract the stated discharge condition**, then **probe it**. Machine-testable conditions — a
196
202
  version on trunk, a published package, a CI run history, an advisory's patched status — rot
197
203
  fastest and are cheapest to check. A human decision is not machine-testable; leave it.
@@ -240,8 +246,9 @@ other gate's verdict — however conclusive — may promote an item a person par
240
246
  is the same repair the leaf-only gate already performs for a ready item that must not be
241
247
  dispatched.
242
248
  4. **On `claimable: true` for an item that still carries a hold, RELEASE it — do not just proceed.**
243
- The hold left durable state behind: the item is out of the queue and flagged as needing a person,
244
- and answering the question does not undo either. Call
249
+ This ready-lane candidate can retain a historical body marker and a human-needed label.
250
+ This step reconciles only candidates still in the ready lane; released holds outside it are
251
+ recovered by `lisa-repair-intake` step 2b (or Phase 2.5 when included in this cycle). Call
245
252
  `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
246
253
  and apply exactly the actions it returns: remove the configured human-needed marker, add the
247
254
  configured ready role back, and post `formatHumanGateReleaseNote()` once. It is the exact inverse
@@ -274,10 +281,12 @@ A JIRA project can oversee multiple repos (`frontend` / `backend` / `infrastruct
274
281
  1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report — do not claim tickets you cannot scope.
275
282
  2. **Cheap path first.** Phase 1's query-time pre-filter has already dropped tickets explicitly stamped for a sibling repo, so the Phase 2 result set is current-repo-labeled + unlabeled tickets. Prefer candidates already carrying `repo:<current>` — a JIRA **label**, or a **component** equal to the repo name (accepted as an alias); the pre-filter is label-only, so a ticket scoped solely by a sibling-repo **component** can still appear and is skipped here. The result set still includes unlabeled tickets so they can be determined and stamped; this gate orders/filters what remains.
276
283
  3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
277
- - Carries `repo:<other>` (label or component) → **skip** (leave it `ready` for that repo's own intake); next candidate.
278
- - **Unlabeled** → determine the target repo(s) from the ticket (description, AC, technical approach) confirmed against the code surfaces, then **stamp** `repo:<name>` via `lisa-atlassian-access` `operation: write-ticket` (add the label / set the component) so later cycles filter cheaply; re-apply with the now-known repo.
279
- - **Multi-repo leaf → split, never claim.** Run the `repo-scope-split` work-time procedure to break it 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.
280
- - **Single-repo leaf for the current repo** → fall through to 3a (leaf-only gate) and 3b (claim).
284
+ - **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.
285
+ - **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.
286
+ - **Exactly one other repository** (`repo:<other>`) → **skip**, leaving the item ready for that repo's intake.
287
+ - **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.
288
+ - **Single-repo leaf for the current repo** → fall through to 3a and 3b.
289
+
281
290
  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.
282
291
 
283
292
  #### 3a. Leaf-only claim gate (skip / safe-block containers)
@@ -188,7 +188,7 @@ Consumers pass business-shaped arguments only; they do not embed GraphQL.
188
188
 
189
189
  **This layer does not accept a state to write. It resolves one.** A `save-issue`
190
190
  that changes the workflow state declares `lifecycle_role:<ROLE>` — the semantic
191
- role it is applying (`ready`, `claimed`, `blocked`, `review`, `done`) — plus `env:<KEY>` when the role is the env-indexed
191
+ role it is applying (for example, `ready`, `claimed`, `blocked`, `review`, `done`; the configured role contract is authoritative) — plus `env:<KEY>` when the role is the env-indexed
192
192
  `done`. This layer then resolves that role against config and the team's own
193
193
  catalog, and sends the ID **it** resolved.
194
194
 
@@ -319,9 +319,9 @@ same mutation, same result shape, same error handling.
319
319
  ```graphql
320
320
  # list-comments project_id:<ID> — page via pageInfo so a long-lived PRD's
321
321
  # feedback history never silently truncates.
322
- query($id:String!){
322
+ query($id:String!,$after:String){
323
323
  project(id:$id){
324
- comments(first:100){
324
+ comments(first:100,after:$after){
325
325
  pageInfo{ hasNextPage endCursor }
326
326
  nodes{ id body createdAt url user{ name } }
327
327
  }
@@ -329,8 +329,47 @@ query($id:String!){
329
329
  }
330
330
  ```
331
331
 
332
- The Issue form reads through `issue(id:$id){ comments{...} }` with the identical
333
- node shape, so a caller consumes one result format for either anchor.
332
+ The Issue form uses `issue(id:$id)` with the same `first:100,after:$after`
333
+ arguments and node shape. For **both** anchors, use this access-layer adapter with
334
+ `linear_graphql` above; it accumulates every page and prints no partial history.
335
+ A read succeeds only after `hasNextPage` is false. Errors, malformed pages, and
336
+ missing or repeated continuation cursors fail the whole read.
337
+
338
+ ```bash
339
+ linear_list_comment_pages() { # issue|project id -> complete JSON node array
340
+ local anchor="$1" id="$2" after=null nodes='[]' seen='[]'
341
+ local query variables response connection cursor
342
+ case "$anchor" in issue|project) ;; *) echo "Error: invalid comment anchor" >&2; return 1 ;; esac
343
+ query='query($id:String!,$after:String){'"$anchor"'(id:$id){comments(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{id body createdAt url user{name}}}}}'
344
+ while :; do
345
+ variables=$(jq -cn --arg id "$id" --argjson after "$after" '{id:$id,after:$after}') || return 1
346
+ response=$(linear_graphql "$query" "$variables") || return 1
347
+ connection=$(printf '%s' "$response" | jq -ce --arg anchor "$anchor" '
348
+ if (.errors != null and .errors != []) then error("GraphQL comments read failed")
349
+ else .data[$anchor].comments end |
350
+ if (.nodes | type) != "array" or (.pageInfo.hasNextPage | type) != "boolean"
351
+ then error("Incomplete comments page") else . end') || return 1
352
+ nodes=$(jq -cn --argjson prior "$nodes" --argjson page "$connection" '$prior + $page.nodes') || return 1
353
+ if [ "$(printf '%s' "$connection" | jq -r '.pageInfo.hasNextPage')" = false ]; then
354
+ printf '%s\n' "$nodes"
355
+ return 0
356
+ fi
357
+ cursor=$(printf '%s' "$connection" | jq -ce '.pageInfo.endCursor | select(type == "string" and length > 0)') || {
358
+ echo "Error: incomplete comments history; missing cursor" >&2; return 1;
359
+ }
360
+ if jq -en --argjson seen "$seen" --argjson cursor "$cursor" '$seen | index($cursor) != null' >/dev/null; then
361
+ echo "Error: incomplete comments history; repeated cursor" >&2
362
+ return 1
363
+ fi
364
+ seen=$(jq -cn --argjson seen "$seen" --argjson cursor "$cursor" '$seen + [$cursor]') || return 1
365
+ after="$cursor"
366
+ done
367
+ }
368
+ ```
369
+
370
+ Call `linear_list_comment_pages project "$PROJECT_ID"` for `project_id`, or
371
+ `linear_list_comment_pages issue "$ISSUE_ID"` for `issue_id`. The caller receives
372
+ one complete node array; it must not interpret a failed command as zero comments.
334
373
 
335
374
  **Substrate.** The Linear MCP exposes comments on Issues only, so the
336
375
  `project_id` form resolves solely through the tier-1 `LINEAR_API_KEY` + GraphQL
@@ -339,7 +378,7 @@ That is not a gap in Linear: the precedence contract already puts GraphQL ahead
339
378
  of the MCP, so the preferred substrate has always been able to do this. Only
340
379
  this wrapper could not, which is what made callers fabricate an Issue to hold a
341
380
  Project's comments. If tier 1 is unavailable, fail with the layer's standard
342
- `Error:` result naming `save-comment project_id` — do **not** silently degrade
381
+ `Error:` result naming the requested `save-comment project_id` or `list-comments project_id` operation — do **not** silently degrade
343
382
  into creating an Issue to comment on.
344
383
 
345
384
  ## `history` — transition history (read-only)
@@ -182,7 +182,15 @@ A blocker is a **claim with a timestamp, not a fact**. It is written once and go
182
182
 
183
183
  For each pre-work candidate that is **not** in `$READY`:
184
184
 
185
- 1. **Human gate first, and it is absolute.** An Issue carrying the configured human-needed label (`linear.labels.build.human_needed`, default `human-needed`) or a `[lisa-human-gate]` marker in its description is **never** auto-selected, whatever any probe says. Skip it and move on.
185
+ 1. **Read release comments before applying the human gate.** Pass all item comments, labels,
186
+ body and configured `humanNeededLabel` to `classifyReadyCandidate(...)`. For a discharged hold,
187
+ call `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
188
+ and retain its plan. Apply human-marker cleanup only as planned; defer any ready-role restoration
189
+ until steps 2–5 have cleared the remaining blockers (map the role to the vendor's state or label).
190
+ A still-active hold is never auto-selected, whatever any probe says. An absent or unreadable
191
+ comment history cannot discharge a hold. A release removes only the human hold: continue the
192
+ remaining blocker checks below before selecting the item.
193
+
186
194
  2. **Extract the stated discharge condition** from the description or the most recent blocking comment — the sentence naming what has to become true.
187
195
  3. **Probe it.** Machine-testable conditions are the ones that rot fastest and are cheapest to check: a version on trunk (`git show origin/<trunk>:<manifest>`), a published package, a run history (`gh run list`), an advisory's patched status. A condition that is a human decision is not machine-testable — leave it and move on.
188
196
  4. **Classify with the shared helper** so the ordering and the evidence requirement cannot drift per vendor:
@@ -234,8 +242,9 @@ other gate's verdict — however conclusive — may promote an item a person par
234
242
  is the same repair the leaf-only gate already performs for a ready item that must not be
235
243
  dispatched.
236
244
  4. **On `claimable: true` for an item that still carries a hold, RELEASE it — do not just proceed.**
237
- The hold left durable state behind: the item is out of the queue and flagged as needing a person,
238
- and answering the question does not undo either. Call
245
+ This ready-lane candidate can retain a historical body marker and a human-needed label.
246
+ This step reconciles only candidates still in the ready lane; released holds outside it are
247
+ recovered by `lisa-repair-intake` step 2b (or Phase 2.5 when included in this cycle). Call
239
248
  `planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
240
249
  and apply exactly the actions it returns: remove the configured human-needed marker, add the
241
250
  configured ready role back, and post `formatHumanGateReleaseNote()` once. It is the exact inverse
@@ -268,10 +277,12 @@ A Linear team can oversee multiple repos (`frontend` / `backend` / `infrastructu
268
277
  1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report.
269
278
  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.
270
279
  3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
271
- - Carries `repo:<other>` → **skip** (leave it `ready` for that repo's own intake); next candidate.
272
- - **Unlabeled** → determine the target repo(s) from the Issue + code surfaces, then **stamp** `repo:<name>` via `lisa-linear-access operation: save-issue` (resolve/create the label via `list_issue_labels`/`create_issue_label`) so later cycles filter cheaply; re-apply with the now-known repo.
273
- - **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.
274
- - **Single-repo leaf for the current repo** → fall through to 3a (leaf-only gate) and 3b (claim).
280
+ - **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.
281
+ - **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.
282
+ - **Exactly one other repository** (`repo:<other>`) → **skip**, leaving the item ready for that repo's intake.
283
+ - **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.
284
+ - **Single-repo leaf for the current repo** → fall through to 3a and 3b.
285
+
275
286
  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.
276
287
 
277
288
  #### 3a. Leaf-only claim gate (skip / safe-block containers)
@@ -130,6 +130,16 @@ Select the first ready project returned by Phase 2 and process only that project
130
130
 
131
131
  #### 3a. Claim
132
132
 
133
+ **Preflight project comments before any claim or destination-ticket creation.** Through
134
+ `lisa-linear-access`, resolve an identity-matched tier-1 GraphQL credential and the
135
+ `save-comment project_id` adapter, then read the candidate's complete project comment
136
+ history with `list-comments project_id:<id>`. The issue-only MCP is insufficient for
137
+ this flow. If the required credential, adapter or read is unavailable, return the
138
+ layer's `Error:` naming the project-comment operation and leave the project ready;
139
+ create no tickets and no substitute comment-holder Issue. This is a read-only
140
+ capability preflight, not a test comment. It establishes the available transport;
141
+ later permission changes or write failures still follow the normal error handler.
142
+
133
143
  Transition labels via `lisa-linear-access operation: save-project({id, labels})`: pass the full new label set with `$READY` removed and `$IN_REVIEW` added. This is the idempotency lock — a re-entrant cycle running concurrently won't see this project because its query filters on `label: "$READY"`.
134
144
 
135
145
  If the update fails (permission error, race condition), log it and skip this project. Do not proceed to validation on a project you didn't successfully claim.
@@ -152,7 +162,7 @@ This call also indirectly invokes `lisa-tracker-source-artifacts` (artifact extr
152
162
 
153
163
  1. Re-invoke `lisa-linear-to-tracker` with `dry_run: false` to actually write the tickets. This re-runs Phases 1-5 and runs the preservation gate (Phase 5.5).
154
164
  2. Capture the created ticket keys from the skill's output.
155
- 3. Post a comment on the project via `lisa-linear-access operation: save-comment project_id:<id>` listing the created tickets (epic, stories, sub-tasks) with their JIRA URLs. Lead with: `"Ticketed by Claude. Created N JIRA issues — see below. Add the $SHIPPED label to the Linear project after the work is delivered."`
165
+ 3. Post a comment on the project via `lisa-linear-access operation: save-comment project_id:<id>` listing the created tickets (epic, stories, sub-tasks) with the URLs returned by the configured destination tracker. Lead with: `"Ticketed by Claude. Created N tickets — see below. Add the $SHIPPED label to the Linear project after the work is delivered."`
156
166
  4. Transition labels: remove `$IN_REVIEW`, add `$TICKETED` via `save_project`.
157
167
  5. **Run Phase 3e (coverage audit)** before considering this PRD done.
158
168
 
@@ -396,6 +406,6 @@ Before this skill can run against a Linear workspace or team, the team must adop
396
406
  1. Apply the `ready` label (default: `prd-ready`) to projects that are ready for ticketing (replaces the Notion `Status = Ready` flip and the Confluence `prd-ready` page label).
397
407
  2. Reserve `in_review`, `blocked`, `ticketed` (defaults: `prd-in-review`, `prd-blocked`, `prd-ticketed`) for this skill — humans should not set them manually except to recover from an error.
398
408
  3. (Optional but recommended) Add the `draft` and `shipped` labels (defaults: `prd-draft`, `prd-shipped`) for in-progress PRDs and delivered work respectively, so the full lifecycle is visible at a glance.
399
- 4. The labels must exist as **project labels** in Linear (`list_project_labels` should return them). Issue-level labels with the same names won't work; Linear keeps the two label kinds separate.
409
+ 4. The labels must exist as **project labels** in Linear (`list-project-labels` should return them). Issue-level labels with the same names won't work; Linear keeps the two label kinds separate.
400
410
 
401
411
  If the workspace hasn't adopted these labels, the first run exits with a label-convention error (not the idle empty-set message) — this distinguishes a setup issue from a genuinely empty queue so operators know to apply the convention rather than assuming there is no work. See Phase 2 for how the skill detects this case.
@@ -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`.