@1aboveio/skills 0.10.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 (397) hide show
  1. package/LICENSE +3 -0
  2. package/README.md +90 -0
  3. package/bin/1aboveio-skills.mjs +18 -0
  4. package/package.json +28 -0
  5. package/runtime/skills/distribution/generated/recipes.json +1189 -0
  6. package/runtime/skills/distribution/scripts/bundles.mjs +280 -0
  7. package/runtime/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
  8. package/skills/backend/airflow-dag-develop/LICENSE +3 -0
  9. package/skills/backend/airflow-dag-develop/SKILL.md +111 -0
  10. package/skills/backend/app-debug/LICENSE +3 -0
  11. package/skills/backend/app-debug/SKILL.md +109 -0
  12. package/skills/backend/app-debug/references/common-errors.md +128 -0
  13. package/skills/backend/python-backend/LICENSE +3 -0
  14. package/skills/backend/python-backend/SKILL.md +326 -0
  15. package/skills/cicd-pipeline/cloud-build/LICENSE +3 -0
  16. package/skills/cicd-pipeline/cloud-build/SKILL.md +707 -0
  17. package/skills/cicd-pipeline/cloud-debug/LICENSE +3 -0
  18. package/skills/cicd-pipeline/cloud-debug/SKILL.md +316 -0
  19. package/skills/cicd-pipeline/cloud-debug/references/build-failures.md +238 -0
  20. package/skills/cicd-pipeline/cloud-debug/references/deploy-failures.md +376 -0
  21. package/skills/cicd-pipeline/cloud-debug/references/pipeline-failures.md +378 -0
  22. package/skills/cicd-pipeline/cloud-deploy/LICENSE +3 -0
  23. package/skills/cicd-pipeline/cloud-deploy/SKILL.md +229 -0
  24. package/skills/cicd-pipeline/cloud-deploy/references/config-templates.md +257 -0
  25. package/skills/cicd-pipeline/docker/LICENSE +3 -0
  26. package/skills/cicd-pipeline/docker/SKILL.md +126 -0
  27. package/skills/cicd-pipeline/google-cloud/LICENSE +3 -0
  28. package/skills/cicd-pipeline/google-cloud/SKILL.md +118 -0
  29. package/skills/cicd-pipeline/google-cloud/references/gcs.md +469 -0
  30. package/skills/cicd-pipeline/google-cloud/references/iam.md +451 -0
  31. package/skills/cicd-pipeline/google-cloud/references/project.md +349 -0
  32. package/skills/cicd-pipeline/google-cloud/references/secrets.md +336 -0
  33. package/skills/cicd-pipeline/google-cloud/references/vpc.md +312 -0
  34. package/skills/cicd-pipeline/google-cloud/scripts/create-sa.sh +36 -0
  35. package/skills/cicd-pipeline/google-cloud/scripts/gcp-config.sh +31 -0
  36. package/skills/cicd-pipeline/google-cloud/scripts/grant-iap.sh +41 -0
  37. package/skills/cicd-pipeline/google-cloud/scripts/setup-secrets.sh +48 -0
  38. package/skills/cicd-pipeline/mergify/LICENSE +3 -0
  39. package/skills/cicd-pipeline/mergify/SKILL.md +138 -0
  40. package/skills/cicd-pipeline/mergify/assets/templates/mergify.yml +237 -0
  41. package/skills/cicd-pipeline/mergify/assets/templates/ruleset.json +46 -0
  42. package/skills/cicd-pipeline/mergify/references/branch-protection.md +277 -0
  43. package/skills/cicd-pipeline/mergify/references/configuration.md +183 -0
  44. package/skills/cicd-pipeline/mergify/references/diagnosis.md +73 -0
  45. package/skills/cicd-pipeline/mergify/references/traps.md +78 -0
  46. package/skills/cicd-pipeline/mergify/references/watch-contract.md +218 -0
  47. package/skills/cicd-pipeline/mergify/scripts/audit-core.mjs +131 -0
  48. package/skills/cicd-pipeline/mergify/scripts/audit.mjs +4 -0
  49. package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery-core.mjs +663 -0
  50. package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery.mjs +4 -0
  51. package/skills/cicd-pipeline/podman/LICENSE +3 -0
  52. package/skills/cicd-pipeline/podman/SKILL.md +70 -0
  53. package/skills/cicd-pipeline/podman/agents/openai.yaml +4 -0
  54. package/skills/cicd-pipeline/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
  55. package/skills/cicd-pipeline/podman/assets/templates/podman-service-override.conf +3 -0
  56. package/skills/cicd-pipeline/podman/references/compose-compatibility.md +70 -0
  57. package/skills/cicd-pipeline/podman/references/networking-and-ports.md +74 -0
  58. package/skills/cicd-pipeline/podman/references/rootless-services-and-sockets.md +156 -0
  59. package/skills/cicd-pipeline/podman/references/troubleshooting.md +98 -0
  60. package/skills/engineering/e2e-test/LICENSE +3 -0
  61. package/skills/engineering/e2e-test/SKILL.md +156 -0
  62. package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +272 -0
  63. package/skills/engineering/e2e-test/assets/ci-gates.github.yml +451 -0
  64. package/skills/engineering/e2e-test/assets/e2e-workflow.yml +282 -0
  65. package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
  66. package/skills/engineering/e2e-test/references/authoring/playwright-config.md +71 -0
  67. package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +219 -0
  68. package/skills/engineering/e2e-test/references/authoring/test-skipping.md +44 -0
  69. package/skills/engineering/e2e-test/references/ci-integration.md +121 -0
  70. package/skills/engineering/e2e-test/references/ci-playwright-container.md +280 -0
  71. package/skills/engineering/e2e-test/references/debugging.md +36 -0
  72. package/skills/engineering/e2e-test/references/presentation-sweep.md +131 -0
  73. package/skills/engineering/e2e-test/references/reviewing.md +39 -0
  74. package/skills/engineering/e2e-test/references/route-discovery.md +50 -0
  75. package/skills/engineering/e2e-test/references/route-manifest.md +44 -0
  76. package/skills/engineering/e2e-test/scripts/detect-routes-fastapi.py +290 -0
  77. package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
  78. package/skills/engineering/e2e-test/scripts/post-visual-evidence.mjs +158 -0
  79. package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
  80. package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
  81. package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
  82. package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
  83. package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +148 -0
  84. package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
  85. package/skills/engineering/e2e-test/scripts/presentation-visual.mjs +84 -0
  86. package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +75 -0
  87. package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
  88. package/skills/engineering/engineering-runtime/LICENSE +3 -0
  89. package/skills/engineering/engineering-runtime/SKILL.md +10 -0
  90. package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
  91. package/skills/engineering/engineering-runtime/coherence/workflow.json +553 -0
  92. package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
  93. package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
  94. package/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
  95. package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
  96. package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
  97. package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
  98. package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
  99. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +576 -0
  100. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +166 -0
  101. package/skills/engineering/ensure-coverage/LICENSE +3 -0
  102. package/skills/engineering/ensure-coverage/SKILL.md +136 -0
  103. package/skills/engineering/ensure-coverage/evals/evals.json +125 -0
  104. package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +91 -0
  105. package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +83 -0
  106. package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
  107. package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
  108. package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
  109. package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +25 -0
  110. package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
  111. package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +87 -0
  112. package/skills/engineering/ensure-coverage/references/depth/test-smells.md +23 -0
  113. package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +164 -0
  114. package/skills/engineering/ensure-coverage/references/enforcement/hooks.md +85 -0
  115. package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +109 -0
  116. package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +33 -0
  117. package/skills/engineering/ensure-coverage/references/presentation/axis.md +78 -0
  118. package/skills/engineering/ensure-coverage/references/presentation/runner-contract.md +74 -0
  119. package/skills/engineering/ensure-coverage/references/process/audit-mode.md +33 -0
  120. package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
  121. package/skills/engineering/ensure-coverage/references/process/review-contract-template.md +119 -0
  122. package/skills/engineering/ensure-coverage/references/process/scope-class.md +178 -0
  123. package/skills/engineering/ensure-coverage/references/process/test-strategy.md +55 -0
  124. package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +45 -0
  125. package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +93 -0
  126. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +178 -0
  127. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +101 -0
  128. package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +358 -0
  129. package/skills/engineering/ensure-coverage/scripts/coverage-checklist.mjs +494 -0
  130. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +663 -0
  131. package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
  132. package/skills/engineering/ensure-coverage/scripts/evidence-block.mjs +367 -0
  133. package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +269 -0
  134. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
  135. package/skills/engineering/ensure-coverage/scripts/package-lock.json +76 -0
  136. package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
  137. package/skills/engineering/ensure-coverage/scripts/scope-class.mjs +554 -0
  138. package/skills/engineering/harness-runtime/LICENSE +3 -0
  139. package/skills/engineering/harness-runtime/SKILL.md +18 -0
  140. package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
  141. package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
  142. package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
  143. package/skills/engineering/harness-runtime/contracts.md +15 -0
  144. package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
  145. package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
  146. package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
  147. package/skills/engineering/harness-runtime/index.mjs +767 -0
  148. package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
  149. package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
  150. package/skills/engineering/harness-runtime/references/model-catalog-seed.json +159 -0
  151. package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
  152. package/skills/engineering/implement-and-pr/LICENSE +3 -0
  153. package/skills/engineering/implement-and-pr/SKILL.md +176 -0
  154. package/skills/engineering/implement-and-pr/references/ci-iteration.md +10 -0
  155. package/skills/engineering/implement-and-pr/references/closeout.md +27 -0
  156. package/skills/engineering/implement-and-pr/references/contract-complete-fix-rounds.md +34 -0
  157. package/skills/engineering/implement-and-pr/references/evidence-rules.md +39 -0
  158. package/skills/engineering/implement-and-pr/references/incremental-plan.md +16 -0
  159. package/skills/engineering/implement-and-pr/references/self-review.md +23 -0
  160. package/skills/engineering/implement-and-pr/references/tdd-mode.md +18 -0
  161. package/skills/engineering/resolve-issues/LICENSE +3 -0
  162. package/skills/engineering/resolve-issues/SKILL.md +167 -0
  163. package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +448 -0
  164. package/skills/engineering/resolve-issues/references/breaker.md +82 -0
  165. package/skills/engineering/resolve-issues/references/deliverables.md +27 -0
  166. package/skills/engineering/resolve-issues/references/delivery.md +108 -0
  167. package/skills/engineering/resolve-issues/references/evidence-lane.md +21 -0
  168. package/skills/engineering/resolve-issues/references/exact-head-ci.md +287 -0
  169. package/skills/engineering/resolve-issues/references/fan-out.md +33 -0
  170. package/skills/engineering/resolve-issues/references/finalization.md +68 -0
  171. package/skills/engineering/resolve-issues/references/guarantees.md +10 -0
  172. package/skills/engineering/resolve-issues/references/high-risk.md +29 -0
  173. package/skills/engineering/resolve-issues/references/incidents/848/README.md +156 -0
  174. package/skills/engineering/resolve-issues/references/intake.md +86 -0
  175. package/skills/engineering/resolve-issues/references/integration-gate.md +53 -0
  176. package/skills/engineering/resolve-issues/references/interference.md +87 -0
  177. package/skills/engineering/resolve-issues/references/loop.md +134 -0
  178. package/skills/engineering/resolve-issues/references/model-catalog.md +9 -0
  179. package/skills/engineering/resolve-issues/references/postmortem.md +27 -0
  180. package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +41 -0
  181. package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +48 -0
  182. package/skills/engineering/resolve-issues/references/pre-flight.md +41 -0
  183. package/skills/engineering/resolve-issues/references/regression-checklist.md +26 -0
  184. package/skills/engineering/resolve-issues/references/run-state.md +288 -0
  185. package/skills/engineering/resolve-issues/references/sandboxed-testing.md +48 -0
  186. package/skills/engineering/resolve-issues/references/spawn-contract.md +96 -0
  187. package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +40 -0
  188. package/skills/engineering/resolve-issues/references/why.md +653 -0
  189. package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +49 -0
  190. package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +721 -0
  191. package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +962 -0
  192. package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +220 -0
  193. package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +420 -0
  194. package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +256 -0
  195. package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +168 -0
  196. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
  197. package/skills/engineering/resolve-issues/scripts/doctrine.mjs +62 -0
  198. package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +191 -0
  199. package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +413 -0
  200. package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +332 -0
  201. package/skills/engineering/resolve-issues/scripts/finalize.mjs +488 -0
  202. package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +3307 -0
  203. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +16 -0
  204. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +51 -0
  205. package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +17 -0
  206. package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +166 -0
  207. package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +92 -0
  208. package/skills/engineering/resolve-issues/scripts/guide-index.mjs +73 -0
  209. package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +159 -0
  210. package/skills/engineering/resolve-issues/scripts/interference.mjs +427 -0
  211. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
  212. package/skills/engineering/resolve-issues/scripts/next-operations.mjs +419 -0
  213. package/skills/engineering/resolve-issues/scripts/postmortem.mjs +909 -0
  214. package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +322 -0
  215. package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +415 -0
  216. package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +697 -0
  217. package/skills/engineering/resolve-issues/scripts/report.mjs +494 -0
  218. package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +172 -0
  219. package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +79 -0
  220. package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +334 -0
  221. package/skills/engineering/resolve-issues/scripts/run-state.mjs +4784 -0
  222. package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +395 -0
  223. package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +290 -0
  224. package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +170 -0
  225. package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +293 -0
  226. package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +197 -0
  227. package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +127 -0
  228. package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +893 -0
  229. package/skills/engineering/resolve-issues/scripts/workspaces.mjs +829 -0
  230. package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +290 -0
  231. package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +80 -0
  232. package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +102 -0
  233. package/skills/engineering/resolve-release/LICENSE +3 -0
  234. package/skills/engineering/resolve-release/SKILL.md +112 -0
  235. package/skills/engineering/resolve-release/references/assembly.md +137 -0
  236. package/skills/engineering/resolve-release/references/auto-when-green.md +56 -0
  237. package/skills/engineering/resolve-release/references/candidate.md +167 -0
  238. package/skills/engineering/resolve-release/references/exposure.md +178 -0
  239. package/skills/engineering/resolve-release/references/handoff.md +24 -0
  240. package/skills/engineering/resolve-release/references/postmortem.md +230 -0
  241. package/skills/engineering/resolve-release/references/preflight.md +207 -0
  242. package/skills/engineering/resolve-release/references/principles.md +94 -0
  243. package/skills/engineering/resolve-release/references/regression-checklist.md +36 -0
  244. package/skills/engineering/resolve-release/references/related-skills.md +12 -0
  245. package/skills/engineering/resolve-release/references/routing.md +149 -0
  246. package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +285 -0
  247. package/skills/engineering/resolve-release/references/versioning.md +202 -0
  248. package/skills/engineering/resolve-release/references/why.md +53 -0
  249. package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +389 -0
  250. package/skills/engineering/resolve-release/scripts/build-changes.mjs +209 -0
  251. package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +407 -0
  252. package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +904 -0
  253. package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +81 -0
  254. package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +612 -0
  255. package/skills/engineering/resolve-release/scripts/close-attempt.mjs +135 -0
  256. package/skills/engineering/resolve-release/scripts/closeout-release.mjs +161 -0
  257. package/skills/engineering/resolve-release/scripts/doctrine.mjs +106 -0
  258. package/skills/engineering/resolve-release/scripts/durable-processing.mjs +522 -0
  259. package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +659 -0
  260. package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +1014 -0
  261. package/skills/engineering/resolve-release/scripts/finalize-release.mjs +526 -0
  262. package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +169 -0
  263. package/skills/engineering/resolve-release/scripts/green-gate.mjs +599 -0
  264. package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +752 -0
  265. package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +56 -0
  266. package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +1538 -0
  267. package/skills/engineering/resolve-release/scripts/postmortem.mjs +381 -0
  268. package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +498 -0
  269. package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +326 -0
  270. package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +272 -0
  271. package/skills/engineering/resolve-release/scripts/report.mjs +417 -0
  272. package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +114 -0
  273. package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +238 -0
  274. package/skills/engineering/resolve-release/scripts/review-packet.mjs +503 -0
  275. package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +263 -0
  276. package/skills/engineering/resolve-release/scripts/version-assert.mjs +339 -0
  277. package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +485 -0
  278. package/skills/engineering/resolve-release/scripts/version.mjs +1199 -0
  279. package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +449 -0
  280. package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +21 -0
  281. package/skills/engineering/resolve-release/vendor/README.md +60 -0
  282. package/skills/engineering/resolve-release/vendor/acorn.mjs +6233 -0
  283. package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +21 -0
  284. package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +341 -0
  285. package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +70 -0
  286. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +2 -0
  287. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +1 -0
  288. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +7 -0
  289. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +1 -0
  290. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +2033 -0
  291. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +2033 -0
  292. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +7 -0
  293. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +1 -0
  294. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +2 -0
  295. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +1 -0
  296. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +2 -0
  297. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +1 -0
  298. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +2 -0
  299. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +1 -0
  300. package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +49 -0
  301. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +21 -0
  302. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +5 -0
  303. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +549 -0
  304. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +549 -0
  305. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +2 -0
  306. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +1 -0
  307. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +2 -0
  308. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +1 -0
  309. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +36 -0
  310. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +47 -0
  311. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +82 -0
  312. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +11 -0
  313. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +22 -0
  314. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +21 -0
  315. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +21 -0
  316. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
  317. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +11 -0
  318. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +25 -0
  319. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
  320. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +85 -0
  321. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +85 -0
  322. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +2 -0
  323. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +1 -0
  324. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +2 -0
  325. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +1 -0
  326. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +20 -0
  327. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +20 -0
  328. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +2 -0
  329. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +1 -0
  330. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +2 -0
  331. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +1 -0
  332. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +61 -0
  333. package/skills/engineering/review-pr/LICENSE +3 -0
  334. package/skills/engineering/review-pr/SKILL.md +123 -0
  335. package/skills/engineering/review-pr/references/adversarial-reviewer-prompt.md +42 -0
  336. package/skills/engineering/review-pr/references/code-correctness.md +5 -0
  337. package/skills/engineering/review-pr/references/contract-freshness.md +9 -0
  338. package/skills/engineering/review-pr/references/coordination.md +18 -0
  339. package/skills/engineering/review-pr/references/domain-hazards.md +123 -0
  340. package/skills/engineering/review-pr/references/finding-themes.md +7 -0
  341. package/skills/engineering/review-pr/references/github-posting.md +98 -0
  342. package/skills/engineering/review-pr/references/golden-path-smoke.md +5 -0
  343. package/skills/engineering/review-pr/references/incremental-output.md +16 -0
  344. package/skills/engineering/review-pr/references/inputs-and-discovery.md +31 -0
  345. package/skills/engineering/review-pr/references/output-format.md +99 -0
  346. package/skills/engineering/review-pr/references/over-mock-screen.md +7 -0
  347. package/skills/engineering/review-pr/references/promotion-prs.md +16 -0
  348. package/skills/engineering/review-pr/references/re-review.md +18 -0
  349. package/skills/engineering/review-pr/references/review-method.md +199 -0
  350. package/skills/engineering/review-pr/references/review-mode.md +30 -0
  351. package/skills/engineering/review-pr/references/review-posture.md +53 -0
  352. package/skills/engineering/review-pr/references/round1-depth.md +62 -0
  353. package/skills/engineering/review-pr/references/scripts.md +17 -0
  354. package/skills/engineering/review-pr/references/workflow.md +16 -0
  355. package/skills/engineering/review-pr/schemas/findings.schema.json +282 -0
  356. package/skills/engineering/review-pr/scripts/finding-contract.mjs +285 -0
  357. package/skills/engineering/review-pr/scripts/post-review.mjs +405 -0
  358. package/skills/engineering/review-pr/scripts/pr-context.mjs +207 -0
  359. package/skills/engineering/review-pr/scripts/scan-diff.mjs +365 -0
  360. package/skills/engineering/review-pr/scripts/theme-contract.mjs +57 -0
  361. package/skills/engineering/smoke/LICENSE +3 -0
  362. package/skills/engineering/smoke/SKILL.md +131 -0
  363. package/skills/engineering/smoke/assets/smoke.manifest.example.json +53 -0
  364. package/skills/engineering/smoke/references/manifest.md +192 -0
  365. package/skills/engineering/smoke/scripts/smoke.mjs +713 -0
  366. package/skills/fullstack/better-auth/LICENSE +3 -0
  367. package/skills/fullstack/better-auth/SKILL.md +601 -0
  368. package/skills/fullstack/better-auth/references/feishu-api.md +270 -0
  369. package/skills/fullstack/monorepo/LICENSE +3 -0
  370. package/skills/fullstack/monorepo/SKILL.md +465 -0
  371. package/skills/fullstack/nextjs-fullstack/LICENSE +3 -0
  372. package/skills/fullstack/nextjs-fullstack/SKILL.md +210 -0
  373. package/skills/fullstack/nextjs-fullstack/conventions.md +318 -0
  374. package/skills/fullstack/nextjs-fullstack/frontend-conventions.md +61 -0
  375. package/skills/fullstack/nextjs-fullstack/nextjs16.md +287 -0
  376. package/skills/fullstack/nextjs-fullstack/server-actions.md +409 -0
  377. package/skills/fullstack/prisma-setup/LICENSE +3 -0
  378. package/skills/fullstack/prisma-setup/SKILL.md +180 -0
  379. package/skills/fullstack/prisma-setup/nextjs.md +258 -0
  380. package/skills/fullstack/prisma-setup/turborepo.md +301 -0
  381. package/skills/fullstack/shadcn/LICENSE +3 -0
  382. package/skills/fullstack/shadcn/SKILL.md +119 -0
  383. package/skills/fullstack/shadcn/assets/shadcn-small.png +0 -0
  384. package/skills/fullstack/shadcn/assets/shadcn.png +0 -0
  385. package/skills/fullstack/shadcn/cli.md +411 -0
  386. package/skills/fullstack/shadcn/customization.md +224 -0
  387. package/skills/fullstack/shadcn/evals/evals.json +90 -0
  388. package/skills/fullstack/shadcn/mcp.md +101 -0
  389. package/skills/fullstack/shadcn/rules/base-vs-radix.md +323 -0
  390. package/skills/fullstack/shadcn/rules/component-selection.md +67 -0
  391. package/skills/fullstack/shadcn/rules/composition.md +195 -0
  392. package/skills/fullstack/shadcn/rules/data-table.md +201 -0
  393. package/skills/fullstack/shadcn/rules/forms.md +255 -0
  394. package/skills/fullstack/shadcn/rules/icons.md +103 -0
  395. package/skills/fullstack/shadcn/rules/styling.md +167 -0
  396. package/skills/fullstack/zod-v4/LICENSE +3 -0
  397. package/skills/fullstack/zod-v4/SKILL.md +287 -0
