@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
@@ -173,9 +173,9 @@ read_notion_token() {
173
173
  # than "this function" is deliberate — the old wording asked for something
174
174
  # unachievable, and an unachievable rule is one that gets quietly dropped.
175
175
  #
176
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
177
- # lead because a project that vendors the resolver has declared which copy it
178
- # wants used, and that decision must survive this change untouched. The plugin
176
+ # Ordered across trusted machine-managed substrates, ending at the installed
177
+ # package. Checkout-local paths are deliberately absent: a familiar generated
178
+ # destination is still repository-controlled executable code. The plugin
179
179
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
180
180
  # pointing at it is reachable from anywhere the plugin itself is installed.
181
181
  # Without one, a consumer repository that vendors none of the leading paths
@@ -184,12 +184,9 @@ read_notion_token() {
184
184
  # lookups. This LADDER is identical in every skill that resolves a credential
185
185
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
186
186
  # happens AFTER the ladder may differ between them.
187
- local candidates=(
188
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
189
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
190
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
191
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
192
- )
187
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
188
+ # candidates are repository-controlled code, not trusted merely by path.
189
+ local candidates=()
193
190
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
194
191
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
195
192
  fi
@@ -208,7 +205,7 @@ read_notion_token() {
208
205
  local via_lisa
209
206
  via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
210
207
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
211
- break
208
+ # Empty/error means this substrate had no answer; try the next trusted one.
212
209
  fi
213
210
  done
214
211
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -64,13 +64,18 @@ The lock remains held through rollback and its deletion is verified before the
64
64
  command reports success:
65
65
 
66
66
  ```bash
67
- aws --profile shared secretsmanager get-secret-value \
68
- --secret-id remote-agent-credentials \
67
+ aws secretsmanager get-secret-value \
68
+ --secret-id <selected-secret-name> \
69
69
  --query SecretString \
70
70
  --output text \
71
71
  | node .claude/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs publish
72
72
  ```
73
73
 
74
+ Use the same secret name passed to `--secret-name`; the default is
75
+ `remote-agent-credentials`. Add `--profile <source-profile>` only when the
76
+ operator's ambient AWS identity cannot read the emission and the project names
77
+ a source profile. A named `shared` profile is not otherwise required.
78
+
74
79
  Resolve the script from the installed runtime or `node_modules` when the
75
80
  `.claude/skills` copy is not present. Never print or place the candidate in an
76
81
  argument or temporary file. A CDK deployment followed later by a human copy is
@@ -97,9 +97,14 @@ A surface that **re-runs this script when a container resumes** — Codex Cloud
97
97
  A surface that **skips this script whenever a filesystem cache exists** — Claude Code web — must not. Materializing here would write the values once and then never refresh them, so a credential rotated on Tuesday would still be serving Monday's value until the cache expired days later. Those surfaces materialize from a session-start hook instead, which runs every session including a resumed one:
98
98
 
99
99
  ```sh
100
- bash scripts/lisa-remote-env/session-start.sh # guard; reruns toolchain, secrets, then hook
100
+ bash "$CLAUDE_PROJECT_DIR/scripts/lisa-remote-env/session-start.sh" # project-scoped absolute hook
101
101
  ```
102
102
 
103
+ The installer also registers a user-scoped hook using the checkout's resolved
104
+ absolute path. If invoking this asset by hand, either use that absolute path or
105
+ change to the repository root first; a bare checkout-relative command is not a
106
+ portable session-start configuration.
107
+
103
108
  The selection comes from the surface's `materializeAt` capability in `lisa-secrets-access`, not from its name, so adding a surface does not mean editing a branch. The hook is committed to the repository, so it also fires on a developer's machine — it exits `0` immediately there rather than failing, because a correct local session must not look broken.
104
109
 
105
110
  ## Detect before you provision
@@ -46,8 +46,27 @@ here="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
46
46
  # SessionStart hosts are fail-open, so they surface that result and still allow
47
47
  # the agent session to continue.
48
48
  bash "${here}/setup.sh" --phase=toolchain "$@"
49
+
50
+ # The toolchain child cannot export into this parent shell. Its durable output
51
+ # is linked into ~/.local/bin, so make that directory visible before the next
52
+ # child tries to execute the provider CLI it just installed.
53
+ case ":$PATH:" in
54
+ *":$HOME/.local/bin:"*) ;;
55
+ *) PATH="$HOME/.local/bin:$PATH"; export PATH ;;
56
+ esac
57
+
49
58
  bash "${here}/setup.sh" --phase=secrets "$@"
50
59
 
60
+ # Materialization writes a Lisa-owned block into .profile that sources the
61
+ # generated environment and removes ambient AWS credential poison. The project
62
+ # hook is another non-login child, so load that block here and let the child
63
+ # inherit the exact environment materialization just produced.
64
+ if [ -r "$HOME/.profile" ]; then
65
+ # The materializer owns this runtime path.
66
+ # shellcheck disable=SC1090
67
+ . "$HOME/.profile"
68
+ fi
69
+
51
70
  # A project hook is part of the remote toolchain contract too. It covers tools
52
71
  # that cannot be expressed by the pinned manifest, so skipping it on a cached
53
72
  # session can leave fresh credentials with no client capable of using them.
@@ -132,6 +132,7 @@ for candidate in \
132
132
  ".claude/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
133
133
  ".agents/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
134
134
  ".codex/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
135
+ ".opencode/skills/lisa/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
135
136
  "plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
136
137
  "node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs"; do
137
138
  if [ -f "$candidate" ]; then
@@ -51,7 +51,10 @@ import { pathDirs } from "../../lisa-setup-workstation/scripts/catalogue.mjs";
51
51
 
52
52
  import { assertPinned, extractVersion, planToolchain } from "./toolchain.mjs";
53
53
 
54
- import { boundedChildOutput } from "../../lisa-setup-workstation/scripts/bounded-child.mjs";
54
+ import {
55
+ boundedChildOutput,
56
+ SETUP_OPERATION_BUDGET_MS,
57
+ } from "../../lisa-setup-workstation/scripts/bounded-child.mjs";
55
58
 
56
59
  const HERE = dirname(fileURLToPath(import.meta.url));
57
60
 
@@ -173,12 +176,14 @@ function installReleaseZip(tool, binDir) {
173
176
  const archive = join(temporary, "download.zip");
174
177
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
175
178
  stdio: "inherit",
179
+ timeout: SETUP_OPERATION_BUDGET_MS,
176
180
  });
