@codyswann/lisa 4.48.0 → 4.50.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 (426) hide show
  1. package/all/copy-overwrite/scripts/check-conflict-markers.mjs +4 -1
  2. package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +163 -0
  3. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +178 -1
  4. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +89 -10
  5. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +44 -0
  6. package/all/copy-overwrite/scripts/lisa-lint-staged-preflight.mjs +1 -1
  7. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +24 -24
  8. package/all/copy-overwrite/scripts/lisa-work-item.mjs +490 -81
  9. package/cdk/package-lisa/package.lisa.json +45 -15
  10. package/dist/configs/vitest/base.d.ts +71 -1
  11. package/dist/configs/vitest/base.d.ts.map +1 -1
  12. package/dist/configs/vitest/base.js +141 -23
  13. package/dist/configs/vitest/base.js.map +1 -1
  14. package/dist/configs/vitest/fleet-admission-global-setup.d.ts +34 -0
  15. package/dist/configs/vitest/fleet-admission-global-setup.d.ts.map +1 -0
  16. package/dist/configs/vitest/fleet-admission-global-setup.js +123 -0
  17. package/dist/configs/vitest/fleet-admission-global-setup.js.map +1 -0
  18. package/dist/configs/vitest/fleet-admission.d.ts +129 -0
  19. package/dist/configs/vitest/fleet-admission.d.ts.map +1 -0
  20. package/dist/configs/vitest/fleet-admission.js +239 -0
  21. package/dist/configs/vitest/fleet-admission.js.map +1 -0
  22. package/dist/configs/vitest/synthesis-scratch-reclaim.d.ts +86 -0
  23. package/dist/configs/vitest/synthesis-scratch-reclaim.d.ts.map +1 -0
  24. package/dist/configs/vitest/synthesis-scratch-reclaim.js +226 -0
  25. package/dist/configs/vitest/synthesis-scratch-reclaim.js.map +1 -0
  26. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  27. package/dist/core/lisa-owned-hash-ledger.js +76 -0
  28. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  29. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  30. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  31. package/dist/core/upstream-evidence-manifest.js +129 -228
  32. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  33. package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +45 -1
  34. package/dist/opencode/plugin-templates/parity-safety-net.sh +44 -0
  35. package/expo/package-lisa/package.lisa.json +83 -53
  36. package/harper-fabric/package-lisa/package.lisa.json +62 -32
  37. package/nestjs/package-lisa/package.lisa.json +57 -27
  38. package/npm-package/package-lisa/package.lisa.json +43 -7
  39. package/package.json +11 -7
  40. package/phaser/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
  41. package/phaser/package-lisa/package.lisa.json +62 -32
  42. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  45. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +4 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +4 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +4 -0
  48. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +4 -1
  49. package/plugins/lisa/hooks/block-direct-issue-create.sh +89 -10
  50. package/plugins/lisa/hooks/discharge-work-item-gates.sh +7 -1
  51. package/plugins/lisa/hooks/parity-safety-net.sh +44 -0
  52. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +12 -0
  53. package/plugins/lisa/rules/eager/00-rule-index.md +79 -0
  54. package/plugins/lisa/rules/eager/base-rules.md +10 -0
  55. package/plugins/lisa/rules/eager/config-resolution.md +1 -88
  56. package/plugins/lisa/rules/reference/automation-runbook-contract.md +97 -0
  57. package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +56 -0
  58. package/plugins/lisa/rules/reference/blocker-containment.md +58 -0
  59. package/plugins/lisa/rules/reference/claim-archaeology.md +42 -0
  60. package/plugins/lisa/rules/reference/claim-evidence-mapping.md +83 -0
  61. package/plugins/lisa/rules/reference/claim-time-guards.md +37 -0
  62. package/plugins/lisa/rules/reference/config-resolution.md +97 -0
  63. package/plugins/lisa/rules/reference/control-reachability.md +44 -0
  64. package/plugins/lisa/rules/reference/convergent-review.md +31 -0
  65. package/plugins/lisa/rules/reference/credential-substrate-precedence.md +57 -0
  66. package/plugins/lisa/rules/reference/dependency-decision-records.md +37 -0
  67. package/plugins/lisa/rules/reference/dependency-internalization-kit.md +47 -0
  68. package/plugins/lisa/rules/reference/dependency-trust-classes.md +48 -0
  69. package/plugins/lisa/rules/reference/deployed-state-readback.md +52 -0
  70. package/plugins/lisa/rules/reference/derived-branch-plan.md +52 -0
  71. package/plugins/lisa/rules/reference/design-source-of-truth.md +43 -0
  72. package/plugins/lisa/rules/reference/design-value-binding.md +103 -0
  73. package/plugins/lisa/rules/reference/documentation-source-paths.md +18 -0
  74. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +75 -0
  75. package/plugins/lisa/rules/reference/observability-audit.md +20 -0
  76. package/plugins/lisa/rules/reference/prd-definition-of-ready.md +37 -0
  77. package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +36 -0
  78. package/plugins/lisa/rules/reference/pre-flight-autofill.md +88 -0
  79. package/plugins/lisa/rules/reference/promotion-contract.md +21 -0
  80. package/plugins/lisa/rules/reference/readiness-rubric.md +63 -0
  81. package/plugins/lisa/rules/reference/ready-role-filing.md +34 -0
  82. package/plugins/lisa/rules/reference/rejection-detection.md +69 -0
  83. package/plugins/lisa/rules/reference/repo-scope-split.md +48 -0
  84. package/plugins/lisa/rules/reference/reset-seed-coverage.md +55 -0
  85. package/plugins/lisa/rules/reference/usage-accounting.md +38 -0
  86. package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +32 -0
  87. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +158 -1
  88. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  89. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +4 -0
  90. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +4 -0
  91. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +4 -0
  92. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +4 -1
  93. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +89 -10
  94. package/plugins/lisa-agy/hooks/parity-safety-net.sh +44 -0
  95. package/plugins/lisa-agy/plugin.json +1 -1
  96. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +158 -1
  97. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  98. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +4 -0
  99. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +4 -0
  100. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +4 -0
  101. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +4 -1
  102. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  104. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  105. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +89 -10
  109. package/plugins/lisa-copilot/hooks/discharge-work-item-gates.sh +7 -1
  110. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +44 -0
  111. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +12 -0
  112. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +79 -0
  113. package/plugins/lisa-copilot/rules/eager/base-rules.md +10 -0
  114. package/plugins/lisa-copilot/rules/eager/config-resolution.md +1 -88
  115. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +97 -0
  116. package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +56 -0
  117. package/plugins/lisa-copilot/rules/reference/blocker-containment.md +58 -0
  118. package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +42 -0
  119. package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +83 -0
  120. package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +37 -0
  121. package/plugins/lisa-copilot/rules/reference/config-resolution.md +97 -0
  122. package/plugins/lisa-copilot/rules/reference/control-reachability.md +44 -0
  123. package/plugins/lisa-copilot/rules/reference/convergent-review.md +31 -0
  124. package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +57 -0
  125. package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +37 -0
  126. package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +47 -0
  127. package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +48 -0
  128. package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +52 -0
  129. package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +52 -0
  130. package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +43 -0
  131. package/plugins/lisa-copilot/rules/reference/design-value-binding.md +103 -0
  132. package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +18 -0
  133. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +75 -0
  134. package/plugins/lisa-copilot/rules/reference/observability-audit.md +20 -0
  135. package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +37 -0
  136. package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +36 -0
  137. package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +88 -0
  138. package/plugins/lisa-copilot/rules/reference/promotion-contract.md +21 -0
  139. package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +63 -0
  140. package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +34 -0
  141. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +69 -0
  142. package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +48 -0
  143. package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +55 -0
  144. package/plugins/lisa-copilot/rules/reference/usage-accounting.md +38 -0
  145. package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +32 -0
  146. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +158 -1
  147. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  148. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +4 -0
  149. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +4 -0
  150. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +4 -0
  151. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +4 -1
  152. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  153. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +89 -10
  154. package/plugins/lisa-cursor/hooks/discharge-work-item-gates.sh +7 -1
  155. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +44 -0
  156. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +12 -0
  157. package/plugins/lisa-cursor/rules/00-rule-index.mdc +84 -0
  158. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +97 -0
  159. package/plugins/lisa-cursor/rules/base-rules.mdc +10 -0
  160. package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +56 -0
  161. package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +58 -0
  162. package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +42 -0
  163. package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +83 -0
  164. package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +37 -0
  165. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +97 -0
  166. package/plugins/lisa-cursor/rules/config-resolution.mdc +1 -88
  167. package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +44 -0
  168. package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +31 -0
  169. package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +57 -0
  170. package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +37 -0
  171. package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +47 -0
  172. package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +48 -0
  173. package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +52 -0
  174. package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +52 -0
  175. package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +43 -0
  176. package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +103 -0
  177. package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +18 -0
  178. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +75 -0
  179. package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +20 -0
  180. package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +37 -0
  181. package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +36 -0
  182. package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +88 -0
  183. package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +21 -0
  184. package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +63 -0
  185. package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +34 -0
  186. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +69 -0
  187. package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +48 -0
  188. package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +55 -0
  189. package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +38 -0
  190. package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +32 -0
  191. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +158 -1
  192. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  193. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +4 -0
  194. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +4 -0
  195. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +4 -0
  196. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +4 -1
  197. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  198. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  199. package/plugins/lisa-expo-agy/plugin.json +1 -1
  200. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  201. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  202. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  203. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  204. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  205. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  206. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  207. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  208. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  209. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  210. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  211. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  212. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  213. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  214. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  215. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  216. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  217. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  218. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  219. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  220. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  221. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  222. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  223. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  224. package/plugins/lisa-rails-agy/plugin.json +1 -1
  225. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  226. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  227. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  228. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  229. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  230. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  231. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  232. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  233. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  234. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  235. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  236. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  237. package/plugins/src/base/hooks/block-direct-issue-create.sh +89 -10
  238. package/plugins/src/base/hooks/discharge-work-item-gates.sh +7 -1
  239. package/plugins/src/base/hooks/parity-safety-net.sh +44 -0
  240. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +12 -0
  241. package/plugins/src/base/rules/eager/00-rule-index.md +79 -0
  242. package/plugins/src/base/rules/eager/base-rules.md +10 -0
  243. package/plugins/src/base/rules/eager/config-resolution.md +1 -88
  244. package/plugins/src/base/rules/reference/automation-runbook-contract.md +97 -0
  245. package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +56 -0
  246. package/plugins/src/base/rules/reference/blocker-containment.md +58 -0
  247. package/plugins/src/base/rules/reference/claim-archaeology.md +42 -0
  248. package/plugins/src/base/rules/reference/claim-evidence-mapping.md +83 -0
  249. package/plugins/src/base/rules/reference/claim-time-guards.md +37 -0
  250. package/plugins/src/base/rules/reference/config-resolution.md +97 -0
  251. package/plugins/src/base/rules/reference/control-reachability.md +44 -0
  252. package/plugins/src/base/rules/reference/convergent-review.md +31 -0
  253. package/plugins/src/base/rules/reference/credential-substrate-precedence.md +57 -0
  254. package/plugins/src/base/rules/reference/dependency-decision-records.md +37 -0
  255. package/plugins/src/base/rules/reference/dependency-internalization-kit.md +47 -0
  256. package/plugins/src/base/rules/reference/dependency-trust-classes.md +48 -0
  257. package/plugins/src/base/rules/reference/deployed-state-readback.md +52 -0
  258. package/plugins/src/base/rules/reference/derived-branch-plan.md +52 -0
  259. package/plugins/src/base/rules/reference/design-source-of-truth.md +43 -0
  260. package/plugins/src/base/rules/reference/design-value-binding.md +103 -0
  261. package/plugins/src/base/rules/reference/documentation-source-paths.md +18 -0
  262. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +75 -0
  263. package/plugins/src/base/rules/reference/observability-audit.md +20 -0
  264. package/plugins/src/base/rules/reference/prd-definition-of-ready.md +37 -0
  265. package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +36 -0
  266. package/plugins/src/base/rules/reference/pre-flight-autofill.md +88 -0
  267. package/plugins/src/base/rules/reference/promotion-contract.md +21 -0
  268. package/plugins/src/base/rules/reference/readiness-rubric.md +63 -0
  269. package/plugins/src/base/rules/reference/ready-role-filing.md +34 -0
  270. package/plugins/src/base/rules/reference/rejection-detection.md +69 -0
  271. package/plugins/src/base/rules/reference/repo-scope-split.md +48 -0
  272. package/plugins/src/base/rules/reference/reset-seed-coverage.md +55 -0
  273. package/plugins/src/base/rules/reference/usage-accounting.md +38 -0
  274. package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +32 -0
  275. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +158 -1
  276. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
  277. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +4 -0
  278. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +4 -0
  279. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +4 -0
  280. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +4 -1
  281. package/rails/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
  282. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
  283. package/scripts/check-eager-rule-budget.mjs +176 -0
  284. package/scripts/check-generated-artifact-merge-coverage.mjs +7 -0
  285. package/scripts/check-rules-pairing.sh +45 -14
  286. package/scripts/check-shipped-surface-removals.mjs +851 -0
  287. package/scripts/cleanup-worktrees.sh +69 -16
  288. package/scripts/generate-export-surface.mjs +318 -0
  289. package/scripts/lib/shipped-surface.mjs +212 -0
  290. package/scripts/lisa-enforcement-fallback.sh +178 -1
  291. package/scripts/remedy-conformance.mjs +435 -0
  292. package/scripts/two-channel-couplings.json +2 -2
  293. package/typescript/copy-contents/.husky/pre-push +147 -2
  294. package/typescript/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
  295. package/typescript/copy-overwrite/audit.ignore.config.json +0 -10
  296. package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +180 -29
  297. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
  298. package/typescript/package-lisa/package.lisa.json +72 -42
  299. package/plugins/lisa/rules/eager/automation-runbook-contract.md +0 -92
  300. package/plugins/lisa/rules/eager/bdd-e2e-coverage.md +0 -51
  301. package/plugins/lisa/rules/eager/blocker-containment.md +0 -53
  302. package/plugins/lisa/rules/eager/claim-archaeology.md +0 -37
  303. package/plugins/lisa/rules/eager/claim-evidence-mapping.md +0 -78
  304. package/plugins/lisa/rules/eager/claim-time-guards.md +0 -32
  305. package/plugins/lisa/rules/eager/control-reachability.md +0 -39
  306. package/plugins/lisa/rules/eager/convergent-review.md +0 -24
  307. package/plugins/lisa/rules/eager/credential-substrate-precedence.md +0 -54
  308. package/plugins/lisa/rules/eager/dependency-decision-records.md +0 -32
  309. package/plugins/lisa/rules/eager/dependency-internalization-kit.md +0 -42
  310. package/plugins/lisa/rules/eager/dependency-trust-classes.md +0 -43
  311. package/plugins/lisa/rules/eager/deployed-state-readback.md +0 -47
  312. package/plugins/lisa/rules/eager/derived-branch-plan.md +0 -47
  313. package/plugins/lisa/rules/eager/design-source-of-truth.md +0 -38
  314. package/plugins/lisa/rules/eager/design-value-binding.md +0 -98
  315. package/plugins/lisa/rules/eager/documentation-source-paths.md +0 -13
  316. package/plugins/lisa/rules/eager/factory-model.md +0 -41
  317. package/plugins/lisa/rules/eager/leaf-only-lifecycle.md +0 -70
  318. package/plugins/lisa/rules/eager/observability-audit.md +0 -15
  319. package/plugins/lisa/rules/eager/prd-definition-of-ready.md +0 -32
  320. package/plugins/lisa/rules/eager/prd-lifecycle-rollup.md +0 -31
  321. package/plugins/lisa/rules/eager/pre-flight-autofill.md +0 -84
  322. package/plugins/lisa/rules/eager/project-learnings.md +0 -22
  323. package/plugins/lisa/rules/eager/promotion-contract.md +0 -18
  324. package/plugins/lisa/rules/eager/readiness-rubric.md +0 -58
  325. package/plugins/lisa/rules/eager/ready-role-filing.md +0 -29
  326. package/plugins/lisa/rules/eager/rejection-detection.md +0 -64
  327. package/plugins/lisa/rules/eager/repo-scope-split.md +0 -43
  328. package/plugins/lisa/rules/eager/reset-seed-coverage.md +0 -50
  329. package/plugins/lisa/rules/eager/usage-accounting.md +0 -33
  330. package/plugins/lisa/rules/eager/work-item-definition-of-ready.md +0 -27
  331. package/plugins/lisa-copilot/rules/eager/automation-runbook-contract.md +0 -92
  332. package/plugins/lisa-copilot/rules/eager/bdd-e2e-coverage.md +0 -51
  333. package/plugins/lisa-copilot/rules/eager/blocker-containment.md +0 -53
  334. package/plugins/lisa-copilot/rules/eager/claim-archaeology.md +0 -37
  335. package/plugins/lisa-copilot/rules/eager/claim-evidence-mapping.md +0 -78
  336. package/plugins/lisa-copilot/rules/eager/claim-time-guards.md +0 -32
  337. package/plugins/lisa-copilot/rules/eager/control-reachability.md +0 -39
  338. package/plugins/lisa-copilot/rules/eager/convergent-review.md +0 -24
  339. package/plugins/lisa-copilot/rules/eager/credential-substrate-precedence.md +0 -54
  340. package/plugins/lisa-copilot/rules/eager/dependency-decision-records.md +0 -32
  341. package/plugins/lisa-copilot/rules/eager/dependency-internalization-kit.md +0 -42
  342. package/plugins/lisa-copilot/rules/eager/dependency-trust-classes.md +0 -43
  343. package/plugins/lisa-copilot/rules/eager/deployed-state-readback.md +0 -47
  344. package/plugins/lisa-copilot/rules/eager/derived-branch-plan.md +0 -47
  345. package/plugins/lisa-copilot/rules/eager/design-source-of-truth.md +0 -38
  346. package/plugins/lisa-copilot/rules/eager/design-value-binding.md +0 -98
  347. package/plugins/lisa-copilot/rules/eager/documentation-source-paths.md +0 -13
  348. package/plugins/lisa-copilot/rules/eager/factory-model.md +0 -41
  349. package/plugins/lisa-copilot/rules/eager/leaf-only-lifecycle.md +0 -70
  350. package/plugins/lisa-copilot/rules/eager/observability-audit.md +0 -15
  351. package/plugins/lisa-copilot/rules/eager/prd-definition-of-ready.md +0 -32
  352. package/plugins/lisa-copilot/rules/eager/prd-lifecycle-rollup.md +0 -31
  353. package/plugins/lisa-copilot/rules/eager/pre-flight-autofill.md +0 -84
  354. package/plugins/lisa-copilot/rules/eager/project-learnings.md +0 -22
  355. package/plugins/lisa-copilot/rules/eager/promotion-contract.md +0 -18
  356. package/plugins/lisa-copilot/rules/eager/readiness-rubric.md +0 -58
  357. package/plugins/lisa-copilot/rules/eager/ready-role-filing.md +0 -29
  358. package/plugins/lisa-copilot/rules/eager/rejection-detection.md +0 -64
  359. package/plugins/lisa-copilot/rules/eager/repo-scope-split.md +0 -43
  360. package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +0 -50
  361. package/plugins/lisa-copilot/rules/eager/usage-accounting.md +0 -33
  362. package/plugins/lisa-copilot/rules/eager/work-item-definition-of-ready.md +0 -27
  363. package/plugins/lisa-cursor/rules/automation-runbook-contract.mdc +0 -97
  364. package/plugins/lisa-cursor/rules/bdd-e2e-coverage.mdc +0 -56
  365. package/plugins/lisa-cursor/rules/blocker-containment.mdc +0 -58
  366. package/plugins/lisa-cursor/rules/claim-archaeology.mdc +0 -42
  367. package/plugins/lisa-cursor/rules/claim-evidence-mapping.mdc +0 -83
  368. package/plugins/lisa-cursor/rules/claim-time-guards.mdc +0 -37
  369. package/plugins/lisa-cursor/rules/control-reachability.mdc +0 -44
  370. package/plugins/lisa-cursor/rules/convergent-review.mdc +0 -29
  371. package/plugins/lisa-cursor/rules/credential-substrate-precedence.mdc +0 -59
  372. package/plugins/lisa-cursor/rules/dependency-decision-records.mdc +0 -37
  373. package/plugins/lisa-cursor/rules/dependency-internalization-kit.mdc +0 -47
  374. package/plugins/lisa-cursor/rules/dependency-trust-classes.mdc +0 -48
  375. package/plugins/lisa-cursor/rules/deployed-state-readback.mdc +0 -52
  376. package/plugins/lisa-cursor/rules/derived-branch-plan.mdc +0 -52
  377. package/plugins/lisa-cursor/rules/design-source-of-truth.mdc +0 -43
  378. package/plugins/lisa-cursor/rules/design-value-binding.mdc +0 -103
  379. package/plugins/lisa-cursor/rules/documentation-source-paths.mdc +0 -18
  380. package/plugins/lisa-cursor/rules/factory-model.mdc +0 -46
  381. package/plugins/lisa-cursor/rules/leaf-only-lifecycle.mdc +0 -75
  382. package/plugins/lisa-cursor/rules/observability-audit.mdc +0 -20
  383. package/plugins/lisa-cursor/rules/prd-definition-of-ready.mdc +0 -37
  384. package/plugins/lisa-cursor/rules/prd-lifecycle-rollup.mdc +0 -36
  385. package/plugins/lisa-cursor/rules/pre-flight-autofill.mdc +0 -89
  386. package/plugins/lisa-cursor/rules/project-learnings.mdc +0 -27
  387. package/plugins/lisa-cursor/rules/promotion-contract.mdc +0 -23
  388. package/plugins/lisa-cursor/rules/readiness-rubric.mdc +0 -63
  389. package/plugins/lisa-cursor/rules/ready-role-filing.mdc +0 -34
  390. package/plugins/lisa-cursor/rules/rejection-detection.mdc +0 -69
  391. package/plugins/lisa-cursor/rules/repo-scope-split.mdc +0 -48
  392. package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +0 -55
  393. package/plugins/lisa-cursor/rules/usage-accounting.mdc +0 -38
  394. package/plugins/lisa-cursor/rules/work-item-definition-of-ready.mdc +0 -32
  395. package/plugins/src/base/rules/eager/automation-runbook-contract.md +0 -92
  396. package/plugins/src/base/rules/eager/bdd-e2e-coverage.md +0 -51
  397. package/plugins/src/base/rules/eager/blocker-containment.md +0 -53
  398. package/plugins/src/base/rules/eager/claim-archaeology.md +0 -37
  399. package/plugins/src/base/rules/eager/claim-evidence-mapping.md +0 -78
  400. package/plugins/src/base/rules/eager/claim-time-guards.md +0 -32
  401. package/plugins/src/base/rules/eager/control-reachability.md +0 -39
  402. package/plugins/src/base/rules/eager/convergent-review.md +0 -24
  403. package/plugins/src/base/rules/eager/credential-substrate-precedence.md +0 -54
  404. package/plugins/src/base/rules/eager/dependency-decision-records.md +0 -32
  405. package/plugins/src/base/rules/eager/dependency-internalization-kit.md +0 -42
  406. package/plugins/src/base/rules/eager/dependency-trust-classes.md +0 -43
  407. package/plugins/src/base/rules/eager/deployed-state-readback.md +0 -47
  408. package/plugins/src/base/rules/eager/derived-branch-plan.md +0 -47
  409. package/plugins/src/base/rules/eager/design-source-of-truth.md +0 -38
  410. package/plugins/src/base/rules/eager/design-value-binding.md +0 -98
  411. package/plugins/src/base/rules/eager/documentation-source-paths.md +0 -13
  412. package/plugins/src/base/rules/eager/factory-model.md +0 -41
  413. package/plugins/src/base/rules/eager/leaf-only-lifecycle.md +0 -70
  414. package/plugins/src/base/rules/eager/observability-audit.md +0 -15
  415. package/plugins/src/base/rules/eager/prd-definition-of-ready.md +0 -32
  416. package/plugins/src/base/rules/eager/prd-lifecycle-rollup.md +0 -31
  417. package/plugins/src/base/rules/eager/pre-flight-autofill.md +0 -84
  418. package/plugins/src/base/rules/eager/project-learnings.md +0 -22
  419. package/plugins/src/base/rules/eager/promotion-contract.md +0 -18
  420. package/plugins/src/base/rules/eager/readiness-rubric.md +0 -58
  421. package/plugins/src/base/rules/eager/ready-role-filing.md +0 -29
  422. package/plugins/src/base/rules/eager/rejection-detection.md +0 -64
  423. package/plugins/src/base/rules/eager/repo-scope-split.md +0 -43
  424. package/plugins/src/base/rules/eager/reset-seed-coverage.md +0 -50
  425. package/plugins/src/base/rules/eager/usage-accounting.md +0 -33
  426. package/plugins/src/base/rules/eager/work-item-definition-of-ready.md +0 -27
