@codyswann/lisa 4.22.8 → 4.22.10

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 (278) hide show
  1. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +127 -0
  2. package/all/copy-overwrite/scripts/lisa-floor-collisions.mjs +63 -8
  3. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +36 -6
  4. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +35 -13
  5. package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +11 -3
  6. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +41 -8
  7. package/all/copy-overwrite/scripts/lisa-schema-validate.mjs +2 -2
  8. package/all/copy-overwrite/scripts/lisa-work-item.mjs +2 -1
  9. package/cdk/package-lisa/package.lisa.json +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  11. package/dist/core/lisa-owned-hash-ledger.js +24 -0
  12. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  13. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  14. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  15. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  16. package/dist/core/upstream-evidence-manifest.js +61 -51
  17. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  18. package/dist/opencode/plugin-templates/parity-safety-net.sh +36 -6
  19. package/nestjs/package-lisa/package.lisa.json +1 -1
  20. package/package.json +3 -3
  21. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +10 -13
  24. package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +4 -2
  25. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  26. package/plugins/lisa/.codex-plugin/skills/lisa-github-sync/SKILL.md +8 -3
  27. package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +7 -10
  28. package/plugins/lisa/.codex-plugin/skills/lisa-jira-journey/SKILL.md +5 -0
  29. package/plugins/lisa/.codex-plugin/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  30. package/plugins/lisa/.codex-plugin/skills/lisa-jira-sync/SKILL.md +1 -1
  31. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +8 -12
  32. package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +1 -1
  33. package/plugins/lisa/.codex-plugin/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  34. package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +7 -10
  35. package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +7 -10
  36. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +19 -3
  37. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  38. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  39. package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +7 -10
  40. package/plugins/lisa/.codex-plugin/skills/lisa-setup-atlassian/SKILL.md +7 -10
  41. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +9 -12
  42. package/plugins/lisa/.codex-plugin/skills/lisa-setup-notion/SKILL.md +7 -10
  43. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  44. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +6 -1
  45. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  48. package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  49. package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  50. package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  51. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +1 -1
  52. package/plugins/lisa/hooks/parity-safety-net.sh +36 -6
  53. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +31 -7
  54. package/plugins/lisa/rules/reference/base-rules.md +1 -1
  55. package/plugins/lisa/rules/reference/config-resolution.md +5 -14
  56. package/plugins/lisa/scripts/install-remote-agent-aws.mjs +5 -1
  57. package/plugins/lisa/scripts/rollup-blocker-classification.mjs +18 -3
  58. package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +10 -13
  59. package/plugins/lisa/skills/lisa-doctor/SKILL.md +4 -2
  60. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  61. package/plugins/lisa/skills/lisa-github-sync/SKILL.md +8 -3
  62. package/plugins/lisa/skills/lisa-jam-access/SKILL.md +7 -10
  63. package/plugins/lisa/skills/lisa-jira-journey/SKILL.md +5 -0
  64. package/plugins/lisa/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  65. package/plugins/lisa/skills/lisa-jira-sync/SKILL.md +1 -1
  66. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +8 -12
  67. package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +1 -1
  68. package/plugins/lisa/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  69. package/plugins/lisa/skills/lisa-notion-access/SKILL.md +7 -10
  70. package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +7 -10
  71. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +19 -3
  72. package/plugins/lisa/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  73. package/plugins/lisa/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  74. package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +7 -10
  75. package/plugins/lisa/skills/lisa-setup-atlassian/SKILL.md +7 -10
  76. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +9 -12
  77. package/plugins/lisa/skills/lisa-setup-notion/SKILL.md +7 -10
  78. package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  79. package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +6 -1
  80. package/plugins/lisa/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  81. package/plugins/lisa/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  82. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  83. package/plugins/lisa/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  84. package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  85. package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  86. package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +1 -1
  87. package/plugins/lisa-agy/hooks/parity-safety-net.sh +36 -6
  88. package/plugins/lisa-agy/plugin.json +1 -1
  89. package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +5 -1
  90. package/plugins/lisa-agy/scripts/rollup-blocker-classification.mjs +18 -3
  91. package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +10 -13
  92. package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +4 -2
  93. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  94. package/plugins/lisa-agy/skills/lisa-github-sync/SKILL.md +8 -3
  95. package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +7 -10
  96. package/plugins/lisa-agy/skills/lisa-jira-journey/SKILL.md +5 -0
  97. package/plugins/lisa-agy/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  98. package/plugins/lisa-agy/skills/lisa-jira-sync/SKILL.md +1 -1
  99. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +8 -12
  100. package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +1 -1
  101. package/plugins/lisa-agy/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  102. package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +7 -10
  103. package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +7 -10
  104. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +19 -3
  105. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  106. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  107. package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +7 -10
  108. package/plugins/lisa-agy/skills/lisa-setup-atlassian/SKILL.md +7 -10
  109. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +9 -12
  110. package/plugins/lisa-agy/skills/lisa-setup-notion/SKILL.md +7 -10
  111. package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  112. package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +6 -1
  113. package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  114. package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  115. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  116. package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  117. package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  118. package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  119. package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +1 -1
  120. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  122. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  123. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +36 -6
  127. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +31 -7
  128. package/plugins/lisa-copilot/rules/reference/base-rules.md +1 -1
  129. package/plugins/lisa-copilot/rules/reference/config-resolution.md +5 -14
  130. package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +5 -1
  131. package/plugins/lisa-copilot/scripts/rollup-blocker-classification.mjs +18 -3
  132. package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +10 -13
  133. package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +4 -2
  134. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  135. package/plugins/lisa-copilot/skills/lisa-github-sync/SKILL.md +8 -3
  136. package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +7 -10
  137. package/plugins/lisa-copilot/skills/lisa-jira-journey/SKILL.md +5 -0
  138. package/plugins/lisa-copilot/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  139. package/plugins/lisa-copilot/skills/lisa-jira-sync/SKILL.md +1 -1
  140. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +8 -12
  141. package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +1 -1
  142. package/plugins/lisa-copilot/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  143. package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +7 -10
  144. package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +7 -10
  145. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +19 -3
  146. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  147. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  148. package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +7 -10
  149. package/plugins/lisa-copilot/skills/lisa-setup-atlassian/SKILL.md +7 -10
  150. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +9 -12
  151. package/plugins/lisa-copilot/skills/lisa-setup-notion/SKILL.md +7 -10
  152. package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  153. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +6 -1
  154. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  155. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  156. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  157. package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  158. package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  159. package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  160. package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +1 -1
  161. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  162. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +36 -6
  163. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +31 -7
  164. package/plugins/lisa-cursor/rules/base-rules-reference.mdc +1 -1
  165. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +5 -14
  166. package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +5 -1
  167. package/plugins/lisa-cursor/scripts/rollup-blocker-classification.mjs +18 -3
  168. package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +10 -13
  169. package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +4 -2
  170. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  171. package/plugins/lisa-cursor/skills/lisa-github-sync/SKILL.md +8 -3
  172. package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +7 -10
  173. package/plugins/lisa-cursor/skills/lisa-jira-journey/SKILL.md +5 -0
  174. package/plugins/lisa-cursor/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  175. package/plugins/lisa-cursor/skills/lisa-jira-sync/SKILL.md +1 -1
  176. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +8 -12
  177. package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +1 -1
  178. package/plugins/lisa-cursor/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  179. package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +7 -10
  180. package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +7 -10
  181. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +19 -3
  182. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  183. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  184. package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +7 -10
  185. package/plugins/lisa-cursor/skills/lisa-setup-atlassian/SKILL.md +7 -10
  186. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +9 -12
  187. package/plugins/lisa-cursor/skills/lisa-setup-notion/SKILL.md +7 -10
  188. package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  189. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +6 -1
  190. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  191. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  192. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  193. package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  194. package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  195. package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  196. package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +1 -1
  197. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  198. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  199. package/plugins/lisa-expo-agy/plugin.json +1 -1
  200. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  201. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  202. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  203. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  204. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  205. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  206. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  207. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  208. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  209. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  210. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  211. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  212. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  213. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  214. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  215. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  216. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  217. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  218. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  219. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  220. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  221. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  222. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  223. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  224. package/plugins/lisa-rails-agy/plugin.json +1 -1
  225. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  226. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  227. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  228. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  229. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  230. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  231. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  232. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  233. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  234. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  235. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  236. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  237. package/plugins/src/base/hooks/parity-safety-net.sh +36 -6
  238. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +31 -7
  239. package/plugins/src/base/rules/reference/base-rules.md +1 -1
  240. package/plugins/src/base/rules/reference/config-resolution.md +5 -14
  241. package/plugins/src/base/scripts/install-remote-agent-aws.mjs +5 -1
  242. package/plugins/src/base/scripts/rollup-blocker-classification.mjs +18 -3
  243. package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +10 -13
  244. package/plugins/src/base/skills/lisa-doctor/SKILL.md +4 -2
  245. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  246. package/plugins/src/base/skills/lisa-github-sync/SKILL.md +8 -3
  247. package/plugins/src/base/skills/lisa-jam-access/SKILL.md +7 -10
  248. package/plugins/src/base/skills/lisa-jira-journey/SKILL.md +5 -0
  249. package/plugins/src/base/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  250. package/plugins/src/base/skills/lisa-jira-sync/SKILL.md +1 -1
  251. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +8 -12
  252. package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +1 -1
  253. package/plugins/src/base/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  254. package/plugins/src/base/skills/lisa-notion-access/SKILL.md +7 -10
  255. package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +7 -10
  256. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +19 -3
  257. package/plugins/src/base/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  258. package/plugins/src/base/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +95 -44
  259. package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +7 -10
  260. package/plugins/src/base/skills/lisa-setup-atlassian/SKILL.md +7 -10
  261. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +9 -12
  262. package/plugins/src/base/skills/lisa-setup-notion/SKILL.md +7 -10
  263. package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  264. package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +6 -1
  265. package/plugins/src/base/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  266. package/plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  267. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  268. package/plugins/src/base/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  269. package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  270. package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  271. package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +1 -1
  272. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +31 -7
  273. package/scripts/lisa-remote-env/session-start.sh +19 -0
  274. package/scripts/lisa-remote-env/setup.sh +1 -0
  275. package/scripts/two-channel-couplings.json +1 -1
  276. package/typescript/copy-contents/.husky/pre-push +1 -1
  277. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +31 -7
  278. package/typescript/package-lisa/package.lisa.json +1 -1
