@codyswann/lisa 2.326.3 → 2.328.0

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 (245) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +55 -29
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/dist/sync/lifecycle-defaults.d.ts +61 -0
  5. package/dist/sync/lifecycle-defaults.d.ts.map +1 -0
  6. package/dist/sync/lifecycle-defaults.js +75 -0
  7. package/dist/sync/lifecycle-defaults.js.map +1 -0
  8. package/dist/sync/registry.d.ts.map +1 -1
  9. package/dist/sync/registry.js +15 -23
  10. package/dist/sync/registry.js.map +1 -1
  11. package/package.json +1 -1
  12. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa/.codex-plugin/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  15. package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/SKILL.md +21 -1
  16. package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  17. package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +1 -1
  18. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +1 -1
  19. package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  20. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +20 -9
  21. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +76 -66
  22. package/plugins/lisa/.codex-plugin/skills/lisa-linear-claim/SKILL.md +1 -1
  23. package/plugins/lisa/.codex-plugin/skills/lisa-linear-evidence/SKILL.md +7 -7
  24. package/plugins/lisa/.codex-plugin/skills/lisa-linear-journey/SKILL.md +2 -2
  25. package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +34 -30
  26. package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  27. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  28. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +8 -7
  29. package/plugins/lisa/.codex-plugin/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  30. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +1 -1
  31. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  32. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +59 -12
  33. package/plugins/lisa/.codex-plugin/skills/lisa-setup-local-env/SKILL.md +95 -0
  34. package/plugins/lisa/.codex-plugin/skills/lisa-setup-local-env/agents/openai.yaml +4 -0
  35. package/plugins/lisa/.codex-plugin/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  36. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +27 -11
  37. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  38. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  39. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +2 -2
  40. package/plugins/lisa/agents/linear-agent.md +6 -6
  41. package/plugins/lisa/agents/linear-build-intake.md +3 -3
  42. package/plugins/lisa/commands/setup/local-env.md +7 -0
  43. package/plugins/lisa/rules/eager/rejection-detection.md +2 -2
  44. package/plugins/lisa/rules/reference/config-resolution.md +37 -9
  45. package/plugins/lisa/rules/reference/rejection-detection.md +2 -2
  46. package/plugins/lisa/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  47. package/plugins/lisa/skills/lisa-detect-tooling/SKILL.md +21 -1
  48. package/plugins/lisa/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  49. package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +1 -1
  50. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +1 -1
  51. package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  52. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +20 -9
  53. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +77 -67
  54. package/plugins/lisa/skills/lisa-linear-claim/SKILL.md +1 -1
  55. package/plugins/lisa/skills/lisa-linear-evidence/SKILL.md +7 -7
  56. package/plugins/lisa/skills/lisa-linear-journey/SKILL.md +2 -2
  57. package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +34 -30
  58. package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  59. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  60. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +8 -7
  61. package/plugins/lisa/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  62. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +1 -1
  63. package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  64. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +59 -12
  65. package/plugins/lisa/skills/lisa-setup-local-env/SKILL.md +95 -0
  66. package/plugins/lisa/skills/lisa-setup-local-env/agents/openai.yaml +4 -0
  67. package/plugins/lisa/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  68. package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +27 -11
  69. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  70. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  71. package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +1 -1
  72. package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +2 -2
  73. package/plugins/lisa-agy/agents/linear-agent.md +6 -6
  74. package/plugins/lisa-agy/agents/linear-build-intake.md +3 -3
  75. package/plugins/lisa-agy/commands/lisa/setup/local-env.md +7 -0
  76. package/plugins/lisa-agy/plugin.json +1 -1
  77. package/plugins/lisa-agy/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  78. package/plugins/lisa-agy/skills/lisa-detect-tooling/SKILL.md +21 -1
  79. package/plugins/lisa-agy/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  80. package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +1 -1
  81. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +1 -1
  82. package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  83. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +20 -9
  84. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +77 -67
  85. package/plugins/lisa-agy/skills/lisa-linear-claim/SKILL.md +1 -1
  86. package/plugins/lisa-agy/skills/lisa-linear-evidence/SKILL.md +7 -7
  87. package/plugins/lisa-agy/skills/lisa-linear-journey/SKILL.md +2 -2
  88. package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +34 -30
  89. package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  90. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  91. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +8 -7
  92. package/plugins/lisa-agy/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  93. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +1 -1
  94. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  95. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +59 -12
  96. package/plugins/lisa-agy/skills/lisa-setup-local-env/SKILL.md +95 -0
  97. package/plugins/lisa-agy/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  98. package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +27 -11
  99. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  100. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  101. package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +1 -1
  102. package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +2 -2
  103. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  106. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-copilot/agents/linear-agent.agent.md +6 -6
  110. package/plugins/lisa-copilot/agents/linear-build-intake.agent.md +3 -3
  111. package/plugins/lisa-copilot/commands/lisa/setup/local-env.md +7 -0
  112. package/plugins/lisa-copilot/rules/eager/rejection-detection.md +2 -2
  113. package/plugins/lisa-copilot/rules/reference/config-resolution.md +37 -9
  114. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +2 -2
  115. package/plugins/lisa-copilot/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  116. package/plugins/lisa-copilot/skills/lisa-detect-tooling/SKILL.md +21 -1
  117. package/plugins/lisa-copilot/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  118. package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +1 -1
  119. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +1 -1
  120. package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  121. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +20 -9
  122. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +77 -67
  123. package/plugins/lisa-copilot/skills/lisa-linear-claim/SKILL.md +1 -1
  124. package/plugins/lisa-copilot/skills/lisa-linear-evidence/SKILL.md +7 -7
  125. package/plugins/lisa-copilot/skills/lisa-linear-journey/SKILL.md +2 -2
  126. package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +34 -30
  127. package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  128. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  129. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +8 -7
  130. package/plugins/lisa-copilot/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  131. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +1 -1
  132. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  133. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +59 -12
  134. package/plugins/lisa-copilot/skills/lisa-setup-local-env/SKILL.md +95 -0
  135. package/plugins/lisa-copilot/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  136. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +27 -11
  137. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  138. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  139. package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +1 -1
  140. package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +2 -2
  141. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-cursor/agents/linear-agent.md +6 -6
  143. package/plugins/lisa-cursor/agents/linear-build-intake.md +3 -3
  144. package/plugins/lisa-cursor/commands/lisa/setup/local-env.md +7 -0
  145. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +37 -9
  146. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +2 -2
  147. package/plugins/lisa-cursor/rules/rejection-detection.mdc +2 -2
  148. package/plugins/lisa-cursor/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  149. package/plugins/lisa-cursor/skills/lisa-detect-tooling/SKILL.md +21 -1
  150. package/plugins/lisa-cursor/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  151. package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +1 -1
  152. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +1 -1
  153. package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  154. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +20 -9
  155. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +77 -67
  156. package/plugins/lisa-cursor/skills/lisa-linear-claim/SKILL.md +1 -1
  157. package/plugins/lisa-cursor/skills/lisa-linear-evidence/SKILL.md +7 -7
  158. package/plugins/lisa-cursor/skills/lisa-linear-journey/SKILL.md +2 -2
  159. package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +34 -30
  160. package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  161. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  162. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +8 -7
  163. package/plugins/lisa-cursor/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  164. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +1 -1
  165. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  166. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +59 -12
  167. package/plugins/lisa-cursor/skills/lisa-setup-local-env/SKILL.md +95 -0
  168. package/plugins/lisa-cursor/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  169. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +27 -11
  170. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  171. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  172. package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +1 -1
  173. package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +2 -2
  174. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  175. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  176. package/plugins/lisa-expo-agy/plugin.json +1 -1
  177. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  178. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  179. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  180. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  181. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  182. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  183. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  184. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  185. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  186. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  187. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  188. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  189. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  190. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  191. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  192. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  193. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  194. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  195. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  196. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  197. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  198. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  199. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  200. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  201. package/plugins/lisa-rails-agy/plugin.json +1 -1
  202. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  203. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  204. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  205. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  206. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  207. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  208. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  209. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  210. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  211. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  212. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  213. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  214. package/plugins/src/base/agents/linear-agent.md +6 -6
  215. package/plugins/src/base/agents/linear-build-intake.md +3 -3
  216. package/plugins/src/base/commands/setup/local-env.md +7 -0
  217. package/plugins/src/base/rules/eager/rejection-detection.md +2 -2
  218. package/plugins/src/base/rules/reference/config-resolution.md +37 -9
  219. package/plugins/src/base/rules/reference/rejection-detection.md +2 -2
  220. package/plugins/src/base/skills/lisa-confluence-to-tracker/SKILL.md +3 -3
  221. package/plugins/src/base/skills/lisa-detect-tooling/SKILL.md +21 -1
  222. package/plugins/src/base/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +25 -13
  223. package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +1 -1
  224. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +1 -1
  225. package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
  226. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +20 -9
  227. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +77 -67
  228. package/plugins/src/base/skills/lisa-linear-claim/SKILL.md +1 -1
  229. package/plugins/src/base/skills/lisa-linear-evidence/SKILL.md +7 -7
  230. package/plugins/src/base/skills/lisa-linear-journey/SKILL.md +2 -2
  231. package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +34 -30
  232. package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  233. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +6 -6
  234. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +8 -7
  235. package/plugins/src/base/skills/lisa-notion-to-tracker/SKILL.md +3 -3
  236. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +1 -1
  237. package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +72 -10
  238. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +59 -12
  239. package/plugins/src/base/skills/lisa-setup-local-env/SKILL.md +95 -0
  240. package/plugins/src/base/skills/lisa-setup-local-env/scripts/local-env.mjs +174 -0
  241. package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +27 -11
  242. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +70 -30
  243. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +132 -6
  244. package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +1 -1
  245. package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +2 -2