177
181
  // Verify before unpacking, not after. An unexpected archive must fail
178
182
  // before any of its contents reach a directory that is on PATH.
179
183
  verifyChecksum(archive, tool.sha256, tool.name);
180
184
  boundedChildOutput("unzip", ["-q", "-o", archive, "-d", temporary], {
181
185
  stdio: "inherit",
186
+ timeout: SETUP_OPERATION_BUDGET_MS,
182
187
  });
183
188
  const binary = join(temporary, tool.binary ?? tool.name);
184
189
  boundedChildOutput(
@@ -186,6 +191,7 @@ function installReleaseZip(tool, binDir) {
186
191
  ["-m", "0755", binary, join(binDir, tool.name)],
187
192
  {
188
193
  stdio: "inherit",
194
+ timeout: SETUP_OPERATION_BUDGET_MS,
189
195
  }
190
196
  );
191
197
  } finally {
@@ -210,10 +216,12 @@ function installReleaseTar(tool, binDir) {
210
216
  const archive = join(temporary, "download.tar.gz");
211
217
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
212
218
  stdio: "inherit",
219
+ timeout: SETUP_OPERATION_BUDGET_MS,
213
220
  });
214
221
  verifyChecksum(archive, tool.sha256, tool.name);
215
222
  boundedChildOutput("tar", ["-xzf", archive, "-C", temporary], {
216
223
  stdio: "inherit",
224
+ timeout: SETUP_OPERATION_BUDGET_MS,
217
225
  });