@@ -1064,8 +1064,38 @@ readonly AGENT_CREDENTIAL='(\.(claude|codex|cursor|copilot|gemini|antigravity)/[
1064
1064
  # 15d. Dotenv files. `.env.example`, `.sample`, `.template`, `.dist` and
1065
1065
  # `.schema` are checked-in documentation of which keys exist, never the values,
1066
1066
  # so they are excluded — that collision is the one this rule most had to avoid.
1067
- readonly DOTENV_SECRET='(^|[[:space:]='"$qc"'./])\.env(\.[[:alnum:]_-]+)?([[:space:]'"$qc"']|$)'
1068
- readonly DOTENV_PUBLIC='\.env\.(example|sample|template|dist|schema|defaults)([[:space:]'"$qc"']|$)'
1067
+ # Decide dotenv privacy per PATH, never by subtracting one command-wide match
1068
+ # from another. A command that names `.env.example` and `.env` contains both a
1069
+ # public path and a private one; seeing the former cannot authorize the latter.
1070
+ #
1071
+ # The one write-only shape this guard has always allowed remains explicit:
1072
+ # seeding a private dotenv destination from one public template. This is safe
1073
+ # because the private path is the destination, not a value being read back.
1074
+ has_private_dotenv_path() {
1075
+ dotenv_scan_status=0
1076
+ dotenv_candidates="$(printf '%s' "$normalized_command_str" \
1077
+ | grep -Eio -- '(^|[[:space:]='"$qc"'./])\.env(\.[[:alnum:]_-]+)?')" \
1078
+ || dotenv_scan_status=$?
1079
+ if [ "$dotenv_scan_status" -gt 1 ]; then
1080
+ block "dotenv path classification failed; denying the credential-store read fail-closed."
1081
+ fi
1082
+
1083
+ while IFS= read -r candidate; do
1084
+ [ -n "$candidate" ] || continue
1085
+ candidate="$(printf '%s' "$candidate" \
1086
+ | tr '[:upper:]' '[:lower:]' \
1087
+ | grep -Eo -- '\.env(\.[[:alnum:]_-]+)?')"
1088
+ case "$candidate" in
1089
+ .env.example | .env.sample | .env.template | .env.dist | .env.schema | .env.defaults) ;;
1090
+ *) return 0 ;;
1091
+ esac
1092
+ done <<EOF
1093
+ $dotenv_candidates
1094
+ EOF
1095
+ return 1
1096
+ }
1097
+
1098
+ readonly SAFE_DOTENV_SEED='^[[:space:]]*(cp|mv)[[:space:]]+\.env\.(example|sample|template|dist|schema|defaults)[[:space:]]+\.env(\.[[:alnum:]_-]+)?[[:space:]]*$'
1069
1099
 
