@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
@@ -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
  }
@@ -56,6 +56,11 @@ const ROLE_ARN =
56
56
  /^arn:(?:aws|aws-us-gov|aws-cn):iam::\d{12}:role\/[\w+=,.@/-]+$/;
57
57
  const PUBLICATION_COORDINATION_SCOPE = "AWS_BOOTSTRAP_";
58
58
  const PUBLICATION_LOCK_TTL_MS = 30 * 60 * 1000;
59
+ // Every provider and AWS child has its own 30-second deadline. Stop starting
60
+ // target mutations five minutes before another publisher may reap this lock,
61
+ // leaving ample room for the in-flight child to finish. This is a monotonic
62
+ // duration budget, not a wall clock used to order or expire provider records.
63
+ const PUBLICATION_CRITICAL_SECTION_BUDGET_MS = 25 * 60 * 1000;
59
64
 
60
65
  /**
61
66
  * Validate and normalize one complete bundle without changing its serialized
@@ -257,6 +262,22 @@ function writeProviderValue(cfg, id, value, write) {
257
262
  }
258
263
  }
259
264
 
265
+ function publicationBudgetGuard(startedAt, monotonicNow) {
266
+ return () => {
267
+ const elapsed = monotonicNow() - startedAt;
268
+ if (!Number.isFinite(elapsed) || elapsed < 0) {
269
+ throw new Error(
270
+ "publication lock monotonic timer became invalid; refusing further provider mutation"
271
+ );
272
+ }
273
+ if (elapsed >= PUBLICATION_CRITICAL_SECTION_BUDGET_MS) {
274
+ throw new Error(
275
+ "publication lock execution budget exceeded; refusing further provider mutation"
276
+ );
277
+ }
278
+ };
279
+ }
280
+
260
281
  /**
261
282
  * Enter the provider election for one AWS bootstrap record.
262
283
  *
@@ -427,23 +448,29 @@ export function releasePublicationLock(cfg, lock, operations = {}) {
427
448
  * snapshot can never overwrite a publication that finished while this process
428
449
  * was entering the election.
429
450
  */
