@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,591 @@
1
+ // design-parity — the FIDELITY gate: does the deliverable match the design
2
+ // source it was derived from?
3
+ //
4
+ // node design-parity.mjs scaffold --source <ref>:<path> --surface <name> --url <path>
5
+ // [--role <name>=<selector> ...]
6
+ // → emits a manifest skeleton carrying EVERY catalog row for every declared
7
+ // role, each `expected: TODO`. The catalog is doctrine-fixed, so the rows
8
+ // you would have forgotten are the ones already in the file.
9
+ // node design-parity.mjs validate <manifest> [--observed <results.json>] [--diff <base>] [--root <dir>]
10
+ // → blocks on: a TODO row, a rejected row with no real reason, a `from:`
11
+ // citation that does not resolve in the source blob, a catalog row missing
12
+ // for a declared role, and — with --observed — any measured divergence.
13
+ // `--diff <base>` adds the re-baseline-to-green check: an `expected` value
14
+ // rewritten to match the page under an unmoved source is a blocker.
15
+ // node design-parity.mjs probe <manifest> --viewport <px>
16
+ // → prints the JS to evaluate in the page; its output IS the results file.
17
+ // Keeps this skill agent-agnostic: e2e-test, agent-browser or a raw
18
+ // Playwright call can drive it, none of them owns it.
19
+ //
20
+ // ---------------------------------------------------------------------------
21
+ // Why this is not a differ.
22
+ //
23
+ // The obvious tool here diffs the design source against the implementation. It
24
+ // only works when both are the same stack — and the incident that produced this
25
+ // script (fmm-express #521) is the case where they happened to be: a React+Tailwind
26
+ // prototype and a React+Tailwind page, so a class-token diff looked general when it
27
+ // was overfit. Point that differ at an HTML prototype, a v0 export or a Figma frame
28
+ // and it returns noise. Comparing ACROSS technologies is judgment and does not
29
+ // mechanize.
30
+ //
31
+ // So the work splits at that line. A human reads both, rendered, once, and writes
32
+ // down the load-bearing properties in units that survive a stack change — computed
33
+ // pixels, not `sm:text-3xl`. That manifest is the interchange format, and it is the
34
+ // only step that needs eyes. Everything after it — is the manifest complete, do its
35
+ // citations resolve, does the built page still measure that way — is mechanical and
36
+ // runs every round.
37
+ //
38
+ // Why FIDELITY and not drift. `presentation:visual` pins a page against a baseline
39
+ // captured FROM THE IMPLEMENTATION, so a baseline shot of the wrong page is green
40
+ // forever — in #521 it would have locked in the card plane and the flattened type
41
+ // scale and reported success. Here every `expected` is read off the SOURCE. That
42
+ // one difference is the whole point of the file.
43
+ //
44
+ // Named incident (fmm-express #521, 2026-08-06): a settled prototype was cited by
45
+ // exact path in the issue, summarized ONCE into four prose bullets, and every
46
+ // downstream artifact descended from the summary. What the summary dropped — page
47
+ // measure, card plane, the Amount input's responsive type steps, Description's
48
+ // scale — could never be found again, because nothing re-read the source and
49
+ // nothing rendered it (one image in a 3,040-message session, and the user pasted
50
+ // it). The criteria doc then listed "visual screenshot baselines" under Non-goals,
51
+ // so a fully green suite shipped a page that took four user corrections to fix.
52
+ // The manifest is that same act of summarizing, with an assertable output: "6px"
53
+ // either holds or it does not, where "the tip sits tight under Amount" was true of
54
+ // the broken page too.
55
+
56
+ import fs from 'node:fs'
57
+ import path from 'node:path'
58
+ import { execFileSync } from 'node:child_process'
59
+ import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
60
+
61
+ // ---- The catalog (doctrine-fixed; you do not pick these per-unit) -----------
62
+ //
63
+ // These are the properties that carry a design across a re-implementation, chosen
64
+ // because each one is a thing #521 actually lost. A unit may REJECT a row with a
65
+ // written reason; it may not quietly omit one. Adding rows is free; the catalog is
66
+ // raise-only, mirroring the Surface Baseline Contract.
67
+ export const PARITY_CATALOG = [
68
+ {
69
+ id: 'container.measure', group: 'Container', role: 'container', css: ['max-width'],
70
+ why: 'the measure the page is laid out to — the first thing lost in a port, and invisible to every functional assertion (shipped max-w-md against a max-w-2xl source)',
71
+ },
72
+ {
73
+ id: 'container.plane', group: 'Container', role: 'container',
74
+ css: ['background-color', 'border-top-width', 'box-shadow'],
75
+ why: 'card vs flat is one decision read through three properties; pinning only one lets a plane come back through the other two',
76
+ },
77
+ {
78
+ id: 'container.padding', group: 'Container', role: 'container', css: ['padding-top', 'padding-left'],
79
+ why: 'a card that lost its border keeps its inset and still reads as a card',
80
+ },
81
+ {
82
+ id: 'primary.scale', group: 'Hierarchy', role: 'primary', css: ['font-size'], perViewport: true,
83
+ why: 'the primary control carries the page. Per-viewport because a responsive step-up is exactly what a hand-port drops silently (source stepped 24px→30px; shipped held 24px)',
84
+ },
85
+ {
86
+ id: 'primary.box', group: 'Hierarchy', role: 'primary', css: ['padding-top', 'border-radius'],
87
+ why: 'type scale alone does not make a control primary — its box has to grow with it',
88
+ },
89
+ {
90
+ id: 'secondary.scale', group: 'Hierarchy', role: 'secondary', css: ['font-size'], perViewport: true,
91
+ why: 'a secondary field inheriting the shared input class reads at primary weight; that is the defect, and only a number catches it',
92
+ },
93
+ {
94
+ id: 'secondary.box', group: 'Hierarchy', role: 'secondary', css: ['padding-top', 'border-radius'],
95
+ why: 'same shape as primary.box, from the other end',
96
+ },
97
+ {
98
+ id: 'rhythm.coupled', group: 'Rhythm', role: 'coupled', css: ['margin-top'],
99
+ why: 'a tight gap says "this modifies that", a loose one says "next section" — the prose form ("sits tight under Amount") was true of the broken page too',
100
+ },
101
+ {
102
+ id: 'hierarchy.order', group: 'Hierarchy', relation: 'primary.font-size > secondary.font-size',
103
+ why: 'the one row that survives both a stack change and a token change: whatever the numbers become, primary must out-scale secondary',
104
+ },
105
+ ]
106
+
107
+ export const ROLES = ['container', 'primary', 'secondary', 'coupled']
108
+
109
+ // The presentation floor viewports (ensure-coverage → presentation/axis.md).
110
+ export const FLOOR_VIEWPORTS = [375, 1280]
111
+
112
+ // A reason has to be a reason. "n/a", "-", "none" are the ritual forms this exists
113
+ // to refuse — they are how a gated exclusion decays back into a silent omission.
114
+ const EMPTY_REASON = /^\s*(n\/?a|none|-+|todo|tbd|\.)?\s*$/i
115
+
116
+ export function isRealReason(s) {
117
+ const t = String(s ?? '').trim()
118
+ return t.length >= 12 && !EMPTY_REASON.test(t)
119
+ }
120
+
121
+ // ---- Manifest expansion ----------------------------------------------------
122
+
123
+ // Catalog × declared roles × viewports → the row set a complete manifest must
124
+ // carry. `validate` re-derives this rather than trusting the file: a manifest is
125
+ // hand-edited, and the edit that deletes an inconvenient row is the one to catch.
126
+ export function expectedRows(elements = {}, viewports = FLOOR_VIEWPORTS) {
127
+ const out = []
128
+ for (const entry of PARITY_CATALOG) {
129
+ if (entry.relation) {
130
+ // A relation is owed only when both of its operands are declared — otherwise
131
+ // it is a row nothing can ever measure, which trains readers to ignore rows.
132
+ const roles = entry.relation.split(/\s*(?:>=|<=|==|>|<)\s*/).map((s) => s.trim().split('.')[0])
133
+ if (roles.every((role) => elements[role])) out.push({ id: entry.id, relation: entry.relation })
134
+ continue
135
+ }
136
+ if (!elements[entry.role]) continue // role not declared → its rows are not owed
137
+ for (const css of entry.css) {
138
+ if (entry.perViewport) for (const v of viewports) out.push({ id: entry.id, role: entry.role, css, viewport: v })
139
+ else out.push({ id: entry.id, role: entry.role, css })
140
+ }
141
+ }
142
+ return out
143
+ }
144
+
145
+ export function rowKey(r) {
146
+ return r.relation
147
+ ? `${r.id}|relation`
148
+ : `${r.id}|${r.role}|${r.css}|${r.viewport ?? '*'}`
149
+ }
150
+
151
+ // ---- Parsing ---------------------------------------------------------------
152
+ //
153
+ // .json is the zero-dep path (and what the tests use); .yml needs js-yaml, which
154
+ // scripts/ already carries for the ledger. Same split, same reason: a gate that
155
+ // cannot run without an install is a gate that gets skipped.
156
+ export async function parseText(raw, nameHint = '') {
157
+ if (/\.json$/i.test(nameHint)) return JSON.parse(raw)
158
+ let yaml
159
+ try { ({ default: yaml } = await import('js-yaml')) }
160
+ catch { throw new Error(`Reading ${nameHint || 'the manifest'} needs YAML support. Run \`npm install js-yaml\` in scripts/, or write the manifest as .json.`) }
161
+ return yaml.load(raw)
162
+ }
163
+
164
+ export async function loadManifest(file) {
165
+ return parseText(fs.readFileSync(file, 'utf8'), file)
166
+ }
167
+
168
+ // ---- Validation ------------------------------------------------------------
169
+
170
+ // `sourceResolver` mirrors coverage-checklist's `fileResolver`: it answers "does
171
+ // this citation point at something real", and when git cannot answer it says so
172
+ // rather than inventing a verdict. Every wrong block here costs a round the gate
173
+ // exists to save, so an unanswerable check downgrades to a warning.
174
+ export function sourceResolver(root, source) {
175
+ if (!source?.path) return null
176
+ const ref = source.sha || source.ref
177
+ if (!ref) return null
178
+ const cache = new Map()
179
+ return (citedPath, line) => {
180
+ // A citation may name the source file bare or with its full repo path.
181
+ const target = citedPath.endsWith(source.path) || source.path.endsWith(citedPath) ? source.path : citedPath
182
+ if (!cache.has(target)) {
183
+ try {
184
+ const blob = execFileSync('git', ['show', `${ref}:${target}`], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
185
+ cache.set(target, blob.split('\n').length)
186
+ } catch { cache.set(target, null) }
187
+ }
188
+ const lines = cache.get(target)
189
+ if (lines === null) return { ok: false, why: `no \`${target}\` in ${ref}` }
190
+ if (line != null && line > lines) return { ok: false, why: `${target} in ${ref} has ${lines} lines, cited :${line}` }
191
+ return { ok: true }
192
+ }
193
+ }
194
+
195
+ const CITATION_RE = /^([\w.][\w./-]*\.\w{1,12}):(\d+)$/
196
+
197
+ export function validateManifest(manifest, opts = {}) {
198
+ const violations = []
199
+ const warnings = []
200
+ const push = (row, problem) => violations.push({ row, problem })
201
+
202
+ const elements = manifest?.elements || {}
203
+ const viewports = manifest?.viewports?.length ? manifest.viewports : FLOOR_VIEWPORTS
204
+ const rows = manifest?.rows || []
205
+
206
+ // 0 — the manifest has to say what it was read off. A parity manifest with no
207
+ // resolvable source is a baseline wearing a fidelity label.
208
+ if (!manifest?.source?.path) push('source', 'no `source.path` — a parity manifest must name the design source it was read off')
209
+ if (!manifest?.source?.sha && !manifest?.source?.ref) push('source', 'no `source.sha` / `source.ref` — pin the design source at a commit; a moving ref is not a source')
210
+ if (!Object.keys(elements).length) push('elements', 'no `elements` — declare at least one role → selector')
211
+ for (const role of Object.keys(elements)) {
212
+ if (!ROLES.includes(role)) warnings.push({ row: `elements.${role}`, problem: `unknown role (catalog roles: ${ROLES.join(', ')}) — its rows are not derived, so nothing checks it` })
213
+ }
214
+
215
+ // 1 — completeness. The anti-omission device: a manifest missing a catalog row
216
+ // for a role it declared is the #521 failure in miniature (the container plane
217
+ // was never on anyone's list, so nobody ever decided against it).
218
+ const present = new Map(rows.map((r) => [rowKey(r), r]))
219
+ for (const owed of expectedRows(elements, viewports)) {
220
+ if (!present.has(rowKey(owed))) {
221
+ const entry = PARITY_CATALOG.find((c) => c.id === owed.id)
222
+ push(owed.id, `missing row ${rowKey(owed)} — ${entry?.why || 'in the catalog'}. Pin it or reject it with a reason; omission is not a decision`)
223
+ }
224
+ }
225
+
226
+ // 2 — every row is decided, and decided against something checkable.
227
+ const resolve = opts.resolveSource
228
+ for (const r of rows) {
229
+ const label = rowKey(r)
230
+ const rejected = r.reject !== undefined && r.reject !== null
231
+ if (rejected) {
232
+ if (!isRealReason(r.reject)) push(label, `rejected with no real reason (\`reject: ${String(r.reject).trim() || '<empty>'}\`) — a gated exclusion needs an argument a reader can disagree with`)
233
+ continue
234
+ }
235
+ if (r.expected === undefined || r.expected === null || /^\s*todo\s*$/i.test(String(r.expected))) {
236
+ if (!r.relation) { push(label, 'expected is TODO — read the value off the rendered source, or reject the row'); continue }
237
+ }
238
+ if (!r.from) { push(label, 'no `from:` citation — every expected value names the line of the source it was read off'); continue }
239
+ const m = CITATION_RE.exec(String(r.from).trim())
240
+ if (!m) { push(label, `\`from: ${r.from}\` is not a \`<path>:<line>\` citation`); continue }
241
+ if (resolve) {
242
+ const res = resolve(m[1], Number(m[2]))
243
+ if (!res.ok) push(label, `\`from: ${r.from}\` does not resolve — ${res.why}`)
244
+ } else {
245
+ warnings.push({ row: label, problem: 'citation not resolved (no git context) — shape checked only' })
246
+ }
247
+ }
248
+
249
+ violations.warnings = warnings
250
+ return violations
251
+ }
252
+
253
+ // ---- Observed comparison ---------------------------------------------------
254
+
255
+ const LENGTH_RE = /^(-?\d+(?:\.\d+)?)px$/
256
+ const TOLERANCE_PX = 0.5 // sub-pixel layout is normal; 0.5px is not a design decision
257
+
258
+ export function normalize(value) {
259
+ const s = String(value ?? '').trim().toLowerCase().replace(/\s+/g, ' ')
260
+ const m = LENGTH_RE.exec(s)
261
+ return m ? { num: Number(m[1]) } : { str: s }
262
+ }
263
+
264
+ export function valuesAgree(expected, observed) {
265
+ const e = normalize(expected)
266
+ const o = normalize(observed)
267
+ if (e.num !== undefined && o.num !== undefined) return Math.abs(e.num - o.num) <= TOLERANCE_PX
268
+ // A bare number in `expected` is a courtesy: "24" means 24px.
269
+ if (e.num === undefined && o.num !== undefined && /^-?\d+(\.\d+)?$/.test(e.str)) return Math.abs(Number(e.str) - o.num) <= TOLERANCE_PX
270
+ return e.str === o.str
271
+ }
272
+
273
+ function observedIndex(observed) {
274
+ const idx = new Map()
275
+ for (const run of observed?.runs || []) {
276
+ for (const mm of run.measurements || []) idx.set(`${mm.role}|${mm.css}|${run.viewport}`, mm)
277
+ }
278
+ return idx
279
+ }
280
+
281
+ function lookup(idx, role, css, viewport, viewports) {
282
+ if (viewport != null) return idx.get(`${role}|${css}|${viewport}`)
283
+ // A viewport-independent row is satisfied by any floor viewport that measured it,
284
+ // but it must AGREE across the ones that did — a property that is 0 at 375 and 24
285
+ // at 1280 is a responsive decision hiding in a row that claims not to be one.
286
+ const hits = viewports.map((v) => idx.get(`${role}|${css}|${v}`)).filter(Boolean)
287
+ if (!hits.length) return undefined
288
+ return hits.every((h) => valuesAgree(hits[0].value, h.value)) ? hits[0] : { ...hits[0], disagrees: hits }
289
+ }
290
+
291
+ export function compareObserved(manifest, observed) {
292
+ const mismatches = []
293
+ const viewports = manifest?.viewports?.length ? manifest.viewports : FLOOR_VIEWPORTS
294
+ const idx = observedIndex(observed)
295
+
296
+ for (const r of manifest?.rows || []) {
297
+ if (r.reject !== undefined && r.reject !== null) continue
298
+ const label = rowKey(r)
299
+
300
+ if (r.relation) {
301
+ const parsed = /^\s*(\w+)\.([\w-]+)\s*(>|>=|<|<=|==)\s*(\w+)\.([\w-]+)\s*$/.exec(r.relation)
302
+ if (!parsed) { mismatches.push({ row: label, problem: `relation \`${r.relation}\` is not \`<role>.<css> <op> <role>.<css>\`` }); continue }
303
+ const [, lRole, lCss, op, rRole, rCss] = parsed
304
+ const l = lookup(idx, lRole, lCss, null, viewports)
305
+ const rr = lookup(idx, rRole, rCss, null, viewports)
306
+ if (!l || !rr) { mismatches.push({ row: label, problem: `not measured (${!l ? `${lRole}.${lCss}` : `${rRole}.${rCss}`} missing from the results)` }); continue }
307
+ const a = normalize(l.value).num
308
+ const b = normalize(rr.value).num
309
+ if (a === undefined || b === undefined) { mismatches.push({ row: label, problem: `relation needs two lengths, got ${l.value} and ${rr.value}` }); continue }
310
+ const ok = op === '>' ? a > b : op === '>=' ? a >= b : op === '<' ? a < b : op === '<=' ? a <= b : a === b
311
+ if (!ok) mismatches.push({ row: label, problem: `${lRole}.${lCss}=${l.value} is not ${op} ${rRole}.${rCss}=${rr.value}` })
312
+ continue
313
+ }
314
+
315
+ const hit = lookup(idx, r.role, r.css, r.viewport, viewports)
316
+ if (!hit) { mismatches.push({ row: label, problem: 'not measured — the results file has no value for this row (element missing, or the probe did not run at this viewport)' }); continue }
317
+ if (hit.value === null || hit.value === undefined) { mismatches.push({ row: label, problem: `element not found on the page (\`${manifest.elements?.[r.role] || r.role}\`)` }); continue }
318
+ if (hit.disagrees) { mismatches.push({ row: label, problem: `pinned as viewport-independent but measures ${hit.disagrees.map((h) => `${h.value}@?`).join(' / ')} across viewports — split it per viewport` }); continue }
319
+ if (!valuesAgree(r.expected, hit.value)) {
320
+ mismatches.push({ row: label, problem: `expected ${r.expected}, page has ${hit.value}${r.from ? ` (source: ${r.from})` : ''}` })
321
+ }
322
+ }
323
+ return mismatches
324
+ }
325
+
326
+ // ---- Expectation rewrites (the re-baseline-to-green analogue) ---------------
327
+ //
328
+ // The cheapest way to make this gate pass is to stop fixing the page and edit the
329
+ // manifest instead: change `expected: 672px` to `448px` and the divergence is gone.
330
+ // That is the same move as a bare `--update-snapshots`, which `review-pr` already
331
+ // blocks for `presentation:visual` — except here it is easier, because it is one
332
+ // character in a YAML file rather than a regenerated PNG, and it leaves no image in
333
+ // the diff for a reviewer to notice.
334
+ //
335
+ // It is also mechanically detectable, because of the one property that makes this
336
+ // fidelity and not drift: the source is pinned at a commit and is therefore
337
+ // IMMUTABLE. If `source.sha` has not moved and a row's `from:` line has not moved,
338
+ // then the value read off that line cannot have changed. A changed `expected` under
339
+ // those conditions is one of exactly two things — a mis-read being corrected (fine,
340
+ // and worth saying out loud) or a capitulation to the page (the thing to stop).
341
+ //
342
+ // So the rewrite is not forbidden; it is made to announce itself. `rebase: <reason>`
343
+ // on the row clears it, which is the same shape as the declared-intended-UI-change
344
+ // rule the visual baseline already uses. When the source itself moved, expectations
345
+ // are expected to change and this warns instead of blocking.
346
+ export function detectExpectationRewrites(prev, next) {
347
+ const out = []
348
+ if (!prev || !next) return out
349
+ const sourceMoved = (prev.source?.sha || prev.source?.ref) !== (next.source?.sha || next.source?.ref)
350
+ const before = new Map((prev.rows || []).map((r) => [rowKey(r), r]))
351
+
352
+ for (const r of next.rows || []) {
353
+ const was = before.get(rowKey(r))
354
+ if (!was) continue // new row: nothing was rewritten
355
+ if (was.expected === undefined || r.expected === undefined) continue
356
+ if (String(was.expected) === String(r.expected)) continue
357
+
358
+ const citationMoved = String(was.from ?? '') !== String(r.from ?? '')
359
+ const declared = isRealReason(r.rebase)
360
+ if (declared) continue
361
+
362
+ if (sourceMoved) {
363
+ out.push({ row: rowKey(r), level: 'warn', problem: `expected ${was.expected} → ${r.expected} and the design source moved (${prev.source?.sha} → ${next.source?.sha}) — plausible, but say so with \`rebase:\` so the reviewer is not re-deriving it` })
364
+ } else if (citationMoved) {
365
+ out.push({ row: rowKey(r), level: 'warn', problem: `expected ${was.expected} → ${r.expected} with a new citation (${was.from} → ${r.from}) — re-read from a different line; \`rebase:\` it if that was the correction` })
366
+ } else {
367
+ out.push({ row: rowKey(r), level: 'block', problem: `expected ${was.expected} → ${r.expected} with the SAME source (${next.source?.sha}) and the SAME citation (${r.from}). The source is pinned and cannot have changed, so this moves the target to the page instead of the page to the target. Fix the page, or state the mis-read with \`rebase: <reason>\`` })
368
+ }
369
+ }
370
+ return out
371
+ }
372
+
373
+ // ---- Probe -----------------------------------------------------------------
374
+ //
375
+ // Emitted rather than executed: e2e-test owns browser runners, this skill owns
376
+ // what must be proven. The snippet is deliberately dependency-free so it pastes
377
+ // into Playwright's `page.evaluate`, agent-browser, or a devtools console alike.
378
+ export function probeScript(manifest, viewport) {
379
+ const wanted = []
380
+ const seen = new Set()
381
+ const viewports = manifest?.viewports?.length ? manifest.viewports : FLOOR_VIEWPORTS
382
+ for (const owed of expectedRows(manifest?.elements || {}, viewports)) {
383
+ if (owed.relation) continue
384
+ const k = `${owed.role}|${owed.css}`
385
+ if (seen.has(k)) continue
386
+ seen.add(k)
387
+ wanted.push({ role: owed.role, css: owed.css, selector: manifest.elements[owed.role] })
388
+ }
389
+ // Relational rows need their operands measured even when no plain row asks for them.
390
+ for (const entry of PARITY_CATALOG) {
391
+ if (!entry.relation) continue
392
+ for (const side of entry.relation.split(/\s*(?:>=|<=|==|>|<)\s*/)) {
393
+ const [role, css] = side.trim().split('.')
394
+ if (!manifest?.elements?.[role] || seen.has(`${role}|${css}`)) continue
395
+ seen.add(`${role}|${css}`)
396
+ wanted.push({ role, css, selector: manifest.elements[role] })
397
+ }
398
+ }
399
+ return `(() => {
400
+ const want = ${JSON.stringify(wanted, null, 2).replace(/\n/g, '\n ')};
401
+ return {
402
+ surface: ${JSON.stringify(manifest?.surface || null)},
403
+ runs: [{
404
+ viewport: ${Number(viewport)},
405
+ measurements: want.map((w) => {
406
+ const el = document.querySelector(w.selector);
407
+ return { role: w.role, css: w.css, value: el ? getComputedStyle(el)[w.css] : null };
408
+ }),
409
+ }],
410
+ };
411
+ })()`
412
+ }
413
+
414
+ // ---- Scaffold --------------------------------------------------------------
415
+ //
416
+ // Rendered as a string rather than dumped from js-yaml, so each group carries the
417
+ // catalog's `why` into the file. The reason a row exists is the thing that stops it
418
+ // being deleted when it is inconvenient.
419
+ export function scaffoldManifest({ source, surface, url, elements = {}, viewports = FLOOR_VIEWPORTS }) {
420
+ const L = []
421
+ L.push(`# design-parity manifest — ${surface || '<surface>'}`)
422
+ L.push('#')
423
+ L.push('# FIDELITY, not drift: every `expected` below is read off the DESIGN SOURCE,')
424
+ L.push('# with it rendered in front of you — never off the implementation. A value')
425
+ L.push('# copied from the page you built is green forever, whatever it looks like.')
426
+ L.push('#')
427
+ L.push('# Fill `expected` in computed units (px), or `reject:` the row with a reason.')
428
+ L.push('# Omitting a row is not a decision and `validate` blocks on it.')
429
+ L.push('')
430
+ L.push('source:')
431
+ L.push(` ref: ${source?.ref || '<branch-or-tag>'}`)
432
+ L.push(` sha: ${source?.sha || '<commit-sha>'}`)
433
+ L.push(` path: ${source?.path || '<path/in/that/ref>'}`)
434
+ L.push(` kind: ${source?.kind || 'react'} # react | html | figma | image`)
435
+ L.push(`surface: ${surface || '<surface-id>'}`)
436
+ L.push(`url: ${url || '<route>'}`)
437
+ L.push(`viewports: [${viewports.join(', ')}]`)
438
+ L.push('')
439
+ L.push('elements:')
440
+ for (const role of ROLES) {
441
+ const sel = elements[role]
442
+ L.push(sel ? ` ${role}: '${sel}'` : ` # ${role}: '<selector>' # undeclared → its catalog rows are not owed`)
443
+ }
444
+ L.push('')
445
+ L.push('rows:')
446
+ const all = expectedRows(elements, viewports)
447
+ let group = null
448
+ for (const entry of PARITY_CATALOG) {
449
+ const owed = all.filter((r) => r.id === entry.id)
450
+ if (!owed.length) continue
451
+ if (entry.group !== group) { group = entry.group; L.push(` # ── ${group} ──`) }
452
+ L.push(` # ${entry.why}`)
453
+ for (const r of owed) {
454
+ L.push(` - id: ${r.id}`)
455
+ if (r.relation) {
456
+ // The relation IS the expectation — there is no value to fill in, only a
457
+ // line to cite. Emitting `expected: TODO` here would teach the reader that
458
+ // TODO is sometimes fine to leave.
459
+ L.push(` relation: '${r.relation}'`)
460
+ } else {
461
+ L.push(` role: ${r.role}`)
462
+ L.push(` css: ${r.css}`)
463
+ if (r.viewport != null) L.push(` viewport: ${r.viewport}`)
464
+ L.push(' expected: TODO')
465
+ }
466
+ L.push(` from: TODO # ${source?.path || '<source>'}:<line>`)
467
+ }
468
+ }
469
+ L.push('')
470
+ return L.join('\n')
471
+ }
472
+
473
+ // ---- CLI -------------------------------------------------------------------
474
+
475
+ function repoRoot(explicit) {
476
+ if (explicit) return explicit
477
+ try { return execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim() }
478
+ catch { return process.cwd() }
479
+ }
480
+
481
+ function flag(argv, name) { const i = argv.indexOf(name); return i === -1 ? null : argv[i + 1] }
482
+
483
+ async function main() {
484
+ const [cmd, ...rest] = process.argv.slice(2)
485
+
486
+ if (cmd === 'scaffold') {
487
+ const src = flag(rest, '--source') || ''
488
+ const ci = src.lastIndexOf(':')
489
+ const ref = ci > 0 ? src.slice(0, ci) : ''
490
+ const p = ci > 0 ? src.slice(ci + 1) : ''
491
+ if (!ref || !p) { console.error('usage: design-parity.mjs scaffold --source <ref>:<path> --surface <name> --url <route> [--role primary=<selector> ...]'); process.exit(2) }
492
+ // If the ref will not resolve here (a prototype branch in another repo, a ref
493
+ // not fetched yet), leave the placeholder rather than copying the ref into
494
+ // `sha:`. A moving ref sitting in the pinned field looks pinned and is not —
495
+ // and the whole manifest rests on the source being a fixed commit.
496
+ let sha = null
497
+ try { sha = execFileSync('git', ['rev-parse', '--short', ref], { cwd: repoRoot(flag(rest, '--root')), encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() } catch { /* leave it unresolved */ }
498
+ if (!sha) console.error(`design-parity: could not resolve \`${ref}\` here — leaving \`sha:\` unfilled. Pin it before validating (\`git rev-parse --short ${ref}\`).`)
499
+ const elements = {}
500
+ for (let i = 0; i < rest.length; i++) {
501
+ if (rest[i] !== '--role') continue
502
+ const [role, ...sel] = String(rest[i + 1] || '').split('=')
503
+ if (role && sel.length) elements[role] = sel.join('=')
504
+ }
505
+ console.log(scaffoldManifest({
506
+ source: { ref, sha, path: p, kind: flag(rest, '--kind') || 'react' },
507
+ surface: flag(rest, '--surface'),
508
+ url: flag(rest, '--url'),
509
+ elements,
510
+ }))
511
+ process.exit(0)
512
+ }
513
+
514
+ if (cmd === 'validate') {
515
+ const file = rest.find((a) => !a.startsWith('--'))
516
+ if (!file || !fs.existsSync(file)) { console.error('usage: design-parity.mjs validate <manifest.yml|.json> [--observed <results.json>] [--root <dir>]'); process.exit(2) }
517
+ const root = repoRoot(flag(rest, '--root'))
518
+ const manifest = await loadManifest(file)
519
+ const observedFile = flag(rest, '--observed')
520
+
521
+ const violations = validateManifest(manifest, { resolveSource: sourceResolver(root, manifest?.source) })
522
+ for (const w of violations.warnings) console.log(` ! ${w.row}: ${w.problem}`)
523
+
524
+ // Rewrite check: needs the manifest as it stood at the merge base. Silent when
525
+ // no --diff is given (the reduced form already says the run is partial), and
526
+ // never fatal on a git failure — a manifest added by this PR has no previous
527
+ // version, which is not a problem to report.
528
+ let rewrites = []
529
+ const base = flag(rest, '--diff')
530
+ if (base) {
531
+ // Both sides through realpath before differencing. `process.cwd()` returns a
532
+ // resolved path while an explicit --root is taken verbatim, so on any host
533
+ // where the tree sits behind a symlink (macOS `/var`→`/private/var`, and this
534
+ // skill family's own symlink-farm install) the naive relative path escapes the
535
+ // repo as `../../..` and `git show` silently finds nothing — which reads as
536
+ // "no previous version", i.e. the check passes exactly where it should fire.
537
+ const real = (p) => { try { return fs.realpathSync(p) } catch { return path.resolve(p) } }
538
+ const rel = path.relative(real(root), real(file))
539
+ try {
540
+ const prevRaw = execFileSync('git', ['show', `${base}:${rel}`], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
541
+ rewrites = detectExpectationRewrites(await parseText(prevRaw, file), manifest)
542
+ } catch { /* new manifest at this base — nothing to compare */ }
543
+ for (const w of rewrites.filter((x) => x.level === 'warn')) console.log(` ! ${w.row}: ${w.problem}`)
544
+ }
545
+ const blockingRewrites = rewrites.filter((x) => x.level === 'block')
546
+
547
+ let mismatches = []
548
+ if (observedFile) {
549
+ if (!fs.existsSync(observedFile)) { console.error(`design-parity: --observed ${observedFile} does not exist`); process.exit(2) }
550
+ mismatches = compareObserved(manifest, JSON.parse(fs.readFileSync(observedFile, 'utf8')))
551
+ } else {
552
+ // Same failure shape coverage-checklist hit: two runs of one command, opposite
553
+ // verdicts, and no way to tell from the output which question was asked. Without
554
+ // --observed this checks that the manifest is COMPLETE, never that the page
555
+ // conforms — and the pass is the misleading one, so it announces itself.
556
+ console.error('design-parity: NOTE — no --observed: this is the reduced form. It checks the manifest is complete and its citations resolve; it does NOT check the page. A green run here is not the green the reviewer will get.')
557
+ }
558
+
559
+ if (!violations.length && !mismatches.length && !blockingRewrites.length) {
560
+ const scope = observedFile ? `${(manifest.rows || []).length} row(s) hold against the rendered page` : `${(manifest.rows || []).length} row(s) decided and cited — manifest only, page unchecked`
561
+ console.log(`design-parity: ✓ ${scope}${violations.warnings.length ? ` (${violations.warnings.length} warning(s) above)` : ''}`)
562
+ process.exit(0)
563
+ }
564
+ if (violations.length) {
565
+ console.log(`design-parity: ✗ ${violations.length} manifest problem(s):\n`)
566
+ for (const v of violations) console.log(` • ${v.row}: ${v.problem}`)
567
+ }
568
+ if (blockingRewrites.length) {
569
+ console.log(`\ndesign-parity: ✗ ${blockingRewrites.length} expectation(s) rewritten to match the page:\n`)
570
+ for (const w of blockingRewrites) console.log(` • ${w.row}: ${w.problem}`)
571
+ }
572
+ if (mismatches.length) {
573
+ console.log(`\ndesign-parity: ✗ ${mismatches.length} divergence(s) from the design source:\n`)
574
+ for (const m of mismatches) console.log(` • ${m.row}: ${m.problem}`)
575
+ }
576
+ process.exit(1)
577
+ }
578
+
579
+ if (cmd === 'probe') {
580
+ const file = rest.find((a) => !a.startsWith('--'))
581
+ const vp = flag(rest, '--viewport')
582
+ if (!file || !fs.existsSync(file) || !vp) { console.error('usage: design-parity.mjs probe <manifest.yml|.json> --viewport <px>'); process.exit(2) }
583
+ console.log(probeScript(await loadManifest(file), vp))
584
+ process.exit(0)
585
+ }
586
+
587
+ console.error('usage: design-parity.mjs <scaffold|validate|probe>')
588
+ process.exit(2)
589
+ }
590
+
591
+ if (isMainModule(import.meta.url)) await main()