@codyswann/lisa 4.54.13 → 4.55.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 (578) hide show
  1. package/all/copy-contents/.gitattributes +8 -0
  2. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +78 -9
  3. package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +17 -1
  4. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +194 -7
  5. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +31 -0
  6. package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +81 -2
  7. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +293 -8
  8. package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +21 -0
  9. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +78 -15
  10. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +21 -0
  11. package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +21 -0
  12. package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +340 -0
  13. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +29 -1
  14. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +183 -1
  15. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +21 -0
  16. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +21 -1
  17. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +100 -5
  18. package/all/copy-overwrite/scripts/lisa-work-item.mjs +1085 -80
  19. package/all/create-only/.github/workflows/continuous-gates.yml +7 -0
  20. package/cdk/create-only/.github/workflows/ci.yml +7 -0
  21. package/cdk/create-only/.github/workflows/deploy.yml +21 -0
  22. package/dist/cli/apply.d.ts.map +1 -1
  23. package/dist/cli/apply.js +9 -1
  24. package/dist/cli/apply.js.map +1 -1
  25. package/dist/cli/doctor-apply-deletions.d.ts +61 -0
  26. package/dist/cli/doctor-apply-deletions.d.ts.map +1 -0
  27. package/dist/cli/doctor-apply-deletions.js +137 -0
  28. package/dist/cli/doctor-apply-deletions.js.map +1 -0
  29. package/dist/cli/doctor-cdk-preset-adoption.d.ts +15 -0
  30. package/dist/cli/doctor-cdk-preset-adoption.d.ts.map +1 -0
  31. package/dist/cli/doctor-cdk-preset-adoption.js +316 -0
  32. package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -0
  33. package/dist/cli/doctor-config-shadowing.d.ts +22 -0
  34. package/dist/cli/doctor-config-shadowing.d.ts.map +1 -0
  35. package/dist/cli/doctor-config-shadowing.js +123 -0
  36. package/dist/cli/doctor-config-shadowing.js.map +1 -0
  37. package/dist/cli/doctor-rails-deploy-intent.d.ts +15 -0
  38. package/dist/cli/doctor-rails-deploy-intent.d.ts.map +1 -0
  39. package/dist/cli/doctor-rails-deploy-intent.js +197 -0
  40. package/dist/cli/doctor-rails-deploy-intent.js.map +1 -0
  41. package/dist/cli/doctor-seeded-artifacts.d.ts +12 -0
  42. package/dist/cli/doctor-seeded-artifacts.d.ts.map +1 -0
  43. package/dist/cli/doctor-seeded-artifacts.js +54 -0
  44. package/dist/cli/doctor-seeded-artifacts.js.map +1 -0
  45. package/dist/cli/doctor-stale-banner-scan.d.ts +44 -0
  46. package/dist/cli/doctor-stale-banner-scan.d.ts.map +1 -0
  47. package/dist/cli/doctor-stale-banner-scan.js +152 -0
  48. package/dist/cli/doctor-stale-banner-scan.js.map +1 -0
  49. package/dist/cli/doctor-stale-managed-banner.d.ts +19 -0
  50. package/dist/cli/doctor-stale-managed-banner.d.ts.map +1 -0
  51. package/dist/cli/doctor-stale-managed-banner.js +198 -0
  52. package/dist/cli/doctor-stale-managed-banner.js.map +1 -0
  53. package/dist/cli/doctor.d.ts.map +1 -1
  54. package/dist/cli/doctor.js +16 -0
  55. package/dist/cli/doctor.js.map +1 -1
  56. package/dist/cli/ui-detected-stacks.d.ts.map +1 -1
  57. package/dist/cli/ui-detected-stacks.js +3 -2
  58. package/dist/cli/ui-detected-stacks.js.map +1 -1
  59. package/dist/cli/update-check.d.ts.map +1 -1
  60. package/dist/cli/update-check.js +14 -0
  61. package/dist/cli/update-check.js.map +1 -1
  62. package/dist/cli/worktree-liveness.d.ts +5 -0
  63. package/dist/cli/worktree-liveness.d.ts.map +1 -1
  64. package/dist/cli/worktree-liveness.js +7 -6
  65. package/dist/cli/worktree-liveness.js.map +1 -1
  66. package/dist/cli/worktree-ownership.d.ts +13 -0
  67. package/dist/cli/worktree-ownership.d.ts.map +1 -1
  68. package/dist/cli/worktree-ownership.js +14 -0
  69. package/dist/cli/worktree-ownership.js.map +1 -1
  70. package/dist/configs/eslint/base.d.ts +34 -0
  71. package/dist/configs/eslint/base.d.ts.map +1 -1
  72. package/dist/configs/eslint/base.js +34 -0
  73. package/dist/configs/eslint/base.js.map +1 -1
  74. package/dist/core/apply-receipt.d.ts +65 -0
  75. package/dist/core/apply-receipt.d.ts.map +1 -1
  76. package/dist/core/apply-receipt.js +62 -2
  77. package/dist/core/apply-receipt.js.map +1 -1
  78. package/dist/core/cdk-preset-adoption.d.ts +136 -0
  79. package/dist/core/cdk-preset-adoption.d.ts.map +1 -0
  80. package/dist/core/cdk-preset-adoption.js +135 -0
  81. package/dist/core/cdk-preset-adoption.js.map +1 -0
  82. package/dist/core/config-shadowing.d.ts +36 -0
  83. package/dist/core/config-shadowing.d.ts.map +1 -1
  84. package/dist/core/config-shadowing.js +38 -0
  85. package/dist/core/config-shadowing.js.map +1 -1
  86. package/dist/core/config.d.ts +11 -0
  87. package/dist/core/config.d.ts.map +1 -1
  88. package/dist/core/config.js.map +1 -1
  89. package/dist/core/learnings-merge-driver.d.ts +10 -0
  90. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  91. package/dist/core/learnings-merge-driver.js +18 -0
  92. package/dist/core/learnings-merge-driver.js.map +1 -1
  93. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  94. package/dist/core/lisa-owned-hash-ledger.js +136 -0
  95. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  96. package/dist/core/lisa.d.ts.map +1 -1
  97. package/dist/core/lisa.js +9 -1
  98. package/dist/core/lisa.js.map +1 -1
  99. package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -5
  100. package/dist/core/nightly-e2e-guard-behavior-certificate.js +7 -7
  101. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  102. package/dist/core/ownership-header.d.ts +74 -0
  103. package/dist/core/ownership-header.d.ts.map +1 -0
  104. package/dist/core/ownership-header.js +76 -0
  105. package/dist/core/ownership-header.js.map +1 -0
  106. package/dist/core/rails-deploy-production-intent.d.ts +36 -0
  107. package/dist/core/rails-deploy-production-intent.d.ts.map +1 -0
  108. package/dist/core/rails-deploy-production-intent.js +79 -0
  109. package/dist/core/rails-deploy-production-intent.js.map +1 -0
  110. package/dist/core/stale-managed-banner.d.ts +78 -0
  111. package/dist/core/stale-managed-banner.d.ts.map +1 -0
  112. package/dist/core/stale-managed-banner.js +118 -0
  113. package/dist/core/stale-managed-banner.js.map +1 -0
  114. package/dist/core/two-channel-delivery-scan.d.ts +33 -1
  115. package/dist/core/two-channel-delivery-scan.d.ts.map +1 -1
  116. package/dist/core/two-channel-delivery-scan.js +133 -6
  117. package/dist/core/two-channel-delivery-scan.js.map +1 -1
  118. package/dist/core/two-channel-delivery.d.ts +101 -4
  119. package/dist/core/two-channel-delivery.d.ts.map +1 -1
  120. package/dist/core/two-channel-delivery.js +121 -13
  121. package/dist/core/two-channel-delivery.js.map +1 -1
  122. package/dist/core/two-channel-staleness.d.ts +137 -0
  123. package/dist/core/two-channel-staleness.d.ts.map +1 -0
  124. package/dist/core/two-channel-staleness.js +268 -0
  125. package/dist/core/two-channel-staleness.js.map +1 -0
  126. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  127. package/dist/core/upstream-evidence-manifest.js +265 -121
  128. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  129. package/dist/core/workflow-deletion-ownership.d.ts +6 -24
  130. package/dist/core/workflow-deletion-ownership.d.ts.map +1 -1
  131. package/dist/core/workflow-deletion-ownership.js +22 -42
  132. package/dist/core/workflow-deletion-ownership.js.map +1 -1
  133. package/dist/detection/detectors/cdk.d.ts.map +1 -1
  134. package/dist/detection/detectors/cdk.js +2 -1
  135. package/dist/detection/detectors/cdk.js.map +1 -1
  136. package/dist/health/template-inspection.d.ts.map +1 -1
  137. package/dist/health/template-inspection.js +2 -1
  138. package/dist/health/template-inspection.js.map +1 -1
  139. package/dist/migrations/ensure-lisa-postinstall.d.ts +16 -0
  140. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  141. package/dist/migrations/ensure-lisa-postinstall.js +16 -0
  142. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  143. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +2 -2
  144. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -1
  145. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +98 -34
  146. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -1
  147. package/dist/migrations/ensure-playwright-dedicated-caller.d.ts.map +1 -1
  148. package/dist/migrations/ensure-playwright-dedicated-caller.js +1 -0
  149. package/dist/migrations/ensure-playwright-dedicated-caller.js.map +1 -1
  150. package/dist/opencode/hooks-installer.d.ts.map +1 -1
  151. package/dist/opencode/hooks-installer.js +1 -0
  152. package/dist/opencode/hooks-installer.js.map +1 -1
  153. package/dist/opencode/plugin-templates/block-managed-file-edits.sh +78 -15
  154. package/dist/opencode/plugin-templates/block-no-verify.sh +21 -0
  155. package/dist/opencode/plugin-templates/guard-dedupe.bash +337 -0
  156. package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +242 -55
  157. package/dist/opencode/plugin-templates/parity-safety-net.sh +29 -1
  158. package/dist/strategies/package-lisa.d.ts.map +1 -1
  159. package/dist/strategies/package-lisa.js +2 -1
  160. package/dist/strategies/package-lisa.js.map +1 -1
  161. package/dist/utils/path-utils.d.ts +29 -0
  162. package/dist/utils/path-utils.d.ts.map +1 -1
  163. package/dist/utils/path-utils.js +41 -0
  164. package/dist/utils/path-utils.js.map +1 -1
  165. package/expo/create-only/.github/workflows/ci.yml +21 -0
  166. package/expo/create-only/.github/workflows/deploy.yml +28 -0
  167. package/expo/create-only/.github/workflows/maestro-e2e.yml +7 -0
  168. package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +124 -13
  169. package/expo/create-only/.github/workflows/nightly-e2e-health.yml +7 -0
  170. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +7 -0
  171. package/expo/create-only/.github/workflows/nightly-e2e-tracking.yml +8 -0
  172. package/expo/create-only/.github/workflows/playwright-e2e.yml +7 -0
  173. package/harper-fabric/copy-overwrite/.github/workflows/ci.yml +7 -0
  174. package/harper-fabric/create-only/.github/workflows/deploy.yml +14 -0
  175. package/nestjs/create-only/.github/workflows/ci.yml +14 -0
  176. package/nestjs/create-only/.github/workflows/deploy.yml +28 -0
  177. package/package.json +10 -5
  178. package/phaser/copy-overwrite/.github/workflows/ci.yml +7 -0
  179. package/plugins/lisa/.claude-plugin/plugin.json +28 -1
  180. package/plugins/lisa/.codex-plugin/hooks.json +27 -0
  181. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  182. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  183. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +27 -4
  184. package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +1 -1
  185. package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
  186. package/plugins/lisa/.codex-plugin/skills/lisa-github-verify/SKILL.md +14 -0
  187. package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +51 -0
  188. package/plugins/lisa/.codex-plugin/skills/lisa-intake/SKILL.md +1 -1
  189. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +27 -4
  190. package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  191. package/plugins/lisa/.codex-plugin/skills/lisa-jira-verify/SKILL.md +14 -0
  192. package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  193. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +55 -2
  194. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +27 -4
  195. package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +47 -30
  196. package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  197. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  198. package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +14 -0
  199. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +54 -3
  200. package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  201. package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +19 -7
  202. package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +28 -3
  203. package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +28 -3
  204. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +94 -24
  205. package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +32 -2
  206. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +4 -4
  207. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
  208. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  209. package/plugins/lisa/agents/linear-prd-intake.md +8 -8
  210. package/plugins/lisa/hooks/block-blind-automerge.sh +81 -2
  211. package/plugins/lisa/hooks/block-direct-issue-create.agy.sh +64 -16
  212. package/plugins/lisa/hooks/block-direct-issue-create.sh +293 -8
  213. package/plugins/lisa/hooks/block-instruction-file-edits.sh +21 -0
  214. package/plugins/lisa/hooks/block-managed-file-edits.sh +78 -15
  215. package/plugins/lisa/hooks/block-no-verify.sh +21 -0
  216. package/plugins/lisa/hooks/block-shell-json-parsing.sh +21 -0
  217. package/plugins/lisa/hooks/failure-signature-index.mjs +240 -5
  218. package/plugins/lisa/hooks/guard-dedupe.bash +337 -0
  219. package/plugins/lisa/hooks/inject-rules.sh +13 -5
  220. package/plugins/lisa/hooks/operational-hazards.mjs +736 -0
  221. package/plugins/lisa/hooks/operational-hazards.sh +28 -0
  222. package/plugins/lisa/hooks/parity-safety-net.sh +29 -1
  223. package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +102 -21
  224. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +130 -2
  225. package/plugins/lisa/hooks/threshold-ratchet.mjs +63 -13
  226. package/plugins/lisa/hooks/worktree-binding-guard.mjs +183 -1
  227. package/plugins/lisa/hooks/worktree-binding-guard.sh +21 -0
  228. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  229. package/plugins/lisa/rules/eager/operational-hazards.md +50 -0
  230. package/plugins/lisa/rules/eager/tracked-work.md +1 -1
  231. package/plugins/lisa/rules/reference/config-resolution.md +1 -1
  232. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
  233. package/plugins/lisa/rules/reference/operational-hazards.md +115 -0
  234. package/plugins/lisa/rules/reference/work-item-trailer-definition.md +129 -0
  235. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +407 -26
  236. package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +507 -0
  237. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  238. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +27 -4
  239. package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +1 -1
  240. package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
  241. package/plugins/lisa/skills/lisa-github-verify/SKILL.md +14 -0
  242. package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +51 -0
  243. package/plugins/lisa/skills/lisa-intake/SKILL.md +1 -1
  244. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +27 -4
  245. package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  246. package/plugins/lisa/skills/lisa-jira-verify/SKILL.md +14 -0
  247. package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  248. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +55 -2
  249. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +27 -4
  250. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  251. package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  252. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  253. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +14 -0
  254. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +54 -3
  255. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  256. package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +19 -7
  257. package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +28 -3
  258. package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +28 -3
  259. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +94 -24
  260. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +32 -2
  261. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +5 -5
  262. package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
  263. package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  264. package/plugins/lisa-agy/agents/linear-prd-intake.md +8 -8
  265. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +81 -2
  266. package/plugins/lisa-agy/hooks/block-direct-issue-create.agy.sh +64 -16
  267. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +293 -8
  268. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +21 -0
  269. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +78 -15
  270. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +21 -0
  271. package/plugins/lisa-agy/hooks/guard-dedupe.bash +337 -0
  272. package/plugins/lisa-agy/hooks/parity-safety-net.sh +29 -1
  273. package/plugins/lisa-agy/hooks.json +1 -1
  274. package/plugins/lisa-agy/plugin.json +1 -1
  275. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +407 -26
  276. package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +507 -0
  277. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  278. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +27 -4
  279. package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +1 -1
  280. package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
  281. package/plugins/lisa-agy/skills/lisa-github-verify/SKILL.md +14 -0
  282. package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +51 -0
  283. package/plugins/lisa-agy/skills/lisa-intake/SKILL.md +1 -1
  284. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +27 -4
  285. package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  286. package/plugins/lisa-agy/skills/lisa-jira-verify/SKILL.md +14 -0
  287. package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  288. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +55 -2
  289. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +27 -4
  290. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  291. package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  292. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  293. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +14 -0
  294. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +54 -3
  295. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  296. package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +19 -7
  297. package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +28 -3
  298. package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +28 -3
  299. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +94 -24
  300. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +32 -2
  301. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +5 -5
  302. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
  303. package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  304. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  305. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  306. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  307. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  308. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  309. package/plugins/lisa-copilot/.claude-plugin/plugin.json +19 -1
  310. package/plugins/lisa-copilot/agents/linear-prd-intake.agent.md +8 -8
  311. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +81 -2
  312. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +293 -8
  313. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +21 -0
  314. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +78 -15
  315. package/plugins/lisa-copilot/hooks/block-no-verify.sh +21 -0
  316. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +21 -0
  317. package/plugins/lisa-copilot/hooks/failure-signature-index.mjs +240 -5
  318. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +337 -0
  319. package/plugins/lisa-copilot/hooks/inject-rules.sh +13 -5
  320. package/plugins/lisa-copilot/hooks/operational-hazards.mjs +736 -0
  321. package/plugins/lisa-copilot/hooks/operational-hazards.sh +28 -0
  322. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +29 -1
  323. package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +102 -21
  324. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +130 -2
  325. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +63 -13
  326. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +183 -1
  327. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +21 -0
  328. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  329. package/plugins/lisa-copilot/rules/eager/operational-hazards.md +50 -0
  330. package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
  331. package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
  332. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  333. package/plugins/lisa-copilot/rules/reference/operational-hazards.md +115 -0
  334. package/plugins/lisa-copilot/rules/reference/work-item-trailer-definition.md +129 -0
  335. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +407 -26
  336. package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +507 -0
  337. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  338. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +27 -4
  339. package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +1 -1
  340. package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
  341. package/plugins/lisa-copilot/skills/lisa-github-verify/SKILL.md +14 -0
  342. package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +51 -0
  343. package/plugins/lisa-copilot/skills/lisa-intake/SKILL.md +1 -1
  344. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +27 -4
  345. package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  346. package/plugins/lisa-copilot/skills/lisa-jira-verify/SKILL.md +14 -0
  347. package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  348. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +55 -2
  349. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +27 -4
  350. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  351. package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  352. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  353. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +14 -0
  354. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +54 -3
  355. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  356. package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +19 -7
  357. package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +28 -3
  358. package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +28 -3
  359. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +94 -24
  360. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +32 -2
  361. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +5 -5
  362. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
  363. package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  364. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  365. package/plugins/lisa-cursor/agents/linear-prd-intake.md +8 -8
  366. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +81 -2
  367. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +293 -8
  368. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +21 -0
  369. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +78 -15
  370. package/plugins/lisa-cursor/hooks/block-no-verify.sh +21 -0
  371. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +21 -0
  372. package/plugins/lisa-cursor/hooks/failure-signature-index.mjs +240 -5
  373. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +337 -0
  374. package/plugins/lisa-cursor/hooks/hooks.json +10 -0
  375. package/plugins/lisa-cursor/hooks/operational-hazards.mjs +736 -0
  376. package/plugins/lisa-cursor/hooks/operational-hazards.sh +28 -0
  377. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +29 -1
  378. package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +102 -21
  379. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +130 -2
  380. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +63 -13
  381. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +183 -1
  382. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +21 -0
  383. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  384. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
  385. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  386. package/plugins/lisa-cursor/rules/operational-hazards-reference.mdc +120 -0
  387. package/plugins/lisa-cursor/rules/operational-hazards.mdc +55 -0
  388. package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
  389. package/plugins/lisa-cursor/rules/work-item-trailer-definition-reference.mdc +134 -0
  390. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +407 -26
  391. package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +507 -0
  392. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  393. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +27 -4
  394. package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +1 -1
  395. package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
  396. package/plugins/lisa-cursor/skills/lisa-github-verify/SKILL.md +14 -0
  397. package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +51 -0
  398. package/plugins/lisa-cursor/skills/lisa-intake/SKILL.md +1 -1
  399. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +27 -4
  400. package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  401. package/plugins/lisa-cursor/skills/lisa-jira-verify/SKILL.md +14 -0
  402. package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  403. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +55 -2
  404. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +27 -4
  405. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  406. package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  407. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  408. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +14 -0
  409. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +54 -3
  410. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  411. package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +19 -7
  412. package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +28 -3
  413. package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +28 -3
  414. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +94 -24
  415. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +32 -2
  416. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +5 -5
  417. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
  418. package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  419. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  420. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  421. package/plugins/lisa-expo-agy/plugin.json +1 -1
  422. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  423. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  424. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  425. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  426. package/plugins/lisa-harper-fabric/hooks/inject-rules.sh +14 -3
  427. package/plugins/lisa-harper-fabric/rules/eager/harper-fabric.md +31 -0
  428. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  429. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  430. package/plugins/lisa-harper-fabric-copilot/hooks/inject-rules.sh +14 -3
  431. package/plugins/lisa-harper-fabric-copilot/rules/eager/harper-fabric.md +31 -0
  432. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  433. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric-reference.mdc +57 -0
  434. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +32 -53
  435. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  436. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  437. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  438. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  439. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  440. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  441. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  442. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  443. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  444. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  445. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  446. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  447. package/plugins/lisa-phaser/hooks/inject-rules.sh +14 -3
  448. package/plugins/lisa-phaser/rules/eager/phaser.md +44 -0
  449. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  450. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  451. package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +14 -3
  452. package/plugins/lisa-phaser-copilot/rules/eager/phaser.md +44 -0
  453. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  454. package/plugins/lisa-phaser-cursor/rules/phaser-reference.mdc +189 -0
  455. package/plugins/lisa-phaser-cursor/rules/phaser.mdc +45 -185
  456. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  457. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  458. package/plugins/lisa-rails/hooks/inject-rules.sh +14 -4
  459. package/plugins/lisa-rails/rules/eager/rails-conventions.md +23 -0
  460. package/plugins/lisa-rails-agy/plugin.json +1 -1
  461. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  462. package/plugins/lisa-rails-copilot/hooks/inject-rules.sh +14 -4
  463. package/plugins/lisa-rails-copilot/rules/eager/rails-conventions.md +23 -0
  464. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  465. package/plugins/lisa-rails-cursor/rules/rails-conventions-reference.mdc +181 -0
  466. package/plugins/lisa-rails-cursor/rules/rails-conventions.mdc +24 -177
  467. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  468. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  469. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  470. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  471. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  472. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  473. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  474. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  475. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  476. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  477. package/plugins/materialized-artifacts.json +1 -0
  478. package/plugins/src/base/.claude-plugin/plugin.json +27 -0
  479. package/plugins/src/base/agents/linear-prd-intake.md +8 -8
  480. package/plugins/src/base/hooks/block-blind-automerge.sh +81 -2
  481. package/plugins/src/base/hooks/block-direct-issue-create.agy.sh +64 -16
  482. package/plugins/src/base/hooks/block-direct-issue-create.sh +293 -8
  483. package/plugins/src/base/hooks/block-instruction-file-edits.sh +21 -0
  484. package/plugins/src/base/hooks/block-managed-file-edits.sh +78 -15
  485. package/plugins/src/base/hooks/block-no-verify.sh +21 -0
  486. package/plugins/src/base/hooks/block-shell-json-parsing.sh +21 -0
  487. package/plugins/src/base/hooks/failure-signature-index.mjs +240 -5
  488. package/plugins/src/base/hooks/guard-dedupe.bash +337 -0
  489. package/plugins/src/base/hooks/inject-rules.sh +13 -5
  490. package/plugins/src/base/hooks/operational-hazards.mjs +736 -0
  491. package/plugins/src/base/hooks/operational-hazards.sh +28 -0
  492. package/plugins/src/base/hooks/parity-safety-net.sh +29 -1
  493. package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +102 -21
  494. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +130 -2
  495. package/plugins/src/base/hooks/threshold-ratchet.mjs +63 -13
  496. package/plugins/src/base/hooks/worktree-binding-guard.mjs +183 -1
  497. package/plugins/src/base/hooks/worktree-binding-guard.sh +21 -0
  498. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  499. package/plugins/src/base/rules/eager/operational-hazards.md +50 -0
  500. package/plugins/src/base/rules/eager/tracked-work.md +1 -1
  501. package/plugins/src/base/rules/reference/config-resolution.md +1 -1
  502. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  503. package/plugins/src/base/rules/reference/operational-hazards.md +115 -0
  504. package/plugins/src/base/rules/reference/work-item-trailer-definition.md +129 -0
  505. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +407 -26
  506. package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +507 -0
  507. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  508. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +27 -4
  509. package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +1 -1
  510. package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
  511. package/plugins/src/base/skills/lisa-github-verify/SKILL.md +14 -0
  512. package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +51 -0
  513. package/plugins/src/base/skills/lisa-intake/SKILL.md +1 -1
  514. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +27 -4
  515. package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  516. package/plugins/src/base/skills/lisa-jira-verify/SKILL.md +14 -0
  517. package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  518. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +55 -2
  519. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +27 -4
  520. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  521. package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  522. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  523. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +14 -0
  524. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +54 -3
  525. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  526. package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +19 -7
  527. package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +28 -3
  528. package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +28 -3
  529. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +94 -24
  530. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +32 -2
  531. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +5 -5
  532. package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
  533. package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  534. package/plugins/src/harper-fabric/hooks/inject-rules.sh +14 -3
  535. package/plugins/src/harper-fabric/rules/eager/harper-fabric.md +31 -0
  536. package/plugins/src/phaser/hooks/inject-rules.sh +14 -3
  537. package/plugins/src/phaser/rules/eager/phaser.md +44 -0
  538. package/plugins/src/rails/hooks/inject-rules.sh +14 -4
  539. package/plugins/src/rails/rules/eager/rails-conventions.md +23 -0
  540. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  541. package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  542. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  543. package/rails/create-only/.github/workflows/ci.yml +8 -0
  544. package/rails/create-only/.github/workflows/deploy.yml +22 -0
  545. package/scripts/build-plugins.sh +2 -1
  546. package/scripts/check-engine-floor.mjs +361 -0
  547. package/scripts/check-guard-parity-notes.mjs +709 -0
  548. package/scripts/check-template-workflow-refs.mjs +9 -0
  549. package/scripts/check-third-party-action-pins.mjs +17 -1
  550. package/scripts/check-workflow-contract-assertions.mjs +480 -0
  551. package/scripts/copy-opencode-plugin-templates.mjs +1 -0
  552. package/scripts/deployed-guard-advice.mjs +636 -0
  553. package/scripts/generate-agy-plugin-artifacts.mjs +35 -10
  554. package/scripts/generate-export-surface.mjs +105 -19
  555. package/scripts/generate-two-channel-couplings.ts +143 -212
  556. package/scripts/lisa-enforcement-fallback.sh +31 -0
  557. package/scripts/measure-scripts-profile-gap.mts +361 -0
  558. package/scripts/mutation-performance-measure.mjs +21 -5
  559. package/scripts/reconcile-release-tags.mjs +65 -84
  560. package/scripts/two-channel-couplings.json +302 -70
  561. package/scripts/workflow-contract-assertion.sh +47 -0
  562. package/typescript/copy-overwrite/.lintstagedrc.json +1 -0
  563. package/typescript/copy-overwrite/eslint.ignore.config.json +1 -0
  564. package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +527 -53
  565. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +301 -13
  566. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  567. package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  568. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  569. package/typescript/create-only/.github/workflows/ci.yml +7 -0
  570. /package/plugins/lisa-harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  571. /package/plugins/lisa-harper-fabric-copilot/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  572. /package/plugins/lisa-phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  573. /package/plugins/lisa-phaser-copilot/rules/{phaser.md → reference/phaser.md} +0 -0
  574. /package/plugins/lisa-rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  575. /package/plugins/lisa-rails-copilot/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  576. /package/plugins/src/harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  577. /package/plugins/src/phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  578. /package/plugins/src/rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Measure how much weaker the `scripts/` rule profile is than the profile the