@@ -287,7 +287,7 @@ For each epic identified in Phase 1, **invoke the `lisa-tracker-write` skill** (
287
287
  - `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub. No filtering at the epic level.
288
288
  - `priority`, `labels`, `components`, `fix_version`: as appropriate
289
289
 
290
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa-tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
290
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa-tracker-write` must not be passed the build-ready role for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready role is applied only in Phase 5.
291
291
 
292
292
  Capture the returned epic key — Phase 4 needs it as the parent for stories.
293
293
 
@@ -318,7 +318,7 @@ For each story, **invoke `lisa-tracker-write`** with:
318
318
  | Infrastructure | `ops`, `reference` |
319
319
  | Mixed / setup ("X.0") | All domains |
320
320
 
321
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa-tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
321
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass the build-ready role to `lisa-tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready role is applied only in Phase 5.
322
322
 
323
323
  Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
324
324
 
@@ -335,7 +335,7 @@ Each sub-task MUST:
335
335
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
336
336
  3. **Carry its own `## Source Requirement` section** (shared format above) with the full verbatim quote(s) from the Phase 1.4 register — a leaf claimed by build-intake in isolation must be self-explanatory. When a sub-task is split per-repo, every split child inherits the same requirement quote(s).
337
337
 
338
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa-tracker-write` applies `status:ready` here so downstream build intake (`lisa-tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa-tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
338
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready role. `lisa-tracker-write` applies the build-ready role here so downstream build intake (`lisa-tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply the build-ready role to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa-tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
339
339
 
340
340
  Sub-tasks inherit their parent story's artifacts by reference (the parent link). Do not pass the same artifact list to every sub-task.
341
341
 
@@ -61,12 +61,12 @@ authenticated_surface: true # → requires Sign-in Required
61
61
  artifacts_attached: true # → requires Source Precedence section
62
62
  relations: [{ id: "ENG-99", type: "blocked_by" }] # known issue relations (may be empty)
63
63
  remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
64
- build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
64
+ build_ready: true # caller asserts the build-ready role (the `Todo` state) is/would be set — see S15
65
65
  child_refs: ["ENG-601", "ENG-602"] # known child work (sub-issues / project-member issues / blocked_by parentage) — see S15
66
66
  prd_source: "https://notion.so/..." # set when the Issue was generated from a PRD — requires the Source Requirement section, see S16
67
67
  ```
68
68
 
69
- If the caller passes only an identifier, fetch the item via `lisa-linear-access operation: get-issue` (Issue) or `lisa-linear-access operation: get-project` (Project), derive the same fields from the fetched data — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa-linear-read-issue`) so S15 can classify the item — then run gates.
69
+ If the caller passes only an identifier, fetch the item via `lisa-linear-access operation: get-issue` (Issue) or `lisa-linear-access operation: get-project` (Project), derive the same fields from the fetched data — including `build_ready` (label set contains `Todo`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa-linear-read-issue`) so S15 can classify the item — then run gates.
70
70
 
71
71
  ## Gates
72
72
 
@@ -235,9 +235,9 @@ This gate depends on S11. It is `N/A` for containers — a **Project** (the Epic
235
235
 
236
236
  #### S15 — Leaf-only build-ready
237
237
 
238
- Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the Linear validator — a stale or hand-applied `status:ready` label on a container is a lifecycle error and must FAIL here, regardless of how the item was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa-linear-write-issue` applies at write time.)
238
+ Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the Linear validator — a stale or hand-applied `Todo` label on a container is a lifecycle error and must FAIL here, regardless of how the item was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa-linear-write-issue` applies at write time.)
239
239
 
240
- **When the gate applies.** Run S15 whenever the item is build-ready — i.e. `build_ready = true`, or the spec/live labels include `status:ready`. If the item is not build-ready, S15 is `N/A` (nothing claims a non-ready item, so the invariant is vacuous).
240
+ **When the gate applies.** Run S15 whenever the item is build-ready — i.e. `build_ready = true`, or the spec/live labels include `Todo`. If the item is not build-ready, S15 is `N/A` (nothing claims a non-ready item, so the invariant is vacuous).
241
241
 
242
242
  **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an item is a **container** if it has child work, whatever its declared type; otherwise the **issue type** decides. Determine child work from (in order) `child_refs`, native sub-issues, project-member issues (an Epic is modeled as a Linear Project), and `blocked_by` / parent references — the same hierarchy resolution `lisa-linear-read-issue` uses. When validating a live identifier, query sub-issues / project members alongside the item fetch.
243
243
 
@@ -255,7 +255,7 @@ PASS (the childless-parent exception) when the item is build-ready and is a **le
255
255
  | Epic (Project) | no | yes | **FAIL** (childless Epic/Project — pure rollup container, exception does not apply) |
256
256
  | any | any | no | **N/A** (not build-ready) |
257
257
 
258
- Remediation: `"Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
258
+ Remediation: `"Build-ready is leaf-only per leaf-only-lifecycle. Move the build-ready role off this container onto its leaf children (or, for a childless Epic, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
259
259
 
260
260
  `product_relevant: false` — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
261
261
 
@@ -362,7 +362,7 @@ system, and never invent or ask for credentials inline.
362
362
 
363
363
  ## Execution
364
364
 
365
- 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa-linear-read-issue`) so S15 can classify the item. Otherwise parse the YAML spec.
365
+ 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields — including `build_ready` (label set contains `Todo`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa-linear-read-issue`) so S15 can classify the item. Otherwise parse the YAML spec.
366
366
  2. Resolve team ID via `lisa-linear-access operation: list-teams({query: <teamKey>})` if any feasibility gate will run.
