@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,178 @@
1
+ #!/usr/bin/env node
2
+ // REFERENCE Next.js App Router discovery adapter — copy and adapt per repo.
3
+ // Emits a surface inventory satisfying references/breadth/inventory-contract.md.
4
+ //
5
+ // node nextjs-inventory.mjs <appDir> [--out docs/tests/_generated/surface-inventory.json] [--check]
6
+ //
7
+ // Routes come from `page.*` files; APIs from `route.*` files. Route groups
8
+ // `(group)` are stripped; dynamic segments `[id]`/`[...slug]` are kept as-is so
9
+ // ids are stable.
10
+ //
11
+ // It also emits the MECHANICAL taxonomy the Surface Baseline Contract needs:
12
+ // - class: route -> read | detail | mutation; api -> read | mutation
13
+ // - reachability: best-effort reachable | guarded (from the layout/middleware chain)
14
+ // These are deliberately conservative heuristics: the adapter may only emit the
15
+ // mechanical class (never `sensitive`/exclusion — those are human-declared). When
16
+ // in doubt about a write surface it biases to `mutation`, because under-calling a
17
+ // write form `read` would wrongly let it be excluded. Reachability here is a hint;
18
+ // the full Route Reachability Protocol (and human review) refine `blocked`/
19
+ // `unreachable`. It does NOT cover DB tables or events — extend for those kinds.
20
+
21
+ import fs from 'node:fs'
22
+ import path from 'node:path'
23
+ import process from 'node:process'
24
+
25
+ const IGNORE = new Set(['node_modules', '.git', '.next'])
26
+ const PAGE_RE = /^page\.(tsx|ts|jsx|js|mdx)$/
27
+ const ROUTE_RE = /^route\.(ts|js)$/
28
+ const LAYOUT_RE = /^layout\.(tsx|ts|jsx|js)$/
29
+ const COMPONENT_RE = /\.(tsx|ts|jsx|js|mdx)$/
30
+
31
+ // A page (or a component colocated in its route segment) that mutates state.
32
+ const WRITE_RE = /<form\b|onSubmit|action=\{|formAction|use(FormState|ActionState|Mutation)\b|type=["']submit["']|method:\s*["'](post|put|patch|delete)["']|fetch\([^)]*method\s*:\s*["'](post|put|patch|delete)|['"]use server['"]/i
33
+ // An API route file exporting a write method.
34
+ const METHOD_EXPORT_RE = /export\s+(?:async\s+function|const|let|var|function)\s+(POST|PUT|PATCH|DELETE)\b|export\s*\{[^}]*\b(POST|PUT|PATCH|DELETE)\b/
35
+ // Guard signals in a layout / middleware that gate reachability.
36
+ const GUARD_RE = /\bredirect\(|\bnotFound\(|getServerSession|\bauth\(\)|requireAuth|getToken|currentUser|\bunauthorized\(|clerkMiddleware|withAuth/i
37
+
38
+ function* walk(dir) {
39
+ let entries
40
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
41
+ for (const e of entries) {
42
+ const full = path.join(dir, e.name)
43
+ if (e.isDirectory()) { if (!IGNORE.has(e.name)) yield* walk(full) }
44
+ else yield full
45
+ }
46
+ }
47
+
48
+ function read(file) { try { return fs.readFileSync(file, 'utf8') } catch { return '' } }
49
+
50
+ function toRoute(appDir, file) {
51
+ const rel = path.relative(appDir, path.dirname(file)).split(path.sep)
52
+ .filter((seg) => seg && !(seg.startsWith('(') && seg.endsWith(')'))) // drop route groups
53
+ .filter((seg) => !seg.startsWith('@')) // drop parallel route slots
54
+ .join('/')
55
+ return '/' + rel
56
+ }
57
+
58
+ // Mechanical class for a page: mutation > detail > read.
59
+ function routeClass(pageFile) {
60
+ const dir = path.dirname(pageFile)
61
+ let text = ''
62
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
63
+ if (!e.isFile() || !COMPONENT_RE.test(e.name)) continue
64
+ if (LAYOUT_RE.test(e.name) || ROUTE_RE.test(e.name)) continue
65
+ text += read(path.join(dir, e.name)) + '\n'
66
+ }
67
+ if (WRITE_RE.test(text)) return 'mutation'
68
+ if (/\[[^\]]+\]/.test(dir)) return 'detail' // dynamic segment -> renders one entity
69
+ return 'read'
70
+ }
71
+
72
+ // Mechanical class for an API route: mutation if it exports a write method.
73
+ function apiClass(routeFile) {
74
+ return METHOD_EXPORT_RE.test(read(routeFile)) ? 'mutation' : 'read'
75
+ }
76
+
77
+ // Best-effort reachability from the layout chain + root middleware.
78
+ function reachability(appDir, pageFile, hasMiddleware) {
79
+ if (hasMiddleware) return 'guarded'
80
+ let dir = path.dirname(pageFile)
81
+ const stop = path.resolve(appDir)
82
+ while (true) {
83
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
84
+ if (e.isFile() && LAYOUT_RE.test(e.name) && GUARD_RE.test(read(path.join(dir, e.name)))) return 'guarded'
85
+ }
86
+ if (path.resolve(dir) === stop) break
87
+ const parent = path.dirname(dir)
88
+ if (parent === dir) break
89
+ dir = parent
90
+ }
91
+ return 'reachable'
92
+ }
93
+
94
+ function detectMiddleware(appDir) {
95
+ const parent = path.dirname(appDir)
96
+ for (const base of [appDir, parent]) {
97
+ for (const m of ['middleware.ts', 'middleware.js']) {
98
+ if (fs.existsSync(path.join(base, m))) return true
99
+ }
100
+ }
101
+ return false
102
+ }
103
+
104
+ // Feature-derived presentation signals (Dimension B escalation). Detected
105
+ // deterministically at the app level and attached to every route, so the ledger
106
+ // raises the presentation floor (dark-theme cell, RTL/long-locale cells). This is
107
+ // the reference granularity; a stack can refine to per-route detection.
108
+ // - themeToggle: a `next-themes` dep or a useTheme/ThemeProvider reference
109
+ // - i18n: a known i18n library dependency
110
+ function detectSignals(appDir) {
111
+ const roots = [appDir, path.dirname(appDir)]
112
+ let pkg = {}
113
+ for (const base of roots) {
114
+ const p = path.join(base, 'package.json')
115
+ if (fs.existsSync(p)) { try { pkg = JSON.parse(fs.readFileSync(p, 'utf8')) } catch { /* ignore */ } break }
116
+ }
117
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
118
+ const dep = (re) => Object.keys(deps).some((d) => re.test(d))
119
+ const I18N_DEP = /^(next-intl|next-i18next|react-i18next|i18next|@lingui|react-intl|@formatjs)/
120
+ const THEME_DEP = /^(next-themes)$/
121
+ // a cheap source grep for theme toggling when there's no dedicated dep
122
+ let themeInSource = false
123
+ for (const f of walk(appDir)) {
124
+ if (!COMPONENT_RE.test(f)) continue
125
+ if (/\buseTheme\b|<ThemeProvider\b|data-theme=/.test(fs.readFileSync(f, 'utf8'))) { themeInSource = true; break }
126
+ }
127
+ const signals = {}
128
+ if (dep(THEME_DEP) || themeInSource) signals.themeToggle = true
129
+ if (dep(I18N_DEP)) signals.i18n = true
130
+ return Object.keys(signals).length ? signals : null
131
+ }
132
+
133
+ function build(appDir) {
134
+ const hasMiddleware = detectMiddleware(appDir)
135
+ const signals = detectSignals(appDir)
136
+ const surfaces = []
137
+ for (const f of walk(appDir)) {
138
+ const base = path.basename(f)
139
+ if (PAGE_RE.test(base)) {
140
+ const s = { id: toRoute(appDir, f), kind: 'route', class: routeClass(f), reachability: reachability(appDir, f, hasMiddleware) }
141
+ if (signals) s.signals = signals
142
+ surfaces.push(s)
143
+ } else if (ROUTE_RE.test(base)) {
144
+ surfaces.push({ id: toRoute(appDir, f), kind: 'api', class: apiClass(f) })
145
+ }
146
+ }
147
+ // de-dupe by id (e.g. page + nested layout edge cases), keep strongest class.
148
+ const byId = new Map()
149
+ const rank = { mutation: 3, detail: 2, read: 1, undefined: 0 }
150
+ for (const s of surfaces) {
151
+ const prev = byId.get(s.id)
152
+ if (!prev || (rank[s.class] || 0) > (rank[prev.class] || 0)) byId.set(s.id, s)
153
+ }
154
+ return { surfaces: [...byId.values()].sort((a, b) => a.id.localeCompare(b.id)) }
155
+ }
156
+
157
+ function main() {
158
+ const [, , appDir, ...rest] = process.argv
159
+ if (!appDir) { console.error('usage: nextjs-inventory.mjs <appDir> [--out file] [--check]'); process.exit(2) }
160
+ let out = 'docs/tests/_generated/surface-inventory.json'
161
+ let check = false
162
+ for (let i = 0; i < rest.length; i++) {
163
+ if (rest[i] === '--out') out = rest[++i]
164
+ else if (rest[i] === '--check') check = true
165
+ }
166
+ const inv = build(appDir)
167
+ const json = JSON.stringify(inv, null, 2) + '\n'
168
+ if (check) {
169
+ const prev = fs.existsSync(out) ? fs.readFileSync(out, 'utf8') : ''
170
+ if (prev !== json) { console.error(`inventory stale: ${out} does not match discovered surfaces. Re-run without --check.`); process.exit(1) }
171
+ console.log(`inventory up to date (${inv.surfaces.length} surfaces)`); return
172
+ }
173
+ fs.mkdirSync(path.dirname(out), { recursive: true })
174
+ fs.writeFileSync(out, json)
175
+ console.log(`wrote ${out} (${inv.surfaces.length} surfaces)`)
176
+ }
177
+
178
+ main()
@@ -0,0 +1,101 @@
1
+ // check-quarantine-expiry — the auto-resurface gate for dated exits.
2
+ //
3
+ // A quarantine (or any deferred-but-tracked suppression) is a holding pen with a
4
+ // dated exit, never a destination. The convention is a single marker, anywhere in
5
+ // a file (a test-file comment, a suppressions list, an ADR):
6
+ //
7
+ // @quarantine(until=2026-07-01, issue=#123) // canonical
8
+ // until=2026-07-01 // also matched (bare)
9
+ // until: 2026-07-01 // also matched (yaml-style)
10
+ //
11
+ // Until the date, the item is allowed to sit non-blocking. On/after the date it
12
+ // EXPIRES: this gate fails, so the item re-blocks until it is fixed or the date is
13
+ // extended with a stated reason. Run it in the BLOCKING lane.
14
+ //
15
+ // node check-quarantine-expiry.mjs --files a,b # specific files
16
+ // node check-quarantine-expiry.mjs # whole-tree scan of test files
17
+ // node check-quarantine-expiry.mjs --now 2026-06-08 # pin "today" (default: system date; for tests/CI determinism)
18
+ // --json # machine output
19
+ //
20
+ // Exit 0 = no expired exits. Exit 1 = at least one expired (the gate blocks).
21
+
22
+ import fs from 'node:fs'
23
+ import path from 'node:path'
24
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
25
+
26
+ // Marker forms: `until=YYYY-MM-DD` or `until: YYYY-MM-DD` (case-insensitive key).
27
+ const UNTIL = /\buntil\s*[:=]\s*(\d{4}-\d{2}-\d{2})\b/gi
28
+ // Pull an issue ref from the same line when present, for the report.
29
+ const ISSUE = /\bissue\s*[:=]\s*(#?\w[\w/-]*)/i
30
+
31
+ export function isTestFile(p) {
32
+ return /\.(test|spec)\.[jt]sx?$/.test(p) || /(^|\/)tests?\//.test(p) || /\.coverage\.ya?ml$/.test(p)
33
+ }
34
+
35
+ // Pure: scan one file's text → expired/pending records against `now` (YYYY-MM-DD).
36
+ export function scanText(file, text, now) {
37
+ const out = []
38
+ const lines = (text || '').split(/\r?\n/)
39
+ lines.forEach((raw, i) => {
40
+ for (const m of raw.matchAll(UNTIL)) {
41
+ const until = m[1]
42
+ const issue = (raw.match(ISSUE) || [])[1] || null
43
+ out.push({ file, line: i + 1, until, issue, expired: until < now, snippet: raw.trim().slice(0, 120) })
44
+ }
45
+ })
46
+ return out
47
+ }
48
+
49
+ function discover(dir, acc = []) {
50
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
51
+ if (e.name === 'node_modules' || e.name.startsWith('.git')) continue
52
+ const full = path.join(dir, e.name)
53
+ if (e.isDirectory()) discover(full, acc)
54
+ else if (isTestFile(full)) acc.push(full)
55
+ }
56
+ return acc
57
+ }
58
+
59
+ function today() {
60
+ // Standalone CI tool — reading the wall clock is correct here (unlike workflow scripts).
61
+ return new Date().toISOString().slice(0, 10)
62
+ }
63
+
64
+ export function runCli(argv, { name = 'check-quarantine-expiry' } = {}) {
65
+ const flags = {}
66
+ for (let i = 0; i < argv.length; i++) {
67
+ const a = argv[i]
68
+ if (a === '--json') flags.json = true
69
+ else if (a === '--now') flags.now = argv[++i]
70
+ else if (a === '--files') flags.files = argv[++i]
71
+ }
72
+ const now = flags.now || today()
73
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(now)) {
74
+ console.error(`${name}: --now must be YYYY-MM-DD, got "${now}"`)
75
+ return 2
76
+ }
77
+
78
+ const files = flags.files
79
+ ? flags.files.split(',').map((s) => s.trim()).filter(Boolean)
80
+ : discover(process.cwd())
81
+
82
+ const records = []
83
+ for (const f of files) {
84
+ if (fs.existsSync(f)) records.push(...scanText(f, fs.readFileSync(f, 'utf8'), now))
85
+ }
86
+ const expired = records.filter((r) => r.expired)
87
+
88
+ if (flags.json) {
89
+ console.log(JSON.stringify({ now, total: records.length, expired }, null, 2))
90
+ } else {
91
+ if (!records.length) console.log(`${name}: no dated exits found.`)
92
+ else if (!expired.length) console.log(`${name}: ${records.length} dated exit(s), none expired (as of ${now}).`)
93
+ else {
94
+ console.log(`${name}: ${expired.length} EXPIRED dated exit(s) (as of ${now}) — fix, or extend the date with a reason:`)
95
+ for (const r of expired) console.log(` ${r.file}:${r.line} until=${r.until}${r.issue ? ` (${r.issue})` : ''} ${r.snippet}`)
96
+ }
97
+ }
98
+ return expired.length ? 1 : 0
99
+ }
100
+
101
+ if (isMainModule(import.meta.url)) process.exit(runCli(process.argv.slice(2)))
@@ -0,0 +1,358 @@
1
+ // CI audit — the meta-gate. Verifies a repo's CI config actually WIRES the gates
2
+ // in references/enforcement/ci-contract.md and keeps them BLOCKING. The gates catch bugs; this catches the
3
+ // gates being deleted, never added, or quietly turned to warn-only.
4
+ //
5
+ // node ci-audit.mjs [--files a.yml,b.yaml] [--allow-warn] [--json] # audit: gate on wired-and-blocking
6
+ // node ci-audit.mjs --adoption [--files …] [--json] # bridge: emit a wiring issue/plan for
7
+ // # a pre-existing CI that lacks the gates
8
+ //
9
+ // Static + best-effort by design: CI configs are heterogeneous, so this is a
10
+ // BENCHMARK that abstains rather than a parser. `absent` and `warn-only` fail the
11
+ // audit; a gate present under an unrecognized command reads as `absent` → a
12
+ // reviewer confirms (false-negative-safe: it never green-lights a missing gate it
13
+ // couldn't see, it flags for a human). Treat the output as leads, not gospel.
14
+
15
+ import fs from 'node:fs'
16
+ import path from 'node:path'
17
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
18
+
19
+ // Each gate is "present" if ANY pattern matches a config line. Signatures are the
20
+ // reference commands; extend per-repo if you wrap them under different names.
21
+ export const GATES = [
22
+ { key: 'inventory', label: 'Gate 1 — inventory freshness', patterns: [/detect-routes/, /gen-inventory/, /surface-inventory/] },
23
+ { key: 'accounting', label: 'Gate 2 — accounting (ledger validate)', patterns: [/coverage-ledger(\.mjs)?\b[^\n]*\bvalidate\b/] },
24
+ { key: 'sweep', label: 'Gate 3 — presentation render sweep', patterns: [/presentation-sweep/] },
25
+ { key: 'e2e', label: 'Gate 4 — functional + journey E2E (integration/nightly lane, not per-PR)', patterns: [/playwright\s+test/, /\btest:e2e\b/, /\brun\s+e2e\b/] },
26
+ { key: 'mock-seam', label: 'Gate 5 — no over-mock (mock-internal-seam, depth axis)', patterns: [/mock-internal-seam/] },
27
+ { key: 'checklist', label: 'Gate 6 — Review-Contract checklist validate', patterns: [/coverage-checklist(\.mjs)?\b[^\n]*\bvalidate\b/] },
28
+ // Newer than the rest: an already-wired repo will report this one absent on its next
29
+ // audit. That is the adoption path working, not a regression — phase it in with
30
+ // --allow-warn while the job is added, rather than treating the red as noise.
31
+ { key: 'evidence-block', label: 'Gate 6b — Review-Contract evidence-block freshness (stamp = HEAD)', patterns: [/evidence-block(\.mjs)?\b[^\n]*\bvalidate\b/] },
32
+ { key: 'quarantine-expiry', label: 'Gate 7 — quarantine auto-resurface (dated-exit expiry)', patterns: [/check-quarantine-expiry/] },
33
+ { key: 'smoke', label: 'Gate 8 — curated E2E smoke (golden-path liveness, per-PR floor)', patterns: [/smoke(\.mjs)?\b[^\n]*\bverdict\b/, /smoke-gate/] },
34
+ ]
35
+
36
+ // Markers that turn a blocking gate into theatre. Proximity-scoped to a gate hit so
37
+ // an unrelated `continue-on-error` elsewhere in a big workflow doesn't false-flag.
38
+ export const WEAKEN = [
39
+ /continue-on-error:\s*true/i,
40
+ /allowFailure:\s*true/i,
41
+ /\|\|\s*true\b/,
42
+ /--warn-only\b/,
43
+ /--no-strict\b/,
44
+ /set\s+\+e\b/,
45
+ ]
46
+
47
+ // The meta-gate (gate 0 / bootstrap): is ci-audit ITSELF run by the CI? Without
48
+ // this, nothing catches a gate being deleted or weakened later — the audit only
49
+ // guards the gates if someone runs it. A repo that wires gates but not this can
50
+ // silently lose them. Advisory in normal audit mode; a required step in adoption.
51
+ export const META_GATE = { key: 'meta', label: 'Meta-gate — ci-audit self-check (guards the gates against rot)', patterns: [/ci-audit(\.mjs)?\b/] }
52
+
53
+ // CI wiring conventions — audited alongside the gates, platform-scoped (a Cloud
54
+ // Build build needs no umbrella job: its single build status IS the one check).
55
+ // `required: true` conventions fail the audit when absent on an applicable platform;
56
+ // `required: false` are PREFERRED — reported as advisory, never failing `ok`
57
+ // (docker-preferred has a sanctioned plain-runner alternative, so it can't hard-fail).
58
+ // Presence-only classification: no weaken-proximity logic (a `timeout-minutes` on the
59
+ // non-blocking quarantine job must not read as weakened).
60
+ export const CONVENTIONS = [
61
+ {
62
+ key: 'umbrella',
63
+ label: 'Umbrella job (all-gates) — branch protection requires exactly ONE check',
64
+ required: true,
65
+ platforms: ['github'],
66
+ patterns: [/^\s*all-gates\s*:/],
67
+ },
68
+ {
69
+ key: 'time-budget',
70
+ label: 'Time budget — PR-lane jobs carry an explicit timeout (target ≤5 min)',
71
+ required: true,
72
+ platforms: ['github', 'cloudbuild'],
73
+ patterns: [/timeout-minutes:\s*\d+/, /^\s*timeout:\s*['"]?\d+(\.\d+)?s['"]?/],
74
+ },
75
+ {
76
+ key: 'playwright-container',
77
+ label: 'Docker-preferred — browser jobs run in the pinned official Playwright container',
78
+ required: false,
79
+ platforms: ['github', 'cloudbuild'],
80
+ patterns: [/mcr\.microsoft\.com\/playwright:v?\d+\.\d+/],
81
+ },
82
+ ]
83
+
84
+ // Pure: classify one convention. Presence-only (no warn-only state): a convention is
85
+ // `present`, `absent`, or `n/a` when the scanned platform is out of its scope.
86
+ export function classifyConvention(corpus, conv, platform) {
87
+ if (!conv.platforms.includes(platform)) {
88
+ return { status: 'n/a', evidence: `not applicable to ${platform || 'unknown'} configs` }
89
+ }
90
+ for (const file of corpus) {
91
+ for (let i = 0; i < file.lines.length; i++) {
92
+ if (conv.patterns.some((p) => p.test(stripComment(file.lines[i])))) {
93
+ return { status: 'present', evidence: `${file.name}:${i + 1}` }
94
+ }
95
+ }
96
+ }
97
+ return { status: 'absent', evidence: 'no matching config found in scanned CI config' }
98
+ }
99
+
100
+ // Visual regression (`presentation:visual`) is the one OPT-IN presentation level —
101
+ // required only when a surface declares it, which CI config alone can't tell us
102
+ // (it lives in coverage.yml, not the workflow). So this is ADVISORY, never part of
103
+ // `ok`: we report whether a `visual-gate` job is wired, but a repo with no opted-in
104
+ // surface legitimately has none. A repo that DOES opt in but lacks the job has a
105
+ // real gap a reviewer must catch — the advisory line is the prompt to check.
106
+ export const VISUAL_GATE = { key: 'visual', label: 'Visual regression (presentation:visual) — advisory, required only when surfaces opt in', patterns: [/visual-gate/, /presentation-visual/] }
107
+
108
+ // Strip a `#` comment so doc/warning text ("NEVER add continue-on-error: true")
109
+ // is not mistaken for a real gate command or a real weakening. Best-effort: ignores
110
+ // `#` inside quotes (acceptable for a benchmark). A real `run: x || true` has no `#`
111
+ // and is still detected.
112
+ export function stripComment(line) {
113
+ return line.replace(/(^|\s)#.*$/, '$1')
114
+ }
115
+
116
+ // A line that runs the quarantine selection (`--grep @quarantine`, not the
117
+ // `--grep-invert` exclusion the blocking gate uses).
118
+ const QUARANTINE_RUN = /--grep(?!-invert)[\s=]+["']?@?quarantine/i
119
+
120
+ // The quarantine *lane's* job/step HEADER (`quarantine:` GH job key, or `- id:
121
+ // quarantine` Cloud Build step). The lane's weaken marker is JOB-LEVEL
122
+ // (`continue-on-error: true` / `allowFailure: true`) and so sits a few lines BELOW
123
+ // this header but can be ~10 lines ABOVE the `--grep @quarantine` run — too far for
124
+ // the run-anchored check below. Anchored on a `quarantine` job/step identifier
125
+ // specifically (line-start, not any mention), so gate 7 (`check-quarantine-expiry`),
126
+ // whose name merely contains the substring, is never mistaken for the lane.
127
+ const QUARANTINE_LANE = /^\s*(?:quarantine\s*:|-\s*id:\s*quarantine\b)/i
128
+
129
+ // Is line j within a few lines of the quarantine *lane* (i.e. a weaken marker that
130
+ // belongs to the non-blocking quarantine job, not to the real gate near it)? Anchored
131
+ // on either the lane's `--grep @quarantine` run or its job/step header — NOT any line
132
+ // mentioning "quarantine" — so the blocking `check-quarantine-expiry` gate (gate 7)
133
+ // can never be mistaken for the lane and can never mask a real weakening on a gate
134
+ // wired next to it.
135
+ function nearQuarantine(lines, j, w = 3) {
136
+ const lo = Math.max(0, j - w)
137
+ const hi = Math.min(lines.length - 1, j + w)
138
+ for (let k = lo; k <= hi; k++) if (QUARANTINE_RUN.test(lines[k]) || QUARANTINE_LANE.test(lines[k])) return true
139
+ return false
140
+ }
141
+
142
+ // Pure: classify one gate against the corpus. corpus: [{ name, lines:[string] }].
143
+ // Matching runs against comment-stripped code so a command/marker only counts when
144
+ // it is actually wired, not when it is mentioned in a comment.
145
+ export function classifyGate(corpus, gate, { proximity = 10 } = {}) {
146
+ const hits = []
147
+ for (const file of corpus) {
148
+ file.lines.forEach((line, i) => {
149
+ const code = stripComment(line)
150
+ // The quarantine lane re-runs a gate command non-blocking (`playwright test
151
+ // --grep @quarantine`); that line is NOT the blocking gate. But the blocking
152
+ // gate's `--grep-invert @quarantine` (excluding quarantine) IS the gate — keep it.
153
+ if (QUARANTINE_RUN.test(code)) return
154
+ if (gate.patterns.some((p) => p.test(code))) hits.push({ file, i })
155
+ })
156
+ }
157
+ if (hits.length === 0) return { status: 'absent', evidence: 'no matching command found in scanned CI config' }
158
+ for (const h of hits) {
159
+ const lo = Math.max(0, h.i - proximity)
160
+ const hi = Math.min(h.file.lines.length - 1, h.i + proximity)
161
+ for (let j = lo; j <= hi; j++) {
162
+ if (WEAKEN.some((w) => w.test(stripComment(h.file.lines[j])))) {
163
+ // A weaken marker belonging to the (by-design non-blocking) quarantine lane
164
+ // must not weaken a real gate hit that happens to sit near it.
165
+ if (nearQuarantine(h.file.lines, j)) continue
166
+ return { status: 'warn-only', evidence: `${h.file.name}:${h.i + 1} (weakened near ${h.file.name}:${j + 1})` }
167
+ }
168
+ }
169
+ }
170
+ return { status: 'present', evidence: `${hits[0].file.name}:${hits[0].i + 1}` }
171
+ }
172
+
173
+ // Pure: is ci-audit itself referenced anywhere in the corpus (the bootstrap check)?
174
+ export function selfAuditWired(corpus) {
175
+ for (const file of corpus) {
176
+ for (let i = 0; i < file.lines.length; i++) {
177
+ if (META_GATE.patterns.some((p) => p.test(stripComment(file.lines[i])))) {
178
+ return { wired: true, evidence: `${file.name}:${i + 1}` }
179
+ }
180
+ }
181
+ }
182
+ return { wired: false, evidence: 'ci-audit is not run by any scanned CI config' }
183
+ }
184
+
185
+ // Pure: audit the whole corpus. ok = every required gate present (warn-only fails
186
+ // unless allowWarn). absent/warn-only are findings.
187
+ export function auditCorpus(files, opts = {}) {
188
+ const corpus = files.map((f) => ({ name: f.name, lines: (f.text || '').split(/\r?\n/) }))
189
+ const gates = GATES.map((g) => ({ key: g.key, label: g.label, ...classifyGate(corpus, g, opts) }))
190
+ const metaGate = selfAuditWired(corpus)
191
+ // Advisory: report whether the opt-in visual gate is wired, but keep it OUT of `ok`
192
+ // (a repo with no opted-in surface correctly has no job — see VISUAL_GATE).
193
+ const visualGate = { key: VISUAL_GATE.key, label: VISUAL_GATE.label, ...classifyGate(corpus, VISUAL_GATE, opts) }
194
+ // Conventions are platform-scoped; on an unrecognized platform they all read n/a.
195
+ // Note this is an ABSTAIN, not a pass the audit vouches for — we don't guess
196
+ // github/cloudbuild conventions for a CI we can't identify; a reviewer owns n/a.
197
+ const platform = detectPlatform(files.map((f) => f.name))
198
+ const conventions = CONVENTIONS.map((c) => ({ key: c.key, label: c.label, required: c.required, ...classifyConvention(corpus, c, platform) }))
199
+ // `ok` = the required content gates + the required conventions. The meta-gate,
200
+ // visual gate, and preferred (required:false) conventions stay advisory here;
201
+ // adoption mode makes the meta-gate a required step.
202
+ const gatesOk = gates.every((g) => g.status === 'present' || (opts.allowWarn && g.status === 'warn-only'))
203
+ const conventionsOk = conventions.every((c) => !c.required || c.status !== 'absent')
204
+ const ok = gatesOk && conventionsOk
205
+ return { gates, conventions, platform, metaGate, visualGate, ok, scanned: files.map((f) => f.name) }
206
+ }
207
+
208
+ // ---- Adoption: turn a gap into one reviewable PR ----
209
+ //
210
+ // A skill can't enforce against a pre-existing pipeline — the gates must be wired
211
+ // in. This closes the gap: given a repo's CI, report which gates are absent/weak,
212
+ // pick the matching template, and emit a checklist scoped to ONLY the gaps (it
213
+ // never asks you to re-wire a gate you already have), including the bootstrap step.
214
+
215
+ const TEMPLATE = {
216
+ github: 'e2e-test/assets/ci-gates.github.yml',
217
+ cloudbuild: 'e2e-test/assets/ci-gates.cloudbuild.yaml',
218
+ unknown: null,
219
+ }
220
+
221
+ // Pure: which template applies, inferred from the scanned paths.
222
+ export function detectPlatform(scanned = []) {
223
+ if (scanned.some((p) => /\.github[/\\]workflows[/\\]/.test(p))) return 'github'
224
+ if (scanned.some((p) => /cloudbuild/i.test(p))) return 'cloudbuild'
225
+ return 'unknown'
226
+ }
227
+
228
+ // Pure: the adoption plan. `toWire` = gates needing work (absent or warn-only);
229
+ // `ok` = gates already present (no action). `needed` is true if anything is left
230
+ // to wire OR the meta-gate isn't wired (the bootstrap).
231
+ export function buildAdoptionPlan(report, { platform } = {}) {
232
+ const plat = platform || detectPlatform(report.scanned || [])
233
+ const toWire = report.gates.filter((g) => g.status !== 'present')
234
+ const okGates = report.gates.filter((g) => g.status === 'present')
235
+ const metaWired = report.metaGate?.wired === true
236
+ // Required conventions that are absent on an applicable platform are part of the
237
+ // gap; preferred (required:false) ones are surfaced as recommendations only.
238
+ const conventionsToWire = (report.conventions || []).filter((c) => c.required && c.status === 'absent')
239
+ const conventionsPreferred = (report.conventions || []).filter((c) => !c.required && c.status === 'absent')
240
+ return {
241
+ needed: toWire.length > 0 || conventionsToWire.length > 0 || !metaWired,
242
+ platform: plat,
243
+ templatePath: TEMPLATE[plat] ?? null,
244
+ toWire,
245
+ conventionsToWire,
246
+ conventionsPreferred,
247
+ ok: okGates.map((g) => ({ key: g.key, label: g.label })),
248
+ metaGate: report.metaGate,
249
+ scanned: report.scanned || [],
250
+ }
251
+ }
252
+
253
+ // Pure: render the plan as a ready-to-file issue body (gh issue create --body-file -).
254
+ export function renderAdoptionIssue(plan) {
255
+ const mark = (s) => (s === 'present' ? '✓' : s === 'warn-only' ? '⚠' : '✗')
256
+ const L = []
257
+ L.push('## Wire coverage gates into CI (ensure-coverage CI contract)')
258
+ L.push('')
259
+ L.push('CI does not fully enforce the coverage gate contract (ensure-coverage → `references/enforcement/ci-contract.md`). Until every gate is wired **and blocking**, a route can ship broken with green CI. Close the gap in one PR.')
260
+ L.push('')
261
+ L.push(`Scanned: ${plan.scanned.length ? plan.scanned.map((s) => '`' + s + '`').join(', ') : '_no CI config found_'}`)
262
+ L.push('')
263
+ L.push('### Gap (from `ci-audit`)')
264
+ for (const g of plan.toWire) L.push(`- ${mark(g.status)} **${g.label}**: ${g.status} (${g.evidence})`)
265
+ for (const g of plan.ok) L.push(`- ✓ ${g.label}: present — no action`)
266
+ L.push(`- ${plan.metaGate?.wired ? '✓' : '✗'} ${META_GATE.label}: ${plan.metaGate?.wired ? `wired (${plan.metaGate.evidence})` : 'not wired'}`)
267
+ L.push('')
268
+ L.push('### Close it (one PR)')
269
+ L.push(plan.templatePath
270
+ ? `Platform detected: **${plan.platform}** → copy template \`${plan.templatePath}\`.`
271
+ : 'Platform not detected — pick the template matching your CI: `e2e-test/assets/ci-gates.github.yml` or `ci-gates.cloudbuild.yaml`.')
272
+ L.push('')
273
+ let n = 1
274
+ if (plan.templatePath) L.push(`${n++}. [ ] Copy \`${plan.templatePath}\` into your CI config.`)
275
+ for (const g of plan.toWire) {
276
+ L.push(g.status === 'warn-only'
277
+ ? `${n++}. [ ] Make **${g.label}** blocking — remove the weakening near \`${g.evidence}\` (\`continue-on-error\` / \`|| true\` / \`--warn-only\` / \`--no-strict\`).`
278
+ : `${n++}. [ ] Wire **${g.label}** as its own blocking job.`)
279
+ }
280
+ for (const c of plan.conventionsToWire || []) {
281
+ L.push(`${n++}. [ ] Wire the **${c.label}** convention (see the template — it encodes this).`)
282
+ }
283
+ for (const c of plan.conventionsPreferred || []) {
284
+ L.push(`${n++}. [ ] (preferred) ${c.label} — recommended, not blocking; the template's default.`)
285
+ }
286
+ if (!plan.metaGate?.wired) L.push(`${n++}. [ ] **Bootstrap** — add a job running \`node <ensure-coverage>/scripts/ci-audit.mjs --files <your ci files>\` so the gates can't be silently deleted or weakened later. Without it, nothing guards the gates.`)
287
+ L.push(`${n++}. [ ] Fill the \`<…>\` placeholders (\`<skill>\` path, \`<seed + migrate>\`, \`<boot app>\`, DB URL) and confirm no gate carries \`continue-on-error\`/\`|| true\`/\`--warn-only\`.`)
288
+ L.push('')
289
+ L.push('### Verify')
290
+ L.push('```bash')
291
+ L.push(`node <ensure-coverage>/scripts/ci-audit.mjs --files ${plan.scanned.join(',') || '<your ci files>'}`)
292
+ L.push('```')
293
+ L.push('Must report every gate `present` (and the meta-gate wired). `absent`/`warn-only` fail the audit.')
294
+ return L.join('\n')
295
+ }
296
+
297
+ // ---- CLI (file discovery) ----
298
+
299
+ function discover(explicit) {
300
+ if (explicit) return explicit.split(',').map((s) => s.trim()).filter(Boolean)
301
+ const found = []
302
+ const wf = '.github/workflows'
303
+ if (fs.existsSync(wf)) for (const f of fs.readdirSync(wf)) if (/\.ya?ml$/.test(f)) found.push(path.join(wf, f))
304
+ for (const f of fs.existsSync('.') ? fs.readdirSync('.') : []) {
305
+ if (/^cloudbuild.*\.ya?ml$/.test(f)) found.push(f)
306
+ }
307
+ for (const f of ['Makefile', 'package.json']) if (fs.existsSync(f)) found.push(f)
308
+ return found
309
+ }
310
+
311
+ function main() {
312
+ const argv = process.argv.slice(2)
313
+ const flags = {}
314
+ for (let i = 0; i < argv.length; i++) {
315
+ const a = argv[i]
316
+ if (a === '--allow-warn') flags.allowWarn = true
317
+ else if (a === '--json') flags.json = true
318
+ else if (a === '--adoption') flags.adoption = true
319
+ else if (a === '--files') flags.files = argv[++i]
320
+ }
321
+ const paths = discover(flags.files)
322
+ if (paths.length === 0) {
323
+ console.error('ci-audit: no CI config files found to scan (pass --files a.yml,b.yaml)')
324
+ process.exit(2)
325
+ }
326
+ const files = paths.map((p) => ({ name: p, text: fs.readFileSync(p, 'utf8') }))
327
+ const report = auditCorpus(files, { allowWarn: flags.allowWarn })
328
+ if (flags.adoption) {
329
+ const plan = buildAdoptionPlan(report)
330
+ if (flags.json) console.log(JSON.stringify(plan, null, 2))
331
+ else if (!plan.needed) console.log(`✓ nothing to adopt — all required gates wired and blocking, and the meta-gate runs (scanned: ${plan.scanned.join(', ')})`)
332
+ else console.log(renderAdoptionIssue(plan))
333
+ process.exit(plan.needed ? 1 : 0)
334
+ }
335
+ if (flags.json) {
336
+ console.log(JSON.stringify(report, null, 2))
337
+ } else {
338
+ console.log(`ci-audit — scanned: ${report.scanned.join(', ')}`)
339
+ for (const g of report.gates) {
340
+ const mark = g.status === 'present' ? '✓' : g.status === 'warn-only' ? '⚠' : '✗'
341
+ console.log(` ${mark} ${g.label}: ${g.status} (${g.evidence})`)
342
+ }
343
+ for (const c of report.conventions || []) {
344
+ const cmark = c.status === 'present' ? '✓' : c.status === 'n/a' ? '○' : c.required ? '✗' : '○'
345
+ const suffix = c.required ? '' : ' — preferred, advisory'
346
+ console.log(` ${cmark} ${c.label}: ${c.status}${suffix} (${c.evidence})`)
347
+ }
348
+ const m = report.metaGate?.wired ? '✓' : '○'
349
+ console.log(` ${m} ${META_GATE.label}: ${report.metaGate?.wired ? 'wired' : 'not wired (advisory — run --adoption)'} (${report.metaGate?.evidence})`)
350
+ const vmark = report.visualGate?.status === 'present' ? '✓' : '○'
351
+ console.log(` ${vmark} ${VISUAL_GATE.label}: ${report.visualGate?.status} (${report.visualGate?.evidence}) — advisory; only a gap if a surface opts into presentation:visual`)
352
+ console.log(report.ok ? '\nPASS — all required gates wired and blocking' : '\nFAIL — missing/weakened gate(s) or missing required convention(s); see references/enforcement/ci-contract.md')
353
+ if (!report.ok || !report.metaGate?.wired) console.log(' → generate a wiring issue: node ci-audit.mjs --adoption --files ' + report.scanned.join(','))
354
+ }
355
+ process.exit(report.ok ? 0 : 1)
356
+ }
357
+
358
+ if (isMainModule(import.meta.url)) main()