218
226
  // Release tarballs usually nest under a versioned directory, so `binary` is
219
227
  // a path within the archive rather than a bare name.
@@ -223,6 +231,7 @@ function installReleaseTar(tool, binDir) {
223
231
  ["-m", "0755", binary, join(binDir, tool.name)],
224
232
  {
225
233
  stdio: "inherit",
234
+ timeout: SETUP_OPERATION_BUDGET_MS,
226
235
  }
227
236
  );
228
237
  } finally {
@@ -272,6 +281,7 @@ function installReleaseTree(tool, binDir) {
272
281
  const archive = join(temporary, "download.archive");
273
282
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
274
283
  stdio: "inherit",
284
+ timeout: SETUP_OPERATION_BUDGET_MS,
275
285
  });
276
286
  // Verify before unpacking, as everywhere else here: an unexpected archive
277
287
  // must fail before any of its contents reach the filesystem.
@@ -285,10 +295,12 @@ function installReleaseTree(tool, binDir) {
285
295
  if (tool.url.endsWith(".zip")) {
286
296
  boundedChildOutput("unzip", ["-q", "-o", archive, "-d", prefix], {
287
297
  stdio: "inherit",
298
+ timeout: SETUP_OPERATION_BUDGET_MS,
288
299
  });
289
300
  } else {
290
301
  boundedChildOutput("tar", ["-xzf", archive, "-C", prefix], {
291
302
  stdio: "inherit",
303
+ timeout: SETUP_OPERATION_BUDGET_MS,
292
304
  });
293
305
  }
294
306
 
@@ -342,6 +354,7 @@ function installReleaseBinary(tool, binDir) {
342
354
  const artifact = join(temporary, tool.name);
343
355
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", artifact], {
344
356
  stdio: "inherit",
357
+ timeout: SETUP_OPERATION_BUDGET_MS,
345
358
  });
346
359
  // Verify before it reaches a directory on PATH. For this kind that ordering
347
360
  // matters more than for the archives: the downloaded file is directly
@@ -353,6 +366,7 @@ function installReleaseBinary(tool, binDir) {
353
366
  ["-m", "0755", artifact, join(binDir, tool.name)],
354
367
  {
355
368
  stdio: "inherit",
369
+ timeout: SETUP_OPERATION_BUDGET_MS,
356
370
  }
357
371
  );
358
372
  } finally {
@@ -370,6 +384,7 @@ function installNpmGlobal(tool) {
370
384
  ["install", "--global", `${tool.package}@${tool.version}`],
371
385
  {
372
386
  stdio: "inherit",
387
+ timeout: SETUP_OPERATION_BUDGET_MS,
373
388
  }
374
389
  );
375
390
  }
@@ -509,7 +524,10 @@ function runHook(hook, dryRun) {
509
524
  console.log(`\nProject hook: ${hook}`);
510
525
  if (dryRun) return;
511
526
  chmodSync(path, 0o755);
512
- boundedChildOutput("bash", [path], { stdio: "inherit" });
527
+ boundedChildOutput("bash", [path], {
528
+ stdio: "inherit",
529
+ timeout: SETUP_OPERATION_BUDGET_MS,
530
+ });
513
531
  }
514
532
 
515
533
  /** Phases this runner can execute, in the order they must happen. */