367
367
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
368
368
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -31,7 +31,7 @@ Linear's data model maps Epic / Story / Sub-task to **different entity types**.
31
31
  | `Bug` | **Issue** | `lisa-linear-access operation: save-issue` | `projectId` if part of an Epic; else top-level |
32
32
  | `Spike` | **Issue** | `lisa-linear-access operation: save-issue` | `projectId` if part of an Epic; else top-level |
33
33
 
34
- Status workflow uses **labels** (`status:ready`, `status:in-progress`, `status:on-dev`, `status:done`) for portability across teamsLinear's per-team workflow state names vary, but labels are workspace-scoped and stable. Native Linear `state` is set to the team's default `Todo` state on create.
34
+ The build lifecycle uses native **workflow states** (`Todo`, `In Progress`, `In Review`, `Blocked`, `On Dev`, `On Stg`, `Done`), resolved per role from `linear.workflow` see "Why Linear uses states, not labels" in `config-resolution`. A new Issue is created in the team's default backlog/unstarted state unless `build_ready` promotes it (below).
35
35
 
36
36
  ## Phase 1 — Resolve Intent
37
37
 
@@ -227,7 +227,8 @@ If the item modifies an existing user-facing surface, a `lisa-product-walkthroug
227
227
 
228
228
  Before create/update, verify each field is populated where applicable:
229
229
 
230
- - **Labels**: include `status:ready` for a new **leaf** work unit (Bug / Task / Sub-task / Improvement with no child work) per `leaf-only-lifecycle`, **unless `build_ready: false`** (see the Build-ready control input below); component labels (`component:<name>`); status / priority labels are NOT redundant with native fields — labels exist for portability and downstream queries. A container (Epic Project / Story with sub-issues / Spike) never receives `status:ready`.
230
+ - **Workflow state**: set the resolved `ready` state (`linear.workflow.ready`, default `Todo`) on a new **leaf** work unit (Bug / Task / Sub-task / Improvement with no child work) per `leaf-only-lifecycle`, **unless `build_ready: false`** (see the Build-ready control input below). A container (Epic Project / Story with sub-issues / Spike) is never put in the build-ready state.
231
+ - **Labels**: taxonomy only — `type:<Kind>`, `repo:<name>`, `component:<name>`, and the `prd-intake-feedback` sentinel. Lifecycle is **not** a label on Linear; do not add `status:*` labels.
231
232
  - **Native priority field**: 0–4 per Linear's scale; explicit, not "unset".
232
233
  - **Native estimate**: per Linear's team-configured estimate scale (often 0–8 Fibonacci); skip for Epic / Spike.
233
234
  - **ProjectMilestone**: when the team uses dated milestones, set the milestone on the Project (Epic) or on the Issue (when an Issue belongs to a milestone).
@@ -238,11 +239,11 @@ For Bug / Task / Sub-task, ensure the summary is prefixed with `[<repo-name>]`.
238
239
 
239
240
  ### Build-ready control input (`build_ready`)
240
241
 
241
- `build_ready` is an optional write-control input (default: **omitted**). It governs whether a **leaf** work unit's `status:ready` label is applied on create. It never overrides `leaf-only-lifecycle` — a container is never stamped build-ready regardless of `build_ready`. "Not build-ready" is not a special state: the Issue is still created with Linear's default native `Todo` state; it just lacks the `status:ready` **label** the build lifecycle keys off, so a human can promote it later.
242
+ `build_ready` is an optional write-control input (default: **omitted**). It governs whether a **leaf** work unit is created **in the resolved `ready` workflow state**. It never overrides `leaf-only-lifecycle` — a container is never stamped build-ready regardless of `build_ready`. "Not build-ready" is not a special state: the Issue is simply left in the team's default backlog/unstarted state, which a human can promote later. This mirrors `lisa-jira-write-ticket`, because Linear is a state-driven tracker like JIRA, not a label-driven one like GitHub.
242
243
 
243
- - **Omitted** → current behavior: a leaf work unit receives `status:ready`. Preserves what every existing caller (`lisa-plan`, the `*-to-tracker` skills) relies on.
244
- - **`build_ready: false`** → create the leaf **without** the `status:ready` label, so it sits in the backlog for a human to review and promote into the queue.
245
- - **`build_ready: true`** → ensure the leaf carries `status:ready` so `lisa-intake` / `lisa-linear-build-intake` auto-picks it up.
244
+ - **Omitted** → current behavior: a leaf work unit is created in the resolved `ready` state. Preserves what every existing caller (`lisa-plan`, the `*-to-tracker` skills) relies on.
245
+ - **`build_ready: false`** → create the leaf **without** the `ready` state, leaving it in the team's default backlog state so it waits for a human to review and promote it into the queue.
246
+ - **`build_ready: true`** → transition the **leaf** to the resolved `ready` state (`.linear.workflow.ready`) so `lisa-intake` / `lisa-linear-build-intake` auto-picks it up. Best-effort: if the state cannot be resolved or the transition is rejected, do not fail the write — leave the Issue in its default state and record the reason.
246
247
 
247
248
  ## Phase 5.5 — Validate (Pre-write Gate)
248
249
 
@@ -268,7 +269,7 @@ If the validator reports `PASS`, continue to Phase 6.
268
269
 
269
270
  ### CREATE — Story / Task / Bug / Spike / Improvement (Issue with projectId)
270
271
 
271
- 1. Resolve any required Issue labels (`component:<name>`, `prd-intake-feedback` only if this is a sentinel issue, etc.) via `lisa-linear-access operation: list-issue-labels` (create via `lisa-linear-access operation: create-issue-label` if missing). Include `status:ready` in `labelIds` only for a **leaf** work unit and only when `build_ready` is not `false` (per the Build-ready control input) — omit it for a container, and for a `build_ready: false` leaf which then waits in the backlog for a human to promote it.
272
+ 1. Resolve any required Issue labels (`type:<Kind>`, `repo:<name>`, `component:<name>`, `prd-intake-feedback` only if this is a sentinel issue) via `lisa-linear-access operation: list-issue-labels` (create via `lisa-linear-access operation: create-issue-label` if missing). Separately resolve the `ready` **state** id via `lisa-linear-access operation: list-workflow-states`, and set it as `stateId` only for a **leaf** work unit and only when `build_ready` is not `false` — omit it for a container, and for a `build_ready: false` leaf which then waits in the default backlog state for a human to promote it.
272
273
  2. Call `lisa-linear-access operation: save-issue` with: `team` (teamId), `title` (summary), `description` (markdown), `projectId` (the Epic Project), `priority` (0–4), `estimate`, `labelIds`, `assignee` if known.
273
274
  3. Capture the returned identifier (e.g. `ENG-123`) — Phase 4 sub-tasks need it as `parentId`.
274
275
  4. Add relationships from Phase 4b via `save_issue` (relations field) or paired relation calls.
@@ -274,7 +274,7 @@ For each PRD epic, **invoke the `lisa-tracker-write` skill** (do not call `creat
274
274
  - `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub, and anyone working on the epic or its descendants must be able to reach the full set from one place. No filtering at the epic level. `lisa-tracker-write` Phase 4c attaches them as remote links.
275
275
  - `priority`, `labels`, `components`, `fix_version`: as appropriate
276
276
 
277
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa-tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
277
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa-tracker-write` must not be passed the build-ready role for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready role is applied only in Phase 5.
278
278
 