1070
1100
  if matches "$SECRET_READ_VERB"; then
1071
1101
  if matches "$SSH_PRIVATE_KEY"; then
@@ -1077,10 +1107,10 @@ if matches "$SECRET_READ_VERB"; then
1077
1107
  if matches "$AGENT_CREDENTIAL"; then
1078
1108
  block "reading, copying or transmitting a coding-agent or package-registry credential store. These tokens impersonate the operator to every service the agent can reach."
1079
1109
  fi
1080
- # `.env` only when the path is NOT one of the public example forms. Ordered as
1081
- # an exclusion rather than a narrower pattern so a new example suffix is one
1082
- # word to add, and so the reason a file is exempt stays readable.
1083
- if matches "$DOTENV_SECRET" && ! matches "$DOTENV_PUBLIC"; then
1110
+ # A public path elsewhere in the command never exempts a private path. The
1111
+ # narrow public-template -> private-destination seed is write-only and remains
1112
+ # allowed; every other command containing a private dotenv path is refused.
1113
+ if has_private_dotenv_path && ! matches "$SAFE_DOTENV_SEED"; then
1084
1114
  block "reading, copying or transmitting a dotenv file holding real values. \`.env.example\`, \`.env.sample\`, \`.env.template\` and \`.env.dist\` are readable, and WRITING a .env is unaffected — only reading one back is refused."
1085
1115
  fi
1086
1116
  # Fail-closed, narrowly. A read whose DIRECTORY is a known credential store
@@ -43,7 +43,7 @@
43
43
  "security:zap": "bash scripts/zap-baseline.sh",
44
44
  "test": "vitest run",
45
45
  "test:unit": "vitest run --exclude='**/integration/**'",
46
- "test:integration:lisa": "vitest run '.integration.' 'integration/' --passWithNoTests",
46
+ "test:integration:lisa": "vitest run '.integration.' 'integration/'",
47
47
  "test:cov": "vitest run --coverage",
48
48
  "test:cov:unit": "LISA_COVERAGE_SCOPE=unit vitest run --coverage --exclude='**/integration/**' --exclude='**/*.integration.*'",
49
49
  "test:mutation": "node scripts/lisa-mutation.mjs",
package/package.json CHANGED
@@ -161,7 +161,7 @@
161
161
  "zod-validation-error": "^4.0.0"
162
162
  },
163
163
  "name": "@codyswann/lisa",
164
- "version": "4.22.8",
164
+ "version": "4.22.10",
165
165
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
166
166
  "main": "dist/index.js",
167
167
  "exports": {
@@ -300,6 +300,6 @@
300
300
  "vite": "^8.0.16"
301
301
  },
302
302
  "type": "module",