3
+ * same file would meet anywhere else, and what that difference costs in
4
+ * findings.
5
+ *
6
+ * WHY THIS EXISTS. `getScriptsFilesOverride()` in `src/configs/eslint/base.ts`
7
+ * relaxes a set of rules for every path matching `scripts/**` or
8
+ * `**\/scripts/**`. Each relaxation is argued in that file and several are
9
+ * plainly right. What nobody could state was the SIZE of the thing: a file
10
+ * written in `scripts/` can carry a defect of a class the wider profile catches
11
+ * and never be told, because in `scripts/` it is not a violation
12
+ * (CodySwannGT/lisa#3773). That is a different failure from a control that did
13
+ * not fire — the control ran, was correct, and its SCOPE excluded the file.
14
+ * The worked example was a `sonarjs/slow-regex` finding that sat unreported in
15
+ * `scripts/` from the day the file was written until the day it was moved into
16
+ * a shipped tree, where a stricter assertion saw it immediately.
17
+ *
18
+ * THIS REPORTS, IT DOES NOT GATE. The exit status is 0 whenever the measurement
19
+ * was made, whatever it found. Aligning the profiles is a policy decision with
20
+ * a large blast radius, and this exists to put a number in front of that
21
+ * decision rather than to take it. The established local shape is
22
+ * `check-engine-floor.mjs`, which prints its strictness backlog and gates on
23
+ * something else entirely.
24
+ *
25
+ * THE DIVERGENT SET IS DERIVED, NEVER LISTED. A hardcoded copy of the
26
+ * override's rule list is a second source of truth that goes stale the first
27
+ * time somebody edits the override. So the set is computed by asking ESLint
28
+ * itself, twice, for the config it resolves for the SAME file extension in two
29
+ * locations — one inside `scripts/`, one outside it — and taking the severity
30
+ * difference. Add a suppression to the override and this measurement grows a
31
+ * row with nobody remembering to update it; delete one and the row disappears.
32
+ *
33
+ * IT ASKS THE REPOSITORY'S OWN CONFIG, not a reconstruction of it. `eslint.
34
+ * config.ts` is imported and used as-is, so `eslint.config.local.ts` — which
35
+ * re-disables one rule for `scripts/**` that a project-local block had silently
36
+ * re-enabled — is part of the answer. A reconstruction assembled from the
37
+ * factory would have missed that, and missing it is the same class of error the
38
+ * measurement is about.
39
+ *
40
+ * WHY THE NODE API RATHER THAN THE `eslint` CLI. Measured on a developer
41
+ * checkout carrying nested worktrees, `npx eslint --format json <90 files>`
42
+ * exhausts an 8 GB heap and returns nothing, because `tsconfig.eslint.json`
43
+ * includes `**\/*.ts` and the typed program grows without bound. The in-process
44
+ * API over the same file set completes. A measurement that cannot run where the
45
+ * code lives is not a measurement, so the slower, working route is the right
46
+ * one; expect several minutes.
47
+ *
48
+ * THE SUBJECT IS THE GIT INDEX, not the disk, for the reason
49
+ * `tests/helpers/shipped-mjs-roster.ts` records at length: untracked scratch
50
+ * files under `scripts/` belong to whoever made them and to no commit, and
51
+ * counting them makes one agent's working tree change another agent's number.
52
+ *
53
+ * IT REFUSES RATHER THAN REPORTING A ZERO IT DID NOT EARN. A config that
54
+ * resolved no rules and an empty subject set both produce "no findings", which
55
+ * is the same shape as a clean tree. Each exits non-zero with the denial first,
56
+ * per `scripts/lib/dependency-tree.mjs`.
57
+ *
58
+ * `--root` exists so the suite can point this at a fixture and prove the
59
+ * derivation follows the config rather than a constant in this file.
60
+ * @module scripts/measure-scripts-profile-gap
61
+ */
62
+ import { execFileSync } from "node:child_process";
63
+ import { existsSync } from "node:fs";
64
+ import * as path from "node:path";
65
+ import process from "node:process";
66
+ import { fileURLToPath } from "node:url";
67
+
68
+ import { ESLint } from "eslint";
69
+
70
+ import { cannotMeasure } from "./lib/dependency-tree.mjs";
71
+ import { invokedAsScript } from "./lib/invoked-as-script.mjs";
72
+
73
+ const DEFAULT_ROOT = path.resolve(
74
+ path.dirname(fileURLToPath(import.meta.url)),
75
+ ".."
76
+ );
77
+
78
+ /** How this measurement names itself in its report and in its refusals. */
79
+ const REPORT = "scripts-profile-gap";
80
+
81
+ /**
82
+ * The outside-`scripts/` probe path whose resolved config is differenced.
83
+ *
84
+ * Both probes are `.mjs` so that the `**\/*.js` / `**\/*.mjs` override applies
85
+ * to each and cancels: what survives the difference is the `scripts/` override
86
+ * alone, not the JavaScript-versus-TypeScript profile. Neither file exists, and
87
+ * neither needs to — ESLint answers for a path.
88
+ */
89
+ const PROBE_OUTSIDE = "__scripts-profile-gap-probe__.mjs";
90
+
91
+ /** The inside-`scripts/` half of {@link PROBE_OUTSIDE}. */
92
+ const PROBE_INSIDE = "scripts/__scripts-profile-gap-probe__.mjs";
93
+
94
+ /** Extensions ESLint has an opinion about in this tree. */
95
+ const LINTABLE: readonly string[] = [".mjs", ".mts", ".ts", ".js"];
96
+
97
+ /** Severity names, indexed by the numeric form ESLint resolves rules to. */
98
+ const SEVERITY: readonly string[] = ["off", "warn", "error"];
99
+
100
+ /** One rule whose severity differs between the two probe locations. */
101
+ export type Divergence = {
102
+ readonly rule: string;
103
+ readonly outside: string;
104
+ readonly inside: string;
105
+ };
106
+
107
+ /** The two-directional difference between the probes, and what was compared. */
108
+ export type Divergences = {
109
+ readonly compared: number;
110
+ readonly weaker: readonly Divergence[];
111
+ readonly stricter: readonly Divergence[];
112
+ };
113
+
114
+ /**
115
+ * Normalise one rule entry to its severity name.
116
+ * @param entry - A rule value: a severity, or `[severity, ...options]`.
117
+ * @returns `"off"`, `"warn"`, or `"error"`.
118
+ */
119
+ export function severityOf(entry: unknown): string {
120
+ const head = Array.isArray(entry) ? entry[0] : entry;
121
+ if (typeof head === "number") return SEVERITY[head] ?? "off";
122
+ return typeof head === "string" && SEVERITY.includes(head) ? head : "off";
123
+ }
124
+
125
+ /**
126
+ * The rules whose severity differs between two resolved configs.
127
+ *
128
+ * Reported in BOTH directions on purpose. "`scripts/` is weaker" is the claim
129
+ * under examination, and a measurement that could only ever confirm it would be
130
+ * worth nothing. This repository has at least one rule the `scripts/` profile
131
+ * promotes ABOVE the surrounding code, and a report unable to show that is a
132
+ * report nobody should trust about the other direction either.
133
+ * @param outside - Rules resolved for a path outside `scripts/`.
134
+ * @param inside - Rules resolved for the same extension inside `scripts/`.
135
+ * @returns The difference, each side sorted by rule name.
136
+ */
137
+ export function divergentRules(
138
+ outside: Readonly<Record<string, unknown>>,
139
+ inside: Readonly<Record<string, unknown>>
140
+ ): Divergences {
141
+ const names = [...new Set([...Object.keys(outside), ...Object.keys(inside)])];
142
+ const rows = names
143
+ .map(rule => ({
144
+ rule,
145
+ outside: severityOf(outside[rule]),
146
+ inside: severityOf(inside[rule]),
147
+ }))
148
+ .filter(row => row.outside !== row.inside)
149
+ .sort((left, right) => (left.rule < right.rule ? -1 : 1));
150
+ const shift = (row: Divergence): number =>
151
+ SEVERITY.indexOf(row.inside) - SEVERITY.indexOf(row.outside);
152
+ return {
153
+ compared: names.length,
154
+ weaker: rows.filter(row => shift(row) < 0),
155
+ stricter: rows.filter(row => shift(row) > 0),
156
+ };
157
+ }
158
+
159
+ /**
160
+ * The lintable files git tracks under `scripts/`, that also exist on disk.
161
+ *
162
+ * A path staged for deletion is still listed by `git ls-files`, and handing
163
+ * ESLint a path with no file behind it turns a clean tree into an error about
164
+ * the harness.
165
+ * @param root - Repository root.
166
+ * @returns Repo-relative paths.
167
+ */
168
+ export function trackedScripts(root: string): readonly string[] {
169
+ const listed = execFileSync("git", ["ls-files", "scripts"], {
170
+ cwd: root,
171
+ encoding: "utf-8",
172
+ maxBuffer: 64 * 1024 * 1024,
173
+ });
174
+ return listed
175
+ .split("\n")
176
+ .map(line => line.trim())
177
+ .filter(line => LINTABLE.includes(path.extname(line)))
178
+ .filter(line => existsSync(path.join(root, line)));
179
+ }
180
+
181
+ /** A finding tally, by rule and by file. */
182
+ export type Tally = {
183
+ readonly byRule: ReadonlyMap<string, number>;
184
+ readonly touched: ReadonlySet<string>;
185
+ readonly total: number;
186
+ };
187
+
188
+ /**
189
+ * Count findings attributable to a restored rule set.
190
+ *
191
+ * Only messages whose rule is in {@link restored} are counted. Anything else
192
+ * ESLint reports is the tree's existing state under the profile as it stands,
193
+ * which is not what this measures.
194
+ * @param root - Repository root.
195
+ * @param results - ESLint results over the subject files.
196
+ * @param restored - The rule ids that were re-armed.
197
+ * @returns The tally.
198
+ */
199
+ export function tally(
200
+ root: string,
201
+ results: readonly ESLint.LintResult[],
202
+ restored: ReadonlySet<string>
203
+ ): Tally {
204
+ const byRule = new Map<string, number>();
205
+ const touched = new Set<string>();
206
+ for (const result of results) {
207
+ for (const message of result.messages) {
208
+ const id = message.ruleId ?? "";
209
+ if (!restored.has(id)) continue;
210
+ byRule.set(id, (byRule.get(id) ?? 0) + 1);
211
+ touched.add(path.relative(root, result.filePath));
212
+ }
213
+ }
214
+ let total = 0;
215
+ for (const count of byRule.values()) total += count;
216
+ return { byRule, touched, total };
217
+ }
218
+
219
+ /**
220
+ * Build the refusal for a measurement that could not be made.
221
+ * @param because - Why the measurement did not happen.
222
+ * @param remedy - What the reader should do.
223
+ * @returns The four-clause refusal text.
224
+ */
225
+ function refusal(because: string, remedy: string): string {
226
+ return cannotMeasure({
227
+ gate: REPORT,
228
+ denial: "that the scripts profile costs nothing",
229
+ because,
230
+ remedy,
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Print the report and return an exit status.
236
+ * @param root - Repository root.
237
+ * @returns `0` when the measurement was made, `1` when it was not.
238
+ */
239
+ export async function main(root: string): Promise<number> {
240
+ const { default: config } = (await import(
241
+ path.join(root, "eslint.config.ts")
242
+ )) as { default: unknown };
243
+ const eslint = new ESLint({
244
+ cwd: root,
245
+ overrideConfigFile: true,
246
+ overrideConfig: config as ESLint.Options["overrideConfig"],
247
+ });
248
+
249
+ const outside = await eslint.calculateConfigForFile(
250
+ path.join(root, PROBE_OUTSIDE)
251
+ );
252
+ const inside = await eslint.calculateConfigForFile(
253
+ path.join(root, PROBE_INSIDE)
254
+ );
255
+ const { weaker, stricter, compared } = divergentRules(
256
+ (outside?.rules ?? {}) as Record<string, unknown>,
257
+ (inside?.rules ?? {}) as Record<string, unknown>
258
+ );
259
+ if (compared === 0) {
260
+ console.error(
261
+ refusal(
262
+ "both probe paths resolved to zero rules, which is the same shape as " +
263
+ "two identical profiles and is not evidence of one.",
264
+ "Check that the ESLint config loads at all before reading this report."
265
+ )
266
+ );
267
+ return 1;
268
+ }
269
+
270
+ const subject = trackedScripts(root);
271
+ if (subject.length === 0) {
272
+ console.error(
273
+ refusal(
274
+ "the git index lists no lintable file under `scripts/`, so a finding " +
275
+ "count of zero would be an empty subject rather than a clean tree.",
276
+ "Run this from a checkout whose index carries the `scripts/` tree."
277
+ )
278
+ );
279
+ return 1;
280
+ }
281
+
282
+ console.log(
283
+ `${REPORT}: ${compared} rule(s) resolved for both probes. ` +
284
+ `${weaker.length} are WEAKER inside scripts/, ${stricter.length} ` +
285
+ `STRICTER. Subject: ${subject.length} tracked lintable file(s).`
286
+ );
287
+ for (const row of stricter) {
288
+ console.log(` stricter ${row.rule}: ${row.outside} → ${row.inside}`);
289
+ }
290
+ if (weaker.length === 0) {
291
+ console.log(
292
+ "✅ No rule is weaker inside scripts/ than outside it — the two " +
293
+ "profiles carry the same bar."
294
+ );
295
+ return 0;
296
+ }
297
+
298
+ const restored = new Set(weaker.map(row => row.rule));
299
+ const armed = new ESLint({
300
+ cwd: root,
301
+ overrideConfigFile: true,
302
+ overrideConfig: [
303
+ ...(config as readonly unknown[]),
304
+ {
305
+ files: ["**/*"],
306
+ rules: Object.fromEntries(weaker.map(row => [row.rule, row.outside])),
307
+ },
308
+ ] as ESLint.Options["overrideConfig"],
309
+ });
310
+ const found = tally(root, await armed.lintFiles([...subject]), restored);
311
+
312
+ console.log("");
313
+ console.log(
314
+ `Measured backlog (NOT gated): restoring all ${weaker.length} weaker ` +
315
+ `rule(s) reports ${found.total} finding(s) across ` +
316
+ `${found.touched.size} of ${subject.length} file(s).`
317
+ );
318
+ const rows = [...weaker].sort(
319
+ (left, right) =>
320
+ (found.byRule.get(right.rule) ?? 0) - (found.byRule.get(left.rule) ?? 0)
321
+ );
322
+ for (const row of rows) {
323
+ const count = found.byRule.get(row.rule) ?? 0;
324
+ console.log(
325
+ ` ${count}\t${row.rule} (${row.inside} here, ${row.outside} elsewhere)`
326
+ );
327
+ }
328
+ console.log("");
329
+ console.log(
330
+ "A rule reporting 0 is a suppression this tree does not currently need. " +
331
+ "A rule reporting more than 0"
332
+ );
333
+ console.log(
334
+ "is a class of finding that exists in `scripts/` and is not reported " +
335
+ "there. Neither is a failure; see"
336
+ );
337
+ console.log(
338
+ "CodySwannGT/lisa#3773 for the decision these numbers exist to inform."
339
+ );
340
+ return 0;
341
+ }
342
+
343
+ /**
344
+ * Resolve the root from `--root <dir>`, defaulting to the repository.
345
+ * @param argv - Arguments after the script name.
346
+ * @returns The absolute root.
347
+ */
348
+ export function rootFrom(argv: readonly string[]): string {
349
+ const flag = argv.indexOf("--root");
350
+ if (flag === -1) return DEFAULT_ROOT;
351
+ const value = argv[flag + 1];
352
+ if (value === undefined) {
353
+ console.error(`${REPORT}: --root requires a value`);
354
+ process.exit(2);
355
+ }
356
+ return path.resolve(value);
357
+ }
358
+
359
+ if (invokedAsScript(import.meta.url)) {
360
+ process.exit(await main(rootFrom(process.argv.slice(2))));
361
+ }
@@ -181,6 +181,25 @@ export function overlayMeasurementReporters(source, reporterPath, reportPath) {
181
181
  };
182
182
  }
183
183
 
184
+ /**
185
+ * Whether a resolved path is a root itself or sits beneath it.
186
+ *
187
+ * The separator is the whole point (CodySwannGT/lisa#3808): a bare
188
+ * `candidate.startsWith(root)` answers "inside" for `<root>-sibling`, because
189
+ * the sibling's name string-prefixes the root's. That is the fail-OPEN
190
+ * direction for a containment guard, and this function is used where the
191
+ * answer decides whether an escape is permitted.
192
+ *
193
+ * Both arguments must already be absolute and resolved; this is a boundary
194
+ * comparison, not a normaliser.
195
+ * @param {string} root - Absolute, resolved containing directory
196
+ * @param {string} candidate - Absolute, resolved path being tested
197
+ * @returns {boolean} True when the candidate is the root or lives beneath it
198
+ */
199
+ function isWithinRoot(root, candidate) {
200
+ return candidate === root || candidate.startsWith(`${root}${path.sep}`);
201
+ }
202
+
184
203
  /** Resolve an artifact path beneath its root and refuse links/escapes. */
185
204
  export function resolveArtifactPath(root, relative) {
186
205
  if (!relative || path.isAbsolute(relative)) {
@@ -188,16 +207,13 @@ export function resolveArtifactPath(root, relative) {
188
207
  }
189
208
  const resolvedRoot = path.resolve(root);
190
209
  const resolved = path.resolve(resolvedRoot, relative);
191
- if (
192
- resolved !== resolvedRoot &&
193
- !resolved.startsWith(`${resolvedRoot}${path.sep}`)
194
- ) {
210
+ if (!isWithinRoot(resolvedRoot, resolved)) {
195
211
  throw new Error("artifact path is outside its root");
196
212
  }
197
213
  const parent = path.dirname(resolved);
198
214
  if (
199
215
  existsSync(parent) &&
200
- !realpathSync(parent).startsWith(realpathSync(resolvedRoot))
216
+ !isWithinRoot(realpathSync(resolvedRoot), realpathSync(parent))
201
217
  ) {
202
218
  throw new Error("artifact parent resolves outside its root");
203
219
  }
@@ -71,9 +71,28 @@
71
71
  * no evidence and return `unprovable` — the verdict that means "do not act".
72
72
  * Use `attempts/<n>/jobs` or `?filter=all`.
73
73
  *
74
+ * ## One probe, not two (issue #3804)
75
+ *
76
+ * The question "did this exact version reach the registry?" has ONE
77
+ * implementation, `verifyPublish` in `check-npm-publish-landed.mjs`, and this
78
+ * sweep calls it. It used to carry its own URL builder, its own fetch and its
79
+ * own body check, written in parallel with that module because the module was
80
+ * unmerged at the time. Two of them agreed only because two people had aligned
81
+ * the semantics by hand — including one real divergence caught in review, where
82
+ * this file accepted any HTTP 200 while the module also required the response
83
+ * body to name the version asked for. Both surfaces reporting `published` about
84
+ * one release have to mean the same thing by it, and that now rests on there
85
+ * being one implementation rather than on an agreement.
86
+ *
87
+ * WHAT DID NOT CONVERGE, DELIBERATELY: the retry policy and the exit code. See
88
+ * `probeVersion` for the first; the second is simply that `main` here returns
89
+ * nothing and the module's exit-1 CLI is never imported.
90
+ *
74
91
  * @module scripts/reconcile-release-tags
75
92
  */
76
93
 
94
+ import { verifyPublish } from "../all/copy-overwrite/scripts/check-npm-publish-landed.mjs";
95
+
77
96
  import { invokedAsScript } from "./lib/invoked-as-script.mjs";
78
97
 
79
98
  /**
@@ -109,21 +128,27 @@ export function parseVersionTag(tag) {
109
128
  }
110
129
 
111
130
  /**
112
- * What a single registry probe establishes.
131
+ * What a single registry probe establishes, in this sweep's vocabulary.
132
+ *
133
+ * The probe itself no longer lives here — `verifyPublish` decides which of the
134
+ * three words an HTTP answer earns, and it already applies the rule this
135
+ * function used to apply: only a definitive 404 on the EXACT-VERSION endpoint
136
+ * proves absence, while a 200 naming a different version, an auth failure, a
137
+ * rate limit, a timeout and any transport error all establish nothing.
113
138
  *
114
- * Only a definitive 404 on the EXACT-VERSION endpoint proves absence. Every
115
- * other outcome including a 200 that is not the version asked for, an auth
116
- * failure, a rate limit and any transport errorestablishes nothing about
117
- * whether the release happened, and says so rather than guessing.
118
- * @param {object} probe The probe result.
119
- * @param {number|null} [probe.status] HTTP status, or null when none was received.
120
- * @param {string|null} [probe.error] Transport-level failure, if any.
139
+ * This is kept as the sweep's own boundary rather than dropped as a
140
+ * pass-through. It is total: ANY outcome it does not recognise a fourth
141
+ * verdict added upstream, a malformed result, no result at allis read as
142
+ * `unprovable`. That default is the safe direction, because the damage this
143
+ * sweep can do is reporting a good release as absent, and an unrecognised word
144
+ * falling through to `missing` is exactly how that would happen.
145
+ * @param {object} outcome The result of one `verifyPublish` call.
146
+ * @param {string|null} [outcome.verdict] The verdict that call settled on.
121
147
  * @returns {string} One of the `VERDICT` values.
122
148
  */
123
- export function classifyProbe({ status = null, error = null } = {}) {
124
- if (error) return VERDICT.UNPROVABLE;
125
- if (status === 200) return VERDICT.PUBLISHED;
126
- if (status === 404) return VERDICT.MISSING;
149
+ export function classifyProbe({ verdict = null } = {}) {
150
+ if (verdict === VERDICT.PUBLISHED) return VERDICT.PUBLISHED;
151
+ if (verdict === VERDICT.MISSING) return VERDICT.MISSING;
127
152
  return VERDICT.UNPROVABLE;
128
153
  }
129
154
 
@@ -153,92 +178,48 @@ export function reconcile({ tags, probe }) {
153
178
  }
154
179
 
155
180
  /**
156
- * The exact-version registry endpoint for one version.
157
- *
158
- * EXACT VERSION, never `dist-tags.latest`. `latest` lags a successful publish
159
- * by minutes, so reconciling against it reports a release that shipped moments
160
- * ago as absent — a false `missing` on the newest and most-scrutinised release.
161
- * @param {string} pkg The package name.
162
- * @param {string} version The exact version.
163
- * @returns {string} The URL to probe.
164
- */
165
- export function registryUrl(pkg, version) {
166
- return `https://registry.npmjs.org/${pkg.replace("/", "%2f")}/${version}`;
167
- }
168
-
169
- /**
170
- * Probe the registry for one version, converting every failure into a shape
171
- * `classifyProbe` can read rather than throwing.
172
- * @param {string} pkg The package name.
173
- * @param {string} version The exact version.
174
- * @param {typeof fetch} [fetchImpl] Injected for tests.
175
- * @returns {Promise<{status: number|null, error: string|null}>} The probe result.
176
- */
177
- export async function probeRegistry(pkg, version, fetchImpl = fetch) {
178
- try {
179
- const response = await fetchImpl(registryUrl(pkg, version), {
180
- method: "GET",
181
- });
182
- if (response.status !== 200)
183
- return { status: response.status, error: null };
184
- // A 200 IS NOT ENOUGH — the body has to name the version that was asked
185
- // for. This matches the release-time check (#3684) exactly: a 200 whose
186
- // body names something else, or which does not parse, settles nothing and
187
- // is `unprovable` rather than `published`. Two surfaces reporting the same
188
- // word about one release have to mean the same thing by it, or comparing
189
- // their reports is meaningless.
190
- let body;
191
- try {
192
- body = await response.json();
193
- } catch (error) {
194
- return {
195
- status: null,
196
- error: `unparseable body: ${String(error?.message ?? error)}`,
197
- };
198
- }
199
- if (body?.version !== version)
200
- return {
201
- status: null,
202
- error: `registry served version ${String(body?.version)} for ${version}`,
203
- };
204
- return { status: 200, error: null };
205
- } catch (error) {
206
- // A transport failure is NOT evidence of absence. Returning it as an error
207
- // is what makes `classifyProbe` answer `unprovable` instead of `missing`.
208
- return { status: null, error: String(error?.message ?? error) };
209
- }
210
- }
211
-
212
- /**
213
- * Probe a version, retrying ONLY while the answer is `unprovable`.
181
+ * Probe a version through the shared implementation, retrying ONLY while the
182
+ * answer is `unprovable`.
214
183
  *
215
- * A retry loop that also re-ran `missing` would be asking the registry to
216
- * change its mind about a definite 404, and one that re-ran `published` would
217
- * double the cost of the common case. Only "we could not look" is worth
218
- * looking again at.
184
+ * THE RETRY POLICY IS THIS SWEEP'S, AND IT IS NOT THE MODULE'S. `verifyPublish`
185
+ * retries both non-`published` verdicts, because at release time a version that
186
+ * has just been published can lag the exact-version endpoint for a moment, so
187
+ * re-asking after a 404 is worth the wait. This sweep looks backwards at tags
188
+ * that are often years old, where a 404 is settled: re-asking would be asking
189
+ * the registry to change its mind, and paying for it 1,774 times. So the module
190
+ * is called with `attempts: 1` — one question per call, no internal retry — and
191
+ * the loop that decides whether to ask again lives here, where the timeline it
192
+ * reasons about lives. Same principle, different positions on that timeline;
193
+ * neither caller's behaviour moved when the probe converged.
219
194
  *
220
- * This matters at sweep scale rather than for a single release. The first real
221
- * run over 1,699 tags produced two `unprovable` rows, and one of them was
222
- * `v2.325.4` — a version that IS published, whose probe simply failed in
223
- * flight. Without a re-probe every sweep carries a couple of rows an operator
224
- * has to chase by hand, which is how a report starts getting skimmed.
195
+ * Re-asking an `unprovable` matters at sweep scale rather than for a single
196
+ * release. The first real run over 1,699 tags produced two `unprovable` rows,
197
+ * and one of them was `v2.325.4` — a version that IS published, whose probe
198
+ * simply failed in flight. Without a re-probe every sweep carries a couple of
199
+ * rows an operator has to chase by hand, which is how a report starts getting
200
+ * skimmed.
225
201
  * @param {string} pkg The package name.
226
202
  * @param {string} version The exact version.
227
203
  * @param {object} [options] Retry controls.
228
204
  * @param {number} [options.attempts] Total attempts, including the first.
229
205
  * @param {() => Promise<void>} [options.pause] Delay between attempts.
230
206
  * @param {typeof fetch} [options.fetchImpl] Injected for tests.
231
- * @returns {Promise<{status: number|null, error: string|null}>} The last probe result.
207
+ * @returns {Promise<{verdict: string, detail: string}>} The settled outcome.
232
208
  */
233
- export async function probeWithRetry(
209
+ export async function probeVersion(
234
210
  pkg,
235
211
  version,
236
212
  { attempts = 2, pause = async () => {}, fetchImpl = fetch } = {}
237
213
  ) {
238
- let last = { status: null, error: "not attempted" };
214
+ let last = { verdict: VERDICT.UNPROVABLE, detail: "not attempted" };
239
215
  for (let attempt = 0; attempt < Math.max(1, attempts); attempt += 1) {
240
216
  if (attempt > 0) await pause();
241
- last = await probeRegistry(pkg, version, fetchImpl);
217
+ last = await verifyPublish({
218
+ packageName: pkg,
219
+ version,
220
+ attempts: 1,
221
+ fetchImpl,
222
+ });
242
223
  if (classifyProbe(last) !== VERDICT.UNPROVABLE) return last;
243
224
  }
244
225
  return last;
@@ -313,7 +294,7 @@ async function main() {
313
294
  for (const tag of tags) {
314
295
  const version = parseVersionTag(tag);
315
296
  if (version === null || probes.has(version)) continue;
316
- probes.set(version, await probeWithRetry(pkg, version));
297
+ probes.set(version, await probeVersion(pkg, version));
317
298
  }
318
299
 
319
300
  const result = reconcile({