@@ -37,6 +37,143 @@ function normalizeLabels(labels) {
37
37
  .filter(label => label.length > 0);
38
38
  }
39
39
 
40
+ /**
41
+ * Decoration a declaration may sit behind, and nothing more.
42
+ *
43
+ * Whitespace, blockquote arrows, list bullets or numbers, emphasis, a comment
44
+ * hash, and an opening HTML comment. `#` is there on EVIDENCE rather than
45
+ * taste: a shell script declares a hold as `# <marker> reason=…` — a bare
46
+ * marker behind a SHELL comment, with no HTML comment anywhere — and that form
47
+ * is in this repository's own guard fixtures. A single character class plus two
48
+ * optional groups —
49
+ * deliberately not a nested-quantifier pattern, because this runs over
50
+ * untrusted work-item bodies and `sonarjs/slow-regex` is right about that
51
+ * shape.
52
+ */
53
+ const LEADING_DECORATION =
54
+ /^[ \t>*_+#-]*(?:\d+[.)][ \t]*)?(?:<!--[ \t]*)?[ \t]*/u;
55
+
56
+ /**
57
+ * The body with fenced blocks and inline code spans removed.
58
+ *
59
+ * Documentation of a declaration is not a declaration, and a fenced example is
60
+ * how the declaration form gets written ABOUT. The same move
61
+ * `pr-arming-sweep.mjs` makes for the auto-merge-off marker
62
+ * (CodySwannGT/lisa#3986), and the same one `block-direct-issue-create.sh`
63
+ * makes when it strips heredoc bodies before tokenising argv.
64
+ * @param {unknown} body - The raw item body
65
+ * @returns {string} The body with quoted and code regions blanked out
66
+ */
67
+ function declarativeText(body) {
68
+ return String(body ?? "")
69
+ .replace(/```[\s\S]*?```/gu, "")
70
+ .replace(/`[^`\n]*`/gu, "");
71
+ }
72
+
73
+ /**
74
+ * Whether an HTML comment on this line carries the marker.
75
+ *
76
+ * The second declaration form, and it is not a concession: an HTML comment is
77
+ * invisible when the body is rendered, so nobody writes one to TALK about the
78
+ * marker — a quotation has to be visible to be a quotation. Prose about the
79
+ * marker is written in sentences, code spans and fenced examples, all of which
80
+ * this rule already excludes.
81
+ *
82
+ * Scanned rather than pattern-matched. A regular expression with two lazy
83
+ * `[^\n]*?` runs around a literal is the ambiguous shape this module avoids
84
+ * elsewhere, and an indexOf walk is both linear and easier to be sure about.
85
+ * @param {string} line - One line of the body
86
+ * @returns {boolean} True when a comment on this line contains the marker
87
+ */
88
+ function commentCarriesMarker(line) {
89
+ const segments = line.split("<!--");
90
+ for (let index = 1; index < segments.length; index += 1) {
91
+ const close = segments[index].indexOf("-->");
92
+ const inside =
93
+ close === -1 ? segments[index] : segments[index].slice(0, close);
94
+ if (inside.includes(HUMAN_GATE_MARKER)) return true;
95
+ }
96
+ return false;
97
+ }
98
+
99
+ /**
100
+ * Whether one line DECLARES a hold rather than mentioning one.
101
+ *
102
+ * Two accepted forms, and the corpus says they cost nothing to combine:
103
+ * measured over this repository's 42 matching bodies, line-leading alone and
104
+ * line-leading-or-comment hold the SAME 29. The comment branch exists because
105
+ * the filing guard reads inputs that are not markdown documents — a one-line
106
+ * `--body` string and a shell script — where a declaration legitimately sits
107
+ * after other text on its line.
108
+ * @param {string} line - One line of the body
109
+ * @returns {boolean} True when the line declares a hold
110
+ */
111
+ function declaresOnLine(line) {
112
+ if (line.replace(LEADING_DECORATION, "").startsWith(HUMAN_GATE_MARKER)) {
113
+ return true;
114
+ }
115
+ return commentCarriesMarker(line);
116
+ }
117
+
118
+ /**
119
+ * Count what a body says about the hold marker, split by what it MEANS.
120
+ *
121
+ * ## Why the substring test had to go
122
+ *
123
+ * `isHumanGated` asked one question — does the body contain the marker
124
+ * anywhere — and that form was deliberate, documented, and correct when it was
125
+ * written: *"it is a marker with no other meaning, so its presence IS the
126
+ * declaration."* The premise expired. The marker acquired a second meaning —
127
+ * **being discussed** — the moment the feature became something people file
128
+ * tickets about, so a sentence quoting it, a Gherkin line naming it, and a
129
+ * ticket about it all read as declared holds (CodySwannGT/lisa#3815).
130
+ *
131
+ * The consequence is not one skipped cycle. `planHumanGateReconciliation`
132
+ * removes the ready role and applies the human-needed label, so a false
133
+ * positive rewrites the item's own metadata and every later sweep agrees with
134
+ * the first. And the affected population GROWS with the documentation: 38
135
+ * matching bodies measured 2026-09-04, 42 on 2026-09-05.
136
+ *
137
+ * ## The rule
138
+ *
139
+ * A declaration is POSITIONAL — the marker leads its line, behind at most
140
+ * blockquote, list, emphasis or opening-comment decoration — evaluated after
141
+ * code regions are removed. Measured over this repository's 42 matching bodies:
142
+ * this holds 29 of them and correctly releases the two live ready-lane items
143
+ * that only discussed the marker.
144
+ *
145
+ * The stricter HTML-comment-only rule was measured and REJECTED: it holds only
146
+ * 18, dropping declarations that carry `human-needed` today. Losing a real hold
147
+ * is the unsafe direction and would need a retrofit of the tracker.
148
+ *
149
+ * ## Why it counts rather than answering yes or no
150
+ *
151
+ * A heuristic that silently drops candidates is indistinguishable from one that
152
+ * found none. Every occurrence this rule declines to honour is counted and
153
+ * reported, so "the rule demoted 33 mentions" is a fact an operator can read
154
+ * rather than an absence they must infer.
155
+ * @param {unknown} body - The item body
156
+ * @returns {{ total: number, declared: number, demoted: number }} Occurrences,
157
+ * those that declare a hold, and those demoted to mentions
158
+ */
159
+ export function humanGateMentions(body) {
160
+ const raw = String(body ?? "");
161
+ const total = raw.split(HUMAN_GATE_MARKER).length - 1;
162
+ const declared = declarativeText(raw)
163
+ .split("\n")
164
+ .filter(line => declaresOnLine(line)).length;
165
+ return { total, declared, demoted: total - declared };
166
+ }
167
+
168
+ /**
169
+ * Whether a body declares a hold, as opposed to discussing one.
170
+ * @param {unknown} body - The item body
171
+ * @returns {boolean} True when at least one line declares a hold
172
+ */
173
+ export function bodyDeclaresHold(body) {
174
+ return humanGateMentions(body).declared > 0;
175
+ }
176
+
40
177
  /**
41
178
  * Whether an item is held for a person, on either surface.
42
179
  *
@@ -64,7 +201,7 @@ export function isHumanGated(input) {
64
201
  return true;
65
202
  }
66
203
 
67
- return String(input.body ?? "").includes(HUMAN_GATE_MARKER);
204
+ return bodyDeclaresHold(input.body);
68
205
  }
69
206
 
70
207
  /**
@@ -432,6 +569,26 @@ export function formatHumanGateNote() {
432
569
  * @param {readonly unknown[]} held - Item references held this cycle
433
570
  * @returns {string} One summary line
434
571
  */
572
+ /**
573
+ * Render the cycle-summary line naming what the precision rule DEMOTED.
574
+ *
575
+ * The counterpart of {@link summarizeHumanGateHolds}, and it exists for the
576
+ * same reason: a mutation nobody can see afterwards is the problem this whole
577
+ * area keeps having. A rule that quietly declines to honour half the marker
578
+ * occurrences it sees reads exactly like a rule that saw none, so the count is
579
+ * printed even when it is zero — measured across this repository's matching
580
+ * bodies, 33 of 62 occurrences are mentions rather than declarations.
581
+ * @param {unknown} demoted - How many occurrences were mentions, not declarations
582
+ * @returns {string} One summary line
583
+ */
584
+ export function summarizeHumanGateMentions(demoted = 0) {
585
+ const count = Number.isFinite(Number(demoted))
586
+ ? Math.trunc(Number(demoted))
587
+ : 0;
588
+ const shown = count > 0 ? String(count) : "none";
589
+ return `Marker mentions demoted (not declarations): ${shown}.`;
590
+ }
591
+
435
592
  export function summarizeHumanGateHolds(held = []) {
436
593
  const names = (Array.isArray(held) ? held : [])
437
594
  .map(item => trimmedString(item))
@@ -1136,10 +1136,26 @@ not there, and a lifecycle step performed by hand is one nothing can verify
1136
1136
  happened.
1137
1137
 
1138
1138
  **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
1139
- reports every claimed item that already has a merged pull request, and
1139
+ reports every open item — in the **ready** lane as well as the **claimed** one —
1140
+ that a commit on a deploy branch DECLARES via its `Work-Item:` trailer, and
1140
1141
  `--apply` completes them. Reporting is the default deliberately — a sweep that
1141
1142
  closes things as a side effect of being run is not one anyone runs twice.
1142
1143
 
1144
+ You do not need to run it. The `🧹 Lifecycle Drift Sweep` workflow runs it
1145
+ daily and files a deduplicated issue when drift exists, because a report with
1146
+ no consumer and no report are the same thing with different amounts of code.
1147
+
1148
+ Two properties worth knowing, because both were once the other way round:
1149
+
1150
+ - **Credit requires a declaration, never a mention.** Evidence is a commit
1151
+ reachable from a deploy branch whose own trailer names the item. It used to
1152
+ be a timeline cross-reference, which any pull request that so much as *named*
1153
+ `#N` created — so one merged land-stack pull request made every sibling in
1154
+ its batch look shipped. Measured: 8 of 53 reported items had no commit
1155
+ declaring them, and every one of the 8 had live work open against it.
1156
+ - **A clean result names the lanes it examined.** "No drift" over a subject
1157
+ list that excluded the ready lane was true and unusable.
1158
+
1143
1159
  ## 4. Terminal states
1144
1160
 
1145
1161
  Loop until one of:
@@ -332,6 +332,10 @@ other gate's verdict — however conclusive — may promote an item a person par
332
332
  4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
333
333
  distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
334
334
  mutation nobody can see afterwards is the same class of problem this gate exists to fix.
335
+ Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
336
+ — the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
337
+ A rule that quietly declines to honour half the occurrences it sees reads exactly like a
338
+ rule that saw none, so the count is printed even when it is zero.
335
339
  5. **Continue to the next candidate.** A held item does not end the cycle.
336
340
 
337
341
 
@@ -237,6 +237,10 @@ other gate's verdict — however conclusive — may promote an item a person par
237
237
  4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
238
238
  distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
239
239
  mutation nobody can see afterwards is the same class of problem this gate exists to fix.
240
+ Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
241
+ — the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
242
+ A rule that quietly declines to honour half the occurrences it sees reads exactly like a
243
+ rule that saw none, so the count is printed even when it is zero.
240
244
  5. **Continue to the next candidate.** A held item does not end the cycle.
241
245
 
242
246
 
@@ -231,6 +231,10 @@ other gate's verdict — however conclusive — may promote an item a person par
231
231
  4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
232
232
  distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
233
233
  mutation nobody can see afterwards is the same class of problem this gate exists to fix.
234
+ Report alongside it what the precision rule SKIPPED, via `summarizeHumanGateMentions(n)`
235
+ — the marker occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815).
236
+ A rule that quietly declines to honour half the occurrences it sees reads exactly like a
237
+ rule that saw none, so the count is printed even when it is zero.
234
238
  5. **Continue to the next candidate.** A held item does not end the cycle.
235
239
 
236
240
 
@@ -994,7 +994,10 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
994
994
  instead — it says that nothing was changed and how a person resumes the item, which is the
995
995
  opposite of what the reconciliation note says and must not be substituted for it. A promotion
996
996
  declined silently is invisible, which is the quieter version of the failure this step exists to
997
- prevent, so name every held item in the cycle summary via `summarizeHumanGateHolds([...])`.
997
+ prevent, so name every held item in the cycle summary via `summarizeHumanGateHolds([...])`. Report what the
998
+ precision rule skipped beside it via `summarizeHumanGateMentions(n)`: the marker
999
+ occurrences that were mentions rather than declarations (CodySwannGT/lisa#3815),
1000
+ because a rule that silently drops candidates reads like one that found none.
998
1001
 
999
1002
  ### Ungated non-ready filing → surface as an incomplete handoff (read-only)
1000
1003
 
@@ -0,0 +1,50 @@
1
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ # Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ id: no-day-truncating-file-age-predicate
5
+ language: bash
6
+ severity: error
7
+ message: |
8
+ `-mtime`, `-atime` and `-ctime` do NOT mean "older than N days".
9
+
10
+ `find` divides the file's age by 86400 and DISCARDS the remainder, so
11
+ `-mtime +N` matches only files older than N+1 days. Measured on this
12
+ repository's own platform with fixtures stamped at known ages:
13
+
14
+ age -mtime +2 -mmin +2880
15
+ 47h no no
16
+ 49h NO yes <- the whole disagreement
17
+ 71h NO yes
18
+ 73h yes yes
19
+
20
+ The 49h-71h band is silently dropped. The failure direction is what makes
21
+ this worth a rule: the under-selection reports "nothing to do", and nobody
22
+ questions an empty result from a cleanup predicate, so it prevents needed
23
+ work instead of causing visible harm. Reported once as a broken `find`; it
24
+ is not. `find` is answering a different question than the flag appears to
25
+ ask, which is exactly why it survives review.
26
+
27
+ Write the question you mean:
28
+
29
+ older than N days -> -mmin +$((N * 1440))
30
+ older than N days -> ! -newermt "<an ISO timestamp N days back>"
31
+ older than N hours -> -mmin +$((N * 60))
32
+
33
+ `-mmin` is minute-granularity and is not truncated to days, so it means
34
+ what it reads. `-newermt` is exact but needs a timestamp, and the two
35
+ `date` implementations disagree on how to produce one: `date -v-2d` is
36
+ BSD/macOS and `date -d '2 days ago'` is GNU/Linux. Prefer `-mmin` in any
37
+ script that runs on both.
38
+ note: |
39
+ This rule matches the flag itself rather than a `find` invocation, so it
40
+ still fires through `/usr/bin/find`, `$FIND`, and `xargs find`. There are
41
+ no legitimate uses in this repository: every shipped age predicate already
42
+ uses `-mmin`, and this rule exists to keep it that way, because `-mtime` is
43
+ the form a maintainer reaches for by reflex and the shell hooks that carry
44
+ these predicates are copied across five per-agent plugin variants, so one
45
+ wrong edit lands in eight files at once.
46
+
47
+ See CodySwannGT/lisa#3905.
48
+ rule:
49
+ kind: word
50
+ regex: ^-(mtime|atime|ctime)$
@@ -75,6 +75,18 @@ export const FAMILIES = [
75
75
  match: /(^|\/)\.github\/k6\/thresholds\/[^/]+\.json$/,
76
76
  kind: "k6",
77
77
  },
78
+ {
79
+ // #3992. The always-on eager rule tier is concatenated into every session
80
+ // AND every subagent start, so its byte ceiling is a quality threshold like
81
+ // any other. `max` because the number is a ceiling: lowering it tightens the
82
+ // gate and is free, raising it re-authorises context the cleanup removed and
83
+ // is the change a human has to see. The tier was cut to 26,540 bytes once
84
+ // and regrew to 201,083 in fourteen weeks with nothing watching it.
85
+ id: "eager-rules",
86
+ match: /(^|\/)eager-rules\.thresholds\.json$/,
87
+ kind: "json-num",
88
+ direction: "max",
89
+ },
78
90
  {
79
91
  id: "lisa-config",
80
92
  match: /(^|\/)\.lisa\.config\.json$/,
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CI gate on the total size of the always-on eager rule tier.
4
+ *
5
+ * WHY THIS EXISTS, AND WHY IT IS A GATE RATHER THAN A REPORT.
6
+ *
7
+ * `plugins/<plugin>/rules/eager/*.md` is concatenated into `additionalContext`
8
+ * by `inject-rules.sh` on **SessionStart and SubagentStart**, mirrored into
9
+ * `.codex/lisa-rules/eager/` and `.opencode/lisa-rules/`, and flattened to
10
+ * Cursor `.mdc` files with `alwaysApply: true`. Every session in every adopting
11
+ * project pays for it before its first tool call, and so does every subagent —
12
+ * a lead plus ten teammates pays it eleven times.
13
+ *
14
+ * This tier has already been cleaned up once and grown back. Measured from git:
15
+ *
16
+ * pre-split (flat rules/) 12 files 190,237 bytes
17
+ * 2026-05-28 after a820527 13 files 26,540 bytes ("~86% smaller")
18
+ * 2026-07-15 19 files 49,488 bytes
19
+ * 2026-07-30 36 files 105,198 bytes
20
+ * 2026-09-05 56 files 201,083 bytes
21
+ *
22
+ * Fourteen weeks took it to 7.6x the post-cleanup figure and 5.7% PAST the
23
+ * payload the cleanup was created to remove. Nothing measured it, so nobody saw
24
+ * it happen — every individual addition was defensible. A fix performed once
25
+ * and undone by accretion needs a guard, not a second tidy (#3992).
26
+ *
27
+ * TOTAL BYTES, NOT PER FILE. A per-file cap is defeated by splitting one rule
28
+ * into two, and the file count is exactly what already moved: 13 -> 56. The
29
+ * cost to a session is the sum, so the sum is what is measured.
30
+ *
31
+ * THE CEILING lives in `eager-rules.thresholds.json` (root, beside the other threshold files) and
32
+ * is registered as a `max`-direction family in `threshold-ratchet-families.mjs`,
33
+ * so it may only tighten: lowering it is free, raising it is a weakening that
34
+ * needs a `thresholdRatchet.allow` entry with a written reason. See
35
+ * `threshold-ratchet.mjs` for why an allow entry is read only from the baseline.
36
+ *
37
+ * ALL THREE ROOTS are checked, not just the source. `plugins/lisa/` and
38
+ * `plugins/lisa-copilot/` are generated copies that ship to Claude and Copilot;
39
+ * checking only `plugins/src/base` would gate the input to a generator while the
40
+ * artifacts that actually reach a session went unmeasured.
41
+ *
42
+ * It refuses to pass an inspection it did not perform: zero roots or zero rule
43
+ * files is exit 2, not a passing zero. Every gate in the `plugins-sync` job
44
+ * takes that stance, and it is why they are trustworthy — a scan that finds
45
+ * nothing and a tree that is clean otherwise print the same tick.
46
+ * @module scripts/check-eager-rule-budget
47
+ */
48
+ import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
49
+ import * as path from "node:path";
50
+ import { fileURLToPath } from "node:url";
51
+
52
+ const REPO_ROOT = path.resolve(
53
+ path.dirname(fileURLToPath(import.meta.url)),
54
+ ".."
55
+ );
56
+
57
+ /** Plugin roots whose eager tier ships to a session. */
58
+ const CHECKED_PLUGINS = ["src/base", "lisa", "lisa-copilot"];
59
+
60
+ /** Committed ceiling, ratcheted by the `eager-rules` threshold family. */
61
+ const THRESHOLDS_FILE = path.join(REPO_ROOT, "eager-rules.thresholds.json");
62
+
63
+ /**
64
+ * Total the `.md` payload of one plugin's eager rules directory.
65
+ * @param {string} pluginRelPath - Plugin path relative to `plugins/`.
66
+ * @returns {{ root: string, files: number, bytes: number } | undefined} The
67
+ * measurement, or undefined when the plugin has no eager directory.
68
+ */
69
+ function measureEagerTier(pluginRelPath) {
70
+ const dir = path.join(REPO_ROOT, "plugins", pluginRelPath, "rules", "eager");
71
+ if (!existsSync(dir) || !statSync(dir).isDirectory()) return undefined;
72
+ const files = readdirSync(dir).filter(name => name.endsWith(".md"));
73
+ const bytes = files.reduce(
74
+ (total, name) =>
75
+ total + Buffer.byteLength(readFileSync(path.join(dir, name))),
76
+ 0
77
+ );
78
+ return {
79
+ root: `plugins/${pluginRelPath}/rules/eager`,
80
+ files: files.length,
81
+ bytes,
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Read the committed byte ceiling.
87
+ * @returns {number} The ceiling in bytes.
88
+ */
89
+ function readCeiling() {
90
+ if (!existsSync(THRESHOLDS_FILE)) {
91
+ process.stderr.write(
92
+ `✗ Missing ${path.relative(REPO_ROOT, THRESHOLDS_FILE)} — the eager budget ` +
93
+ `cannot be checked without its committed ceiling.\n`
94
+ );
95
+ process.exit(2);
96
+ }
97
+ const parsed = JSON.parse(readFileSync(THRESHOLDS_FILE, "utf8"));
98
+ const ceiling = parsed.totalBytes;
99
+ if (
100
+ typeof ceiling !== "number" ||
101
+ !Number.isFinite(ceiling) ||
102
+ ceiling <= 0
103
+ ) {
104
+ process.stderr.write(
105
+ `✗ ${path.relative(REPO_ROOT, THRESHOLDS_FILE)} has no usable ` +
106
+ `"totalBytes" number; refusing to check against an unreadable ceiling.\n`
107
+ );
108
+ process.exit(2);
109
+ }
110
+ return ceiling;
111
+ }
112
+
113
+ const ceiling = readCeiling();
114
+ const measured = CHECKED_PLUGINS.map(measureEagerTier).filter(Boolean);
115
+
116
+ // VACUITY GUARD. A tree with no eager rules and a tree this script failed to
117
+ // read produce the same zero, and the passing branch cannot tell them apart.
118
+ // Exit 2 keeps "I could not measure" distinct from "it is within budget".
119
+ if (measured.length === 0) {
120
+ process.stderr.write(
121
+ `✗ No eager rules directory found under any of ${CHECKED_PLUGINS.join(", ")}. ` +
122
+ `Refusing to report a passing zero for a measurement that did not happen.\n`
123
+ );
124
+ process.exit(2);
125
+ }
126
+ const empty = measured.filter(m => m.files === 0);
127
+ if (empty.length > 0) {
128
+ process.stderr.write(
129
+ `✗ Eager rules directories with zero .md files: ${empty
130
+ .map(m => m.root)
131
+ .join(", ")}. Refusing to report a passing zero.\n`
132
+ );
133
+ process.exit(2);
134
+ }
135
+
136
+ const over = measured.filter(m => m.bytes > ceiling);
137
+ for (const m of measured) {
138
+ const margin =
139
+ m.bytes > ceiling
140
+ ? `over by ${m.bytes - ceiling}`
141
+ : `${ceiling - m.bytes} to spare`;
142
+ process.stdout.write(
143
+ `${(m.bytes > ceiling ? "OVER" : "ok").padEnd(4)} ${m.root}: ${m.files} ` +
144
+ `files, ${m.bytes} bytes (ceiling ${ceiling}, ${margin})\n`
145
+ );
146
+ }
147
+
148
+ if (over.length > 0) {
149
+ process.stderr.write("\n");
150
+ for (const m of over) {
151
+ process.stderr.write(
152
+ `✗ ${m.root} is ${m.bytes - ceiling} bytes over the eager budget ` +
153
+ `(${m.bytes} measured, ceiling ${ceiling}).\n`
154
+ );
155
+ }
156
+ process.stderr.write(
157
+ "\n Every session AND every subagent in every adopting project pays this " +
158
+ "payload\n before its first tool call. Fix it by demoting, not by raising " +
159
+ "the ceiling:\n\n" +
160
+ " 1. Move the rule's body to plugins/src/base/rules/reference/<slug>.md\n" +
161
+ " 2. Add a one-line entry to plugins/src/base/rules/eager/00-rule-index.md\n" +
162
+ " 3. Delete the eager head and re-run this gate\n\n" +
163
+ " A rule stays eager only when an agent behaves WRONGLY without it in a " +
164
+ "session\n that never asks for it. Being true, well-argued, and cited by " +
165
+ "ten skills is not\n that bar — those rules belong in the index.\n\n" +
166
+ " Raising the ceiling in eager-rules.thresholds.json is a weakening: the " +
167
+ "threshold\n ratchet refuses it without a thresholdRatchet.allow entry " +
168
+ "naming a reason.\n"
169
+ );
170
+ process.exit(1);
171
+ }
172
+
173
+ process.stdout.write(
174
+ `\n✓ Eager rule budget passed across ${measured.length} plugin roots ` +
175
+ `(ceiling ${ceiling} bytes).\n`
176
+ );
@@ -106,6 +106,13 @@ export const GENERATED_ARTIFACTS = Object.freeze([
106
106
  reason:
107
107
  "nested: a contractVersion / packageVersions / provenances record under each digest, which the driver's flat one-level parser rejects",
108
108
  }),
109
+ Object.freeze({
110
+ path: "src/core/export-surface.json",
111
+ check: "check:export-surface",
112
+ disposition: DISPOSITION.UNSUPPORTED,
113
+ reason:
114
+ "nested: a `surface` sub-object mapping each module path to an array of exported names, which the driver's flat one-level parser rejects",
115
+ }),
109
116
  ]);
110
117
 
111
118
  /**
@@ -1,9 +1,15 @@
1
1
  #!/usr/bin/env bash
2
- # Fails if any plugin's rules/eager/X.md is missing its rules/reference/X.md
3
- # pair (or vice versa). The eager/reference split documents the contract that
4
- # every eager head points at a reference body for the long-form detail; an
5
- # unpaired file means either a head without a body (broken breadcrumb) or a
6
- # body without a head (orphaned, never injected).
2
+ # Fails when a rule body is unreachable, in either direction.
3
+ #
4
+ # Forward (unchanged): every rules/eager/X.md must have its rules/reference/X.md,
5
+ # because the head's breadcrumb points at that body and a broken pointer is a
6
+ # defect.
7
+ #
8
+ # Reverse (#3992): every rules/reference/X.md must be pointed at by SOMETHING
9
+ # eager — its own head, or a line in rules/eager/00-rule-index.md. This used to
10
+ # demand a dedicated head, which made writing any long-form contract compel a
11
+ # permanent per-session payload; the eager tier grew 7.6x on that incentive.
12
+ # The property being protected is reachability, and an index line provides it.
7
13
  #
8
14
  # Bootstrap files are exempt: rules/eager/00-bootstrap.md (and any other file
9
15
  # matching rules/eager/00-*.md) is allowed to have no reference pair. The
@@ -35,6 +41,13 @@ check_plugin() {
35
41
  exempt_pattern="$(grep -v '^[[:space:]]*#' "$exempt_file" | grep -v '^[[:space:]]*$' || true)"
36
42
  fi
37
43
 
44
+ # The eager rule index, when this plugin has one. It is itself a 00-* file, so
45
+ # it is exempt from needing a body of its own.
46
+ local index_file=""
47
+ if [ -f "$eager_dir/00-rule-index.md" ]; then
48
+ index_file="$eager_dir/00-rule-index.md"
49
+ fi
50
+
38
51
  # Every eager/X.md (except 00-bootstrap-style files and explicit exemptions)
39
52
  # must have a reference/X.md pair.
40
53
  while IFS= read -r eager_file; do
@@ -55,8 +68,17 @@ check_plugin() {
55
68
  fi
56
69
  done < <(find "$eager_dir" -maxdepth 1 -type f -name '*.md' | sort)
57
70
 
58
- # Every reference/X.md must have an eager/X.md pair (catch orphans on the
59
- # other side: a reference body with no breadcrumb pointing to it is dead).
71
+ # Every reference/X.md must be POINTED AT by something eager — either its own
72
+ # eager/X.md head, or a line in the eager rule index. The invariant is
73
+ # reachability, not pairing: a body nothing names is dead, and that is what
74
+ # this arm has always been protecting.
75
+ #
76
+ # The index satisfies it because #3992 changed the mechanism. Requiring a
77
+ # dedicated head made authoring any long-form contract compel an always-on
78
+ # per-session payload, and the tier grew from 26,540 to 201,083 bytes on that
79
+ # incentive alone — `credential-substrate-precedence.md` was added purely to
80
+ # turn this check green. One index line carries the same pointer at ~1% of the
81
+ # context cost, so the cheap way to satisfy the gate is now also the right one.
60
82
  if [ -d "$reference_dir" ]; then
61
83
  while IFS= read -r ref_file; do
62
84
  local base
@@ -64,11 +86,19 @@ check_plugin() {
64
86
  if [ -n "$exempt_pattern" ] && echo "$exempt_pattern" | grep -qxF "$base"; then
65
87
  continue
66
88
  fi
67
- if [ ! -f "$eager_dir/$base" ]; then
68
- echo "✗ Orphaned reference body (no eager head): $ref_file" >&2
69
- echo " Expected: $eager_dir/$base" >&2
70
- failed=1
89
+ if [ -f "$eager_dir/$base" ]; then
90
+ continue
91
+ fi
92
+ if [ -n "$index_file" ] && grep -qF "reference/$base" "$index_file"; then
93
+ continue
71
94
  fi
95
+ echo "✗ Unreachable reference body: $ref_file" >&2
96
+ echo " Nothing eager points at it. Give it either:" >&2
97
+ echo " - a paired head at $eager_dir/$base, or" >&2
98
+ echo " - a line naming reference/$base in $eager_dir/00-rule-index.md" >&2
99
+ echo " Prefer the index — a head is loaded into every session and every" >&2
100
+ echo " subagent, whether or not anything asks for it." >&2
101
+ failed=1
72
102
  done < <(find "$reference_dir" -maxdepth 1 -type f -name '*.md' | sort)
73
103
  fi
74
104
  }
@@ -80,9 +110,10 @@ done < <(find "$ROOT_DIR/plugins" -type d -name rules | sort)
80
110
 
81
111
  if [ "$failed" -ne 0 ]; then
82
112
  echo "" >&2
83
- echo " Every rules/eager/X.md must have a paired rules/reference/X.md" >&2
84
- echo " (and vice versa) so the eager head's breadcrumb to the full" >&2
85
- echo " reference body resolves. To exempt a file, either name it" >&2
113
+ echo " Every rules/eager/X.md must have a paired rules/reference/X.md so" >&2
114
+ echo " its breadcrumb resolves, and every rules/reference/X.md must be" >&2
115
+ echo " reachable from either its own head or the eager rule index." >&2
116
+ echo " To exempt a file, either name it" >&2
86
117
  echo " 00-*.md (bootstrap convention) or list its basename in" >&2
87
118
  echo " <plugin>/rules/.pair-exempt (one per line)." >&2
88
119
  exit 1