430
- function withPublicationLock(cfg, operations, task) {
451
+ function withPublicationLock(cfg, operations, task, completedMessage) {
431
452
  const fetch = operations.fetch ?? fetchAll;
432
453
  const acquire = operations.acquireLock ?? acquirePublicationLock;
433
454
  const release = operations.releaseLock ?? releasePublicationLock;
455
+ const monotonicNow = operations.monotonicNow ?? (() => performance.now());
456
+ const startedAt = monotonicNow();
457
+ const assertWithinBudget = publicationBudgetGuard(startedAt, monotonicNow);
434
458
  const target = currentEntry(cfg, fetch);
435
459
  const lock = acquire(cfg, target, operations);
436
460
  let result;
437
461
  let taskError;
438
462
 
439
463
  try {
464
+ assertWithinBudget();
440
465
  const current = currentEntry(cfg, fetch);
441
466
  if (current.id !== target.id || current.projectId !== target.projectId) {
442
467
  throw new Error(
443
468
  `${BOOTSTRAP_KEY} moved after publication lock acquisition; retry from the new provider record`
444
469
  );
445
470
  }
446
- result = task(current);
471
+ assertWithinBudget();
472
+ result = task(current, assertWithinBudget);
473
+ assertWithinBudget();
447
474
  } catch (error) {
448
475
  taskError = error;
449
476
  }
@@ -455,15 +482,18 @@ function withPublicationLock(cfg, operations, task) {
455
482
  if (taskError) {
456
483
  throw new Error(`${firstLine(taskError)}; ${releaseReason}`);
457
484
  }
458
- throw new Error(releaseReason);
485
+ throw new Error(
486
+ `${completedMessage}; ${releaseReason}; remove the coordination record before the next publication`
487
+ );
459
488
  }
460
489
 
461
490
  if (taskError) throw taskError;
462
491
  return result;
463
492
  }
464
493
 
465
- function preflightUnderLock(cfg, entry, write) {
494
+ function preflightUnderLock(cfg, entry, write, assertWithinBudget) {
466
495
  try {
496
+ assertWithinBudget();
467
497
  writeProviderValue(cfg, entry.id, entry.value, write);
468
498
  } catch (error) {
469
499
  throw new Error(
@@ -482,8 +512,12 @@ function preflightUnderLock(cfg, entry, write) {
482
512
  */
483
513
  export function preflightAwsBootstrap(cfg, operations = {}) {
484
514
  const write = operations.write ?? writeSecret;
485
- return withPublicationLock(cfg, operations, entry =>
486
- preflightUnderLock(cfg, entry, write)
515
+ return withPublicationLock(
516
+ cfg,
517
+ operations,
518
+ (entry, assertWithinBudget) =>
519
+ preflightUnderLock(cfg, entry, write, assertWithinBudget),
520
+ "preflight completed"
487
521
  );
488
522
  }
489
523
 
@@ -496,12 +530,20 @@ export function preflightAwsBootstrap(cfg, operations = {}) {
496
530
  * @param {Function} write Provider writer.
497
531
  * @returns {string} Human-readable rollback outcome with no secret material.
498
532
  */
499
- function rollback(cfg, previous, candidateRaw, fetch, write) {
533
+ function rollback(
534
+ cfg,
535
+ previous,
536
+ candidateRaw,
537
+ fetch,
538
+ write,
539
+ assertWithinBudget
540
+ ) {
500
541
  try {
501
542
  const current = currentEntry(cfg, fetch);
502
543
  if (current.value !== candidateRaw) {
503
544
  return "rollback skipped because the provider changed after this publication";
504
545
  }
546
+ assertWithinBudget();
505
547
  writeProviderValue(cfg, previous.id, previous.value, write);
506
548
  const restored = currentEntry(cfg, fetch);
507
549
  if (restored.value !== previous.value) {
@@ -528,47 +570,56 @@ export function publishAwsBootstrap(raw, cfg, operations = {}) {
528
570
  const write = operations.write ?? writeSecret;
529
571
  const verify = operations.verify ?? verifyAwsBootstrap;
530
572
  const candidate = validateAwsBootstrap(raw);
531
- return withPublicationLock(cfg, operations, previous => {
532
- preflightUnderLock(cfg, previous, write);
533
-
534
- // Verify before the write. A candidate that cannot assume one of its roles
535
- // never becomes the value every new remote session receives.
536
- verify(candidate);
537
-
538
- if (candidate.raw === previous.value) {
539
- return {
540
- changed: false,
541
- profiles: candidate.profiles.map(profile => profile.name),
542
- };
543
- }
573
+ return withPublicationLock(
574
+ cfg,
575
+ operations,
576
+ (previous, assertWithinBudget) => {
577
+ preflightUnderLock(cfg, previous, write, assertWithinBudget);
578
+
579
+ // Verify before the write. A candidate that cannot assume one of its roles
580
+ // never becomes the value every new remote session receives.
581
+ verify(candidate);
582
+ assertWithinBudget();
583
+
584
+ if (candidate.raw === previous.value) {
585
+ return {
586
+ changed: false,
587
+ profiles: candidate.profiles.map(profile => profile.name),
588
+ };
589
+ }
544
590
 
545
- try {
546
- writeProviderValue(cfg, previous.id, candidate.raw, write);
547
- const stored = currentEntry(cfg, fetch);
548
- if (stored.value !== candidate.raw) {
591
+ try {
592
+ assertWithinBudget();
593
+ writeProviderValue(cfg, previous.id, candidate.raw, write);
594
+ const stored = currentEntry(cfg, fetch);
595
+ if (stored.value !== candidate.raw) {
596
+ throw new Error(
597
+ "provider read-back did not match the candidate exactly"
598
+ );
599
+ }
600
+ const storedCandidate = validateAwsBootstrap(stored.value);
601
+ verify(storedCandidate);
602
+ assertWithinBudget();
603
+ return {
604
+ changed: true,
605
+ profiles: storedCandidate.profiles.map(profile => profile.name),
606
+ };
607
+ } catch (error) {
608
+ const rollbackOutcome = rollback(
609
+ cfg,
610
+ previous,
611
+ candidate.raw,
612
+ fetch,
613
+ write,
614
+ assertWithinBudget
615
+ );
549
616
  throw new Error(
550
- "provider read-back did not match the candidate exactly"
617
+ `${BOOTSTRAP_KEY} publication failed: ${firstLine(error)}; ${rollbackOutcome}`
551
618
  );
552
619
  }
553
- const storedCandidate = validateAwsBootstrap(stored.value);
554
- verify(storedCandidate);
555
- return {
556
- changed: true,
557
- profiles: storedCandidate.profiles.map(profile => profile.name),
558
- };
559
- } catch (error) {
560
- const rollbackOutcome = rollback(
561
- cfg,
562
- previous,
563
- candidate.raw,
564
- fetch,
565
- write
566
- );
567
- throw new Error(
568
- `${BOOTSTRAP_KEY} publication failed: ${firstLine(error)}; ${rollbackOutcome}`
569
- );
570
- }
571
- });
620
+ },
621
+ "publication completed"
622
+ );
572
623
  }
573
624
 
574
625
  /** Read a candidate from stdin without ever placing it in argv. */
@@ -48,9 +48,9 @@ interactively and headlessly — which is why it leads. The REST tier uses:
48
48
  read_sentry_token() {
49
49
  [ -n "${SENTRY_AUTH_TOKEN:-}" ] && { echo "$SENTRY_AUTH_TOKEN"; 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_sentry_token() {
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_sentry_token() {
83
80
  local via_lisa
84
81
  via_lisa=$(node "$resolver" get SENTRY_AUTH_TOKEN 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
@@ -86,9 +86,9 @@ read_token() {
86
86
  # than "this function" is deliberate — the old wording asked for something
87
87
  # unachievable, and an unachievable rule is one that gets quietly dropped.
88
88
  #
89
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
90
- # lead because a project that vendors the resolver has declared which copy it
91
- # wants used, and that decision must survive this change untouched. The plugin
89
+ # Ordered across trusted machine-managed substrates, ending at the installed
90
+ # package. Checkout-local paths are deliberately absent: a familiar generated
91
+ # destination is still repository-controlled executable code. The plugin
92
92
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
93
93
  # pointing at it is reachable from anywhere the plugin itself is installed.
94
94
  # Without one, a consumer repository that vendors none of the leading paths
@@ -97,12 +97,9 @@ read_token() {
97
97
  # lookups. This LADDER is identical in every skill that resolves a credential
98
98
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
99
99
  # happens AFTER the ladder may differ between them.
100
- local candidates=(
101
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
102
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
103
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
104
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
105
- )
100
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
101
+ # candidates are repository-controlled code, not trusted merely by path.
102
+ local candidates=()
106
103
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
107
104
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
108
105
  fi
@@ -121,7 +118,7 @@ read_token() {
121
118
  local via_lisa
122
119
  via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
123
120
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
124
- break
121
+ # Empty/error means this substrate had no answer; try the next trusted one.
125
122
  fi
126
123
  done
127
124
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -104,17 +104,14 @@ read_linear_key() { # $1=workspace slug
104
104
  # `linear-access` grew to seven, leaving `/lisa:setup:linear` unable to reach
105
105
  # a key that `lisa-linear-access` could read from the same repository.
106
106
  #
107
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
108
- # lead because a project that vendors the resolver has declared which copy it
109
- # wants used. The plugin rungs are the floor: `resolve-secret.mjs` ships
110
- # beside this skill, so a rung pointing at it is reachable from anywhere the
111
- # plugin itself is installed.
112
- local candidates=(
113
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
114
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
115
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
116
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
117
- )
107
+ # Ordered across trusted machine-managed substrates, ending at the installed
108
+ # package. Checkout-local paths are deliberately absent: a familiar generated
109
+ # destination is still repository-controlled executable code. The plugin
110
+ # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
111
+ # pointing at it is reachable from anywhere the plugin itself is installed.
112
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
113
+ # candidates are repository-controlled code, not trusted merely by path.
114
+ local candidates=()
118
115
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
119
116
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
120
117
  fi
@@ -133,7 +130,7 @@ read_linear_key() { # $1=workspace slug
133
130
  local via_lisa
134
131
  via_lisa=$(node "$resolver" get LINEAR_API_KEY 2>/dev/null) \
135
132
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
136
- break
133
+ # Empty/error means this substrate had no answer; try the next trusted one.
137
134
  fi
138
135
  done
139
136
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -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;