@@ -789,7 +807,10 @@ export function installUserSessionHook(repoRoot, options = {}) {
789
807
 
790
808
  const dir = join(home, ".claude");
791
809
  const settingsPath = join(dir, "settings.json");
792
- const command = `bash ${script}`;
810
+ // Settings stores shell source, so the absolute path still needs quoting.
811
+ // JSON string syntax is accepted by the shell for ordinary path text and
812
+ // safely escapes spaces, quotes, dollars and backticks.
813
+ const command = `bash ${JSON.stringify(script)}`;
793
814
 
794
815
  let settings = {};
795
816
  if (exists(settingsPath)) {
@@ -1506,7 +1527,7 @@ async function main() {
1506
1527
  boundedChildOutput(
1507
1528
  "node",
1508
1529
  dryRun ? [materialize, "--dry-run"] : [materialize],
1509
- { stdio: "inherit" }
1530
+ { stdio: "inherit", timeout: SETUP_OPERATION_BUDGET_MS }
1510
1531
  );
1511
1532
  } catch (err) {
1512
1533
  if (!retried) throw err;
@@ -83,6 +83,15 @@ import { execFileSync, spawnSync } from "node:child_process";
83
83
  */
84
84
  export const CHILD_BUDGET_MS = 30_000;
85
85
 
86
+ /**
87
+ * Deadline for setup operations that legitimately include downloads, package
88
+ * installation, secret-provider calls, or a project-declared hook.
89
+ *
90
+ * Kept separate from {@link CHILD_BUDGET_MS}: probes should still fail quickly,
91
+ * while a cold network install must not be killed by the probe budget.
92
+ */
93
+ export const SETUP_OPERATION_BUDGET_MS = 10 * 60_000;
94
+
86
95
  /**
87
96
  * Whether this value is a child that was killed at its deadline.
88
97
  *
@@ -36,7 +36,10 @@
36
36
  import { existsSync, lstatSync, mkdirSync, rmSync, symlinkSync } from "node:fs";
37
37
  import { join } from "node:path";
38
38
 
39
- import { boundedChildOutput } from "./bounded-child.mjs";
39
+ import {
40
+ boundedChildOutput,
41
+ SETUP_OPERATION_BUDGET_MS,
42
+ } from "./bounded-child.mjs";
40
43
 
41
44
  import {
42
45
  AGENTS,
@@ -387,7 +390,9 @@ export function installEntry(entry, row, deps = {}) {
387
390
 
388
391
  try {
389
392
  if (entry.kind === "npm-global") {
390
- run("npm", ["install", "-g", entry.package]);
393
+ run("npm", ["install", "-g", entry.package], {
394
+ timeout: SETUP_OPERATION_BUDGET_MS,
395
+ });
391
396
  } else if (entry.kind === "vendor-script") {
392
397
  if (!entry.script) {
393
398
  return {
@@ -410,12 +415,11 @@ export function installEntry(entry, row, deps = {}) {
410
415
  // exit status is the SHELL's, not curl's, so a 404 feeds an empty script
411
416
  // to bash, bash exits 0, and a tool that was never installed is reported
412
417
  // as installed. That is exactly what `sonarsource.com/install` did.
413
- run("bash", [
414
- "-o",
415
- "pipefail",
416
- "-c",
417
- `curl -fsSL ${entry.script} | bash`,
418
- ]);
418
+ run(
419
+ "bash",
420
+ ["-o", "pipefail", "-c", `curl -fsSL ${entry.script} | bash`],
421
+ { timeout: SETUP_OPERATION_BUDGET_MS }
422
+ );
419
423
  } else if (entry.kind === "aws-cli") {
420
424
  // macOS ships a .pkg that wants an administrator, which a headless run
421
425
  // has no business prompting for. Reported as manual with the one-line
@@ -436,20 +440,24 @@ export function installEntry(entry, row, deps = {}) {
436
440
  const url = `https://awscli.amazonaws.com/awscli-exe-linux-${
437
441
  process.arch === "arm64" ? "aarch64" : "x86_64"
438
442
  }.zip`;
439
- run("bash", [
440
- "-o",
441
- "pipefail",
442
- "-c",
443
+ run(
444
+ "bash",
443
445
  [
444
- "set -e",
445
- 'tmp="$(mktemp -d)"',
446
- `curl -fsSL "${url}" -o "$tmp/awscli.zip"`,
447
- 'unzip -q "$tmp/awscli.zip" -d "$tmp"',
448
- `"$tmp/aws/install" --install-dir "${home}/.local/aws-cli" ` +
449
- `--bin-dir "${home}/.local/bin" --update`,
450
- 'rm -rf "$tmp"',
451
- ].join("\n"),
452
- ]);
446
+ "-o",
447
+ "pipefail",
448
+ "-c",
449
+ [
450
+ "set -e",
451
+ 'tmp="$(mktemp -d)"',
452
+ `curl -fsSL "${url}" -o "$tmp/awscli.zip"`,
453
+ 'unzip -q "$tmp/awscli.zip" -d "$tmp"',
454
+ `"$tmp/aws/install" --install-dir "${home}/.local/aws-cli" ` +
455
+ `--bin-dir "${home}/.local/bin" --update`,
456
+ 'rm -rf "$tmp"',
457
+ ].join("\n"),
458
+ ],
459
+ { timeout: SETUP_OPERATION_BUDGET_MS }
460
+ );
453
461
  } else if (CHECKSUMMED.has(entry.kind)) {
454
462
  // The pinned, checksummed path belongs to the remote-env installer. It is
455
463
  // borrowed rather than reimplemented so the pins and checksums cannot
@@ -63,9 +63,9 @@ read_sonar_secret() {
63
63
  local name="$1"
64
64
  [ -n "${!name:-}" ] && { echo "${!name}"; return; }
65
65
  #
66
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
67
- # lead because a project that vendors the resolver has declared which copy it
68
- # wants used, and that decision must survive this change untouched. The plugin
66
+ # Ordered across trusted machine-managed substrates, ending at the installed
67
+ # package. Checkout-local paths are deliberately absent: a familiar generated
68
+ # destination is still repository-controlled executable code. The plugin
69
69
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
70
70
  # pointing at it is reachable from anywhere the plugin itself is installed.
71
71
  # Without one, a consumer repository that vendors none of the leading paths
@@ -74,12 +74,9 @@ read_sonar_secret() {
74
74
  # lookups. This LADDER is identical in every skill that resolves a credential
75
75
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
76
76
  # happens AFTER the ladder may differ between them.
77
- local candidates=(
78
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
79
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
80
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
81
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
82
- )
77
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
78
+ # candidates are repository-controlled code, not trusted merely by path.
79
+ local candidates=()
83
80
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
84
81
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
85
82
  fi
@@ -98,7 +95,7 @@ read_sonar_secret() {
98
95
  local via_lisa
99
96
  via_lisa=$(node "$resolver" get "$name" 2>/dev/null) \
100
97
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
101
- break
98
+ # Empty/error means this substrate had no answer; try the next trusted one.
102
99
  fi
103
100
  done
104
101
  # Name every path. A bare `return 1` sends the next reader hunting for a
@@ -58,7 +58,7 @@ The state machine (first match wins, evaluated over the **required** leaves only
58
58
  | else any leaf has **started** (claimed or in review, or shipped while a sibling has not) | active / in-progress | `claimed` (or `review` where supported) |
59
59
  | else (leaves exist, none started) | unchanged | — |
60
60
 
61
- - **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others 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**.
61
+ - **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress. It never says *which* child or *which kind* of hold. Resolve the shared classifier from `${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}/scripts/rollup-blocker-classification.mjs` (the current-directory fallback supports a Lisa checkout), run it over the resolved child graph, and 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 for every vendor: no lifecycle mutation and no rollup-comment mutation. Report the failure, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
62
62
  - **Least-advanced env wins** — a parent reaches an env only once all required leaves have reached at least that env (all `On Stg` → `On Stg`; mixed dev/staging → the dev value). Native terminal closure fires only at the production `done`, never at an intermediate env.
63
63
  - **The parent never carries `ready`** — `ready` is a human "claim this leaf" signal; rollup only moves a parent between non-ready container states.
64
64
  - **Rollup is recursive** — an Epic rolls up from its Stories, each of which rolls up from its own leaves. Evaluate bottom-up.
@@ -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
@@ -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"
@@ -329,12 +329,16 @@ the complete bundle from the shared account. Do not extract or distribute its
329
329
  individual access-key fields:
330
330
 
331
331
  \`\`\`bash
332
- aws --profile shared secretsmanager get-secret-value \\
332
+ aws secretsmanager get-secret-value \\
333
333
  --secret-id ${secretName} \\
334
334
  --query SecretString \\
335
335
  --output text
336
336
  \`\`\`
337
337
 
338
+ This uses the ambient AWS identity. Add \`--profile <source-profile>\` only when
339
+ this project requires a named source profile to read the emission; \`shared\` is
340
+ not an implicit requirement.
341
+
338
342
  Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
339
343
  remote-agent platform. Run the setup command and require its live
340
344
  \`sts:GetCallerIdentity\` check to pass before considering the platform ready.
@@ -293,6 +293,8 @@ function collectHolds(children, context, path, seen, tally) {
293
293
  * container?: { ref?: unknown, type?: unknown }
294
294
  * readError?: unknown
295
295
  * children?: unknown
296
+ * renderedState?: unknown
297
+ * childTally?: unknown
296
298
  * blockedStates?: unknown
297
299
  * markerNames?: { specDefect?: unknown, humanNeeded?: unknown }
298
300
  * }} input
@@ -339,6 +341,10 @@ export function classifyRollupBlockers(input = {}) {
339
341
  blockers.filter(blocker => blocker.class === name),
340
342
  ]).filter(([, group]) => group.length > 0)
341
343
  );
344
+ const renderedState = trimmedString(input.renderedState);
345
+ const childTally =
346
+ trimmedString(input.childTally) ||
347
+ `examined=${tally.examined}, held=${blockers.length}, unreadable=${tally.unexaminable.length}`;
342
348
 
343
349
  return {
344
350
  ok: true,
@@ -348,6 +354,8 @@ export function classifyRollupBlockers(input = {}) {
348
354
  blockers,
349
355
  byClass,
350
356
  unexaminable: tally.unexaminable,
357
+ renderedState,
358
+ childTally,
351
359
  };
352
360
  }
353
361
 
@@ -433,8 +441,8 @@ export function renderRollupBlockerReport(result) {
433
441
  }
434
442
 
435
443
  /**
436
- * A stable identity for a rollup verdict: the container, plus every held item
437
- * and its class, sorted.
444
+ * A stable identity for a rollup note: the rendered lifecycle state, child
445
+ * tally, container, and every held item plus its class, sorted.
438
446
  *
439
447
  * This is what lets a repeat cycle stay quiet. 32 identical hold comments over
440
448
  * six weeks was the same verdict restated on a schedule; comparing this string
@@ -451,7 +459,14 @@ export function rollupBlockerFingerprint(result) {
451
459
  .map(blocker => `${blocker.ref}:${blocker.class}`)
452
460
  .sort((left, right) => left.localeCompare(right));
453
461
 
454
- return `container=${result.container.ref};verdict=${result.verdict};examined=${result.examined};holds=${pairs.join(",")}`;
462
+ return [
463
+ `container=${result.container.ref}`,
464
+ `verdict=${result.verdict}`,
465
+ `rendered=${JSON.stringify(trimmedString(result.renderedState))}`,
466
+ `tally=${JSON.stringify(trimmedString(result.childTally))}`,
467
+ `examined=${result.examined}`,
468
+ `holds=${pairs.join(",")}`,
469
+ ].join(";");
455
470
  }
456
471
 
457
472
  /**
@@ -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