279
279
  Capture the returned epic key — Phase 4 needs it as the parent for stories.
280
280
 
@@ -308,7 +308,7 @@ For each story, **invoke `lisa-tracker-write`** with:
308
308
  - `artifacts`: the Phase 1.5 artifacts filtered by domain per the inheritance table below — `lisa-tracker-write` Phase 4c attaches them as remote links
309
309
  - `priority`, `labels`, `components`, `fix_version`: as appropriate
310
310
 
311
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa-tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
311
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass the build-ready role to `lisa-tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready role is applied only in Phase 5.
312
312
 
313
313
  Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
314
314
 
@@ -336,7 +336,7 @@ Each sub-task MUST:
336
336
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
337
337
  3. **Carry its own `## Source Requirement` section** (shared format above) with the full verbatim quote(s) from the Phase 1.4 register — a leaf claimed by build-intake in isolation must be self-explanatory. When a sub-task is split per-repo, every split child inherits the same requirement quote(s).
338
338
 
339
- **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa-tracker-write` applies `status:ready` here so downstream build intake (`lisa-tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa-tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
339
+ **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready role. `lisa-tracker-write` applies the build-ready role here so downstream build intake (`lisa-tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply the build-ready role to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa-tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
340
340
 
341
341
  **Verification plan examples by stack:**
342
342
  - **Backend APIs**: curl GraphQL/REST calls with auth token, database queries, checking audit entries
@@ -178,7 +178,7 @@ rule — never hardcode status/label strings. The relevant repair roles:
178
178
  |-----------|--------|----------------------|------------------|----------------------------|
179
179
  | Build | JIRA | `jira.workflow.claimed` (`In Progress`) | `jira.workflow.blocked` (`Blocked`) | env-resolved `jira.workflow.done` |
180
180
  | Build | GitHub | `github.labels.build.claimed` (`status:in-progress`) | `github.labels.build.blocked` (`status:blocked`) | env-resolved `github.labels.build.done` (`status:done`) |
181
- | Build | Linear | `linear.labels.build.claimed` (`status:in-progress`) | `linear.labels.build.blocked` (`status:blocked`) | env-resolved `linear.labels.build.done` (`status:done`) |
181
+ | Build | Linear | `linear.workflow.claimed` (`In Progress`) | `linear.workflow.blocked` (`Blocked`) | env-resolved `linear.workflow.done` (`Done`) |
182
182
  | PRD | Notion | `notion.values.in_review` (`In Review`) | `notion.values.blocked` (`Blocked`) | `notion.values.shipped` (`Shipped`) |
183
183
  | PRD | GitHub | `github.labels.prd.in_review` (`prd-in-review`) | `github.labels.prd.blocked` (`prd-blocked`) | `github.labels.prd.shipped` (`prd-shipped`) |
184
184
  | PRD | Linear | `linear.labels.prd.in_review` (`prd-in-review`) | `linear.labels.prd.blocked` (`prd-blocked`) | `linear.labels.prd.shipped` (`prd-shipped`) |
@@ -73,8 +73,16 @@ function bindingsFor(surface) {
73
73
  return SURFACE_BINDINGS[surface] ?? ["repository"];
74
74
  }
75
75
 
76
- /** Install methods the toolchain runner supports. */
77
- const INSTALL_METHODS = new Set(["release-zip", "npm-global"]);
76
+ /**
77
+ * Install methods the toolchain runner supports.
78
+ *
79
+ * `release-tar` was missing here while the runner had supported it for as long
80
+ * as gh has been pinned — so this validator would have rejected the project's
81
+ * own manifest, and the only reason nobody hit it is that nothing ran the two
82
+ * against each other. Kept in step with `assertPinned` in `toolchain.mjs`,
83
+ * which is the list that actually decides.
84
+ */
85
+ const INSTALL_METHODS = new Set(["release-zip", "release-tar", "npm-global"]);
78
86
 
79
87
  /**
80
88
  * Validate the `secrets` block.
@@ -143,6 +151,44 @@ export function validateSecrets(secrets) {
143
151
  return problems;
144
152
  }
145
153
 
154
+ /**
155
+ * Validate the artifact half of one install entry.
156
+ *
157
+ * Split out because the same obligations apply whether the fields sit directly
158
+ * on the entry or inside a per-platform block: a method must be one Lisa can
159
+ * execute, an archive must carry a checksum, and an npm install must name a
160
+ * package. Duplicating those three rules per shape is how one shape ends up
161
+ * quietly weaker than the other.
162
+ * @param {string} label How to name this entry in a message.
163
+ * @param {object} entry The entry or platform block.
164
+ * @param {string[]} problems Accumulator.
165
+ */
166
+ function validateInstallArtifact(label, entry, problems) {
167
+ if (!INSTALL_METHODS.has(entry.install)) {
168
+ problems.push(
169
+ `remoteEnv install ${label} has method ${JSON.stringify(entry.install)}. ` +
170
+ `Supported: ${[...INSTALL_METHODS].join(", ")}.`
171
+ );
172
+ return;
173
+ }
174
+ // Both archive kinds, not just zip. A tarball pinned without a checksum
175
+ // trusts whatever the URL serves today exactly as much as a zip does, and
176
+ // checking only one of them meant the tool Lisa's guardrails shell out to —
177
+ // gh, which ships a tarball on Linux — was the one going unverified.
178
+ if (
179
+ (entry.install === "release-zip" || entry.install === "release-tar") &&
180
+ !(entry.url && entry.sha256)
181
+ ) {
182
+ problems.push(
183
+ `remoteEnv install ${label} needs both url and sha256. A pinned ` +
184
+ `version with no checksum still trusts whatever the URL serves today.`
185
+ );
186
+ }
187
+ if (entry.install === "npm-global" && !entry.package) {
188
+ problems.push(`remoteEnv install ${label} needs a package`);
189
+ }
190
+ }
191
+
146
192
  /**
147
193
  * Validate the `remoteEnv` block.
148
194
  * @param {object|undefined} remoteEnv The block, if present.
@@ -163,21 +209,37 @@ export function validateRemoteEnv(remoteEnv) {
163
209
  if (!tool.version) {
164
210
  problems.push(`remoteEnv install "${tool.name}" has no pinned version`);
165
211
  }
166
- if (!INSTALL_METHODS.has(tool.install)) {
212
+ // A flat entry means one artifact serves every platform — true of npm-global
213
+ // and of nothing else. Anything downloaded is platform-specific, so those
214
+ // declare a block per platform and each block is validated in full: a
215
+ // half-filled map is the failure this shape exists to prevent, and it is
216
+ // invisible until someone runs setup on the platform that was left out.
217
+ if (tool.platforms === undefined) {
218
+ validateInstallArtifact(`"${tool.name}"`, tool, problems);
219
+ continue;
220
+ }
221
+ if (
222
+ typeof tool.platforms !== "object" ||
223
+ tool.platforms === null ||
224
+ Array.isArray(tool.platforms)
225
+ ) {
167
226
  problems.push(
168
- `remoteEnv install "${tool.name}" has method ${JSON.stringify(tool.install)}. ` +
169
- `Supported: ${[...INSTALL_METHODS].join(", ")}.`
227
+ `remoteEnv install "${tool.name}" has a platforms field that is not an ` +
228
+ `object keyed by <platform>-<arch>. Omit it when one artifact serves ` +
229
+ `every platform.`
170
230
  );
171
231
  continue;
172
232
  }
173
- if (tool.install === "release-zip" && !(tool.url && tool.sha256)) {
233
+ const platforms = Object.entries(tool.platforms);
234
+ if (platforms.length === 0) {
174
235
  problems.push(
175
- `remoteEnv install "${tool.name}" needs both url and sha256. A pinned ` +
176
- `version with no checksum still trusts whatever the URL serves today.`
236
+ `remoteEnv install "${tool.name}" declares platforms but lists none, so ` +
237
+ `it can never be installed anywhere.`
177
238
  );
239
+ continue;
178
240
  }
179
- if (tool.install === "npm-global" && !tool.package) {
180
- problems.push(`remoteEnv install "${tool.name}" needs a package`);
241
+ for (const [platform, block] of platforms) {
242
+ validateInstallArtifact(`"${tool.name}" (${platform})`, block, problems);
181
243
  }
182
244
  }
183
245
 
@@ -152,19 +152,41 @@ read_role() { # $1=namespace (build|prd) $2=role $3=default
152
152
  }
153
153
  ```
154
154
 
155
- #### 3a. Build-queue labelsISSUE labels (only if Linear is the tracker)
155
+ #### 3a. Build-queue lifecycleWORKFLOW STATES (only if Linear is the tracker)
156
156
 
157
- Probe with `lisa-linear-access operation: list-issue-labels` (scoped to the team). For each role's resolved name, create it via `lisa-linear-access operation: create-issue-label` only if absent. The `done` role is env-keyed create all three defaults; collapse to a single string in config later if the project's terminal state is env-independent.
157
+ The build lane resolves to native workflow **states**, not labels see "Why Linear uses states, not labels" in `config-resolution`. Read role state name with the same ladder, against `linear.workflow`:
158
158
 
159
- | Role | Default |
160
- |------|---------|
161
- | `ready` | `status:ready` |
162
- | `claimed` | `status:in-progress` |
163
- | `review` | `status:code-review` |
164
- | `blocked` | `status:blocked` |
165
- | `done.dev` | `status:on-dev` |
166
- | `done.staging` | `status:on-stg` |
167
- | `done.production` | `status:done` |
159
+ ```bash
160
+ read_state() { # $1=role path (e.g. ready, done.dev) $2=default
161
+ local role="$1" default="$2" local_v global_v
162
+ local_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.local.json 2>/dev/null)
163
+ global_v=$(jq -r ".linear.workflow.${role} // empty" .lisa.config.json 2>/dev/null)
164
+ echo "${local_v:-${global_v:-$default}}"
165
+ }
166
+ ```
167
+
168
+ Enumerate the team's states with `lisa-linear-access operation: list-workflow-states` (each carries `id`, `name`, `type`, `position`). For each role, resolve in this order — the same cascade `lisa-setup-jira` uses, with one extra rung Linear affords that JIRA does not:
169
+
170
+ 1. **Exact name match** → resolved, nothing to do.
171
+ 2. **A plausible existing state of the right `type`** (`ready` → `unstarted`, `claimed`/`review` → `started`, `blocked` → `started` or `unstarted`, terminal `done` → `completed`) → present the team's state list via `AskUserQuestion` and let the user pick which state means this role. Record the choice as a config override in Step 4.
172
+ 3. **Nothing plausible** → offer to **create** the state via `lisa-linear-access operation: create-workflow-state` (name, `type`, `position`, colour), showing the exact name and type first. Linear's API permits this where JIRA's workflow editing is admin-gated — which is why this rung exists here and not there.
173
+ 4. **User declines creation** → stop and say which role is unresolvable and that the lifecycle cannot run without it. Never silently fall back to a state whose meaning differs, and never invent a name in config that does not exist in the team.
174
+
175
+ | Role | Default state | `type` | Ships with a stock team? |
176
+ |------|---------------|--------|--------------------------|
177
+ | `ready` | `Todo` | `unstarted` | yes |
178
+ | `claimed` | `In Progress` | `started` | yes |
179
+ | `review` | `In Review` | `started` | yes |
180
+ | `blocked` | `Blocked` | `unstarted` | **no — must be created or mapped** |
181
+ | `done.dev` | `On Dev` | `started` | **no — must be created or mapped** |
182
+ | `done.staging` | `On Stg` | `started` | **no — must be created or mapped** |
183
+ | `done.production` | `Done` | `completed` | yes |
184
+
185
+ **The env rungs are deliberately `started`, not `completed`.** `On Dev` and `On Stg` mean "merged and deployed *that far*" — work that is emphatically not finished. Typing them `completed` would make Linear treat them as closed: they would leave the active board, stop counting in cycles, and re-create the exact premature-closure problem this model exists to fix. Only `done.production` is `completed`.
186
+
187
+ **Position them between `In Review` and `Done`** so the board reads left-to-right in real lifecycle order. A team that orders its board differently can pass its own `position`.
188
+
189
+ **Turn off the team's `merge → Done` git automation.** Linear's per-team git automations (Settings → Team → Workflow, or the `gitAutomationStates` API) auto-complete an Issue on merge to **any** branch. With this model that automation is an unwanted second writer: it jumps an Issue straight to `Done` at a `dev` merge, skipping `On Dev` / `On Stg` and asserting production-done. Lisa itself moves the state at each rung, so the automation is redundant as well as wrong. Detect it and offer to delete it; leave `start` and `review` alone — those assert non-terminal states and are harmless.
168
190
 
169
191
  #### 3b. PRD-lifecycle labels — PROJECT labels (only if Linear is the PRD source)
170
192
 
@@ -205,14 +227,39 @@ if [ -n "$TEAM_KEY" ]; then
205
227
  .lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
206
228
  fi
207
229
 
208
- # Conditionally write label overrides (only non-default role names).
230
+ # Conditionally write label overrides (markers + PRD lane only the build lane
231
+ # is states now, and lives under .linear.workflow below).
209
232
  if [ -n "$LABEL_OVERRIDES_JSON" ] && [ "$LABEL_OVERRIDES_JSON" != "{}" ]; then
210
233
  jq --argjson o "$LABEL_OVERRIDES_JSON" \
211
234
  '.linear.labels = ((.linear.labels // {}) * $o)' \
212
235
  .lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
213
236
  fi
237
+
238
+ # Workflow-state overrides: only roles whose resolved state name differs from
239
+ # the default, INCLUDING any the user mapped onto an existing state in 3a.
240
+ if [ -n "$WORKFLOW_OVERRIDES_JSON" ] && [ "$WORKFLOW_OVERRIDES_JSON" != "{}" ]; then
241
+ jq --argjson w "$WORKFLOW_OVERRIDES_JSON" \
242
+ '.linear.workflow = ((.linear.workflow // {}) * $w)' \
243
+ .lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
244
+ fi
214
245
  ```
215
246
 
247
+ **Migrating a project that predates the state model.** A config carrying
248
+ `linear.labels.build.{ready,claimed,review,blocked,done}` was written against the
249
+ old label-driven lane. Those keys are inert now — nothing reads them — but
250
+ leaving them in place reads as configuration and will mislead the next person.
251
+ Migrate in one pass, and do it before the first intake cycle runs, or that cycle
252
+ sees an empty queue:
253
+
254
+ 1. Resolve each build role to a state per 3a, writing `linear.workflow`.
255
+ 2. **Backfill live Issues**: for every Issue carrying a `status:*` label, set its
256
+ workflow state to the role that label encoded. Do this before deleting
257
+ anything — the labels are the only record of where each Issue sits.
258
+ 3. Drop `build.{ready,claimed,review,blocked,done}` from `linear.labels`, keeping
259
+ `build.human_needed` and the whole `prd` map.
260
+ 4. Leave the `status:*` labels themselves in the workspace, unapplied, until the
261
+ first intake cycle after the migration has run green. They are the rollback.
262
+
216
263
  No secrets in config — the API key stays in keychain / `LINEAR_API_KEY`, the MCP session in its own store.
217
264
 
218
265
  ### Step 5 — Offer to set top-level `tracker` / `source`
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: lisa-setup-local-env
3
+ description: "Bring a developer's machine in…"
4
+ allowed-tools: ["Bash", "Read", "Skill"]
5
+ ---
6
+
7
+ # Setup Local Environment: $ARGUMENTS
8
+
9
+ Make this machine match what the project says it needs.
10
+
11
+ ## Why this exists separately
12
+
13
+ `remoteEnv.tools` was always the manifest for **every** surface, and the executor always
14
+ understood `local`. What did not exist was a way to ask for it. The only path ran a file named
15
+ `setup-remote-env.mjs`, out of a directory named `lisa-remote-env`, via a skill named
16
+ `lisa-setup-remote-env` — and it only existed at all once someone had provisioned a *remote*
17
+ environment, because that is what writes it into the repository.
18
+
19
+ So the machine most likely to be missing a tool was the one with no way to ask about it.
20
+
21
+ This skill is that surface. It adds no manifest, no pins, and no installer: it imports the
22
+ ones the remote flow owns. A second installer would be a second thing to keep honest, and the
23
+ one people run least is the one that rots.
24
+
25
+ ## Usage
26
+
27
+ ```sh
28
+ node scripts/local-env.mjs # report what diverges
29
+ node scripts/local-env.mjs --install-tools # install what is missing
30
+ node scripts/local-env.mjs --json # machine-readable plan
31
+ ```
32
+
33
+ Exit status is non-zero when a tool cannot be resolved, so this is usable as a gate.
34
+
35
+ ## What differs from a container
36
+
37
+ Two things, and both are because a person is here.
38
+
39
+ **Consent.** A container is disposable and provisions itself silently — that is the whole
40
+ point of it. A laptop belongs to someone, and putting pinned binaries into their
41
+ `~/.local/bin` uninvited is not ours to decide. Nothing installs until `--install-tools`.
42
+ Reporting happens either way, because knowing the machine diverges from what the project
43
+ declares is most of the value.
44
+
45
+ **The pin is a floor, not an equality.** A container should hold exactly the pinned version;
46
+ it exists to be reproducible. A developer's machine is shared with every other project they
47
+ work on, so installing a pinned binary ahead of a **newer** one already on `PATH` is a
48
+ downgrade this project imposed on all of them. A newer tool is reported as `newer` and left
49
+ alone. Older, or absent, is an install.
50
+
51
+ ## Platform pins
52
+
53
+ An install entry either serves every platform — true of `npm-global` and nothing else — or
54
+ declares a `platforms` map keyed `<platform>-<arch>`, each block carrying its own `install`
55
+ method, `url`, and `sha256`:
56
+
57
+ ```json
58
+ {
59
+ "name": "gh",
60
+ "version": "2.83.0",
61
+ "platforms": {
62
+ "linux-x64": { "install": "release-tar", "url": "...linux_amd64.tar.gz", "sha256": "...", "binary": "gh_2.83.0_linux_amd64/bin/gh" },
63
+ "darwin-arm64": { "install": "release-zip", "url": "...macOS_arm64.zip", "sha256": "...", "binary": "gh_2.83.0_macOS_arm64/bin/gh" }
64
+ }
65
+ }
66
+ ```
67
+
68
+ The method lives **inside** each block because it varies: gh publishes a `.tar.gz` for Linux
69
+ and a `.zip` for macOS, so a single method would have forced one platform onto an archive kind
70
+ its vendor does not ship.
71
+
72
+ When a tool has no block for the running platform, this skill says so and stops there. It will
73
+ not guess a download URL — a guessed artifact is one the checksum cannot vouch for, and the
74
+ checksum is the only thing standing between a pinned entry and whatever a URL serves today.
75
+ Add the block, or install that one tool however the platform normally would.
76
+
77
+ ## Detect before you install
78
+
79
+ Run `/lisa:detect-tooling` when the report looks thin. The manifest is the only thing that puts
80
+ a binary on `PATH`, and nothing populates it automatically — so a project ships npm scripts
81
+ invoking `maestro`, wires an MCP server whose CLI it also shells out to, and configures
82
+ Playwright thresholds, while `remoteEnv.tools` stays empty and each of those fails at the
83
+ moment of use instead of at setup.
84
+
85
+ The detector proposes and a human decides. It writes nothing, so a tool still only ever reaches
86
+ a machine from a reviewed, pinned, checksummed entry.
87
+
88
+ ## What it will not do
89
+
90
+ - **No unpinned installs.** Every install goes through the same `assertPinned` gate the remote
91
+ flow uses, and the same in-process checksum verification, which refuses before unpacking.
92
+ - **No package-manager fallback.** Shelling out to Homebrew would give no version pin and no
93
+ checksum, which is the property this design exists to enforce.
94
+ - **No writes to the manifest.** Proposing entries belongs to `/lisa:detect-tooling`; deciding
95
+ belongs to a human reviewing a diff.
@@ -0,0 +1,4 @@
1
+ display_name: "Setup Local Env"
2
+ short_description: "Bring a developer's machine in…"
3
+ default_prompt:
4
+ - "Use $lisa-setup-local-env: Bring a developer's machine in…."