voidmachine 0.0.0 → 4.0.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 (341) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -4
  3. package/bin/void-harness.mjs +12 -0
  4. package/bin/void-machine.mjs +12 -0
  5. package/core-assets/.claude-plugin/plugin.json +253 -0
  6. package/core-assets/.gitkeep +0 -0
  7. package/core-assets/PHILOSOPHY.md +218 -0
  8. package/core-assets/PROJECT-DOCTRINE.template.md +14 -0
  9. package/core-assets/adapters/security/manifest.yaml +96 -0
  10. package/core-assets/agents/.gitkeep +0 -0
  11. package/core-assets/agents/accessibility-specialist.md +59 -0
  12. package/core-assets/agents/api-integration-engineer.md +59 -0
  13. package/core-assets/agents/code-explorer.md +94 -0
  14. package/core-assets/agents/code-explorer.source +7 -0
  15. package/core-assets/agents/data-migration-engineer.md +59 -0
  16. package/core-assets/agents/devex-docs-engineer.md +57 -0
  17. package/core-assets/agents/doctrine-critic.md +91 -0
  18. package/core-assets/agents/doctrine-critic.source +11 -0
  19. package/core-assets/agents/domain-architect.md +56 -0
  20. package/core-assets/agents/experience-designer.md +59 -0
  21. package/core-assets/agents/frontend-engineer.md +60 -0
  22. package/core-assets/agents/independent-code-reviewer.md +61 -0
  23. package/core-assets/agents/migration-planner.md +97 -0
  24. package/core-assets/agents/migration-planner.source +7 -0
  25. package/core-assets/agents/observability-sre-engineer.md +59 -0
  26. package/core-assets/agents/pdf-specialist.md +58 -0
  27. package/core-assets/agents/performance-engineer.md +58 -0
  28. package/core-assets/agents/product-challenger.md +55 -0
  29. package/core-assets/agents/security-engineer.md +66 -0
  30. package/core-assets/agents/silent-failure-hunter.md +94 -0
  31. package/core-assets/agents/silent-failure-hunter.source +8 -0
  32. package/core-assets/agents/solution-architect.md +57 -0
  33. package/core-assets/agents/test-qa-engineer.md +60 -0
  34. package/core-assets/agents/type-design-analyzer.md +93 -0
  35. package/core-assets/agents/type-design-analyzer.source +8 -0
  36. package/core-assets/agents/visual-craft-director.md +62 -0
  37. package/core-assets/codex/hooks.json +157 -0
  38. package/core-assets/contract-history/visual-craft-director/v2.yaml +43 -0
  39. package/core-assets/data/catalog.v3.json +10479 -0
  40. package/core-assets/data/certification.json +1800 -0
  41. package/core-assets/data/identity.json +8 -0
  42. package/core-assets/data/model.json +4226 -0
  43. package/core-assets/enforce/ci-enforce.sh +183 -0
  44. package/core-assets/hooks/_checks.sh +145 -0
  45. package/core-assets/hooks/_hooklib.sh +163 -0
  46. package/core-assets/hooks/_syntax-worker.cjs +503 -0
  47. package/core-assets/hooks/_void-hook.mjs +5459 -0
  48. package/core-assets/hooks/activation-meter.sh +13 -0
  49. package/core-assets/hooks/anti-ai-slop-grep.sh +38 -0
  50. package/core-assets/hooks/auto-format.sh +9 -0
  51. package/core-assets/hooks/axe-precommit.sh +35 -0
  52. package/core-assets/hooks/block-dangerous-bash.sh +10 -0
  53. package/core-assets/hooks/block-protected-push.sh +81 -0
  54. package/core-assets/hooks/boundary-direction-check.sh +10 -0
  55. package/core-assets/hooks/control-character-grep.sh +10 -0
  56. package/core-assets/hooks/fowler-refactors.txt +71 -0
  57. package/core-assets/hooks/large-cl-grep.sh +9 -0
  58. package/core-assets/hooks/llm-cost-precommit.sh +55 -0
  59. package/core-assets/hooks/migration-lint.sh +58 -0
  60. package/core-assets/hooks/migration-pr-template.sh +55 -0
  61. package/core-assets/hooks/no-ai-design-slop.sh +10 -0
  62. package/core-assets/hooks/no-any-grep.sh +10 -0
  63. package/core-assets/hooks/no-as-cast-grep.sh +10 -0
  64. package/core-assets/hooks/no-console-log-grep.sh +10 -0
  65. package/core-assets/hooks/no-null-grep.sh +10 -0
  66. package/core-assets/hooks/no-only-no-skip.sh +10 -0
  67. package/core-assets/hooks/outcome-meter.sh +13 -0
  68. package/core-assets/hooks/pre-PR-review-evidence.sh +47 -0
  69. package/core-assets/hooks/protect-sensitive-files.sh +10 -0
  70. package/core-assets/hooks/refactor-named-grep.sh +49 -0
  71. package/core-assets/hooks/secret-in-content.sh +10 -0
  72. package/core-assets/hooks/sessionstart-context.sh +9 -0
  73. package/core-assets/hooks/stop-typecheck.sh +9 -0
  74. package/core-assets/hooks/tdd-guard.md +90 -0
  75. package/core-assets/hooks/tdd-guard.sh +10 -0
  76. package/core-assets/hooks/test-name-lint.sh +10 -0
  77. package/core-assets/hooks/tidying-commit-prefix.sh +37 -0
  78. package/core-assets/hooks/trim-large-output.sh +9 -0
  79. package/core-assets/hooks/tsc-noemit-precommit.sh +22 -0
  80. package/core-assets/hooks/ubiquitous-language-lint.sh +58 -0
  81. package/core-assets/hooks/viewport-screenshot-gate.sh +48 -0
  82. package/core-assets/modules/.gitkeep +0 -0
  83. package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/.source +7 -0
  84. package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/SKILL.md +164 -0
  85. package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/harness.yaml +14 -0
  86. package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/.source +7 -0
  87. package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/SKILL.md +178 -0
  88. package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/harness.yaml +14 -0
  89. package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/.source +8 -0
  90. package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/SKILL.md +209 -0
  91. package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/harness.yaml +14 -0
  92. package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/.source +7 -0
  93. package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/SKILL.md +194 -0
  94. package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/harness.yaml +14 -0
  95. package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/.source +5 -0
  96. package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/SKILL.md +160 -0
  97. package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/harness.yaml +14 -0
  98. package/core-assets/packs/pack-monorepo/skills/void-package-extraction/.source +5 -0
  99. package/core-assets/packs/pack-monorepo/skills/void-package-extraction/SKILL.md +70 -0
  100. package/core-assets/packs/pack-monorepo/skills/void-package-extraction/harness.yaml +14 -0
  101. package/core-assets/packs/pack-monorepo/skills/void-service-package/.source +5 -0
  102. package/core-assets/packs/pack-monorepo/skills/void-service-package/SKILL.md +122 -0
  103. package/core-assets/packs/pack-monorepo/skills/void-service-package/harness.yaml +14 -0
  104. package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/.source +7 -0
  105. package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/SKILL.md +116 -0
  106. package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/harness.yaml +14 -0
  107. package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/.source +5 -0
  108. package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/SKILL.md +162 -0
  109. package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/harness.yaml +14 -0
  110. package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/.source +5 -0
  111. package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/SKILL.md +161 -0
  112. package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/harness.yaml +14 -0
  113. package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/.source +5 -0
  114. package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/SKILL.md +176 -0
  115. package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/harness.yaml +14 -0
  116. package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/.source +5 -0
  117. package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/SKILL.md +129 -0
  118. package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/harness.yaml +14 -0
  119. package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/.source +5 -0
  120. package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/SKILL.md +107 -0
  121. package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/harness.yaml +14 -0
  122. package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/.source +5 -0
  123. package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/SKILL.md +153 -0
  124. package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/harness.yaml +14 -0
  125. package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/.source +5 -0
  126. package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/SKILL.md +204 -0
  127. package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/harness.yaml +14 -0
  128. package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/.source +8 -0
  129. package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/SKILL.md +266 -0
  130. package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/harness.yaml +14 -0
  131. package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/.source +5 -0
  132. package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/SKILL.md +178 -0
  133. package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/harness.yaml +14 -0
  134. package/core-assets/packs/pack-react/skills/void-accessibility-check/.source +8 -0
  135. package/core-assets/packs/pack-react/skills/void-accessibility-check/SKILL.md +136 -0
  136. package/core-assets/packs/pack-react/skills/void-accessibility-check/harness.yaml +14 -0
  137. package/core-assets/packs/pack-react/skills/void-client-vs-server-component/.source +5 -0
  138. package/core-assets/packs/pack-react/skills/void-client-vs-server-component/SKILL.md +116 -0
  139. package/core-assets/packs/pack-react/skills/void-client-vs-server-component/harness.yaml +14 -0
  140. package/core-assets/packs/pack-react/skills/void-form-pattern/.source +7 -0
  141. package/core-assets/packs/pack-react/skills/void-form-pattern/SKILL.md +157 -0
  142. package/core-assets/packs/pack-react/skills/void-form-pattern/harness.yaml +14 -0
  143. package/core-assets/packs/pack-react/skills/void-state-architecture/.source +5 -0
  144. package/core-assets/packs/pack-react/skills/void-state-architecture/SKILL.md +160 -0
  145. package/core-assets/packs/pack-react/skills/void-state-architecture/harness.yaml +14 -0
  146. package/core-assets/packs/pack-server/skills/void-background-job-pattern/.source +6 -0
  147. package/core-assets/packs/pack-server/skills/void-background-job-pattern/SKILL.md +214 -0
  148. package/core-assets/packs/pack-server/skills/void-background-job-pattern/harness.yaml +14 -0
  149. package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/.source +6 -0
  150. package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/SKILL.md +168 -0
  151. package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/harness.yaml +14 -0
  152. package/core-assets/packs/pack-server/skills/void-env-validation/.source +6 -0
  153. package/core-assets/packs/pack-server/skills/void-env-validation/SKILL.md +131 -0
  154. package/core-assets/packs/pack-server/skills/void-env-validation/harness.yaml +14 -0
  155. package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/.source +6 -0
  156. package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/SKILL.md +149 -0
  157. package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/harness.yaml +14 -0
  158. package/core-assets/packs/pack-server/skills/void-server-action/.source +7 -0
  159. package/core-assets/packs/pack-server/skills/void-server-action/SKILL.md +181 -0
  160. package/core-assets/packs/pack-server/skills/void-server-action/harness.yaml +14 -0
  161. package/core-assets/packs/pack-server/skills/void-testing-server-modules/.source +7 -0
  162. package/core-assets/packs/pack-server/skills/void-testing-server-modules/SKILL.md +73 -0
  163. package/core-assets/packs/pack-server/skills/void-testing-server-modules/harness.yaml +14 -0
  164. package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/.source +7 -0
  165. package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/SKILL.md +205 -0
  166. package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/harness.yaml +14 -0
  167. package/core-assets/policies/core.yaml +95 -0
  168. package/core-assets/policies/ui.yaml +23 -0
  169. package/core-assets/profiles/base.yaml +29 -0
  170. package/core-assets/profiles/expo-config.yaml +36 -0
  171. package/core-assets/profiles/expo.yaml +35 -0
  172. package/core-assets/profiles/monorepo.yaml +33 -0
  173. package/core-assets/profiles/nextjs-config.yaml +31 -0
  174. package/core-assets/profiles/nextjs.yaml +31 -0
  175. package/core-assets/profiles/node-server.yaml +31 -0
  176. package/core-assets/profiles/pwa.yaml +33 -0
  177. package/core-assets/profiles/react.yaml +39 -0
  178. package/core-assets/profiles/sql.yaml +39 -0
  179. package/core-assets/profiles/typescript.yaml +39 -0
  180. package/core-assets/skills/.gitkeep +0 -0
  181. package/core-assets/skills/void-accessibility/.source +15 -0
  182. package/core-assets/skills/void-accessibility/SKILL.md +275 -0
  183. package/core-assets/skills/void-accessibility/harness.yaml +17 -0
  184. package/core-assets/skills/void-api-and-interface-design/.source +13 -0
  185. package/core-assets/skills/void-api-and-interface-design/SKILL.md +231 -0
  186. package/core-assets/skills/void-api-and-interface-design/harness.yaml +16 -0
  187. package/core-assets/skills/void-async-safety/.source +13 -0
  188. package/core-assets/skills/void-async-safety/SKILL.md +287 -0
  189. package/core-assets/skills/void-async-safety/harness.yaml +16 -0
  190. package/core-assets/skills/void-audit/.source +15 -0
  191. package/core-assets/skills/void-audit/SKILL.md +83 -0
  192. package/core-assets/skills/void-audit/harness.yaml +14 -0
  193. package/core-assets/skills/void-autopilot/.source +21 -0
  194. package/core-assets/skills/void-autopilot/SKILL.md +358 -0
  195. package/core-assets/skills/void-autopilot/harness.yaml +14 -0
  196. package/core-assets/skills/void-brainstorm/.source +14 -0
  197. package/core-assets/skills/void-brainstorm/SKILL.md +271 -0
  198. package/core-assets/skills/void-brainstorm/harness.yaml +14 -0
  199. package/core-assets/skills/void-checkpoint/.source +39 -0
  200. package/core-assets/skills/void-checkpoint/SKILL.md +227 -0
  201. package/core-assets/skills/void-checkpoint/harness.yaml +14 -0
  202. package/core-assets/skills/void-claude-md/.source +15 -0
  203. package/core-assets/skills/void-claude-md/SKILL.md +180 -0
  204. package/core-assets/skills/void-claude-md/harness.yaml +14 -0
  205. package/core-assets/skills/void-code-review/.source +14 -0
  206. package/core-assets/skills/void-code-review/SKILL.md +232 -0
  207. package/core-assets/skills/void-code-review/harness.yaml +15 -0
  208. package/core-assets/skills/void-commit-discipline/.source +9 -0
  209. package/core-assets/skills/void-commit-discipline/SKILL.md +211 -0
  210. package/core-assets/skills/void-commit-discipline/harness.yaml +15 -0
  211. package/core-assets/skills/void-context/.source +15 -0
  212. package/core-assets/skills/void-context/SKILL.md +167 -0
  213. package/core-assets/skills/void-context/harness.yaml +14 -0
  214. package/core-assets/skills/void-debug/.source +11 -0
  215. package/core-assets/skills/void-debug/SKILL.md +211 -0
  216. package/core-assets/skills/void-debug/harness.yaml +14 -0
  217. package/core-assets/skills/void-decide/.source +9 -0
  218. package/core-assets/skills/void-decide/SKILL.md +147 -0
  219. package/core-assets/skills/void-decide/harness.yaml +14 -0
  220. package/core-assets/skills/void-devex-audit/.source +8 -0
  221. package/core-assets/skills/void-devex-audit/SKILL.md +113 -0
  222. package/core-assets/skills/void-devex-audit/harness.yaml +15 -0
  223. package/core-assets/skills/void-doctor/.source +15 -0
  224. package/core-assets/skills/void-doctor/SKILL.md +83 -0
  225. package/core-assets/skills/void-doctor/harness.yaml +14 -0
  226. package/core-assets/skills/void-domain-driven-design/.source +13 -0
  227. package/core-assets/skills/void-domain-driven-design/SKILL.md +292 -0
  228. package/core-assets/skills/void-domain-driven-design/harness.yaml +15 -0
  229. package/core-assets/skills/void-frontend-design/.source +19 -0
  230. package/core-assets/skills/void-frontend-design/SKILL.md +274 -0
  231. package/core-assets/skills/void-frontend-design/harness.yaml +17 -0
  232. package/core-assets/skills/void-functional/.source +14 -0
  233. package/core-assets/skills/void-functional/SKILL.md +312 -0
  234. package/core-assets/skills/void-functional/harness.yaml +15 -0
  235. package/core-assets/skills/void-graph/.source +15 -0
  236. package/core-assets/skills/void-graph/SKILL.md +80 -0
  237. package/core-assets/skills/void-graph/harness.yaml +14 -0
  238. package/core-assets/skills/void-hexagonal-architecture/.source +12 -0
  239. package/core-assets/skills/void-hexagonal-architecture/SKILL.md +276 -0
  240. package/core-assets/skills/void-hexagonal-architecture/harness.yaml +15 -0
  241. package/core-assets/skills/void-implement/.source +39 -0
  242. package/core-assets/skills/void-implement/SKILL.md +183 -0
  243. package/core-assets/skills/void-implement/harness.yaml +14 -0
  244. package/core-assets/skills/void-learn/.source +13 -0
  245. package/core-assets/skills/void-learn/SKILL.md +226 -0
  246. package/core-assets/skills/void-learn/harness.yaml +14 -0
  247. package/core-assets/skills/void-llm-cost-discipline/.source +13 -0
  248. package/core-assets/skills/void-llm-cost-discipline/SKILL.md +299 -0
  249. package/core-assets/skills/void-llm-cost-discipline/harness.yaml +15 -0
  250. package/core-assets/skills/void-make-pdf/.source +9 -0
  251. package/core-assets/skills/void-make-pdf/SKILL.md +52 -0
  252. package/core-assets/skills/void-make-pdf/harness.yaml +15 -0
  253. package/core-assets/skills/void-merge/.source +8 -0
  254. package/core-assets/skills/void-merge/SKILL.md +111 -0
  255. package/core-assets/skills/void-merge/harness.yaml +14 -0
  256. package/core-assets/skills/void-migrations/.source +13 -0
  257. package/core-assets/skills/void-migrations/SKILL.md +264 -0
  258. package/core-assets/skills/void-migrations/harness.yaml +17 -0
  259. package/core-assets/skills/void-observability/.source +12 -0
  260. package/core-assets/skills/void-observability/SKILL.md +245 -0
  261. package/core-assets/skills/void-observability/harness.yaml +15 -0
  262. package/core-assets/skills/void-plan/.source +15 -0
  263. package/core-assets/skills/void-plan/SKILL.md +239 -0
  264. package/core-assets/skills/void-plan/harness.yaml +14 -0
  265. package/core-assets/skills/void-plan-review/.source +12 -0
  266. package/core-assets/skills/void-plan-review/SKILL.md +128 -0
  267. package/core-assets/skills/void-plan-review/harness.yaml +15 -0
  268. package/core-assets/skills/void-qa/.source +15 -0
  269. package/core-assets/skills/void-qa/SKILL.md +103 -0
  270. package/core-assets/skills/void-qa/harness.yaml +15 -0
  271. package/core-assets/skills/void-refactor/.source +9 -0
  272. package/core-assets/skills/void-refactor/SKILL.md +220 -0
  273. package/core-assets/skills/void-refactor/harness.yaml +15 -0
  274. package/core-assets/skills/void-retrospective/.source +9 -0
  275. package/core-assets/skills/void-retrospective/SKILL.md +71 -0
  276. package/core-assets/skills/void-retrospective/harness.yaml +15 -0
  277. package/core-assets/skills/void-security-audit/.source +12 -0
  278. package/core-assets/skills/void-security-audit/SKILL.md +138 -0
  279. package/core-assets/skills/void-security-audit/harness.yaml +15 -0
  280. package/core-assets/skills/void-security-guidance/.source +12 -0
  281. package/core-assets/skills/void-security-guidance/SKILL.md +255 -0
  282. package/core-assets/skills/void-security-guidance/harness.yaml +15 -0
  283. package/core-assets/skills/void-source-driven-development/.source +12 -0
  284. package/core-assets/skills/void-source-driven-development/SKILL.md +158 -0
  285. package/core-assets/skills/void-source-driven-development/harness.yaml +15 -0
  286. package/core-assets/skills/void-tdd/.source +15 -0
  287. package/core-assets/skills/void-tdd/SKILL.md +394 -0
  288. package/core-assets/skills/void-tdd/harness.yaml +15 -0
  289. package/core-assets/skills/void-testing/.source +19 -0
  290. package/core-assets/skills/void-testing/SKILL.md +301 -0
  291. package/core-assets/skills/void-testing/harness.yaml +17 -0
  292. package/core-assets/skills/void-ticket/.source +18 -0
  293. package/core-assets/skills/void-ticket/SKILL.md +239 -0
  294. package/core-assets/skills/void-ticket/harness.yaml +14 -0
  295. package/core-assets/skills/void-typescript-strict/.source +10 -0
  296. package/core-assets/skills/void-typescript-strict/SKILL.md +294 -0
  297. package/core-assets/skills/void-typescript-strict/harness.yaml +17 -0
  298. package/core-assets/skills/void-ui-review/.source +12 -0
  299. package/core-assets/skills/void-ui-review/SKILL.md +97 -0
  300. package/core-assets/skills/void-ui-review/harness.yaml +15 -0
  301. package/core-assets/skills/void-verify/.source +12 -0
  302. package/core-assets/skills/void-verify/SKILL.md +184 -0
  303. package/core-assets/skills/void-verify/harness.yaml +15 -0
  304. package/core-assets/specialists/accessibility-specialist.source +11 -0
  305. package/core-assets/specialists/accessibility-specialist.yaml +30 -0
  306. package/core-assets/specialists/api-integration-engineer.source +11 -0
  307. package/core-assets/specialists/api-integration-engineer.yaml +30 -0
  308. package/core-assets/specialists/data-migration-engineer.source +11 -0
  309. package/core-assets/specialists/data-migration-engineer.yaml +30 -0
  310. package/core-assets/specialists/devex-docs-engineer.source +11 -0
  311. package/core-assets/specialists/devex-docs-engineer.yaml +29 -0
  312. package/core-assets/specialists/domain-architect.source +11 -0
  313. package/core-assets/specialists/domain-architect.yaml +28 -0
  314. package/core-assets/specialists/experience-designer.source +15 -0
  315. package/core-assets/specialists/experience-designer.yaml +38 -0
  316. package/core-assets/specialists/frontend-engineer.source +11 -0
  317. package/core-assets/specialists/frontend-engineer.yaml +32 -0
  318. package/core-assets/specialists/independent-code-reviewer.source +10 -0
  319. package/core-assets/specialists/independent-code-reviewer.yaml +37 -0
  320. package/core-assets/specialists/migrations.json +15 -0
  321. package/core-assets/specialists/observability-sre-engineer.source +11 -0
  322. package/core-assets/specialists/observability-sre-engineer.yaml +30 -0
  323. package/core-assets/specialists/pdf-specialist.source +11 -0
  324. package/core-assets/specialists/pdf-specialist.yaml +29 -0
  325. package/core-assets/specialists/performance-engineer.source +11 -0
  326. package/core-assets/specialists/performance-engineer.yaml +29 -0
  327. package/core-assets/specialists/product-challenger.source +10 -0
  328. package/core-assets/specialists/product-challenger.yaml +27 -0
  329. package/core-assets/specialists/security-engineer.source +13 -0
  330. package/core-assets/specialists/security-engineer.yaml +41 -0
  331. package/core-assets/specialists/solution-architect.source +13 -0
  332. package/core-assets/specialists/solution-architect.yaml +32 -0
  333. package/core-assets/specialists/test-qa-engineer.source +14 -0
  334. package/core-assets/specialists/test-qa-engineer.yaml +35 -0
  335. package/core-assets/specialists/visual-craft-director.source +17 -0
  336. package/core-assets/specialists/visual-craft-director.yaml +60 -0
  337. package/core-assets/templates/github/dependency-audit.mjs +54 -0
  338. package/core-assets/templates/github/void-dependency-audit.yml +35 -0
  339. package/core-assets/templates/github/void-security.yml +85 -0
  340. package/dist/main.js +57001 -0
  341. package/package.json +66 -4