@@ -0,0 +1,663 @@
1
+ #!/usr/bin/env node
2
+ // Coverage Ledger reference tool for the ensure-coverage skill.
3
+ //
4
+ // node coverage-ledger.mjs aggregate # build the computed coverage-index
5
+ // node coverage-ledger.mjs validate # run reconciliation checks, exit non-zero on errors
6
+ // node coverage-ledger.mjs report # render a human-readable index + ADR matrix view
7
+ //
8
+ // It is framework-agnostic: it consumes a generated surface inventory (see
9
+ // references/breadth/inventory-contract.md) plus the curated per-requirement
10
+ // `*.coverage.yml` tree, and resolves evidence from `@covers` annotations in
11
+ // tests. Surface DISCOVERY is repo-specific and is NOT done here — supply the
12
+ // inventory via your own adapter (a reference Next.js one ships in adapters/).
13
+ //
14
+ // Curated files may be .yml/.yaml (needs `js-yaml`) or .json (zero-dep).
15
+
16
+ import fs from 'node:fs'
17
+ import path from 'node:path'
18
+ import process from 'node:process'
19
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
20
+
21
+ // ---------- config ----------
22
+
23
+ const DEFAULTS = {
24
+ inventory: 'docs/tests/_generated/surface-inventory.json',
25
+ coverageDir: 'docs/tests',
26
+ testRoots: ['.'],
27
+ indexOut: 'docs/tests/_generated/coverage-index.json',
28
+ reportOut: 'docs/tests/_generated/coverage-index.md',
29
+ // Optional acceptance-gate inputs. Left empty -> gate is skipped with a notice.
30
+ requirementGlobs: {}, // e.g. { adr: 'docs/adr', prd: 'docs/prd' }
31
+ acceptedMarker: /status:\s*accepted/i,
32
+ // A statically-adequate but un-attested test lands at `Unverified`. Doctrine
33
+ // makes that a BLOCKING state (a yellow that fails CI until a human attests
34
+ // via `verified: true`). Set false to phase in (downgrade Unverified to warn).
35
+ unverifiedBlocks: true,
36
+ }
37
+
38
+ const IGNORE_DIRS = new Set(['node_modules', '.git', '.next', 'dist', 'build', 'coverage'])
39
+ const TEST_RE = /\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs)$/
40
+ // Internal interception / first-party mocking that makes a surface LOOK covered
41
+ // while proving only proxy wiring. Playwright route faking plus MSW server/worker
42
+ // setup (MSW handlers for first-party paths are the non-Playwright vector).
43
+ const INTERCEPT_RE = /\broute\.fulfill\b|\bpage\.route\s*\(|\bsetupServer\s*\(|\bsetupWorker\s*\(|from\s+['"]msw['"]/
44
+ // Real-rendered-page evidence: a Presentation invariant must be proven against a
45
+ // browser that actually lays out and paints the page. These markers signal a real
46
+ // browser drive (Playwright). Their ABSENCE on a presentation-level test means the
47
+ // claim rests on the DOM/accessibility tree only — the axis's over-mock.
48
+ const BROWSER_RE = /@playwright\/test|\bplaywright\b|\bpage\.(goto|locator|evaluate|getByRole|getByText|getByLabel|setViewportSize|click|fill|type|press|check)\b|\bboundingBox\b|\btoHaveScreenshot\b/
49
+ // JSDOM / Testing-Library render: a non-painting DOM. Useful as an explicit
50
+ // negative signal for the DOM-only over-mock note.
51
+ const DOMONLY_RE = /\bjsdom\b|@testing-library|environment['"\s:]+jsdom/
52
+
53
+ // ---------- surface baseline contract ----------
54
+ //
55
+ // Every inventory surface carries a minimum required-level FLOOR keyed on its
56
+ // (kind, class). `class` is mechanically derived by the adapter (read/detail/
57
+ // mutation); humans may only RAISE the floor (curated requiredLevels, sensitive)
58
+ // or EXCLUDE a surface (gated below). Internal surfaces carry no floor — they are
59
+ // proven transitively and only dead-surface-warned when nothing reaches them.
60
+ // Presentation axis (third coverage axis — can a human perceive/act on the page,
61
+ // not just does the DOM exist). Every live browser route auto-carries a presentation
62
+ // floor on top of its functional browser-journey floor.
63
+ //
64
+ // This file GRADES presentation levels (does a qualifying real-render test exist,
65
+ // is it non-intercepting, is it attested) — it does NOT render the page. The
66
+ // instruments that actually open a browser and observe these invariants live in
67
+ // the e2e-test skill's presentation sweep; ensure-coverage owns the CONTRACT
68
+ // (references/presentation/runner-contract.md), not the runner.
69
+ //
70
+ // Levels, spine first:
71
+ // render-health — the page renders without a pageerror / console.error /
72
+ // error boundary / HTTP >=500. The cheapest, most foundational
73
+ // catch (an uncaught TypeError on mount fails here first), so it
74
+ // is the SPINE of the axis.
75
+ // spatial — no unintended horizontal overflow / element overlap.
76
+ // perceivability — contrast / invisible controls; negative: wrong-role/absent.
77
+ // reachability — navigational reachability + actionable affordance cues.
78
+ // temporal — layout shift (first-party hermetic; third-party sandbox lane).
79
+ const PRESENTATION_ROUTE_FLOOR = ['presentation:render-health', 'presentation:spatial', 'presentation:perceivability', 'presentation:reachability', 'presentation:temporal']
80
+ // The condition-matrix floor the presentation instruments run at. Feature-derived
81
+ // escalation adds cells (dark theme, RTL, …); see the Presentation axis section of
82
+ // SKILL.md. Kept here so the floor is one source of truth for the e2e instrument.
83
+ const PRESENTATION_VIEWPORT_FLOOR = [375, 1280]
84
+
85
+ const BASELINE = {
86
+ // browser routes: every live page must be driven (browser-journey floor);
87
+ // class only changes WHAT the journey must additionally assert (durable state
88
+ // for mutation, seeded entity for detail) — enforced via depth refutation.
89
+ // Every route also carries the Presentation floor (see PRESENTATION_ROUTE_FLOOR).
90
+ route: {
91
+ read: ['browser-journey', ...PRESENTATION_ROUTE_FLOOR],
92
+ detail: ['browser-journey', ...PRESENTATION_ROUTE_FLOOR],
93
+ mutation: ['browser-journey', ...PRESENTATION_ROUTE_FLOOR],
94
+ _default: ['browser-journey', ...PRESENTATION_ROUTE_FLOOR],
95
+ },
96
+ // bare API endpoints (no browser route): contract floor; writes also need durable-state.
97
+ api: { read: ['api'], mutation: ['api', 'integration'], _default: ['api'] },
98
+ permission: { _default: ['integration'] },
99
+ integration: { _default: ['integration'] },
100
+ // internal surfaces — no independent baseline obligation.
101
+ table: { _default: [] },
102
+ event: { _default: [] },
103
+ job: { _default: [] },
104
+ }
105
+ const INTERNAL_KINDS = new Set(['table', 'event', 'job'])
106
+
107
+ function baselineFor(kind, cls) {
108
+ const k = BASELINE[kind]
109
+ if (!k) return []
110
+ return k[cls] || k._default || []
111
+ }
112
+
113
+ // `sensitive` (human-declared escalation): credential/admin/settings surfaces
114
+ // add a contract + auth obligation on top of the mechanical floor.
115
+ function sensitiveExtra(kind) {
116
+ if (kind === 'route') return ['api'] // page journey + API contract; auth asserted at depth
117
+ return []
118
+ }
119
+
120
+ function strictest(...levelLists) {
121
+ return [...new Set(levelLists.flat().filter(Boolean))]
122
+ }
123
+
124
+ // Exclusion is gated by adapter-confirmed signals, never by a human reason alone:
125
+ // a reachable write surface can NEVER be excluded below its journey floor.
126
+ function exclusionAllowed(kind, cls, reachability) {
127
+ if (kind === 'route') {
128
+ if (cls === 'read') return { ok: true } // static/content read pages may drop to render-health
129
+ if (reachability === 'blocked' || reachability === 'unreachable') return { ok: true }
130
+ return { ok: false, why: `reachable ${cls} route cannot be excluded (reachability=${reachability || 'unknown'}); a reachable write surface must keep its journey floor` }
131
+ }
132
+ if (kind === 'api') {
133
+ if (cls === 'mutation') return { ok: false, why: 'mutation API cannot be excluded' }
134
+ return { ok: true }
135
+ }
136
+ return { ok: true } // internal kinds are excludable
137
+ }
138
+
139
+ // A required level is "behavior" (not satisfiable by unit/mock alone).
140
+ const BEHAVIOR_LEVELS = new Set([
141
+ 'pg-api', 'api', 'integration', 'browser-journey', 'journey', 'ui-e2e', 'e2e',
142
+ 'no-secret-assertion',
143
+ ])
144
+ // Presentation levels are proven against a REAL rendered page (not the DOM tree).
145
+ // They get a stricter "clean test" rule than ordinary behavior levels: the proving
146
+ // test must drive a real browser (BROWSER_RE); a DOM-only/JSDOM test is Over-mocked.
147
+ // `render-health` is the spine (page renders without pageerror/console.error/error
148
+ // boundary/5xx); spatial, perceivability, reachability, temporal sit on top. These
149
+ // are the GRADED tokens; the e2e-test presentation sweep is the runner that emits
150
+ // pass/fail per (route, level) — see references/presentation/runner-contract.md.
151
+ const PRESENTATION_LEVELS = new Set([
152
+ 'presentation:render-health',
153
+ 'presentation:spatial',
154
+ 'presentation:perceivability',
155
+ 'presentation:reachability',
156
+ 'presentation:temporal',
157
+ // `presentation:visual` is a graded presentation token like the rest, but it is
158
+ // OPT-IN, not part of the route floor (PRESENTATION_ROUTE_FLOOR above): a surface
159
+ // carries it only by declaring it in its curated requiredLevels. A pixel baseline
160
+ // is a maintenance liability, so which routes earn one is a per-surface breadth
161
+ // call — see references/presentation/runner-contract.md ("the opt-in level").
162
+ 'presentation:visual',
163
+ // `presentation:fidelity` is the other opt-in token, and the only one that is not
164
+ // SELF-referential: every level above (visual included) takes the implementation
165
+ // as its own authority, so a page built wrong from a right design passes all of
166
+ // them — a pixel baseline shot of the wrong page is green forever. Fidelity asks
167
+ // whether the surface matches the design source it was DERIVED from, so a surface
168
+ // carries it only when its work item cites one (prototype/mockup/layout-fixing
169
+ // doc). Proven by a real-browser spec that drives `design-parity.mjs probe` and
170
+ // asserts via `design-parity.mjs validate --observed` — see
171
+ // references/presentation/axis.md ("presentation:fidelity").
172
+ 'presentation:fidelity',
173
+ ])
174
+
175
+ // Any `presentation:*` level (incl. cell-qualified ones like
176
+ // `presentation:perceivability@dark`) is a presentation level — proven against a
177
+ // real rendered page. Prefix-based so feature-derived escalation can mint new
178
+ // condition cells without enumerating every combination here.
179
+ function isPresentationLevel(level) {
180
+ return typeof level === 'string' && level.startsWith('presentation:')
181
+ }
182
+
183
+ // Feature-derived escalation (Dimension B of the condition matrix). The adapter
184
+ // emits deterministic per-surface `signals`; each signal RAISES the floor by adding
185
+ // cell-qualified presentation levels. Pure + deterministic: same signals → same
186
+ // levels (sorted), so two agents on the same code compute identical obligations.
187
+ // - themeToggle → perceivability must also hold in dark theme
188
+ // - i18n → spatial must also hold under a long locale and RTL
189
+ function presentationEscalation(signals = {}) {
190
+ const out = []
191
+ if (signals.themeToggle) out.push('presentation:perceivability@dark')
192
+ if (signals.i18n) out.push('presentation:spatial@long-locale', 'presentation:spatial@rtl')
193
+ return [...new Set(out)].sort()
194
+ }
195
+
196
+ // Gated removal of an escalated cell (Dimension B exclusion). A cell may be dropped
197
+ // only with a `category` + `reason`; the mechanical base floor (unqualified levels)
198
+ // can NEVER be removed this way, and every removal is recorded for the census.
199
+ // `cellExclusions` is a map: { 'presentation:perceivability@dark': { category, reason } }.
200
+ function applyCellExclusions(levels, cellExclusions = {}) {
201
+ const kept = [], census = [], rejected = []
202
+ for (const lvl of levels) {
203
+ const ex = cellExclusions[lvl]
204
+ if (!ex) { kept.push(lvl); continue }
205
+ if (!lvl.includes('@')) { rejected.push({ level: lvl, why: 'cannot exclude a base (unqualified) floor level' }); kept.push(lvl); continue }
206
+ if (!ex.category || !ex.reason) { rejected.push({ level: lvl, why: 'cell exclusion needs category + reason' }); kept.push(lvl); continue }
207
+ census.push({ level: lvl, category: ex.category, reason: ex.reason })
208
+ }
209
+ return { kept, census, rejected }
210
+ }
211
+
212
+ // Synonyms: a test level satisfies a required level if it intersects this set.
213
+ const SYNONYMS = {
214
+ 'pg-api': ['pg-api', 'integration', 'api', 'db'],
215
+ 'api': ['api', 'pg-api', 'integration'],
216
+ 'integration': ['integration', 'pg-api', 'api', 'db'],
217
+ 'browser-journey': ['browser-journey', 'journey', 'e2e', 'ui-e2e'],
218
+ 'journey': ['journey', 'browser-journey', 'e2e', 'ui-e2e'],
219
+ 'ui-e2e': ['ui-e2e', 'e2e', 'browser-journey', 'journey'],
220
+ 'e2e': ['e2e', 'journey', 'browser-journey', 'ui-e2e'],
221
+ 'unit': ['unit'],
222
+ // `render-health` is a minimal BROWSER reachability/render floor (NOT a behavior
223
+ // level — see BEHAVIOR_LEVELS — and deliberately NOT satisfiable by `api`: a real
224
+ // browser must load the page and render it, not just return HTTP 200). A fuller
225
+ // browser journey also satisfies it. This is the breadth-floor sibling of the
226
+ // presentation spine `presentation:render-health` and of e2e-test's Tier-1
227
+ // (Render-health). `smoke` is kept as a back-compat alias (same floor, old name).
228
+ 'render-health': ['render-health', 'smoke', 'browser-journey', 'journey', 'ui-e2e', 'e2e'],
229
+ 'smoke': ['render-health', 'smoke', 'browser-journey', 'journey', 'ui-e2e', 'e2e'],
230
+ 'no-secret-assertion': ['no-secret-assertion'],
231
+ // Presentation levels are exact-match: a `presentation:spatial` obligation is
232
+ // only satisfied by a test that declares `@level presentation:spatial`. A
233
+ // functional browser-journey does NOT satisfy it (a journey clicks the DOM; it
234
+ // does not assert the page is laid out without overflow).
235
+ 'presentation:spatial': ['presentation:spatial'],
236
+ 'presentation:perceivability': ['presentation:perceivability'],
237
+ 'presentation:reachability': ['presentation:reachability'],
238
+ 'presentation:temporal': ['presentation:temporal'],
239
+ // Opt-in pixel baseline — exact-match like its siblings (a functional journey or
240
+ // any other presentation level does not satisfy it). Only present on a surface
241
+ // whose curated requiredLevels raised it in.
242
+ 'presentation:visual': ['presentation:visual'],
243
+ // Exact-match for the same reason, and one more: `presentation:visual` is the
244
+ // level most likely to be offered as a substitute ("we have a baseline, isn't
245
+ // that the same check?"). It is not — a baseline captured from the built page
246
+ // ratifies whatever shipped, which is precisely the failure fidelity exists to
247
+ // catch. Neither satisfies the other.
248
+ 'presentation:fidelity': ['presentation:fidelity'],
249
+ }
250
+
251
+ // ---------- small fs helpers ----------
252
+
253
+ function* walk(dir) {
254
+ let entries
255
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
256
+ for (const e of entries) {
257
+ if (e.name.startsWith('.') && e.name !== '.') continue
258
+ const full = path.join(dir, e.name)
259
+ if (e.isDirectory()) {
260
+ if (IGNORE_DIRS.has(e.name)) continue
261
+ yield* walk(full)
262
+ } else {
263
+ yield full
264
+ }
265
+ }
266
+ }
267
+
268
+ async function loadStructured(file) {
269
+ const raw = fs.readFileSync(file, 'utf8')
270
+ if (file.endsWith('.json')) return JSON.parse(raw)
271
+ let yaml
272
+ try { ({ default: yaml } = await import('js-yaml')) }
273
+ catch { throw new Error(`Reading ${file} needs YAML support. Run \`npm install js-yaml\` in scripts/, or use .json coverage files.`) }
274
+ return yaml.load(raw)
275
+ }
276
+
277
+ // ---------- loaders ----------
278
+
279
+ function loadInventory(cfg) {
280
+ if (!fs.existsSync(cfg.inventory)) {
281
+ throw new Error(`Inventory not found: ${cfg.inventory}. Generate it first (see references/breadth/inventory-contract.md).`)
282
+ }
283
+ const data = JSON.parse(fs.readFileSync(cfg.inventory, 'utf8'))
284
+ const surfaces = new Map() // id -> { kind, class, reachability }
285
+ if (Array.isArray(data.surfaces)) {
286
+ for (const s of data.surfaces) surfaces.set(s.id, { kind: s.kind || 'unknown', class: s.class || null, reachability: s.reachability || null, signals: s.signals || null })
287
+ } else {
288
+ // lenient grouped form: { routes:[...], apis:[...], tables:[...], events:[...] }
289
+ // (no class/reachability; entry surfaces fall back to the _default floor)
290
+ for (const [group, list] of Object.entries(data)) {
291
+ if (!Array.isArray(list)) continue
292
+ const kind = group.replace(/s$/, '')
293
+ for (const id of list) surfaces.set(id, { kind, class: null, reachability: null })
294
+ }
295
+ }
296
+ return surfaces
297
+ }
298
+
299
+ async function loadCurated(cfg) {
300
+ const files = []
301
+ for (const f of walk(cfg.coverageDir)) {
302
+ if (!/\.coverage\.(ya?ml|json)$/.test(f)) continue
303
+ const source = path.basename(path.dirname(f)) // adr | prd | design | ...
304
+ const base = path.basename(f).replace(/\.coverage\.(ya?ml|json)$/, '')
305
+ const id = base.split('-')[0]
306
+ const ref = `${source}:${id}`
307
+ let doc
308
+ try { doc = await loadStructured(f) } catch (e) { throw new Error(`${f}: ${e.message}`) }
309
+ const surfaces = doc?.surfaces || {}
310
+ files.push({ file: f, source, id, ref, criteria: doc?.criteria || null, surfaces })
311
+ }
312
+ return files
313
+ }
314
+
315
+ function scanTests(cfg) {
316
+ const index = new Map() // surfaceId -> [{file, levels:Set, intercepts}]
317
+ for (const root of cfg.testRoots) {
318
+ for (const f of walk(root)) {
319
+ if (!TEST_RE.test(f)) continue
320
+ const text = fs.readFileSync(f, 'utf8')
321
+ const covers = [...text.matchAll(/@covers\s+(\S+)/g)].map((m) => m[1].replace(/^route:/, ''))
322
+ if (covers.length === 0) continue
323
+ const levels = new Set([...text.matchAll(/@level\s+(\S+)/g)].map((m) => m[1]))
324
+ if (levels.size === 0) inferLevels(f).forEach((l) => levels.add(l))
325
+ const intercepts = INTERCEPT_RE.test(text)
326
+ const realBrowser = BROWSER_RE.test(text)
327
+ const domOnly = DOMONLY_RE.test(text)
328
+ for (const id of covers) {
329
+ if (!index.has(id)) index.set(id, [])
330
+ index.get(id).push({ file: f, levels, intercepts, realBrowser, domOnly })
331
+ }
332
+ }
333
+ }
334
+ return index
335
+ }
336
+
337
+ function inferLevels(file) {
338
+ const p = file.replace(/\\/g, '/')
339
+ if (/\/e2e\//.test(p)) return ['e2e']
340
+ if (/\/integration\//.test(p)) return ['integration']
341
+ if (/\/unit\//.test(p)) return ['unit']
342
+ return ['unit']
343
+ }
344
+
345
+ // ---------- aggregation ----------
346
+
347
+ function satisfies(testLevels, required) {
348
+ const syn = SYNONYMS[required] || [required]
349
+ for (const tl of testLevels) if (syn.includes(tl)) return true
350
+ return false
351
+ }
352
+
353
+ function depthStatus(requiredLevels, tests, opts = {}) {
354
+ if (tests.length === 0) return { status: 'Missing', note: 'no test annotated @covers this surface' }
355
+ const required = requiredLevels.length ? requiredLevels : ['unit']
356
+ let unmet = []
357
+ let blockedByInterception = false
358
+ let presentationDomOnly = [] // presentation levels proven without real-render evidence
359
+ for (const rl of required) {
360
+ const proving = tests.filter((t) => satisfies(t.levels, rl))
361
+ if (proving.length === 0) { unmet.push(rl); continue }
362
+ if (isPresentationLevel(rl)) {
363
+ // Presentation invariants must be proven against a real rendered page; a
364
+ // DOM-only / JSDOM test (no real browser drive) is the axis's over-mock.
365
+ const rendered = proving.filter((t) => t.realBrowser && !t.intercepts)
366
+ if (rendered.length === 0) { presentationDomOnly.push(rl); unmet.push(rl) }
367
+ } else if (BEHAVIOR_LEVELS.has(rl)) {
368
+ const clean = proving.filter((t) => !t.intercepts)
369
+ if (clean.length === 0) { blockedByInterception = true; unmet.push(rl) }
370
+ }
371
+ }
372
+ if (blockedByInterception) return { status: 'Over-mocked', note: `behavior level met only by internal interception / first-party mocking (route.fulfill/page.route/msw): ${unmet.join(', ')}` }
373
+ if (presentationDomOnly.length) return { status: 'Over-mocked', note: `presentation level proven without real rendered-page evidence (DOM-only/JSDOM — drive a real browser): ${presentationDomOnly.join(', ')}` }
374
+ if (unmet.length) return { status: 'Wrong Level', note: `required level(s) not proven: ${unmet.join(', ')}` }
375
+ // A self-declared @level can never reach Covered on its own. With reviewer
376
+ // attestation (`verified: true` in the curated file) it promotes; otherwise it
377
+ // lands at a BLOCKING Unverified.
378
+ if (opts.attested) return { status: 'Covered', note: 'required level(s) proven by clean tests and reviewer-attested (verified)' }
379
+ return { status: 'Unverified', note: 'level satisfied statically but not reviewer-attested — assertion/execution quality unconfirmed; add `verified: true` (with evidence) to the curated file to promote to Covered' }
380
+ }
381
+
382
+ // Turn a depth status into a finding. Missing/Over-mocked/Wrong Level always
383
+ // fail; Unverified blocks the gate by default (see cfg.unverifiedBlocks).
384
+ function emitDepth(findings, cfg, sid, ds) {
385
+ if (ds.status === 'Missing' || ds.status === 'Over-mocked' || ds.status === 'Wrong Level') {
386
+ findings.push({ severity: 'error', kind: ds.status, surface: sid, detail: ds.note })
387
+ } else if (ds.status === 'Unverified') {
388
+ findings.push({ severity: cfg.unverifiedBlocks === false ? 'warn' : 'error', kind: 'Unverified', surface: sid, detail: ds.note })
389
+ }
390
+ }
391
+
392
+ async function build(cfg) {
393
+ const inventory = loadInventory(cfg)
394
+ const curated = await loadCurated(cfg)
395
+ const tests = scanTests(cfg)
396
+
397
+ const index = new Map() // surfaceId -> row
398
+ const findings = []
399
+
400
+ // owned surfaces from curated files
401
+ const owners = new Map() // surfaceId -> { refs, requiredLevels, files, sensitive, verified, exclude }
402
+ for (const cf of curated) {
403
+ if (cf.criteria && !fs.existsSync(cf.criteria)) {
404
+ findings.push({ severity: 'error', kind: 'Stale link', surface: '(criteria)', detail: `${cf.ref} criteria missing: ${cf.criteria}` })
405
+ }
406
+ for (const [sid, meta] of Object.entries(cf.surfaces)) {
407
+ if (!owners.has(sid)) owners.set(sid, { refs: new Set(), requiredLevels: new Set(), files: [], sensitive: false, verified: false, exclude: null, excludeCells: {} })
408
+ const o = owners.get(sid)
409
+ o.refs.add(cf.ref)
410
+ for (const l of meta?.requiredLevels || []) o.requiredLevels.add(l)
411
+ if (meta?.sensitive) o.sensitive = true
412
+ if (meta?.verified) o.verified = true
413
+ if (meta?.exclude) o.exclude = meta.exclude
414
+ if (meta?.excludeCells) Object.assign(o.excludeCells, meta.excludeCells)
415
+ o.files.push(cf.file)
416
+ }
417
+ }
418
+
419
+ // Unrealized: curated surface absent from inventory
420
+ for (const [sid, o] of owners) {
421
+ if (!inventory.has(sid)) {
422
+ index.set(sid, { surface: sid, requirementRefs: [...o.refs], requiredLevels: [...o.requiredLevels], alignment: 'Unrealized requirement', depthStatus: null, evidence: [] })
423
+ findings.push({ severity: 'error', kind: 'Unrealized requirement', surface: sid, detail: `owned by ${[...o.refs].join(', ')} but not in inventory` })
424
+ }
425
+ }
426
+
427
+ // Inventory surfaces. Orphan is no longer a hard error: ownership (traceability
428
+ // to a requirement) is decoupled from the TEST obligation. The baseline floor
429
+ // auto-applies and is graded at depth whether or not a requirement owns it.
430
+ for (const [sid, surf] of inventory) {
431
+ const { kind, class: cls, reachability } = surf
432
+ const o = owners.get(sid)
433
+ const ts = tests.get(sid) || []
434
+
435
+ // Exclusion (human-declared, gated by adapter signals).
436
+ if (o?.exclude) {
437
+ const ex = o.exclude
438
+ const allowed = exclusionAllowed(kind, cls, reachability)
439
+ if (!ex.category || !ex.reason) {
440
+ findings.push({ severity: 'error', kind: 'Exclusion invalid', surface: sid, detail: 'exclude requires both `category` and `reason`' })
441
+ } else if (!allowed.ok) {
442
+ findings.push({ severity: 'error', kind: 'Exclusion rejected', surface: sid, detail: allowed.why })
443
+ } else {
444
+ // `static` keeps a minimal render-health floor (no surface ships with zero
445
+ // test); seed/deprecated/internal drop to manifest-only.
446
+ const residual = ex.category === 'static' ? ['render-health'] : []
447
+ if (residual.length) {
448
+ const ds = depthStatus(residual, ts, { attested: !!o.verified })
449
+ index.set(sid, { surface: sid, kind, class: cls, requirementRefs: [...o.refs].sort(), requiredLevels: residual, alignment: o ? 'Aligned' : 'Orphan surface', depthStatus: ds.status, depthNote: ds.note, evidence: ts.map((t) => t.file) })
450
+ findings.push({ severity: 'warn', kind: 'Excluded', surface: sid, detail: `${ex.category} → render-health floor: ${ex.reason}${ex.gapIssue ? ` (${ex.gapIssue})` : ''}` })
451
+ emitDepth(findings, cfg, sid, ds)
452
+ } else {
453
+ index.set(sid, { surface: sid, kind, class: cls, requirementRefs: [...o.refs].sort(), requiredLevels: [], alignment: 'Excluded', depthStatus: 'Excluded', evidence: ts.map((t) => t.file) })
454
+ findings.push({ severity: 'warn', kind: 'Excluded', surface: sid, detail: `${ex.category}: ${ex.reason}${ex.gapIssue ? ` (${ex.gapIssue})` : ''}` })
455
+ }
456
+ continue
457
+ }
458
+ }
459
+
460
+ // Internal surfaces carry no baseline; warn only when nothing reaches them.
461
+ if (INTERNAL_KINDS.has(kind)) {
462
+ const reached = ts.length > 0
463
+ index.set(sid, { surface: sid, kind, class: cls, requirementRefs: [...(o?.refs || [])].sort(), requiredLevels: [], alignment: o ? 'Aligned' : 'Orphan surface', depthStatus: reached ? 'Reached' : 'Dead', evidence: ts.map((t) => t.file) })
464
+ if (!reached) findings.push({ severity: 'warn', kind: 'Dead surface', surface: sid, detail: `${kind} reached by no test/entry evidence — likely dead or untracked` })
465
+ continue
466
+ }
467
+
468
+ // Entry surfaces: floor = strictest(baseline, curated raise, sensitive
469
+ // escalation, feature-derived presentation cells), minus any gated cell
470
+ // exclusions. The base mechanical floor can never be removed; only escalated
471
+ // (`@`-qualified) cells may be excluded with a category + reason.
472
+ const sensitive = !!o?.sensitive
473
+ const escalation = presentationEscalation(surf.signals || {})
474
+ const fullFloor = strictest(baselineFor(kind, cls), [...(o?.requiredLevels || [])], sensitive ? sensitiveExtra(kind) : [], escalation)
475
+ const { kept: required, census: cellCensus, rejected: cellRejected } = applyCellExclusions(fullFloor, o?.excludeCells || {})
476
+ for (const c of cellCensus) findings.push({ severity: 'warn', kind: 'Cell excluded', surface: sid, detail: `${c.level}: ${c.category} — ${c.reason}` })
477
+ for (const r of cellRejected) findings.push({ severity: 'error', kind: 'Cell exclusion rejected', surface: sid, detail: `${r.level}: ${r.why}` })
478
+ const ds = depthStatus(required, ts, { attested: !!o?.verified })
479
+ index.set(sid, {
480
+ surface: sid, kind, class: cls,
481
+ requirementRefs: [...(o?.refs || [])].sort(),
482
+ requiredLevels: required,
483
+ alignment: o ? 'Aligned' : 'Orphan surface',
484
+ depthStatus: ds.status,
485
+ depthNote: ds.note,
486
+ evidence: ts.map((t) => t.file),
487
+ })
488
+
489
+ // Ownership gap is a WARN (decoupled), and notes the baseline still applies.
490
+ if (!o) findings.push({ severity: 'warn', kind: 'Orphan surface', surface: sid, detail: `${kind} owned by no requirement; baseline ${required.join(' ') || '-'} still enforced` })
491
+
492
+ // Test obligation is the hard gate.
493
+ emitDepth(findings, cfg, sid, ds)
494
+ }
495
+
496
+ // Acceptance gate (optional)
497
+ gateFindings(cfg, curated, findings)
498
+
499
+ return { cfg, index, findings }
500
+ }
501
+
502
+ function gateFindings(cfg, curated, findings) {
503
+ const sources = Object.entries(cfg.requirementGlobs || {})
504
+ if (sources.length === 0) {
505
+ findings.push({ severity: 'info', kind: 'Gate skipped', surface: '-', detail: 'no requirementGlobs configured; acceptance gate not enforced' })
506
+ return
507
+ }
508
+ const haveCriteria = new Set(curated.map((c) => c.ref))
509
+ for (const [source, dir] of sources) {
510
+ for (const f of walk(dir)) {
511
+ if (!/\.mdx?$/.test(f)) continue
512
+ const text = fs.readFileSync(f, 'utf8')
513
+ if (!cfg.acceptedMarker.test(text)) continue
514
+ const id = path.basename(f).split('-')[0].replace(/\.mdx?$/, '')
515
+ const ref = `${source}:${id}`
516
+ const noRuntime = /no runtime behavior/i.test(text)
517
+ if (!haveCriteria.has(ref) && !noRuntime) {
518
+ findings.push({ severity: 'error', kind: 'Gate', surface: ref, detail: `accepted ${ref} has no ${source}/${id}-*.coverage.* file` })
519
+ }
520
+ }
521
+ }
522
+ }
523
+
524
+ // ---------- outputs ----------
525
+
526
+ function indexToObject(index) {
527
+ const out = {}
528
+ for (const [sid, row] of [...index].sort((a, b) => a[0].localeCompare(b[0]))) {
529
+ const { surface, ...rest } = row
530
+ out[sid] = rest
531
+ }
532
+ return out
533
+ }
534
+
535
+ // Over-mock rate (depth-axis honesty metric). A raw over-mock *count* rises just
536
+ // because the suite grew; a *rate* trends whether the tests that exist are honest.
537
+ // Denominator is surfaces that owe a depth check AND have tests — over-mock is a
538
+ // property of a test that exists but fakes its own internals, so a `Missing`
539
+ // surface (no test at all) is not over-mocked, just absent, and `Excluded`
540
+ // surfaces owe nothing. Numerator is the `Over-mocked` depthStatus the ledger
541
+ // already assigns (behavior level met only by interception, or a presentation
542
+ // level proven DOM-only). Split by axis via the note so a regression points at
543
+ // the right axis. `overMockRate` is 0 when nothing is tested (no honesty to
544
+ // measure), never NaN — a trend consumer can read it unconditionally.
545
+ const DEPTH_GRADED = new Set(['Missing', 'Over-mocked', 'Wrong Level', 'Covered', 'Unverified', 'Partial'])
546
+ function computeMetrics(index) {
547
+ const rows = [...index.values()]
548
+ // Only depth-graded surfaces count — not the `Reached`/`Dead` reachability
549
+ // states (events/jobs), `Excluded`, or `null` (unrealized requirements).
550
+ const graded = rows.filter((r) => DEPTH_GRADED.has(r.depthStatus))
551
+ const tested = graded.filter((r) => r.depthStatus !== 'Missing')
552
+ const overMocked = graded.filter((r) => r.depthStatus === 'Over-mocked')
553
+ const overMockedPresentation = overMocked.filter((r) => /presentation level/i.test(r.depthNote || '')).length
554
+ const overMockedBehavior = overMocked.length - overMockedPresentation
555
+ const rate = tested.length ? overMocked.length / tested.length : 0
556
+ return {
557
+ surfaces: index.size,
558
+ depthGraded: graded.length,
559
+ tested: tested.length,
560
+ overMocked: overMocked.length,
561
+ overMockedBehavior,
562
+ overMockedPresentation,
563
+ overMockRate: Math.round(rate * 1e4) / 1e4,
564
+ overMockRatePct: Math.round(rate * 1000) / 10,
565
+ }
566
+ }
567
+
568
+ const fmtOverMock = (m) => `${m.overMocked}/${m.tested} tested surfaces over-mocked (${m.overMockRatePct}%)`
569
+
570
+ function renderReport(index) {
571
+ const rows = [...index.values()].sort((a, b) => a.surface.localeCompare(b.surface))
572
+ const line = (r) => `| \`${r.surface}\` | ${r.kind || '-'} | ${r.alignment} | ${r.depthStatus || '-'} | ${(r.requirementRefs || []).join(', ') || '-'} | ${(r.requiredLevels || []).join(' ') || '-'} | ${(r.evidence || []).length} |`
573
+ const out = []
574
+ out.push('# Coverage Index (generated — do not hand-edit)\n')
575
+ out.push(`**Over-mock rate:** ${fmtOverMock(computeMetrics(index))} — the share of tested surfaces whose depth level is met only by faking first-party internals (behavior interception or DOM-only presentation). Lower is healthier; the gate requires 0.\n`)
576
+ out.push('| Surface | Kind | Alignment | Depth | Requirements | Required levels | Evidence files |')
577
+ out.push('|---|---|---|---|---|---|---|')
578
+ for (const r of rows) out.push(line(r))
579
+ const adr = rows.filter((r) => (r.requirementRefs || []).some((x) => x.startsWith('adr:')))
580
+ if (adr.length) {
581
+ out.push('\n## ADR Coverage Matrix (view: rows with `adr:` refs)\n')
582
+ out.push('| Surface | Alignment | Depth | ADRs | Required levels |')
583
+ out.push('|---|---|---|---|---|')
584
+ for (const r of adr) out.push(`| \`${r.surface}\` | ${r.alignment} | ${r.depthStatus || '-'} | ${(r.requirementRefs).filter((x) => x.startsWith('adr:')).join(', ')} | ${(r.requiredLevels || []).join(' ') || '-'} |`)
585
+ }
586
+ return out.join('\n') + '\n'
587
+ }
588
+
589
+ // ---------- CLI ----------
590
+
591
+ function parseArgs(argv) {
592
+ const cmd = argv[2]
593
+ const cfg = { ...DEFAULTS }
594
+ const flags = {}
595
+ for (let i = 3; i < argv.length; i++) {
596
+ const a = argv[i]
597
+ if (a === '--strict') flags.strict = true
598
+ else if (a === '--warn-only') flags.warnOnly = true
599
+ else if (a.startsWith('--')) { flags[a.slice(2)] = argv[++i] }
600
+ }
601
+ if (flags.config && fs.existsSync(flags.config)) Object.assign(cfg, JSON.parse(fs.readFileSync(flags.config, 'utf8')))
602
+ if (flags.inventory) cfg.inventory = flags.inventory
603
+ if (flags['coverage-dir']) cfg.coverageDir = flags['coverage-dir']
604
+ if (flags['test-roots']) cfg.testRoots = flags['test-roots'].split(',')
605
+ if (flags.out) cfg.indexOut = cfg.reportOut = flags.out
606
+ if (cfg.acceptedMarker && typeof cfg.acceptedMarker === 'string') cfg.acceptedMarker = new RegExp(cfg.acceptedMarker, 'i')
607
+ return { cmd, cfg, flags }
608
+ }
609
+
610
+ function ensureDir(p) { fs.mkdirSync(path.dirname(p), { recursive: true }) }
611
+
612
+ async function main() {
613
+ const { cmd, cfg, flags } = parseArgs(process.argv)
614
+ if (!['aggregate', 'validate', 'report'].includes(cmd)) {
615
+ console.error('usage: coverage-ledger <aggregate|validate|report> [--config f] [--inventory f] [--coverage-dir d] [--test-roots a,b] [--out f] [--strict] [--warn-only]')
616
+ process.exit(2)
617
+ }
618
+ const { index, findings } = await build(cfg)
619
+
620
+ if (cmd === 'aggregate') {
621
+ ensureDir(cfg.indexOut)
622
+ fs.writeFileSync(cfg.indexOut, JSON.stringify(indexToObject(index), null, 2) + '\n')
623
+ // Emit the metrics as a sibling artifact (not a key in the surface-keyed
624
+ // index, which consumers iterate) so CI / review-pr can trend the rate over
625
+ // time instead of only gating on zero.
626
+ const metrics = computeMetrics(index)
627
+ const metricsOut = path.join(path.dirname(cfg.indexOut), 'coverage-metrics.json')
628
+ fs.writeFileSync(metricsOut, JSON.stringify(metrics, null, 2) + '\n')
629
+ console.log(`wrote ${cfg.indexOut} (${index.size} surfaces) · ${metricsOut} (${fmtOverMock(metrics)})`)
630
+ return
631
+ }
632
+ if (cmd === 'report') {
633
+ ensureDir(cfg.reportOut)
634
+ fs.writeFileSync(cfg.reportOut, renderReport(index))
635
+ console.log(`wrote ${cfg.reportOut}`)
636
+ return
637
+ }
638
+
639
+ // validate
640
+ const errors = findings.filter((f) => f.severity === 'error' || (flags.strict && f.severity === 'warn'))
641
+ const warns = findings.filter((f) => f.severity === 'warn' && !flags.strict)
642
+ const infos = findings.filter((f) => f.severity === 'info')
643
+ const fmt = (f) => ` [${f.kind}] ${f.surface}: ${f.detail}`
644
+ if (errors.length) { console.log(`\nERRORS (${errors.length}):`); errors.forEach((f) => console.log(fmt(f))) }
645
+ if (warns.length) { console.log(`\nWARNINGS (${warns.length}):`); warns.forEach((f) => console.log(fmt(f))) }
646
+ if (infos.length) infos.forEach((f) => console.log(`\nnote: ${f.detail}`))
647
+ const aligned = [...index.values()].filter((r) => r.alignment === 'Aligned').length
648
+ console.log(`\n${index.size} surfaces · ${aligned} aligned · ${fmtOverMock(computeMetrics(index))} · ${errors.length} errors · ${warns.length} warnings`)
649
+ if (errors.length && !flags.warnOnly) process.exit(1)
650
+ }
651
+
652
+ // Run the CLI only when invoked directly; importing this module (e.g. from tests)
653
+ // must not execute main().
654
+ if (isMainModule(import.meta.url)) {
655
+ main().catch((e) => { console.error(`coverage-ledger: ${e.message}`); process.exit(2) })
656
+ }
657
+
658
+ export {
659
+ build, depthStatus, baselineFor, sensitiveExtra, strictest, satisfies,
660
+ isPresentationLevel, presentationEscalation, applyCellExclusions, computeMetrics,
661
+ BASELINE, SYNONYMS, BEHAVIOR_LEVELS, PRESENTATION_LEVELS,
662
+ PRESENTATION_ROUTE_FLOOR, PRESENTATION_VIEWPORT_FLOOR,
663
+ }