303
- "lisaReleaseCommit": "aef178549f447caa476ef4664eed332c7212cd49",
304
- "gitHead": "aef178549f447caa476ef4664eed332c7212cd49"
303
+ "lisaReleaseCommit": "0e3dc332d948df9ab08ef99e564a2cd8132a89b7",
304
+ "gitHead": "0e3dc332d948df9ab08ef99e564a2cd8132a89b7"
305
305
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.22.8",
3
+ "version": "4.22.10",
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.22.8",
3
+ "version": "4.22.10",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -57,9 +57,9 @@ read_atlassian_token() {
57
57
  # OS keychain here — a second reader is how the same credential ends up living
58
58
  # in two places and drifting.
59
59
  #
60
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
61
- # lead because a project that vendors the resolver has declared which copy it
62
- # wants used, and that decision must survive this change untouched. The plugin
60
+ # Ordered across trusted machine-managed substrates, ending at the installed
61
+ # package. Checkout-local paths are deliberately absent: a familiar generated
62
+ # destination is still repository-controlled executable code. The plugin
63
63
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
64
64
  # pointing at it is reachable from anywhere the plugin itself is installed.
65
65
  # Without one, a consumer repository that vendors none of the leading paths
@@ -68,12 +68,9 @@ read_atlassian_token() {
68
68
  # lookups. This LADDER is identical in every skill that resolves a credential
69
69
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
70
70
  # happens AFTER the ladder may differ between them.
71
- local candidates=(
72
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
73
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
74
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
75
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
76
- )
71
+ # Executable resolvers are a code boundary. Checkout-local copies are mutable
72
+ # repository content, so use only machine-managed plugin/package locations.
73
+ local candidates=()
77
74
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
78
75
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
79
76
  fi
@@ -92,7 +89,7 @@ read_atlassian_token() {
92
89
  local via_lisa
93
90
  via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
94
91
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
95
- break
92
+ # Empty/error means this substrate had no answer; try the next trusted one.
96
93
  fi
97
94
  done
98
95
  # Legacy fallback: the OS keychain written by the guided /lisa:setup:atlassian
@@ -152,7 +149,7 @@ public static class LisaCred {
152
149
  echo "Error: could not resolve ATLASSIAN_API_TOKEN through lisa-secrets-access or the legacy keychain." >&2
153
150
  echo "Tried, in order (relative paths are from $PWD):" >&2
154
151
  printf ' %s\n' "${tried[@]}" >&2
155
- echo " <OS keychain> service=lisa-atlassian account=$email" >&2
152
+ echo " <OS keychain> service=lisa-atlassian account=<configured>" >&2
156
153
  return 1
157
154
  }
158
155
  TOKEN=$(read_atlassian_token "$EMAIL")
@@ -288,7 +285,7 @@ if [ -n "$site" ] && [ "$current_site" != "$site" ]; then
288
285
  fi
289
286
 
290
287
  if [ -n "$email" ] && [ -n "$current_email" ] && [ "$current_email" != "$email" ]; then
291
- echo "Error: acli active account is '$current_email', but .lisa.config.json requires '$email'. Run /lisa:setup:atlassian to add or repair the matching profile." >&2
288
+ echo "Error: acli active account does not match the configured Atlassian account. Run /lisa:setup:atlassian to add or repair the matching profile." >&2
292
289
  exit 1
293
290
  fi
294
291
  ```
@@ -310,7 +307,7 @@ if [ -z "$me_email" ]; then
310
307
  exit 1
311
308
  fi
312
309
  if [ "$me_email" != "$email" ]; then
313
- echo "Error: ATLASSIAN_API_TOKEN belongs to '$me_email', but .lisa.config.local.json declares '$email'. Multi-account misconfiguration." >&2
310
+ echo "Error: ATLASSIAN_API_TOKEN identity does not match the configured Atlassian account. Multi-account misconfiguration; re-run /lisa:setup:atlassian." >&2
314
311
  exit 1
315
312
  fi
316
313
  ```
@@ -480,8 +480,8 @@ against both enforcing surfaces on paths a machine already takes:
480
480
  | the shipped ruleset template a repository is provisioned from | `lisa doctor` (one operator-readable line) and `lisa doctor --json` → `declarationDrift.templates` | no |
481
481
  | the live branch-protection ruleset | `lisa doctor --json` → `declarationDrift.live`, and the `github.declared-checks` check in `lisa health` | yes |
482
482
 
483
- Both report one verdict per required context, and the vocabulary is six values rather than
484
- matched/unmatched, because four of the mismatches need four different actions:
483
+ Both report one verdict per required context, and the vocabulary is seven values rather than
484
+ matched/unmatched, because the mismatches and an unreadable surface need different actions:
485
485
 
486
486
  - `matched` — declared `required`, and the surface requires it.
487
487
  - `declared-not-enforced` — the settings file says it must pass and nothing requires it. The
@@ -495,6 +495,8 @@ matched/unmatched, because four of the mismatches need four different actions:
495
495
  - `enforced-not-lisa-owned` — protection requires it and no registry gate produces it. Third-party
496
496
  checks are required by construction and declared by nobody; this bucket exists so they can be told
497
497
  apart from a Lisa gate that fell out of the settings file, never so they can be removed.
498
+ - `unproven` — the surface could not be read, so no enforcement comparison was made. It is never a
499
+ match and never drift; the operator fixes access/availability and reruns the observation.
498
500
 
499
501
  `enforced-declared-off` and `declared-not-enforced` fail the health check; the two gaps warn. No
500
502
  remedy in that vocabulary removes a required context — the type has no such member, so the
@@ -343,9 +343,18 @@ auto-merge armed (section 1); when it returns, re-read `headRefOid` and reset
343
343
 
344
344
  **Merging past a rate-limited CodeRabbit is permitted — but ONLY when CodeRabbit
345
345
  is the sole gate still blocking the merge.** When the CodeRabbit context reports
346
- `Review rate limited` (or stays pending/queued because of the vendor cap) and it
347
- is the *only* thing standing between the PR and `MERGED`, do not wait it out:
348
- with `auto_merge=true` the PR already has auto-merge enabled (section 1), so
346
+ `Review rate limited` and it is the *only* thing standing between the PR and
347
+ `MERGED`, do not wait it out. Prove that vendor-cap signal from the live check
348
+ description; `statusCheckRollup` does not carry enough detail, so read it with:
349
+
350
+ ```bash
351
+ gh pr checks <pr> --json name,state,description,bucket \
352
+ --jq '.[] | select(.name == "CodeRabbit")'
353
+ ```
354
+
355
+ A merely pending or queued CodeRabbit check is not proof of a vendor cap and
356
+ must keep polling. Once that signal is explicit, and with `auto_merge=true`, the
357
+ PR already has auto-merge enabled (section 1), so
349
358
  leave the latch armed and merge directly with `gh pr merge <pr> --<merge_method>`
350
359
  (pass `--admin` only if branch protection lists the rate-limited context as
351
360
  required and refuses the plain merge). "Sole gate" means every one of these is
@@ -355,7 +364,15 @@ from memory:
355
364
  - every other required check in `statusCheckRollup` is green (no FAILURE, no
356
365
  other PENDING);
357
366
  - zero unresolved review threads (human or bot);
358
- - `reviewDecision` is not `CHANGES_REQUESTED`;
367
+ - the repository's human-approval requirement is demonstrably satisfied at the
368
+ current head: read the live branch/ruleset requirement, and when it requires
369
+ one or more approvals require `reviewDecision == APPROVED` plus the current
370
+ **effective** non-dismissed approval count. Compute effective reviews from the
371
+ latest non-dismissed review per reviewer, ordered by `submitted_at` then id;
372
+ an older approval from a reviewer never satisfies a later `CHANGES_REQUESTED`
373
+ from that reviewer. If the required count is zero, record that live
374
+ policy result. `REVIEW_REQUIRED`, `CHANGES_REQUESTED`, and `null` never stand
375
+ in for this proof before an admin merge;
359
376
  - `mergeable == MERGEABLE` and `mergeStateStatus` is not `BEHIND`/`DIRTY`;
360
377
  - no pending auto-fix PR into this branch (step f).
361
378
 
@@ -380,6 +397,14 @@ Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
380
397
  auto-merge — dismissing the stale review after resolving all threads is what
381
398
  unblocks it.
382
399
 
400
+ When reading review history, derive the current review set rather than counting
401
+ the whole array. One reproducible REST shape is:
402
+
403
+ ```bash
404
+ gh api --paginate repos/<owner>/<repo>/pulls/<pr>/reviews --slurp \
405
+ --jq 'add | map(select(.state != "DISMISSED")) | sort_by(.submitted_at, .id) | reduce .[] as $review ({}; .[$review.user.login] = $review) | [.[]]'
406
+ ```
407
+
383
408
  ### f. Pending auto-fix PR into this branch
384
409
  If an open PR from `claude-auto-fix-<headRefName>` targets this PR's head
385
410
  branch (the CI auto-fix workflow engaged before this session took the lease),
@@ -113,10 +113,15 @@ If the `subIssues` field is unavailable (older GHES), fall back to body parentag
113
113
  - **Blocked dominates — and the rollup must say which child and which kind** — a single blocked child surfaces `status:blocked` on the parent even while siblings progress, so a human sees the parent needs attention. `status:blocked` alone is a single bit and cannot tell a child waiting on an external event from one whose acceptance criteria are unbuildable; the second never clears on its own. Run the shared classifier over the resolved child graph before writing the label or the comment:
114
114
 
115
115
  ```bash
116
- node "${CLAUDE_PLUGIN_ROOT}/scripts/rollup-blocker-classification.mjs" --input=<graph.json>
116
+ CLASSIFIER_ROOT="${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}"
117
+ node "${CLASSIFIER_ROOT}/scripts/rollup-blocker-classification.mjs" --input=<graph.json>
117
118
  ```
118
119
 
119
- Its report names, per class, the blocking leaf, the path to it (`#1495 -> #1515 -> #1547`) and **who must act** — that text goes in the rollup comment verbatim. It exits non-zero when it classified nothing (unreadable graph, no children, no readable child): treat that as a rollup that could not be derived and fall through to the **Safe default** below, never to "no blocked children". Never infer a class from prose and never apply the `spec_defect` marker from a flow — see `leaf-only-lifecycle` → **Classifying a hold**.
120
+ The current-directory `plugins/lisa` fallback is for a Lisa checkout where no
121
+ plugin-root variable is injected; it must be tried before declaring the shared
122
+ classifier absent.
123
+
124
+ Its report names, per class, the blocking leaf, the path to it (`#1495 -> #1515 -> #1547`) and **who must act** — that text goes in the rollup comment verbatim. It exits non-zero when it classified nothing (unreadable graph, no children, no readable child): that is a strict **no-write** result. Do not change a label and do not post/update a rollup comment; report the classifier failure to the caller. Never fall through to "no blocked children", infer a class from prose, or apply the `spec_defect` marker from a flow — see `leaf-only-lifecycle` → **Classifying a hold**.
120
125
  - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native closure (`gh issue close --reason completed`) fires only when the resolved env is the production `status:done`, never at `status:on-dev`/`status:on-stg`.
121
126
  - **"Required" children only** — a child labelled won't-do / optional does not hold the parent open; only leaves that must ship count toward the env-rollup check.
122
127
  - **Recursive** — a parent reaches an env only when its children have all reached at least that env; an Epic reaches it only when its Stories have themselves rolled up to it. Evaluate bottom-up.
@@ -131,7 +136,7 @@ gh issue edit <parent-number> --repo <org>/<repo> \
131
136
  --remove-label "<current status:*>" --add-label "<derived status:*>"
132
137
  ```
133
138
 
134
- **Safe default.** If rollup cannot be applied automatically (e.g. ambiguous required-set, GraphQL hierarchy unavailable, or a derived terminal that the env logic cannot resolve), this skill does **not** guess — it posts the derived suggestion as a comment and leaves the parent's label untouched. No unsafe transition is ever made.
139
+ **Safe default.** If a successfully-read rollup cannot be applied automatically (e.g. ambiguous required-set or a derived terminal that the env logic cannot resolve), this skill does **not** guess — it posts the derived suggestion as a comment and leaves the parent's label untouched. A classifier/read failure is stricter: it writes neither label nor comment. No unsafe transition is ever made.
135
140
 
136
141
  ## Important Notes
137
142
 
@@ -48,9 +48,9 @@ export PATH="$HOME/.local/bin:$PATH"
48
48
  read_jam_pat() {
49
49
  [ -n "${JAM_PAT:-}" ] && { echo "$JAM_PAT"; return; }
50
50
  #
51
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
52
- # lead because a project that vendors the resolver has declared which copy it
53
- # wants used, and that decision must survive this change untouched. The plugin
51
+ # Ordered across trusted machine-managed substrates, ending at the installed
52
+ # package. Checkout-local paths are deliberately absent: a familiar generated
53
+ # destination is still repository-controlled executable code. The plugin
54
54
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
55
55
  # pointing at it is reachable from anywhere the plugin itself is installed.
56
56
  # Without one, a consumer repository that vendors none of the leading paths
@@ -59,12 +59,9 @@ read_jam_pat() {
59
59
  # lookups. This LADDER is identical in every skill that resolves a credential
60
60
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
61
61
  # happens AFTER the ladder may differ between them.
62
- local candidates=(
63
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
64
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
65
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
66
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
67
- )
62
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
63
+ # candidates are repository-controlled code, not trusted merely by path.
64
+ local candidates=()
68
65
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
69
66
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
70
67
  fi
@@ -83,7 +80,7 @@ read_jam_pat() {
83
80
  local via_lisa
84
81
  via_lisa=$(node "$resolver" get JAM_PAT 2>/dev/null) \
85
82
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
86
- break
83
+ # Empty/error means this substrate had no answer; try the next trusted one.
87
84
  fi
88
85
  done
89
86
  # Name every path. A bare `return 1` sends the next reader hunting for a
@@ -35,6 +35,11 @@ Run the parser script to extract the Validation Journey from the JIRA ticket des
35
35
  python3 .claude/skills/jira-journey/scripts/parse-plan.py <TICKET_ID>
36
36
  ```
37
37
 
38
+ The parser resolves Jira configuration from the checkout first: it searches
39
+ `$CLAUDE_PROJECT_DIR/.lisa/jira-cli/.config.yml`, then the current directory and
40
+ its parents, and only then falls back to `~/.config/.jira/.config.yml`. Do not
41
+ copy project settings into the home-level file just to make this step work.
42
+
38
43
  The script outputs JSON with: `ticket`, `prerequisites`, `steps`, `viewports`, `assertions`.
39
44
 
40
45
  Note: `viewports` may be empty for TypeScript tickets — that is expected.
@@ -41,9 +41,43 @@ from pathlib import Path
41
41
  LOCAL_EVIDENCE_PATTERN = re.compile(r'\[(SCREENSHOT|EVIDENCE):\s*([^\]]+)\]')
42
42
 
43
43
 
44
+ def jira_config_candidates():
45
+ """Return project-first jira-cli config candidates without duplicates."""
46
+ roots = []
47
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", "").strip()
48
+ if project_dir:
49
+ roots.append(Path(project_dir).expanduser().resolve())
50
+
51
+ current = Path.cwd().resolve()
52
+ roots.extend([current, *current.parents])
53
+
54
+ candidates = []
55
+ seen = set()
56
+ for root in roots:
57
+ candidate = root / ".lisa" / "jira-cli" / ".config.yml"
58
+ key = str(candidate)
59
+ if key not in seen:
60
+ candidates.append(candidate)
61
+ seen.add(key)
62
+
63
+ home_candidate = (Path.home() / ".config" / ".jira" / ".config.yml").resolve()
64
+ if str(home_candidate) not in seen:
65
+ candidates.append(home_candidate)
66
+ return candidates
67
+
68
+
69
+ def resolve_jira_config_path():
70
+ """Resolve the first existing project config, then the home fallback."""
71
+ candidates = jira_config_candidates()
72
+ for candidate in candidates:
73
+ if candidate.exists():
74
+ return candidate
75
+ return candidates[-1]
76
+
77
+
44
78
  def get_jira_config():
45
- """Read JIRA server and login from jira-cli config."""
46
- config_path = Path.home() / ".config" / ".jira" / ".config.yml"
79
+ """Read JIRA server and login from the project-first jira-cli config."""
80
+ config_path = resolve_jira_config_path()
47
81
  if not config_path.exists():
48
82
  print(f"ERROR: jira-cli config not found at {config_path}", file=sys.stderr)
49
83
  sys.exit(1)
@@ -94,7 +94,7 @@ When invoked with `--rollup`, this skill **derives a parent/container ticket's s
94
94
  | else any child has **started** (`In Progress` / `Code Review`, or shipped to an env while a sibling has not) | `claimed` | `In Progress` |
95
95
  | else (children exist, none started) | — | unchanged — parent keeps its non-ready container status |
96
96
 
97
- - **Blocked dominates** — a single blocked child surfaces `Blocked` on the parent even while siblings progress. It never says *which* child or *which kind* of hold; run `scripts/rollup-blocker-classification.mjs` over the resolved child graph and carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A non-zero exit means it classified nothing; that is a failure to report, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
97
+ - **Blocked dominates** — a single blocked child surfaces `Blocked` on the parent even while siblings progress. It never says *which* child or *which kind* of hold; resolve and run the shared classifier exactly as `lisa-tracker-sync` specifies, then carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A missing classifier or non-zero exit is a strict **no-write** result: do not transition the parent and do not post/update a rollup comment; report the failure, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
98
98
  - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Apply native terminal resolution (the `leaf-only-lifecycle` Terminal native closure) only when the resolved env is the production `Done`, never at `On Dev`/`On Stg`.
99
99
  - **"Required" children only** — won't-do / optional children do not hold the parent open.
100
100
  - **Recursive** — an Epic reaches an env only when its Stories have themselves rolled up to at least that env; a Story reaches it only when its Sub-tasks have. Evaluate bottom-up.
@@ -85,22 +85,18 @@ All GraphQL calls use:
85
85
  read_linear_key() {
86
86
  [ -n "${LINEAR_API_KEY:-}" ] && { echo "$LINEAR_API_KEY"; return; }
87
87
 
88
- # The ladder is ordered repo-first and ends at the plugin's own copy.
89
- #
90
- # Repo-relative rungs lead because a project that vendors the resolver has
91
- # declared which copy it wants used, and that decision must survive this
92
- # change untouched. The plugin rungs are the floor: `resolve-secret.mjs`
88
+ # The ladder is ordered across trusted machine-managed substrates and ends at
89
+ # the installed package. Checkout-local paths are deliberately absent: a
90
+ # familiar generated destination is still repository-controlled executable
91
+ # code. The plugin rungs are the floor: `resolve-secret.mjs`
93
92
  # ships beside this skill, so a rung pointing at it is reachable from
94
93
  # anywhere the plugin itself is installed. Without one, a repo that vendors
95
94
  # none of the leading paths has no route to the key at all — which is what
96
95
  # every consumer of this skill in a `.opencode`-layout repository actually
97
96
  # hit, and why agents started improvising their own key lookups.
98
- local candidates=(
99
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
100
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
101
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
102
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
103
- )
97
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
98
+ # candidates are repository-controlled code, not trusted merely by path.
99
+ local candidates=()
104
100
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
105
101
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
106
102
  fi
@@ -119,7 +115,7 @@ read_linear_key() {
119
115
  local via_lisa
120
116
  via_lisa=$(node "$resolver" get LINEAR_API_KEY 2>/dev/null) \
121
117
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
122
- break
118
+ # Empty/error means this substrate had no answer; try the next trusted one.
123
119
  fi
124
120
  done
125
121
 
@@ -140,7 +140,7 @@ When the caller passes `--rollup`, this skill **derives a parent/container's wor
140
140
  | else any child has **started** (at the `claimed` state, at the `review` state where the project configures one, or shipped to an env while a sibling has not) | `claimed` | the configured `claimed` state |
141
141
  | else (children exist, none started) | — | unchanged — parent keeps its non-ready container state |
142
142
 
143
- - **Blocked dominates** — one blocked child surfaces `Blocked` on the parent even while siblings progress. It never says *which* child or *which kind* of hold; run `scripts/rollup-blocker-classification.mjs` over the resolved child graph and carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A non-zero exit means it classified nothing; that is a failure to report, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
143
+ - **Blocked dominates** — one blocked child surfaces `Blocked` on the parent even while siblings progress. It never says *which* child or *which kind* of hold; resolve and run the shared classifier exactly as `lisa-tracker-sync` specifies, then carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A missing classifier or non-zero exit is a strict **no-write** result: do not save parent state and do not post/update a rollup comment; report the failure, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
144
144
  - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native completion (moving the workflow `state` to Done) fires only when the resolved env is the production `Done`, never at `On Dev`/`On Stg`.
145
145
  - **"Required" children only** — won't-do / optional (e.g. `Canceled`) children do not hold the parent open.
146
146
  - **Recursive** — a Project reaches an env only when its Issues have themselves rolled up to at least that env. Evaluate bottom-up.
@@ -25,8 +25,14 @@ The caller provides pre-computed context:
25
25
  9. Run the project's typecheck script with the provided package manager to catch type errors early — same reasoning as step 7, so `npm run typecheck` / `yarn typecheck` / `bun run typecheck`, falling back to `./node_modules/.bin/tsc --noEmit` rather than `npx tsc`:
26
26
 
27
27
  ```sh
28
+ case "$PACKAGE_MANAGER" in
29
+ npm) runner=(npm run) ;;
30
+ yarn) runner=(yarn) ;;
31
+ bun) runner=(bun run) ;;
32
+ *) echo "unsupported package manager: $PACKAGE_MANAGER" >&2; exit 2 ;;
33
+ esac
28
34
  status=0
29
- bun run typecheck >tsc.log 2>&1 || status=$?
35
+ "${runner[@]}" typecheck >tsc.log 2>&1 || status=$?
30
36
  head -n 30 tsc.log; echo "exit=$status"
31
37
  ```
32
38
 
@@ -58,9 +58,9 @@ read_notion_token() {
58
58
  # OS keychain here — a second reader is how the same credential ends up living
59
59
  # in two places and drifting.
60
60
  #
61
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
62
- # lead because a project that vendors the resolver has declared which copy it
63
- # wants used, and that decision must survive this change untouched. The plugin
61
+ # Ordered across trusted machine-managed substrates, ending at the installed
62
+ # package. Checkout-local paths are deliberately absent: a familiar generated
63
+ # destination is still repository-controlled executable code. The plugin
64
64
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
65
65
  # pointing at it is reachable from anywhere the plugin itself is installed.
66
66
  # Without one, a consumer repository that vendors none of the leading paths
@@ -69,12 +69,9 @@ read_notion_token() {
69
69
  # lookups. This LADDER is identical in every skill that resolves a credential
70
70
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
71
71
  # happens AFTER the ladder may differ between them.
72
- local candidates=(
73
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
74
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
75
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
76
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
77
- )
72
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
73
+ # candidates are repository-controlled code, not trusted merely by path.
74
+ local candidates=()
78
75
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
79
76
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
80
77
  fi
@@ -93,7 +90,7 @@ read_notion_token() {
93
90
  local via_lisa
94
91
  via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
95
92
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
96
- break
93
+ # Empty/error means this substrate had no answer; try the next trusted one.
97
94
  fi
98
95
  done
99
96
  # Legacy fallback: the OS keychain written by the guided /lisa:setup:notion
@@ -49,9 +49,9 @@ POSTHOG_HOST=${POSTHOG_HOST:-https://app.posthog.com}
49
49
  read_posthog_key() {
50
50
  [ -n "${POSTHOG_PERSONAL_API_KEY:-}" ] && { echo "$POSTHOG_PERSONAL_API_KEY"; return; }
51
51
  #
52
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
53
- # lead because a project that vendors the resolver has declared which copy it
54
- # wants used, and that decision must survive this change untouched. The plugin
52
+ # Ordered across trusted machine-managed substrates, ending at the installed
53
+ # package. Checkout-local paths are deliberately absent: a familiar generated
54
+ # destination is still repository-controlled executable code. The plugin
55
55
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
56
56
  # pointing at it is reachable from anywhere the plugin itself is installed.
57
57
  # Without one, a consumer repository that vendors none of the leading paths
@@ -60,12 +60,9 @@ read_posthog_key() {
60
60
  # lookups. This LADDER is identical in every skill that resolves a credential
61
61
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
62
62
  # happens AFTER the ladder may differ between them.
63
- local candidates=(
64
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
65
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
66
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
67
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
68
- )
63
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
64
+ # candidates are repository-controlled code, not trusted merely by path.
65
+ local candidates=()
69
66
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
70
67
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
71
68
  fi
@@ -84,7 +81,7 @@ read_posthog_key() {
84
81
  local via_lisa
85
82
  via_lisa=$(node "$resolver" get POSTHOG_PERSONAL_API_KEY 2>/dev/null) \
86
83
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
87
- break
84
+ # Empty/error means this substrate had no answer; try the next trusted one.
88
85
  fi
89
86
  done
90
87
  # Name every path. A bare `return 1` sends the next reader hunting for a
@@ -383,6 +383,11 @@ invisible on that surface — the exact state this recovery path exists to catch
383
383
  only via search (no `Closes #` / native dev link) would otherwise never be discovered, and the leaf
384
384
  would never recover. Read each PR with the vendor's native state, e.g. GitHub
385
385
  `gh pr view <n> --json state,mergedAt,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,comments,reviews`.
386
+ For `reviews`, derive the effective set from the **latest non-dismissed review
387
+ per reviewer**, ordered by `submitted_at` then id. Never count the raw history:
388
+ an older approval does not satisfy a later `CHANGES_REQUESTED` review by the
389
+ same reviewer. Use the same paginated REST reduction documented by
390
+ `lisa-drive-pr-to-merge` when the `gh pr view` projection is incomplete.
386
391
 
387
392
  **2. PR already merged → recover, don't re-dispatch.** If `state == MERGED`, the build is effectively
388
393
  complete and the only thing missing is the env transition the build-intake never applied (its merge
@@ -695,10 +700,18 @@ left in a status it should not carry, including a stale build-ready `ready`).
695
700
 
696
701
  ```bash
697
702
  ROLLUP_DIR="$(mktemp -d)"
703
+ # Serialize the EXACT graph resolved in step 1 before any classifier reads it.
704
+ # RESOLVED_CHILD_GRAPH_JSON is the in-memory object from that read; do not
705
+ # re-query the tracker here or hand-author a smaller substitute.
706
+ printf '%s\n' "$RESOLVED_CHILD_GRAPH_JSON" \
707
+ | jq -e '{container, children, readError, renderedState, childTally}' \
708
+ > "$ROLLUP_DIR/graph.json"
709
+ test -s "$ROLLUP_DIR/graph.json"
710
+
698
711
  # children[]: { ref, state, labels[], blockedBy[{ref, open}], children[] } — the graph
699
712
  # step 1 already resolved, nested as deep as it was read. The script probes nothing.
700
713
  jq -n --slurpfile g "$ROLLUP_DIR/graph.json" \
701
- '{container: $g[0].container, children: $g[0].children, readError: $g[0].readError}' \
714
+ '{container: $g[0].container, children: $g[0].children, readError: $g[0].readError, renderedState: $g[0].renderedState, childTally: $g[0].childTally}' \
702
715
  > "$ROLLUP_DIR/input.json"
703
716
 
704
717
  node "${CLAUDE_PLUGIN_ROOT}/scripts/rollup-blocker-classification.mjs" \
@@ -707,7 +720,8 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/rollup-blocker-classification.mjs" \
707
720
 
708
721
  It **exits non-zero and classifies nothing** when the tracker could not be read, the container
709
722
  has no children, or no child was readable. That is a repair failure to report, never an
710
- all-clear: do not fall through to "no blocked children" on a non-zero exit. On success its
723
+ all-clear and a strict **no-write** result: do not transition lifecycle state and do not
724
+ post/update the rollup note. Do not fall through to "no blocked children" on a non-zero exit. On success its
711
725
  report names, per class, the blocking leaf, the path to it, and **who must act** — those lines
712
726
  are what goes in the rollup note, verbatim. Never set the `spec_defect` marker yourself and
713
727
  never infer a class from prose; a hold with nothing recorded classifies `unknown`, and the
@@ -717,7 +731,9 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/rollup-blocker-classification.mjs" \
717
731
  lifecycle write (JIRA transition, GitHub/Linear label swap keeping exactly one `status:*`),
718
732
  removing any conflicting stale build lifecycle role — **including a stale `ready`** the parent
719
733
  should never carry. Post an idempotent `[lisa-repair-intake]` rollup note naming the derived
720
- state and the child tally (honor the backoff window + fingerprint). **When the derived state is
734
+ state and the child tally (honor the backoff window + fingerprint). Include that exact rendered
735
+ state and tally in the classifier input: its fingerprint deduplicates the complete note, not only
736
+ the blocker classes. **When the derived state is
721
737
  `blocked`, the note carries the classifier's per-class report** — the blocking leaf, its path,
722
738
  and its actor — so an operator never descends the tree by hand to find out which item and which
723
739
  kind. Use the classifier's `change.summary` as the dedupe test: an unchanged verdict has nothing
@@ -161,12 +161,16 @@ function stripFamilyBlocks(text, family) {
161
161
  const opened = findFamily(out, start);
162
162
  if (opened.index === -1) return out;
163
163
  const closed = findFamily(out, end, opened.index + opened.length);
164
- // A truncated block (opened, never closed) would otherwise swallow the rest
165
- // of the file on every subsequent write.
166
- const after =
167
- closed.index === -1
168
- ? ""
169
- : out.slice(closed.index + closed.length).replace(/^\n/, "");
164
+ // A truncated block (opened, never closed) is not safe to interpret. Any
165
+ // guessed boundary could delete operator-authored content after the marker,
166
+ // so refuse before the caller writes and preserve the original byte-for-byte.
167
+ if (closed.index === -1) {
168
+ throw new Error(
169
+ `Refusing to rewrite a managed ${family} block with no closing marker. ` +
170
+ `Repair the marker pair first; no file was changed.`
171
+ );
172
+ }
173
+ const after = out.slice(closed.index + closed.length).replace(/^\n/, "");
170
174
  out = `${out.slice(0, opened.index)}${after}`;
171
175
  }
172
176
  }