@@ -0,0 +1,214 @@
1
+ ---
2
+ name: void-background-job-pattern
3
+ description: "Run async work outside the request lifecycle: when to use a queue (Inngest, Trigger, Cloudflare Queues) vs a cron route. Idempotency, retry, dead-letter. Self-contained, no harness wrappers."
4
+ ---
5
+
6
+ # background-job-pattern
7
+
8
+ Use when work needs to happen **outside the user's request lifecycle** — too slow for the 10s HTTP timeout, or scheduled, or needing retry. Default in void-machine: a managed queue (Inngest, Trigger.dev, Cloudflare Queues) for event-driven jobs; route handlers + cron config for time-based.
9
+
10
+ If the work completes in < 200ms AND happens during the request anyway, do it inline. Background jobs add latency to the UX (work happens later) and infrastructure cost. Not the default.
11
+
12
+ ## When to use a background job
13
+
14
+ - Sending an email after signup (don't block the signup response on SMTP)
15
+ - Generating a PDF/export (multi-second work)
16
+ - Recomputing a cache after data change
17
+ - Polling an external API on schedule
18
+ - Cleanup tasks (delete soft-deleted rows after 30 days)
19
+ - Webhook retries when initial handler fails recoverably
20
+
21
+ ## When NOT to use a background job
22
+
23
+ - Work that the user is actively waiting for (use Server Actions, await inline)
24
+ - Work that depends on per-request context (cookies, headers) the queue won't have
25
+ - Sub-100ms operations (queue overhead > work cost)
26
+ - One-off scripts (use a runbook, not a job)
27
+
28
+ ## Three job types
29
+
30
+ | Type | Trigger | Tool example |
31
+ |---|---|---|
32
+ | **Event-driven** | Code event ("user.signed_up") | Inngest, Trigger.dev, Cloudflare Queues |
33
+ | **Scheduled** | Cron expression | Vercel Cron, Inngest schedule, Upstash QStash |
34
+ | **One-shot** | Manual or webhook | Same as event-driven |
35
+
36
+ Pick the type by **the trigger**, not by the work.
37
+
38
+ ## The 5 layers every job must implement
39
+
40
+ ```
41
+ 1. Validate input — Zod schema on the event payload
42
+ 2. Idempotency — same event delivered twice = one effect
43
+ 3. Trace context — log job name + event ID
44
+ 4. Service call — the business work in src/services/
45
+ 5. Retry classification — throw retryable errors, classify permanent ones
46
+ ```
47
+
48
+ These hold whether you use a queue's native helpers or write the job by hand. The pattern is the substance.
49
+
50
+ ## Event-driven jobs (Inngest example)
51
+
52
+ ```ts
53
+ // apps/web/src/jobs/send-welcome-email.ts
54
+ import { inngest } from '@/adapters/inngest';
55
+ import { z } from 'zod';
56
+ import { logger } from '@repo/core';
57
+ import { emailService } from '@/services/email';
58
+
59
+ // 1. Validate event payload at the boundary (defense in depth)
60
+ const EventData = z.object({
61
+ userId: z.string().uuid(),
62
+ email: z.string().email(),
63
+ });
64
+
65
+ export const sendWelcomeEmail = inngest.createFunction(
66
+ // 5. Retry classification: Inngest auto-retries thrown errors.
67
+ // Non-retryable: throw NonRetriableError (Inngest's marker class).
68
+ { id: 'send-welcome-email', retries: 3 },
69
+ { event: 'user.signed_up' },
70
+ async ({ event, step }) => {
71
+ // 1. Zod validate at the job boundary
72
+ const data = EventData.parse(event.data);
73
+
74
+ // 3. Trace context (event.id is Inngest's correlation key)
75
+ logger.info({
76
+ event: 'job.start',
77
+ name: 'send-welcome-email',
78
+ userId: data.userId,
79
+ eventId: event.id,
80
+ });
81
+
82
+ // 4. Do the work in a step (Inngest's retry checkpoint — only re-runs
83
+ // failed steps on retry, not the whole function)
84
+ await step.run('send-email', async () => {
85
+ await emailService.sendWelcome({ to: data.email });
86
+ });
87
+
88
+ // 2. Idempotency: Inngest dedupes by event.id automatically for the
89
+ // same function. For non-Inngest queues, do `INSERT ON CONFLICT
90
+ // DO NOTHING` in an inbox table with event.id as key.
91
+
92
+ return { ok: true };
93
+ },
94
+ );
95
+ ```
96
+
97
+ Trigger from a Server Action:
98
+
99
+ ```ts
100
+ await inngest.send({ name: 'user.signed_up', data: { userId, email } });
101
+ ```
102
+
103
+ Inngest's `step.run` IS the retry checkpoint. Trigger.dev and Cloudflare Queues have similar primitives (`task`, `step`). Use what your queue gives you natively. No wrapper from this pack required.
104
+
105
+ ## Scheduled jobs (Vercel Cron example)
106
+
107
+ ```ts
108
+ // apps/web/src/app/api/cron/cleanup/route.ts
109
+ import { NextRequest, NextResponse } from 'next/server';
110
+ import { env, logger } from '@repo/core';
111
+ import { cleanupSoftDeleted } from '@/services/admin';
112
+ import * as Sentry from '@sentry/nextjs';
113
+
114
+ export async function GET(req: NextRequest) {
115
+ // 1. Auth via cron secret (Vercel injects the Authorization header
116
+ // based on the CRON_SECRET env var)
117
+ const auth = req.headers.get('authorization');
118
+ if (auth !== `Bearer ${env.CRON_SECRET}`) {
119
+ return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
120
+ }
121
+
122
+ // 2. Concurrency lock — skip if a previous run is still in-flight.
123
+ // Implement with Redis SETNX, Postgres advisory lock, or your
124
+ // queue's native job-deduplication.
125
+ const lock = await tryAcquireLock('cron:cleanup', { ttlMs: 10 * 60 * 1000 });
126
+ if (!lock) {
127
+ logger.warn({ event: 'cron.skip', reason: 'previous-run-active', name: 'cleanup' });
128
+ return NextResponse.json({ ok: true, skipped: true });
129
+ }
130
+
131
+ try {
132
+ // 3. Trace context
133
+ logger.info({ event: 'cron.start', name: 'cleanup' });
134
+
135
+ // 4. Do the work
136
+ const result = await cleanupSoftDeleted({ olderThanDays: 30 });
137
+
138
+ logger.info({ event: 'cron.done', name: 'cleanup', deleted: result.count });
139
+ return NextResponse.json({ ok: true, deleted: result.count });
140
+ } catch (err) {
141
+ // 5. Always log + notify on cron failure; silent 500 means next run
142
+ // might also fail without anyone noticing
143
+ Sentry.captureException(err, { tags: { cron: 'cleanup' } });
144
+ return NextResponse.json({ ok: false }, { status: 500 });
145
+ } finally {
146
+ await releaseLock(lock);
147
+ }
148
+ }
149
+ ```
150
+
151
+ `vercel.json`:
152
+
153
+ ```json
154
+ {
155
+ "crons": [
156
+ { "path": "/api/cron/cleanup", "schedule": "0 3 * * *" }
157
+ ]
158
+ }
159
+ ```
160
+
161
+ The 5 layers (auth, concurrency lock, observability, work, error handling) are explicit. No wrappers required. `tryAcquireLock` / `releaseLock` are project-side primitives (Redis-backed, advisory lock, whatever fits) — typically 30 lines of code owned by the consumer.
162
+
163
+ ## Idempotency strategies
164
+
165
+ Pick the one that matches your storage layer:
166
+
167
+ - **Queue-native dedupe** (Inngest, Trigger): they dedupe on the event ID for the same function — free.
168
+ - **Inbox table**: `INSERT ON CONFLICT DO NOTHING` on a key column; if no row inserted, you've seen this event.
169
+ - **Service-level idempotence**: design the work so re-running is safe (`UPDATE WHERE status = 'pending'`).
170
+ - **Cron with concurrency lock**: not strictly idempotent but prevents pile-up; combine with service-level idempotence for true safety.
171
+
172
+ If none of these fit, you have a job that's unsafe to retry — flag it, write an ADR (`void-decide`).
173
+
174
+ ## Retry classification
175
+
176
+ Most queues retry on throwing handlers. Distinguish:
177
+
178
+ - **Retryable**: network errors, 5xx from external API, transient DB unavailability → throw normally
179
+ - **Non-retryable**: validation failures (Zod), 4xx from external API, "user doesn't exist" → throw a marked error class your queue recognizes (Inngest: `NonRetriableError`; Trigger: `AbortTaskRunError`; Cloudflare Queues: custom field)
180
+
181
+ If you can't tell, default to retryable. Worst case you waste retries; best case you survive a transient.
182
+
183
+ ## Dead-letter
184
+
185
+ After retry budget exhausted:
186
+
187
+ - Inngest, Trigger.dev, Cloudflare Queues have native DLQs — configure them
188
+ - Or write your own `job_dead_letter` table the team reviews
189
+ - Surface in `Settings → Background jobs → Dead letter` UI
190
+ - Never silently drop a failed job
191
+
192
+ ## Anti-patterns
193
+
194
+ - ✗ **`Promise.resolve().then(work)`** after a Server Action — serverless instance shuts down with the request; the work never runs
195
+ - ✗ **One mega-job that does 10 things** — split per concern; one job per event type
196
+ - ✗ **Cron without concurrency lock** — slow runs pile up, take down the worker
197
+ - ✗ **Cron without secret check** — anyone hitting the URL triggers your cron
198
+ - ✗ **Reading per-request context** — jobs don't have the user's cookies; pass identity in the event payload
199
+ - ✗ **Logging without job name + event ID** — can't correlate failures back to triggers
200
+ - ✗ **Sharing Zod schemas via copy-paste between sender and handler** — they drift; share via a `schemas/events.ts` module
201
+
202
+ ## Local development
203
+
204
+ - **Inngest CLI**: `npx inngest-cli dev` runs a local worker + UI for piping events
205
+ - **Trigger.dev**: `npx trigger.dev dev`
206
+ - **Vercel Cron**: no local equivalent; call the route handler directly with the cron secret to simulate
207
+
208
+ ## Composition (informational — each skill stands alone)
209
+
210
+ - `void-async-safety` — generic retry/idempotency/dead-letter doctrine.
211
+ - `void-webhook-handler-pattern` — webhooks often emit events that trigger jobs.
212
+ - `void-server-action` — actions emit events for async follow-up.
213
+ - `void-env-validation` — `CRON_SECRET`, queue API keys validated.
214
+ - `void-observability` — trace context links action → event → job.
@@ -0,0 +1,14 @@
1
+ # Harness metadata for this skill. Not part of the Agent Skills
2
+ # specification, and never copied into a consumer project: the SKILL.md
3
+ # beside it stays portable and validates against the official six fields.
4
+ # Read by this repository's graph, and by the installer for `runtimes`.
5
+ kind: standard
6
+ owner: folpe
7
+ runtimes: [claude, codex]
8
+ enforcement:
9
+ floor: ci
10
+ inline:
11
+ claude: active
12
+ codex: active
13
+ hermes: ci-only
14
+ eval_targets: [claude/anthropic/opus]
@@ -0,0 +1,6 @@
1
+ Sources for `void-drizzle-migration-safe` skill (see `docs/plans/skill-audits/void-drizzle-migration-safe.md` for the full audit):
2
+
3
+ - No external source audited — native Drizzle concretization of the `void-migrations` doctrine.
4
+ - Tooling referenced (not vendored): Drizzle Kit (drizzle-kit generate/push), Postgres CONCURRENTLY / NOT VALID semantics.
5
+
6
+ Strategy: native. Same principles as migrations, rewritten as concrete Drizzle SQL and code samples; no upstream skill was copied.
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: void-drizzle-migration-safe
3
+ description: "Write Drizzle migrations safe under concurrent traffic: zero-downtime add/rename of columns, indexes, FKs, enums. Composes with migrations for the generic discipline."
4
+ ---
5
+
6
+ # drizzle-migration-safe
7
+
8
+ Use when adding, modifying, or removing anything in a Drizzle schema that will be deployed to a production database with concurrent traffic. Composes with `void-migrations` (the generic doctrine); this skill ships the concrete Drizzle patterns.
9
+
10
+ If you are working on a fresh schema before any prod deployment, this skill does not apply — use raw `drizzle-kit generate` and `drizzle-kit push`. The discipline starts at the first paid user.
11
+
12
+ ## When this skill triggers
13
+
14
+ - "Add a column to the users table"
15
+ - "Rename `created_at` to `inserted_at`"
16
+ - "Make `email` not null"
17
+ - "Add an index on (org_id, status)"
18
+ - Any `pnpm db:generate` / `bun db:generate` followed by a manual review
19
+
20
+ ## Iron rules
21
+
22
+ 1. **Never combine schema change + backfill in one migration.** Each migration does one thing.
23
+ 2. **Never `ALTER COLUMN ... NOT NULL` without a backfill migration in between.** Two deploys minimum.
24
+ 3. **Always use `CREATE INDEX CONCURRENTLY` on Postgres** in production migrations — drizzle-kit doesn't add this by default, you must edit the generated SQL.
25
+ 4. **Never `DROP COLUMN` in the same release as the deploy that stops writing to it.** The old running pods will still write. Wait one deploy cycle.
26
+ 5. **Renames are deletes + adds.** Same as rule 4: ship the new column, dual-write for one cycle, then drop.
27
+
28
+ ## Pattern: adding a NOT NULL column
29
+
30
+ ```ts
31
+ // Step 1 — migration 0042_add_users_locale.sql (deploy A)
32
+ ALTER TABLE users ADD COLUMN locale text; -- nullable, no default
33
+ ```
34
+
35
+ ```ts
36
+ // Step 2 — backfill (deploy A.5, runs once)
37
+ UPDATE users SET locale = 'fr' WHERE locale IS NULL;
38
+ ```
39
+
40
+ ```ts
41
+ // Step 3 — migration 0043_users_locale_not_null.sql (deploy B)
42
+ ALTER TABLE users ALTER COLUMN locale SET NOT NULL;
43
+ ```
44
+
45
+ Three migrations, two deploys minimum. The drizzle schema file:
46
+
47
+ ```ts
48
+ // schema.ts (eventually consistent — reflects the END state)
49
+ export const users = pgTable('users', {
50
+ id: uuid().primaryKey(),
51
+ email: text().notNull(),
52
+ locale: text().notNull(), // safe to declare here after step 3
53
+ });
54
+ ```
55
+
56
+ ## Pattern: adding an index on a large table
57
+
58
+ ```sql
59
+ -- Generated by drizzle-kit (DO NOT use as-is in prod):
60
+ CREATE INDEX users_org_status_idx ON users (org_id, status);
61
+
62
+ -- Edit before deploying:
63
+ CREATE INDEX CONCURRENTLY users_org_status_idx ON users (org_id, status);
64
+ ```
65
+
66
+ `CONCURRENTLY` doesn't lock writes. The cost: it can fail mid-creation and leave an INVALID index. Always verify:
67
+
68
+ ```sql
69
+ SELECT indexrelid::regclass AS idx, indisvalid
70
+ FROM pg_index WHERE indexrelid = 'users_org_status_idx'::regclass;
71
+ ```
72
+
73
+ If `indisvalid = false`, drop and recreate.
74
+
75
+ ## Pattern: renaming a column
76
+
77
+ The naive way (`ALTER TABLE users RENAME COLUMN old TO new`) is one statement but takes an exclusive lock and breaks every running pod still reading `old`.
78
+
79
+ Safe way (three deploys):
80
+
81
+ 1. **Deploy A**: add `new`, dual-write app code (write both `old` and `new`, read from `old`)
82
+ 2. **Backfill**: `UPDATE users SET new = old WHERE new IS NULL`
83
+ 3. **Deploy B**: app reads from `new` (still writes both)
84
+ 4. **Deploy C**: app stops writing `old`, migration drops `old`
85
+
86
+ Painful for a rename. That's the cost of zero downtime — most renames are not worth it. Bias toward "keep the old name forever" unless the column is user-facing in an API.
87
+
88
+ ## Pattern: dropping a column
89
+
90
+ ```ts
91
+ // Deploy A: stop reading + writing the column in app code (PR is type-safe — TS removes it from inserts/selects)
92
+ // Deploy B: migration `ALTER TABLE users DROP COLUMN obsolete;`
93
+ ```
94
+
95
+ Two deploys. Never the same release.
96
+
97
+ ## Pattern: adding a foreign key
98
+
99
+ ```sql
100
+ -- Direct ADD CONSTRAINT takes an ACCESS EXCLUSIVE lock to validate every row:
101
+ ALTER TABLE orders ADD CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id);
102
+
103
+ -- Two-step (no exclusive lock):
104
+ ALTER TABLE orders ADD CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;
105
+ ALTER TABLE orders VALIDATE CONSTRAINT fk_orders_user;
106
+ ```
107
+
108
+ `NOT VALID` adds the constraint for new rows immediately, then `VALIDATE` scans the table without blocking writes.
109
+
110
+ ## Pattern: changing an enum
111
+
112
+ Postgres enums are awkward to change. Use this checklist:
113
+
114
+ - **Add a value**: `ALTER TYPE my_enum ADD VALUE 'new_one';` — safe, takes a row-level lock per row touched. On Postgres 12+ you can also `ALTER TYPE my_enum ADD VALUE 'new_one' IF NOT EXISTS` for idempotency.
115
+ - **Rename a value**: `ALTER TYPE my_enum RENAME VALUE 'old' TO 'new';` — Postgres 12+ only. App code must already accept both.
116
+ - **Remove a value**: not supported by Postgres. You must create a new enum, alter the column, drop the old enum. 4+ migrations. Almost never worth it; consider a text column with a CHECK constraint instead.
117
+
118
+ ## Drizzle-specific gotchas
119
+
120
+ - **`drizzle-kit generate` won't add `CONCURRENTLY` or `NOT VALID`** — always edit the generated SQL.
121
+ - **Schema declarations should match the END state**, not intermediate states. Drizzle infers types from the schema; if you declare `notNull()` before step 3 above, TS thinks the field is always defined while step-1 prod rows still have NULL.
122
+ - **Migration ordering**: `drizzle-kit generate` names migrations by timestamp. Never rename or reorder a migration that has been deployed to staging or prod.
123
+ - **`pgEnum` migrations**: Drizzle generates `CREATE TYPE` once. Re-running `generate` after an enum change can produce broken SQL — verify the diff before commit.
124
+
125
+ ## Workflow
126
+
127
+ 1. **Plan the change** before opening Drizzle Kit. Sketch on paper: this is X migrations, Y deploys.
128
+ 2. **Generate**: `pnpm db:generate` (or `bun db:generate`).
129
+ 3. **Inspect the SQL** in `drizzle/0042_*.sql`. Edit by hand if needed (CONCURRENTLY, NOT VALID, splitting one migration in two).
130
+ 4. **Run staging**: `pnpm db:migrate` against staging DB. Verify schema diff, run perf-sensitive queries, watch lock activity.
131
+ 5. **Open PR** with a "Deploy plan" section listing the migration's effect and required app code changes.
132
+ 6. **Deploy**: monitor `pg_stat_activity` for blocked queries; abort and rollback if writes back up.
133
+
134
+ ## Applying the migration: who runs `migrate`, and where
135
+
136
+ Two environments, two owners. Never blur them.
137
+
138
+ **Dev / local (the agent applies).** When a ticket ships a schema change, the migration must land in the dev/local DB *before* the test passes run — Drizzle infers types from the schema and the integration/E2E suite queries the real tables, so a stale DB either fails spuriously or (worse) passes against the wrong shape.
139
+
140
+ ```bash
141
+ # after `pnpm db:generate` + hand-review of the SQL:
142
+ pnpm db:migrate # local Postgres / pglite
143
+ # or against an ephemeral Neon dev branch (isolated, throwaway):
144
+ neonctl branches create --name dev/$TICKET --parent main
145
+ DATABASE_URL="$(neonctl connection-string dev/$TICKET)" pnpm db:migrate
146
+ # then run the suite against that same DATABASE_URL
147
+ ```
148
+
149
+ A Neon dev branch is the right target when the migration is lock-sensitive or needs realistic row counts (see `void-migrations` lock-impact dry run); pglite/local is enough for small tables. Delete the branch when the ticket lands.
150
+
151
+ **Production (CI applies, never the agent).** The worker/session stops at a green branch. `drizzle-kit migrate` against prod runs in a gated CI job on merge — a human-approved GitHub Actions step, never a local command and never on push to a feature branch:
152
+
153
+ ```yaml
154
+ # .github/workflows/deploy.yml (excerpt) — runs after review + merge to main
155
+ - name: Apply migrations
156
+ run: pnpm db:migrate
157
+ env:
158
+ DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
159
+ ```
160
+
161
+ This mirrors the `void-migrations` anti-rule "MUST NOT auto-apply migrations on push to main": prod DDL is a deploy decision, not a coding-cycle side effect.
162
+
163
+ ## Composition
164
+
165
+ - `void-migrations` — generic doctrine (this skill is the Drizzle concretization).
166
+ - `void-server-action` — service code calling the schema must handle nullable-then-not-null gracefully during the intermediate deploy.
167
+ - `void-observability` — log migration start/end with row counts; surface long-running ones in Sentry.
168
+ - `void-async-safety` — backfills that touch >100k rows should batch (LIMIT + LOOP), not one statement.
@@ -0,0 +1,14 @@
1
+ # Harness metadata for this skill. Not part of the Agent Skills
2
+ # specification, and never copied into a consumer project: the SKILL.md
3
+ # beside it stays portable and validates against the official six fields.
4
+ # Read by this repository's graph, and by the installer for `runtimes`.
5
+ kind: standard
6
+ owner: folpe
7
+ runtimes: [claude, codex]
8
+ enforcement:
9
+ floor: ci
10
+ inline:
11
+ claude: active
12
+ codex: active
13
+ hermes: ci-only
14
+ eval_targets: [claude/anthropic/opus]
@@ -0,0 +1,6 @@
1
+ Sources for `void-env-validation` skill (see `docs/plans/skill-audits/void-env-validation.md` for the full audit):
2
+
3
+ - No external source audited — native distillation of the @repo/core/env pattern (Zod-validated, fail-at-boot).
4
+ - Tooling referenced (not vendored): Zod, the per-runtime split (Node / Edge / browser) and NEXT_PUBLIC_* convention.
5
+
6
+ Strategy: native. Authored for void-machine from the @repo/core/env pattern; no upstream skill was copied.
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: void-env-validation
3
+ description: Validate environment variables at boot via Zod in @repo/core/env. Separate PUBLIC (NEXT_PUBLIC_*) from server-only. Fail fast at startup; never raw process.env in business code.
4
+ ---
5
+
6
+ # env-validation
7
+
8
+ Use when adding any new environment variable, or when working in a project that doesn't yet have `@repo/core/env`. Env vars are an invisible trust boundary — they're inputs from outside the process, and "the database URL is undefined" should explode at boot, not at the first query.
9
+
10
+ This skill is the void-machine operational form. Composes with `void-security-guidance` (env doctrine) and enforced by the `no-process-env-in-app` hook.
11
+
12
+ ## The principle
13
+
14
+ ```
15
+ process.env.X → used ONLY inside @repo/core/env
16
+ import { env } from '@repo/core' → used everywhere else
17
+ ```
18
+
19
+ `@repo/core/env` parses + validates `process.env` ONCE, exposes a typed object. Any business code reading `process.env` directly is forbidden (enforced by the `no-process-env-in-app` hook).
20
+
21
+ ## The schema
22
+
23
+ ```ts
24
+ // packages/core/src/env.ts
25
+ import { z } from 'zod';
26
+
27
+ const ServerSchema = z.object({
28
+ NODE_ENV: z.enum(['development', 'production', 'test']),
29
+ DATABASE_URL: z.string().url(),
30
+ STRIPE_SECRET_KEY: z.string().regex(/^sk_(test|live)_/),
31
+ STRIPE_WEBHOOK_SECRET: z.string().min(10),
32
+ SENTRY_DSN: z.string().url().optional(),
33
+ OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
34
+ });
35
+
36
+ const ClientSchema = z.object({
37
+ NEXT_PUBLIC_APP_URL: z.string().url(),
38
+ NEXT_PUBLIC_SENTRY_DSN: z.string().url().optional(),
39
+ });
40
+
41
+ function parseEnv() {
42
+ const isServer = typeof window === 'undefined';
43
+ const server = isServer ? ServerSchema.parse(process.env) : ({} as z.infer<typeof ServerSchema>);
44
+ const client = ClientSchema.parse({
45
+ NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
46
+ NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
47
+ });
48
+ return { ...client, ...server };
49
+ }
50
+
51
+ export const env = parseEnv();
52
+ export type Env = typeof env;
53
+ ```
54
+
55
+ Three rules in this file:
56
+
57
+ 1. **Server schema parsed only on server** (`typeof window === 'undefined'`). Otherwise the client bundle would include the server schema, leak field names + (worse) attempt to parse missing values.
58
+ 2. **Client schema parsed both sides**, but Client schema only reads `NEXT_PUBLIC_*`. Anything else is a leak.
59
+ 3. **Throws at module-load if missing/invalid.** This is intentional. App fails to boot rather than crash mid-request.
60
+
61
+ ## Per-runtime sanity
62
+
63
+ | Runtime | What's available | What env reads |
64
+ |---|---|---|
65
+ | Node server | Full `process.env` | Server + Client schemas |
66
+ | Edge runtime | Subset (only NEXT_PUBLIC_* + a few injected) | Server schema may fail — exclude server-only vars from edge routes |
67
+ | Browser | Only NEXT_PUBLIC_* + Next-injected | Client schema only |
68
+
69
+ If a server-only variable is read in an Edge route, the import chain pulls in the server schema, which fails on Edge (no `process.env.DATABASE_URL`). Solution: import `env` granularly:
70
+
71
+ ```ts
72
+ // Edge-safe — explicit imports
73
+ import { env } from '@repo/core/env-client';
74
+ // Server-only
75
+ import { env } from '@repo/core/env-server';
76
+ ```
77
+
78
+ Or split into `env-client.ts` and `env-server.ts` exports if your stack needs Edge support.
79
+
80
+ ## Naming convention
81
+
82
+ - `NEXT_PUBLIC_*` for browser-visible (Next.js convention; baked into the bundle at build time)
83
+ - `<DOMAIN>_<NAME>` for server-only (e.g., `STRIPE_SECRET_KEY`, not `SECRET_KEY`)
84
+ - Never use single-word ambiguous names (`API_KEY` — for what?)
85
+
86
+ ## The `.env.example` discipline
87
+
88
+ Every env added to the schema MUST be added to `.env.example` (committed):
89
+
90
+ ```bash
91
+ # .env.example
92
+ DATABASE_URL=postgresql://user:pass@localhost:5432/dev
93
+ STRIPE_SECRET_KEY=sk_test_REPLACE_ME
94
+ STRIPE_WEBHOOK_SECRET=whsec_REPLACE_ME
95
+ SENTRY_DSN= # optional
96
+ ```
97
+
98
+ New developer clones → `cp .env.example .env` → fill values → boot. If `.env.example` is incomplete, onboarding breaks for 30 minutes per dev.
99
+
100
+ ## What goes in `.env` vs secrets manager
101
+
102
+ - `.env.local` (gitignored): local dev secrets, test keys, dev DB
103
+ - Vercel/Doppler/etc. env settings: staging + production secrets
104
+ - Never commit `.env` (only `.env.example`)
105
+
106
+ ## Anti-patterns
107
+
108
+ - ✗ **`process.env.STRIPE_KEY ?? 'default'`** — silent fallback hides config errors; the schema's `.parse` would throw correctly
109
+ - ✗ **`process.env.STRIPE_KEY!` (non-null assertion)** — bypasses TS type system; if missing at runtime, you get a useless undefined error mid-request instead of a clear schema error at boot
110
+ - ✗ **NEXT_PUBLIC for secrets** — anything `NEXT_PUBLIC_*` is in the bundle and visible to anyone
111
+ - ✗ **Server-only env imported in a Client Component** — caught at build time, but the import chain matters: keep `env-server` imports out of `'use client'` files
112
+ - ✗ **Optional everywhere** — if it's optional, the code must handle absence. If the code crashes when absent, it was actually required → mark as required in the schema
113
+
114
+ ## Verification
115
+
116
+ In dev, simulate missing env:
117
+
118
+ ```bash
119
+ DATABASE_URL= bun run dev
120
+ # Expected: app fails to start with Zod's error showing which field
121
+ ```
122
+
123
+ If app starts and crashes later, your schema isn't comprehensive — find the missed import or non-null assertion.
124
+
125
+ ## Composition
126
+
127
+ - `void-security-guidance` — env-as-trust-boundary doctrine.
128
+ - `void-server-action` — actions import `env`, never `process.env`.
129
+ - `void-webhook-handler-pattern` — webhook secrets pulled from `env`.
130
+ - `void-instrumentation-setup` — Sentry/OTel DSN from `env`.
131
+ - `no-process-env-in-app` hook (harness-server) — blocks `process.env.X` in `apps/*/src/` files outside the env module itself.
@@ -0,0 +1,14 @@
1
+ # Harness metadata for this skill. Not part of the Agent Skills
2
+ # specification, and never copied into a consumer project: the SKILL.md
3
+ # beside it stays portable and validates against the official six fields.
4
+ # Read by this repository's graph, and by the installer for `runtimes`.
5
+ kind: standard
6
+ owner: folpe
7
+ runtimes: [claude, codex]
8
+ enforcement:
9
+ floor: ci
10
+ inline:
11
+ claude: active
12
+ codex: active
13
+ hermes: ci-only
14
+ eval_targets: [claude/anthropic/opus]
@@ -0,0 +1,6 @@
1
+ Sources for `void-rate-limit-strategy` skill (see `docs/plans/skill-audits/void-rate-limit-strategy.md` for the full audit):
2
+
3
+ - No external source audited — native distillation of the void-machine rate-limit presets per action class.
4
+ - Doctrine referenced (not vendored): `void-security-guidance` ("rate limit at every boundary"); this skill supplies the HOW (window / max / key).
5
+
6
+ Strategy: native. Presets and key strategies authored for void-machine; no upstream skill was copied.