groundwork-method 0.0.1 → 0.11.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 (647) hide show
  1. package/CHANGELOG.md +823 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -29
  4. package/bin/groundwork.js +1723 -0
  5. package/dist/src/generators/add-capability/generator.d.ts +8 -0
  6. package/dist/src/generators/add-capability/generator.js +60 -0
  7. package/dist/src/generators/add-capability/generator.js.map +1 -0
  8. package/dist/src/generators/cli-app/generator.d.ts +9 -0
  9. package/dist/src/generators/cli-app/generator.js +140 -0
  10. package/dist/src/generators/cli-app/generator.js.map +1 -0
  11. package/dist/src/generators/docs-site/generator.d.ts +5 -0
  12. package/dist/src/generators/docs-site/generator.js +441 -0
  13. package/dist/src/generators/docs-site/generator.js.map +1 -0
  14. package/dist/src/generators/electron-app/generator.d.ts +6 -0
  15. package/dist/src/generators/electron-app/generator.js +261 -0
  16. package/dist/src/generators/electron-app/generator.js.map +1 -0
  17. package/dist/src/generators/flutter-app/generator.d.ts +6 -0
  18. package/dist/src/generators/flutter-app/generator.js +314 -0
  19. package/dist/src/generators/flutter-app/generator.js.map +1 -0
  20. package/dist/src/generators/go-microservice/generator.d.ts +8 -0
  21. package/dist/src/generators/go-microservice/generator.js +232 -0
  22. package/dist/src/generators/go-microservice/generator.js.map +1 -0
  23. package/dist/src/generators/nextjs-app/generator.d.ts +8 -0
  24. package/dist/src/generators/nextjs-app/generator.js +294 -0
  25. package/dist/src/generators/nextjs-app/generator.js.map +1 -0
  26. package/dist/src/generators/python-microservice/generator.d.ts +13 -0
  27. package/dist/src/generators/python-microservice/generator.js +265 -0
  28. package/dist/src/generators/python-microservice/generator.js.map +1 -0
  29. package/dist/src/generators/shared/brand-tokens.d.ts +89 -0
  30. package/dist/src/generators/shared/brand-tokens.js +308 -0
  31. package/dist/src/generators/shared/brand-tokens.js.map +1 -0
  32. package/dist/src/generators/shared/capabilities.d.ts +101 -0
  33. package/dist/src/generators/shared/capabilities.js +279 -0
  34. package/dist/src/generators/shared/capabilities.js.map +1 -0
  35. package/dist/src/generators/shared/provenance.d.ts +2 -0
  36. package/dist/src/generators/shared/provenance.js +85 -0
  37. package/dist/src/generators/shared/provenance.js.map +1 -0
  38. package/dist/src/generators/shared/scaffold-helpers.d.ts +72 -0
  39. package/dist/src/generators/shared/scaffold-helpers.js +309 -0
  40. package/dist/src/generators/shared/scaffold-helpers.js.map +1 -0
  41. package/dist/src/generators/system-test-runner/generator.d.ts +23 -0
  42. package/dist/src/generators/system-test-runner/generator.js +173 -0
  43. package/dist/src/generators/system-test-runner/generator.js.map +1 -0
  44. package/dist/src/generators/workspace-dev-cli/generator.d.ts +7 -0
  45. package/dist/src/generators/workspace-dev-cli/generator.js +138 -0
  46. package/dist/src/generators/workspace-dev-cli/generator.js.map +1 -0
  47. package/generators.json +57 -0
  48. package/lib/repo-map/grammars/tree-sitter-c.wasm +0 -0
  49. package/lib/repo-map/grammars/tree-sitter-cpp.wasm +0 -0
  50. package/lib/repo-map/grammars/tree-sitter-csharp.wasm +0 -0
  51. package/lib/repo-map/grammars/tree-sitter-dart.wasm +0 -0
  52. package/lib/repo-map/grammars/tree-sitter-go.wasm +0 -0
  53. package/lib/repo-map/grammars/tree-sitter-java.wasm +0 -0
  54. package/lib/repo-map/grammars/tree-sitter-javascript.wasm +0 -0
  55. package/lib/repo-map/grammars/tree-sitter-kotlin.wasm +0 -0
  56. package/lib/repo-map/grammars/tree-sitter-lua.wasm +0 -0
  57. package/lib/repo-map/grammars/tree-sitter-php.wasm +0 -0
  58. package/lib/repo-map/grammars/tree-sitter-python.wasm +0 -0
  59. package/lib/repo-map/grammars/tree-sitter-ruby.wasm +0 -0
  60. package/lib/repo-map/grammars/tree-sitter-rust.wasm +0 -0
  61. package/lib/repo-map/grammars/tree-sitter-scala.wasm +0 -0
  62. package/lib/repo-map/grammars/tree-sitter-swift.wasm +0 -0
  63. package/lib/repo-map/grammars/tree-sitter-tsx.wasm +0 -0
  64. package/lib/repo-map/grammars/tree-sitter-typescript.wasm +0 -0
  65. package/lib/repo-map/index.js +386 -0
  66. package/lib/repo-map/languages.js +514 -0
  67. package/lib/repo-map/pagerank.js +59 -0
  68. package/migrations/README.md +60 -0
  69. package/migrations/_template/cli-migration.js +27 -0
  70. package/migrations/gw-bet-prose-redesign.js +105 -0
  71. package/migrations/gw-drop-test-manifest.js +37 -0
  72. package/migrations/gw-register-serena-mcp.js +42 -0
  73. package/migrations/gw-relocate-hidden-skills.js +40 -0
  74. package/migrations/gw-seed-config-toml.js +24 -0
  75. package/migrations/index.json +40 -0
  76. package/package.json +70 -6
  77. package/src/AGENTS.md +36 -0
  78. package/src/config/config.toml +30 -0
  79. package/src/config/groundwork-state.json +5 -0
  80. package/src/docs/llms.txt +72 -0
  81. package/src/docs/principles/ai-native/agent-native-systems.md +90 -0
  82. package/src/docs/principles/ai-native/agentic-systems.md +78 -0
  83. package/src/docs/principles/ai-native/ai-engineering.md +100 -0
  84. package/src/docs/principles/ai-native/ai-native-product.md +76 -0
  85. package/src/docs/principles/delivery/cost-engineering.md +89 -0
  86. package/src/docs/principles/delivery/day-2-operational-baseline.md +57 -0
  87. package/src/docs/principles/delivery/devex.md +88 -0
  88. package/src/docs/principles/delivery/platform.md +101 -0
  89. package/src/docs/principles/delivery/progressive-delivery.md +92 -0
  90. package/src/docs/principles/design/ai-native-design.md +73 -0
  91. package/src/docs/principles/design/design-foundations.md +80 -0
  92. package/src/docs/principles/design/design-systems-and-tokens.md +72 -0
  93. package/src/docs/principles/design/interaction-and-motion.md +69 -0
  94. package/src/docs/principles/design/layout-and-space.md +72 -0
  95. package/src/docs/principles/design/usability-and-ux.md +79 -0
  96. package/src/docs/principles/design/visual-design.md +84 -0
  97. package/src/docs/principles/foundations/code-craft.md +86 -0
  98. package/src/docs/principles/foundations/continuous-discovery.md +75 -0
  99. package/src/docs/principles/foundations/documentation.md +102 -0
  100. package/src/docs/principles/foundations/prioritization-and-appetite.md +78 -0
  101. package/src/docs/principles/foundations/product-engineering.md +90 -0
  102. package/src/docs/principles/foundations/product-risks.md +89 -0
  103. package/src/docs/principles/foundations/requirements-and-specs.md +80 -0
  104. package/src/docs/principles/foundations/success-metrics.md +66 -0
  105. package/src/docs/principles/foundations/testing.md +108 -0
  106. package/src/docs/principles/index.md +24 -0
  107. package/src/docs/principles/quality/accessibility.md +88 -0
  108. package/src/docs/principles/quality/observability.md +84 -0
  109. package/src/docs/principles/quality/performance.md +84 -0
  110. package/src/docs/principles/quality/privacy.md +92 -0
  111. package/src/docs/principles/quality/reliability.md +89 -0
  112. package/src/docs/principles/quality/security.md +78 -0
  113. package/src/docs/principles/stack/postgres.md +100 -0
  114. package/src/docs/principles/system-design/api-design.md +86 -0
  115. package/src/docs/principles/system-design/architecture-decisions.md +81 -0
  116. package/src/docs/principles/system-design/code-structure.md +104 -0
  117. package/src/docs/principles/system-design/data-engineering.md +87 -0
  118. package/src/docs/principles/system-design/durable-execution.md +89 -0
  119. package/src/docs/principles/system-design/evolutionary-architecture.md +81 -0
  120. package/src/docs/principles/system-design/identity-and-access.md +76 -0
  121. package/src/docs/principles/system-design/integration-patterns.md +84 -0
  122. package/src/docs/principles/system-design/real-time.md +83 -0
  123. package/src/docs/principles/system-design/surface-architecture.md +74 -0
  124. package/src/docs/ways-of-working/documentation.md +69 -0
  125. package/src/docs/ways-of-working/how-we-work.md +76 -0
  126. package/src/docs/ways-of-working/units-of-work.md +40 -0
  127. package/src/engineer-skills/groundwork-electron-engineer/SKILL.md +123 -0
  128. package/src/engineer-skills/groundwork-electron-engineer/references/documentation.md +126 -0
  129. package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
  130. package/src/engineer-skills/groundwork-electron-engineer/references/observability.md +37 -0
  131. package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -0
  132. package/src/engineer-skills/groundwork-electron-engineer/references/performance-and-reliability.md +80 -0
  133. package/src/engineer-skills/groundwork-electron-engineer/references/process-model.md +94 -0
  134. package/src/engineer-skills/groundwork-electron-engineer/references/security.md +107 -0
  135. package/src/engineer-skills/groundwork-electron-engineer/references/testing-and-smoke.md +129 -0
  136. package/src/engineer-skills/groundwork-electron-engineer/references/theming-and-tokens.md +74 -0
  137. package/src/engineer-skills/groundwork-electron-engineer/sync-anchor.md +22 -0
  138. package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +114 -0
  139. package/src/engineer-skills/groundwork-flutter-engineer/references/accessibility.md +92 -0
  140. package/src/engineer-skills/groundwork-flutter-engineer/references/architecture.md +189 -0
  141. package/src/engineer-skills/groundwork-flutter-engineer/references/data-and-contracts.md +136 -0
  142. package/src/engineer-skills/groundwork-flutter-engineer/references/documentation.md +122 -0
  143. package/src/engineer-skills/groundwork-flutter-engineer/references/navigation.md +122 -0
  144. package/src/engineer-skills/groundwork-flutter-engineer/references/observability.md +37 -0
  145. package/src/engineer-skills/groundwork-flutter-engineer/references/performance-and-reliability.md +100 -0
  146. package/src/engineer-skills/groundwork-flutter-engineer/references/platform-channels.md +93 -0
  147. package/src/engineer-skills/groundwork-flutter-engineer/references/releases-and-distribution.md +84 -0
  148. package/src/engineer-skills/groundwork-flutter-engineer/references/security.md +96 -0
  149. package/src/engineer-skills/groundwork-flutter-engineer/references/state-management.md +166 -0
  150. package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +160 -0
  151. package/src/engineer-skills/groundwork-flutter-engineer/references/theming-and-design-tokens.md +109 -0
  152. package/src/engineer-skills/groundwork-flutter-engineer/references/widgets-and-composition.md +123 -0
  153. package/src/engineer-skills/groundwork-flutter-engineer/sync-anchor.md +24 -0
  154. package/src/engineer-skills/groundwork-go-engineer/SKILL.md +174 -0
  155. package/src/engineer-skills/groundwork-go-engineer/references/api-design.md +82 -0
  156. package/src/engineer-skills/groundwork-go-engineer/references/architecture.md +42 -0
  157. package/src/engineer-skills/groundwork-go-engineer/references/capability-ports.md +50 -0
  158. package/src/engineer-skills/groundwork-go-engineer/references/code-craft-security.md +34 -0
  159. package/src/engineer-skills/groundwork-go-engineer/references/concurrency.md +108 -0
  160. package/src/engineer-skills/groundwork-go-engineer/references/documentation.md +130 -0
  161. package/src/engineer-skills/groundwork-go-engineer/references/go-services.md +77 -0
  162. package/src/engineer-skills/groundwork-go-engineer/references/http-handlers.md +172 -0
  163. package/src/engineer-skills/groundwork-go-engineer/references/implementation-patterns.md +156 -0
  164. package/src/engineer-skills/groundwork-go-engineer/references/integration-realtime-data.md +57 -0
  165. package/src/engineer-skills/groundwork-go-engineer/references/observability.md +49 -0
  166. package/src/engineer-skills/groundwork-go-engineer/references/postgres.md +41 -0
  167. package/src/engineer-skills/groundwork-go-engineer/references/reliability-performance.md +105 -0
  168. package/src/engineer-skills/groundwork-go-engineer/references/testing.md +201 -0
  169. package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +20 -0
  170. package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +112 -0
  171. package/src/engineer-skills/groundwork-nextjs-engineer/references/accessibility.md +111 -0
  172. package/src/engineer-skills/groundwork-nextjs-engineer/references/architecture.md +323 -0
  173. package/src/engineer-skills/groundwork-nextjs-engineer/references/data-fetching.md +458 -0
  174. package/src/engineer-skills/groundwork-nextjs-engineer/references/documentation.md +324 -0
  175. package/src/engineer-skills/groundwork-nextjs-engineer/references/error-boundaries.md +383 -0
  176. package/src/engineer-skills/groundwork-nextjs-engineer/references/mutations-and-forms.md +396 -0
  177. package/src/engineer-skills/groundwork-nextjs-engineer/references/observability.md +48 -0
  178. package/src/engineer-skills/groundwork-nextjs-engineer/references/performance-and-deployment.md +947 -0
  179. package/src/engineer-skills/groundwork-nextjs-engineer/references/routing-and-navigation.md +405 -0
  180. package/src/engineer-skills/groundwork-nextjs-engineer/references/security.md +131 -0
  181. package/src/engineer-skills/groundwork-nextjs-engineer/references/server-components.md +394 -0
  182. package/src/engineer-skills/groundwork-nextjs-engineer/references/tailwind-and-styling.md +134 -0
  183. package/src/engineer-skills/groundwork-nextjs-engineer/references/testing.md +491 -0
  184. package/src/engineer-skills/groundwork-nextjs-engineer/references/type-system.md +368 -0
  185. package/src/engineer-skills/groundwork-nextjs-engineer/references/ux-principles.md +230 -0
  186. package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
  187. package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +16 -0
  188. package/src/engineer-skills/groundwork-python-engineer/SKILL.md +199 -0
  189. package/src/engineer-skills/groundwork-python-engineer/references/api-standards.md +88 -0
  190. package/src/engineer-skills/groundwork-python-engineer/references/architecture.md +57 -0
  191. package/src/engineer-skills/groundwork-python-engineer/references/async-patterns.md +103 -0
  192. package/src/engineer-skills/groundwork-python-engineer/references/capability-ports.md +44 -0
  193. package/src/engineer-skills/groundwork-python-engineer/references/database.md +88 -0
  194. package/src/engineer-skills/groundwork-python-engineer/references/documentation-mcp.md +167 -0
  195. package/src/engineer-skills/groundwork-python-engineer/references/implementation-patterns.md +166 -0
  196. package/src/engineer-skills/groundwork-python-engineer/references/ml-pipelines.md +119 -0
  197. package/src/engineer-skills/groundwork-python-engineer/references/ml-systems-ai-engineering.md +74 -0
  198. package/src/engineer-skills/groundwork-python-engineer/references/observability.md +57 -0
  199. package/src/engineer-skills/groundwork-python-engineer/references/resilience.md +126 -0
  200. package/src/engineer-skills/groundwork-python-engineer/references/security.md +148 -0
  201. package/src/engineer-skills/groundwork-python-engineer/references/testing.md +216 -0
  202. package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +20 -0
  203. package/src/generators/add-capability/generator.ts +70 -0
  204. package/src/generators/add-capability/schema.json +30 -0
  205. package/src/generators/capabilities/llm/capability.json +28 -0
  206. package/src/generators/capabilities/llm/providers/anthropic/footprint.json +13 -0
  207. package/src/generators/capabilities/llm/providers/anthropic/stacks/go/internal/llm/llm.go.template +102 -0
  208. package/src/generators/capabilities/llm/providers/anthropic/stacks/python/src/__packageName__/adapters/llm.py.template +61 -0
  209. package/src/generators/capabilities/llm/providers/local/footprint.json +13 -0
  210. package/src/generators/capabilities/llm/providers/local/stacks/go/internal/llm/llm.go.template +102 -0
  211. package/src/generators/capabilities/llm/providers/local/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
  212. package/src/generators/capabilities/llm/providers/localai/footprint.json +29 -0
  213. package/src/generators/capabilities/llm/providers/localai/stacks/go/internal/llm/llm.go.template +102 -0
  214. package/src/generators/capabilities/llm/providers/localai/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
  215. package/src/generators/capabilities/llm/providers/none/footprint.json +9 -0
  216. package/src/generators/capabilities/llm/providers/none/stacks/go/internal/llm/llm.go.template +35 -0
  217. package/src/generators/capabilities/llm/providers/none/stacks/python/src/__packageName__/adapters/llm.py.template +25 -0
  218. package/src/generators/capabilities/llm/providers/ollama/footprint.json +20 -0
  219. package/src/generators/capabilities/llm/providers/ollama/stacks/go/internal/llm/llm.go.template +102 -0
  220. package/src/generators/capabilities/llm/providers/ollama/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
  221. package/src/generators/capabilities/llm/providers/openai/footprint.json +13 -0
  222. package/src/generators/capabilities/llm/providers/openai/stacks/go/internal/llm/llm.go.template +98 -0
  223. package/src/generators/capabilities/llm/providers/openai/stacks/python/src/__packageName__/adapters/llm.py.template +60 -0
  224. package/src/generators/capabilities/llm/stacks/go/internal/core/service/llm.go.template +12 -0
  225. package/src/generators/capabilities/llm/stacks/go/internal/llm/llm_test.go.template +33 -0
  226. package/src/generators/capabilities/llm/stacks/python/src/__packageName__/core/llm.py.template +15 -0
  227. package/src/generators/capabilities/llm/stacks/python/tests/contracts/test_llm.py.template +37 -0
  228. package/src/generators/cli-app/files/README.md.template +76 -0
  229. package/src/generators/cli-app/files/build.mjs.template +15 -0
  230. package/src/generators/cli-app/files/package.json.template +21 -0
  231. package/src/generators/cli-app/files/src/cli.ts.template +67 -0
  232. package/src/generators/cli-app/files/src/commands/hello.ts.template +17 -0
  233. package/src/generators/cli-app/files/src/commands/status.ts.template +23 -0
  234. package/src/generators/cli-app/files/src/core/client.test.ts.template +80 -0
  235. package/src/generators/cli-app/files/src/core/client.ts.template +64 -0
  236. package/src/generators/cli-app/files/src/registry.test.ts.template +35 -0
  237. package/src/generators/cli-app/files/src/registry.ts.template +31 -0
  238. package/src/generators/cli-app/files/tsconfig.json.template +16 -0
  239. package/src/generators/cli-app/files/tsconfig.test.json.template +11 -0
  240. package/src/generators/cli-app/generator.ts +138 -0
  241. package/src/generators/cli-app/schema.json +24 -0
  242. package/src/generators/docs-site/files/.gitignore.ejs +40 -0
  243. package/src/generators/docs-site/files/app/docs/__slug__/page.tsx +101 -0
  244. package/src/generators/docs-site/files/app/docs/layout.tsx +14 -0
  245. package/src/generators/docs-site/files/app/docs.css +43 -0
  246. package/src/generators/docs-site/files/app/layout.tsx +24 -0
  247. package/src/generators/docs-site/files/app/page.tsx +135 -0
  248. package/src/generators/docs-site/files/app/source.ts +8 -0
  249. package/src/generators/docs-site/files/components/mermaid.tsx +67 -0
  250. package/src/generators/docs-site/files/next.config.mjs +10 -0
  251. package/src/generators/docs-site/files/package.json +32 -0
  252. package/src/generators/docs-site/files/pnpm-workspace.yaml +7 -0
  253. package/src/generators/docs-site/files/postcss.config.mjs +6 -0
  254. package/src/generators/docs-site/files/source.config.ts +77 -0
  255. package/src/generators/docs-site/files/tailwind.config.js +10 -0
  256. package/src/generators/docs-site/files/tsconfig.json +27 -0
  257. package/src/generators/docs-site/generator.ts +476 -0
  258. package/src/generators/docs-site/schema.json +17 -0
  259. package/src/generators/electron-app/docs/principles/stack/electron/index.md +49 -0
  260. package/src/generators/electron-app/docs/principles/stack/electron/ipc-contracts.md +71 -0
  261. package/src/generators/electron-app/docs/principles/stack/electron/packaging-and-updates.md +59 -0
  262. package/src/generators/electron-app/docs/principles/stack/electron/process-model.md +53 -0
  263. package/src/generators/electron-app/docs/principles/stack/electron/security.md +70 -0
  264. package/src/generators/electron-app/docs/principles/stack/typescript/frontend.md +65 -0
  265. package/src/generators/electron-app/files/.gitignore.template +20 -0
  266. package/src/generators/electron-app/files/README.md.template +125 -0
  267. package/src/generators/electron-app/files/electron.vite.config.ts +31 -0
  268. package/src/generators/electron-app/files/eslint.config.mjs +92 -0
  269. package/src/generators/electron-app/files/forge.config.ts.template +44 -0
  270. package/src/generators/electron-app/files/package.json.template +54 -0
  271. package/src/generators/electron-app/files/playwright.config.ts +18 -0
  272. package/src/generators/electron-app/files/project.json.template +65 -0
  273. package/src/generators/electron-app/files/src/main/core-client.test.ts +81 -0
  274. package/src/generators/electron-app/files/src/main/core-client.ts +55 -0
  275. package/src/generators/electron-app/files/src/main/index.ts +157 -0
  276. package/src/generators/electron-app/files/src/main/ipc.ts +52 -0
  277. package/src/generators/electron-app/files/src/main/policy.test.ts +71 -0
  278. package/src/generators/electron-app/files/src/main/policy.ts +73 -0
  279. package/src/generators/electron-app/files/src/preload/index.ts +23 -0
  280. package/src/generators/electron-app/files/src/renderer/index.html.template +20 -0
  281. package/src/generators/electron-app/files/src/renderer/src/App.test.tsx +61 -0
  282. package/src/generators/electron-app/files/src/renderer/src/App.tsx.template +43 -0
  283. package/src/generators/electron-app/files/src/renderer/src/assets/main.css +40 -0
  284. package/src/generators/electron-app/files/src/renderer/src/env.d.ts +14 -0
  285. package/src/generators/electron-app/files/src/renderer/src/main.tsx +25 -0
  286. package/src/generators/electron-app/files/src/shared/ipc.ts +54 -0
  287. package/src/generators/electron-app/files/tests/smoke/app.spec.ts.template +133 -0
  288. package/src/generators/electron-app/files/tool/electron_exec.sh.template +83 -0
  289. package/src/generators/electron-app/files/tsconfig.json +7 -0
  290. package/src/generators/electron-app/files/tsconfig.node.json +27 -0
  291. package/src/generators/electron-app/files/tsconfig.web.json +22 -0
  292. package/src/generators/electron-app/files/vitest.config.ts +32 -0
  293. package/src/generators/electron-app/files/vitest.setup.ts +1 -0
  294. package/src/generators/electron-app/generator.ts +288 -0
  295. package/src/generators/electron-app/schema.json +23 -0
  296. package/src/generators/flutter-app/docs/principles/stack/flutter/architecture.md +78 -0
  297. package/src/generators/flutter-app/docs/principles/stack/flutter/index.md +38 -0
  298. package/src/generators/flutter-app/docs/principles/stack/flutter/platform-channels.md +51 -0
  299. package/src/generators/flutter-app/docs/principles/stack/flutter/releases-and-distribution.md +59 -0
  300. package/src/generators/flutter-app/docs/principles/stack/flutter/state-management.md +85 -0
  301. package/src/generators/flutter-app/docs/principles/stack/flutter/testing.md +86 -0
  302. package/src/generators/flutter-app/docs/principles/stack/flutter/widgets-and-composition.md +69 -0
  303. package/src/generators/flutter-app/files/.gitignore.template +30 -0
  304. package/src/generators/flutter-app/files/README.md.template +100 -0
  305. package/src/generators/flutter-app/files/analysis_options.yaml.template +18 -0
  306. package/src/generators/flutter-app/files/integration_test/app_test.dart.template +64 -0
  307. package/src/generators/flutter-app/files/lib/app.dart.template +24 -0
  308. package/src/generators/flutter-app/files/lib/config/app_config.dart +15 -0
  309. package/src/generators/flutter-app/files/lib/data/repositories/status_repository.dart +36 -0
  310. package/src/generators/flutter-app/files/lib/data/services/api_client.dart +71 -0
  311. package/src/generators/flutter-app/files/lib/domain/models/health_status.dart +23 -0
  312. package/src/generators/flutter-app/files/lib/main.dart +11 -0
  313. package/src/generators/flutter-app/files/lib/router.dart +23 -0
  314. package/src/generators/flutter-app/files/lib/ui/core/theme/app_theme.dart +110 -0
  315. package/src/generators/flutter-app/files/lib/ui/home/home_view.dart +89 -0
  316. package/src/generators/flutter-app/files/lib/ui/home/home_view_model.dart.template +38 -0
  317. package/src/generators/flutter-app/files/project.json.template +51 -0
  318. package/src/generators/flutter-app/files/pubspec.yaml.template +47 -0
  319. package/src/generators/flutter-app/files/test/api_client_test.dart.template +63 -0
  320. package/src/generators/flutter-app/files/test/fakes/fake_status_repository.dart.template +19 -0
  321. package/src/generators/flutter-app/files/test/home_view_test.dart.template +58 -0
  322. package/src/generators/flutter-app/files/tool/flutter_exec.sh.template +60 -0
  323. package/src/generators/flutter-app/generator.ts +362 -0
  324. package/src/generators/flutter-app/schema.json +23 -0
  325. package/src/generators/go-microservice/docs/principles/stack/go/concurrency.md +123 -0
  326. package/src/generators/go-microservice/docs/principles/stack/go/index.md +70 -0
  327. package/src/generators/go-microservice/docs/principles/stack/go/testing.md +168 -0
  328. package/src/generators/go-microservice/files/.air.toml.template +38 -0
  329. package/src/generators/go-microservice/files/.env.template +4 -0
  330. package/src/generators/go-microservice/files/.golangci.yml.template +82 -0
  331. package/src/generators/go-microservice/files/Dockerfile.dev.template +12 -0
  332. package/src/generators/go-microservice/files/asyncapi-pubsub.yaml.template +33 -0
  333. package/src/generators/go-microservice/files/asyncapi-ws.yaml.template +34 -0
  334. package/src/generators/go-microservice/files/cmd/api/main.go.template +149 -0
  335. package/src/generators/go-microservice/files/cmd/api/main_test.go.template +99 -0
  336. package/src/generators/go-microservice/files/cmd/worker/cleanup/main.go.template +39 -0
  337. package/src/generators/go-microservice/files/db/schema.sql.template +24 -0
  338. package/src/generators/go-microservice/files/go.mod.template +39 -0
  339. package/src/generators/go-microservice/files/internal/config/config.go.template +52 -0
  340. package/src/generators/go-microservice/files/internal/config/otel.go.template +93 -0
  341. package/src/generators/go-microservice/files/internal/core/domain/errors.go.template +16 -0
  342. package/src/generators/go-microservice/files/internal/core/domain/model.go.template +28 -0
  343. package/src/generators/go-microservice/files/internal/core/domain/user.go.template +13 -0
  344. package/src/generators/go-microservice/files/internal/core/pagination.go.template +16 -0
  345. package/src/generators/go-microservice/files/internal/core/service/app_service.go.template +79 -0
  346. package/src/generators/go-microservice/files/internal/core/service/event_hub.go.template +9 -0
  347. package/src/generators/go-microservice/files/internal/core/service/message_queue.go.template +10 -0
  348. package/src/generators/go-microservice/files/internal/core/service/outbox_repository.go.template +31 -0
  349. package/src/generators/go-microservice/files/internal/core/service/repository.go.template +23 -0
  350. package/src/generators/go-microservice/files/internal/core/service/user_repository.go.template +15 -0
  351. package/src/generators/go-microservice/files/internal/core/service/user_service.go.template +43 -0
  352. package/src/generators/go-microservice/files/internal/entrypoints/api/app_handler.go.template +108 -0
  353. package/src/generators/go-microservice/files/internal/entrypoints/api/auth_middleware_test.go.template +52 -0
  354. package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook.go.template +202 -0
  355. package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook_test.go.template +82 -0
  356. package/src/generators/go-microservice/files/internal/entrypoints/api/health_handler.go.template +80 -0
  357. package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware.go.template +87 -0
  358. package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware_test.go.template +76 -0
  359. package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/repository.go.template +37 -0
  360. package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_auth.go.template +40 -0
  361. package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_loadshed.go.template +38 -0
  362. package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_logging.go.template +40 -0
  363. package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_ratelimit.go.template +48 -0
  364. package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_test.go.template +81 -0
  365. package/src/generators/go-microservice/files/internal/entrypoints/api/router.go.template +105 -0
  366. package/src/generators/go-microservice/files/internal/entrypoints/api/types.go.template +70 -0
  367. package/src/generators/go-microservice/files/internal/entrypoints/api/websocket_handler.go.template +39 -0
  368. package/src/generators/go-microservice/files/internal/httpclient/http_client.go.template +87 -0
  369. package/src/generators/go-microservice/files/internal/kafka/kafka.go.template +34 -0
  370. package/src/generators/go-microservice/files/internal/postgres/postgres.go.template +195 -0
  371. package/src/generators/go-microservice/files/internal/postgres/postgres_test.go.template +156 -0
  372. package/src/generators/go-microservice/files/internal/postgres/user_repository.go.template +56 -0
  373. package/src/generators/go-microservice/files/internal/pubsub/gcp_pubsub.go.template +35 -0
  374. package/src/generators/go-microservice/files/internal/websocket/client.go.template +151 -0
  375. package/src/generators/go-microservice/files/internal/websocket/hub.go.template +261 -0
  376. package/src/generators/go-microservice/files/scripts/apply-schema.sh.template +21 -0
  377. package/src/generators/go-microservice/files/tools/tools.go.template +10 -0
  378. package/src/generators/go-microservice/generator.ts +240 -0
  379. package/src/generators/go-microservice/schema.json +63 -0
  380. package/src/generators/nextjs-app/docs/principles/stack/typescript/frontend.md +65 -0
  381. package/src/generators/nextjs-app/files/.dockerignore.template +7 -0
  382. package/src/generators/nextjs-app/files/.env.example.template +24 -0
  383. package/src/generators/nextjs-app/files/.gitignore.template +5 -0
  384. package/src/generators/nextjs-app/files/Dockerfile +53 -0
  385. package/src/generators/nextjs-app/files/app/(auth)/sign-in/__sign-in__/page.tsx.template +9 -0
  386. package/src/generators/nextjs-app/files/app/(auth)/sign-up/__sign-up__/page.tsx.template +9 -0
  387. package/src/generators/nextjs-app/files/app/api/config/route.ts.template +39 -0
  388. package/src/generators/nextjs-app/files/app/api/healthz/route.test.ts +15 -0
  389. package/src/generators/nextjs-app/files/app/api/healthz/route.ts +5 -0
  390. package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.test.ts.template +55 -0
  391. package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.ts.template +126 -0
  392. package/src/generators/nextjs-app/files/app/error.tsx +39 -0
  393. package/src/generators/nextjs-app/files/app/global-error.tsx +68 -0
  394. package/src/generators/nextjs-app/files/app/globals.css +105 -0
  395. package/src/generators/nextjs-app/files/app/layout.tsx +59 -0
  396. package/src/generators/nextjs-app/files/app/loading.tsx +13 -0
  397. package/src/generators/nextjs-app/files/app/not-found.tsx +30 -0
  398. package/src/generators/nextjs-app/files/app/page.tsx +20 -0
  399. package/src/generators/nextjs-app/files/components/providers/default.tsx +19 -0
  400. package/src/generators/nextjs-app/files/components/providers/production.tsx +32 -0
  401. package/src/generators/nextjs-app/files/components/providers/telemetry.tsx +76 -0
  402. package/src/generators/nextjs-app/files/components/render-smoke.test.tsx +29 -0
  403. package/src/generators/nextjs-app/files/components/theme-provider.tsx +11 -0
  404. package/src/generators/nextjs-app/files/components.json +21 -0
  405. package/src/generators/nextjs-app/files/eslint.config.mjs +120 -0
  406. package/src/generators/nextjs-app/files/hooks/use-toast.ts +7 -0
  407. package/src/generators/nextjs-app/files/instrumentation.ts +90 -0
  408. package/src/generators/nextjs-app/files/lib/api/fetcher.ts.template +130 -0
  409. package/src/generators/nextjs-app/files/lib/config.ts +21 -0
  410. package/src/generators/nextjs-app/files/lib/logger.ts +29 -0
  411. package/src/generators/nextjs-app/files/lib/schemas/index.ts +19 -0
  412. package/src/generators/nextjs-app/files/lib/utils.ts +6 -0
  413. package/src/generators/nextjs-app/files/next.config.mjs +9 -0
  414. package/src/generators/nextjs-app/files/package.json +70 -0
  415. package/src/generators/nextjs-app/files/postcss.config.mjs +8 -0
  416. package/src/generators/nextjs-app/files/proxy.test.ts.template +30 -0
  417. package/src/generators/nextjs-app/files/proxy.ts +31 -0
  418. package/src/generators/nextjs-app/files/public/.gitkeep +1 -0
  419. package/src/generators/nextjs-app/files/tsconfig.json +42 -0
  420. package/src/generators/nextjs-app/files/vitest.config.mts +15 -0
  421. package/src/generators/nextjs-app/files/vitest.setup.ts +7 -0
  422. package/src/generators/nextjs-app/generator.ts +307 -0
  423. package/src/generators/nextjs-app/schema.json +44 -0
  424. package/src/generators/python-microservice/docs/principles/stack/python/async.md +168 -0
  425. package/src/generators/python-microservice/docs/principles/stack/python/documentation.md +240 -0
  426. package/src/generators/python-microservice/docs/principles/stack/python/mcp.md +147 -0
  427. package/src/generators/python-microservice/docs/principles/stack/python/resilience.md +193 -0
  428. package/src/generators/python-microservice/docs/principles/stack/python/testing.md +322 -0
  429. package/src/generators/python-microservice/files/.env.example.template +30 -0
  430. package/src/generators/python-microservice/files/Dockerfile.template +36 -0
  431. package/src/generators/python-microservice/files/db/schema.sql.template +19 -0
  432. package/src/generators/python-microservice/files/pyproject.toml.template +76 -0
  433. package/src/generators/python-microservice/files/scripts/apply-schema.sh.template +25 -0
  434. package/src/generators/python-microservice/files/src/__packageName__/adapters/comfyui.py.template +87 -0
  435. package/src/generators/python-microservice/files/src/__packageName__/adapters/config.py.template +48 -0
  436. package/src/generators/python-microservice/files/src/__packageName__/adapters/database.py.template +21 -0
  437. package/src/generators/python-microservice/files/src/__packageName__/adapters/message_queue.py.template +29 -0
  438. package/src/generators/python-microservice/files/src/__packageName__/adapters/repository.py.template +130 -0
  439. package/src/generators/python-microservice/files/src/__packageName__/adapters/telemetry.py.template +68 -0
  440. package/src/generators/python-microservice/files/src/__packageName__/adapters/websocket_hub.py.template +36 -0
  441. package/src/generators/python-microservice/files/src/__packageName__/core/domain/entities.py.template +22 -0
  442. package/src/generators/python-microservice/files/src/__packageName__/core/domain/exceptions.py.template +43 -0
  443. package/src/generators/python-microservice/files/src/__packageName__/core/ports.py.template +42 -0
  444. package/src/generators/python-microservice/files/src/__packageName__/core/service/example_service.py.template +68 -0
  445. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/dependencies.py.template +50 -0
  446. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/middleware.py.template +131 -0
  447. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/router.py.template +37 -0
  448. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/websocket_handler.py.template +20 -0
  449. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/cleanup.py.template +35 -0
  450. package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/worker.py.template +28 -0
  451. package/src/generators/python-microservice/files/src/__packageName__/main.py.template +108 -0
  452. package/src/generators/python-microservice/files/tests/test_main.py.template +74 -0
  453. package/src/generators/python-microservice/files/tests/test_middleware.py.template +109 -0
  454. package/src/generators/python-microservice/files/tests/test_worker.py.template +16 -0
  455. package/src/generators/python-microservice/generator.ts +286 -0
  456. package/src/generators/python-microservice/schema.json +86 -0
  457. package/src/generators/shared/brand-tokens.ts +301 -0
  458. package/src/generators/shared/capabilities.ts +349 -0
  459. package/src/generators/shared/provenance.ts +61 -0
  460. package/src/generators/shared/scaffold-helpers.ts +309 -0
  461. package/src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md +20 -0
  462. package/src/generators/system-test-runner/files/tests/bets/.gitkeep +0 -0
  463. package/src/generators/system-test-runner/files/tests/bets/_archive/.gitkeep +0 -0
  464. package/src/generators/system-test-runner/files/tests/conftest.py.template +503 -0
  465. package/src/generators/system-test-runner/files/tests/pyproject.toml.template +20 -0
  466. package/src/generators/system-test-runner/files/tests/system/pages/__init__.py.template +9 -0
  467. package/src/generators/system-test-runner/files/tests/system/pages/base_page.py.template +36 -0
  468. package/src/generators/system-test-runner/files/tests/system/test_a11y_smoke.py.template +132 -0
  469. package/src/generators/system-test-runner/files/tests/system/test_contract_conformance.py.template +140 -0
  470. package/src/generators/system-test-runner/files/tests/system/test_layout_geometry.py.template +109 -0
  471. package/src/generators/system-test-runner/files/tests/system/test_render_smoke.py.template +257 -0
  472. package/src/generators/system-test-runner/files/tests/system/test_system.py.template +158 -0
  473. package/src/generators/system-test-runner/files/tests/system/test_token_conformance.py.template +206 -0
  474. package/src/generators/system-test-runner/files/tests/system/test_visual_regression.py.template +104 -0
  475. package/src/generators/system-test-runner/generator.ts +196 -0
  476. package/src/generators/system-test-runner/schema.json +24 -0
  477. package/src/generators/workspace-dev-cli/cli-src/build.mjs +42 -0
  478. package/src/generators/workspace-dev-cli/cli-src/dist/dev-bundle.js +2168 -0
  479. package/src/generators/workspace-dev-cli/cli-src/src/commands/bet.ts +442 -0
  480. package/src/generators/workspace-dev-cli/cli-src/src/commands/completion.ts +87 -0
  481. package/src/generators/workspace-dev-cli/cli-src/src/commands/doctor.ts +139 -0
  482. package/src/generators/workspace-dev-cli/cli-src/src/commands/lifecycle.ts +548 -0
  483. package/src/generators/workspace-dev-cli/cli-src/src/commands/quality.ts +127 -0
  484. package/src/generators/workspace-dev-cli/cli-src/src/commands/surface.ts +214 -0
  485. package/src/generators/workspace-dev-cli/cli-src/src/index.ts +127 -0
  486. package/src/generators/workspace-dev-cli/cli-src/src/registry.ts +194 -0
  487. package/src/generators/workspace-dev-cli/cli-src/src/theme/color.ts +130 -0
  488. package/src/generators/workspace-dev-cli/cli-src/src/theme/render.ts +158 -0
  489. package/src/generators/workspace-dev-cli/cli-src/src/theme/tokens.ts +122 -0
  490. package/src/generators/workspace-dev-cli/cli-src/src/util/context.ts +43 -0
  491. package/src/generators/workspace-dev-cli/cli-src/src/util/extensions.ts +99 -0
  492. package/src/generators/workspace-dev-cli/cli-src/src/util/paths.ts +46 -0
  493. package/src/generators/workspace-dev-cli/cli-src/src/util/proc.ts +106 -0
  494. package/src/generators/workspace-dev-cli/cli-src/src/util/prompt.ts +108 -0
  495. package/src/generators/workspace-dev-cli/cli-src/src/util/runners.ts +70 -0
  496. package/src/generators/workspace-dev-cli/cli-src/src/util/services.ts +221 -0
  497. package/src/generators/workspace-dev-cli/cli-src/src/util/version.ts +21 -0
  498. package/src/generators/workspace-dev-cli/cli-src/tsconfig.json +16 -0
  499. package/src/generators/workspace-dev-cli/files/.agents/skills/workspace-cli/SKILL.md.template +74 -0
  500. package/src/generators/workspace-dev-cli/files/dev.template +16 -0
  501. package/src/generators/workspace-dev-cli/files/docker-compose.yml.template +20 -0
  502. package/src/generators/workspace-dev-cli/files/scripts/cli/templates/milestone-test.pytmpl.template +46 -0
  503. package/src/generators/workspace-dev-cli/files/scripts/cli/templates/slice-test.pytmpl.template +38 -0
  504. package/src/generators/workspace-dev-cli/generator.ts +136 -0
  505. package/src/generators/workspace-dev-cli/schema.json +22 -0
  506. package/src/hidden-skills/code-intelligence.md +135 -0
  507. package/src/hidden-skills/groundwork-architect/SKILL.md +114 -0
  508. package/src/hidden-skills/groundwork-architect/references/agentic-systems.md +44 -0
  509. package/src/hidden-skills/groundwork-architect/references/ai-native-architecture.md +37 -0
  510. package/src/hidden-skills/groundwork-architect/references/api-and-contracts.md +45 -0
  511. package/src/hidden-skills/groundwork-architect/references/core-and-boundaries.md +45 -0
  512. package/src/hidden-skills/groundwork-architect/references/data-architecture.md +33 -0
  513. package/src/hidden-skills/groundwork-architect/references/decision-records.md +34 -0
  514. package/src/hidden-skills/groundwork-architect/references/durable-execution.md +45 -0
  515. package/src/hidden-skills/groundwork-architect/references/evolutionary-architecture.md +37 -0
  516. package/src/hidden-skills/groundwork-architect/references/identity-and-access.md +41 -0
  517. package/src/hidden-skills/groundwork-architect/references/integration-patterns.md +39 -0
  518. package/src/hidden-skills/groundwork-architect/references/observability.md +36 -0
  519. package/src/hidden-skills/groundwork-architect/references/performance-and-scale.md +41 -0
  520. package/src/hidden-skills/groundwork-architect/references/platform-and-delivery.md +47 -0
  521. package/src/hidden-skills/groundwork-architect/references/realtime-and-async.md +28 -0
  522. package/src/hidden-skills/groundwork-architect/references/reliability.md +31 -0
  523. package/src/hidden-skills/groundwork-architect/references/security-and-trust.md +47 -0
  524. package/src/hidden-skills/groundwork-architect/references/surface-architecture.md +40 -0
  525. package/src/hidden-skills/groundwork-architect/sync-anchor.md +34 -0
  526. package/src/hidden-skills/groundwork-architecture/architecture-template.md +50 -0
  527. package/src/hidden-skills/groundwork-architecture/instructions.md +139 -0
  528. package/src/hidden-skills/groundwork-architecture/phases/01-context-ingestion.md +18 -0
  529. package/src/hidden-skills/groundwork-architecture/phases/02-technical-constraints.md +27 -0
  530. package/src/hidden-skills/groundwork-architecture/phases/03-service-design.md +19 -0
  531. package/src/hidden-skills/groundwork-architecture/phases/04-data-flow-communication.md +23 -0
  532. package/src/hidden-skills/groundwork-architecture/phases/05-component-boundaries-contracts.md +17 -0
  533. package/src/hidden-skills/groundwork-architecture/phases/06-draft-review-present.md +38 -0
  534. package/src/hidden-skills/groundwork-architecture/phases/07-commit.md +33 -0
  535. package/src/hidden-skills/groundwork-architecture/templates/architecture-cache.md +43 -0
  536. package/src/hidden-skills/groundwork-architecture-extract/instructions.md +163 -0
  537. package/src/hidden-skills/groundwork-architecture-extract/templates/architecture-extract-cache.md +21 -0
  538. package/src/hidden-skills/groundwork-bet/briefs/acceptance-auditor.md +68 -0
  539. package/src/hidden-skills/groundwork-bet/briefs/blind-reviewer.md +56 -0
  540. package/src/hidden-skills/groundwork-bet/briefs/coverage-auditor.md +95 -0
  541. package/src/hidden-skills/groundwork-bet/briefs/edge-case-tracer.md +64 -0
  542. package/src/hidden-skills/groundwork-bet/briefs/experience-auditor.md +83 -0
  543. package/src/hidden-skills/groundwork-bet/briefs/slice-worker.md +257 -0
  544. package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
  545. package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +115 -0
  546. package/src/hidden-skills/groundwork-bet/templates/change-proposal.md +38 -0
  547. package/src/hidden-skills/groundwork-bet/templates/decomposition/meta.json +4 -0
  548. package/src/hidden-skills/groundwork-bet/templates/decomposition/milestone-index.md +31 -0
  549. package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +31 -0
  550. package/src/hidden-skills/groundwork-bet/templates/pitch.md +45 -0
  551. package/src/hidden-skills/groundwork-bet/templates/technical-design/01-ui-design.md +51 -0
  552. package/src/hidden-skills/groundwork-bet/templates/technical-design/02-data-flows.md +36 -0
  553. package/src/hidden-skills/groundwork-bet/templates/technical-design/03-api-design.md +90 -0
  554. package/src/hidden-skills/groundwork-bet/templates/technical-design/04-data-design.md +29 -0
  555. package/src/hidden-skills/groundwork-bet/workflows/01-discovery.md +200 -0
  556. package/src/hidden-skills/groundwork-bet/workflows/02-design.md +178 -0
  557. package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +242 -0
  558. package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +226 -0
  559. package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +210 -0
  560. package/src/hidden-skills/groundwork-design-system/instructions.md +125 -0
  561. package/src/hidden-skills/groundwork-design-system/templates/brand-tokens.md +182 -0
  562. package/src/hidden-skills/groundwork-design-system/templates/design-system-cache.md +64 -0
  563. package/src/hidden-skills/groundwork-design-system/tracks/_foundation.md +136 -0
  564. package/src/hidden-skills/groundwork-design-system/tracks/agentic-protocol.md +269 -0
  565. package/src/hidden-skills/groundwork-design-system/tracks/cli.md +355 -0
  566. package/src/hidden-skills/groundwork-design-system/tracks/graphical-ui.md +330 -0
  567. package/src/hidden-skills/groundwork-design-system-extract/instructions.md +124 -0
  568. package/src/hidden-skills/groundwork-design-system-extract/templates/design-system-extract-cache.md +19 -0
  569. package/src/hidden-skills/groundwork-designer/SKILL.md +108 -0
  570. package/src/hidden-skills/groundwork-designer/references/accessibility.md +33 -0
  571. package/src/hidden-skills/groundwork-designer/references/ai-native-design.md +37 -0
  572. package/src/hidden-skills/groundwork-designer/references/design-review.md +29 -0
  573. package/src/hidden-skills/groundwork-designer/references/design-systems-and-tokens.md +33 -0
  574. package/src/hidden-skills/groundwork-designer/references/interaction-and-motion.md +37 -0
  575. package/src/hidden-skills/groundwork-designer/references/layout-and-space.md +33 -0
  576. package/src/hidden-skills/groundwork-designer/references/usability-and-ux.md +33 -0
  577. package/src/hidden-skills/groundwork-designer/references/visual-craft.md +49 -0
  578. package/src/hidden-skills/groundwork-designer/sync-anchor.md +20 -0
  579. package/src/hidden-skills/groundwork-doc-sync/instructions.md +100 -0
  580. package/src/hidden-skills/groundwork-elicit/instructions.md +66 -0
  581. package/src/hidden-skills/groundwork-elicit/methods.md +65 -0
  582. package/src/hidden-skills/groundwork-infra-adopt/instructions.md +168 -0
  583. package/src/hidden-skills/groundwork-infra-adopt/templates/infra-adopt-cache.md +21 -0
  584. package/src/hidden-skills/groundwork-mvp/instructions.md +223 -0
  585. package/src/hidden-skills/groundwork-mvp/templates/mvp-cache.md +9 -0
  586. package/src/hidden-skills/groundwork-patch/instructions.md +40 -0
  587. package/src/hidden-skills/groundwork-persona/instructions.md +65 -0
  588. package/src/hidden-skills/groundwork-product/SKILL.md +102 -0
  589. package/src/hidden-skills/groundwork-product/references/ai-native-product.md +45 -0
  590. package/src/hidden-skills/groundwork-product/references/discovery-and-opportunity.md +38 -0
  591. package/src/hidden-skills/groundwork-product/references/product-risks.md +52 -0
  592. package/src/hidden-skills/groundwork-product/references/requirements-and-specs.md +39 -0
  593. package/src/hidden-skills/groundwork-product/references/scope-and-sequencing.md +35 -0
  594. package/src/hidden-skills/groundwork-product/references/shaping-and-appetite.md +48 -0
  595. package/src/hidden-skills/groundwork-product/references/success-metrics-and-signals.md +37 -0
  596. package/src/hidden-skills/groundwork-product/sync-anchor.md +19 -0
  597. package/src/hidden-skills/groundwork-product-brief/instructions.md +231 -0
  598. package/src/hidden-skills/groundwork-product-brief-extract/instructions.md +139 -0
  599. package/src/hidden-skills/groundwork-product-brief-extract/templates/product-brief-extract-cache.md +17 -0
  600. package/src/hidden-skills/groundwork-review/checklists/architecture.md +93 -0
  601. package/src/hidden-skills/groundwork-review/checklists/bet-pitch.md +94 -0
  602. package/src/hidden-skills/groundwork-review/checklists/decomposition.md +135 -0
  603. package/src/hidden-skills/groundwork-review/checklists/design-system.md +85 -0
  604. package/src/hidden-skills/groundwork-review/checklists/domain-entity.md +66 -0
  605. package/src/hidden-skills/groundwork-review/checklists/implementation-readiness.md +47 -0
  606. package/src/hidden-skills/groundwork-review/checklists/infrastructure.md +68 -0
  607. package/src/hidden-skills/groundwork-review/checklists/maturity.md +71 -0
  608. package/src/hidden-skills/groundwork-review/checklists/product-brief.md +69 -0
  609. package/src/hidden-skills/groundwork-review/checklists/technical-design.md +112 -0
  610. package/src/hidden-skills/groundwork-review/instructions.md +181 -0
  611. package/src/hidden-skills/groundwork-scaffold/instructions.md +254 -0
  612. package/src/hidden-skills/groundwork-scaffold/phases/01-ingestion-service-mapping.md +87 -0
  613. package/src/hidden-skills/groundwork-scaffold/phases/02-scaffolding-execution.md +15 -0
  614. package/src/hidden-skills/groundwork-scaffold/phases/03-service-documentation-api-stubs.md +100 -0
  615. package/src/hidden-skills/groundwork-scaffold/phases/04-infrastructure-verification.md +17 -0
  616. package/src/hidden-skills/groundwork-scaffold/phases/05-draft-review.md +19 -0
  617. package/src/hidden-skills/groundwork-scaffold/phases/06-commit.md +19 -0
  618. package/src/hidden-skills/groundwork-scaffold/templates/scaffold-cache.md +23 -0
  619. package/src/hidden-skills/groundwork-scan/instructions.md +164 -0
  620. package/src/hidden-skills/groundwork-scan/references/digest-schema.md +66 -0
  621. package/src/hidden-skills/groundwork-scan/references/exclusions.md +44 -0
  622. package/src/hidden-skills/groundwork-scan/templates/architecture-findings.md +42 -0
  623. package/src/hidden-skills/groundwork-scan/templates/design-findings.md +23 -0
  624. package/src/hidden-skills/groundwork-scan/templates/overview.md +26 -0
  625. package/src/hidden-skills/groundwork-scan/templates/product-findings.md +23 -0
  626. package/src/hidden-skills/groundwork-scan/templates/scan-state.json +19 -0
  627. package/src/hidden-skills/groundwork-stack-forge/instructions.md +150 -0
  628. package/src/hidden-skills/groundwork-stack-forge/references/authoring-engineer-skills.md +107 -0
  629. package/src/hidden-skills/groundwork-surface-activation/instructions.md +138 -0
  630. package/src/hidden-skills/groundwork-update/briefs/reconcile-worker.md +196 -0
  631. package/src/hidden-skills/groundwork-update/instructions.md +200 -0
  632. package/src/hidden-skills/groundwork-writer/SKILL.md +278 -0
  633. package/src/hidden-skills/maturity-model.md +125 -0
  634. package/src/hidden-skills/operating-contract.md +400 -0
  635. package/src/hidden-skills/repo-map-schema.md +90 -0
  636. package/src/hidden-skills/templates/adr.md +57 -0
  637. package/src/hidden-skills/templates/capability-ports.md +71 -0
  638. package/src/hidden-skills/templates/discovery-notes.md +33 -0
  639. package/src/hidden-skills/templates/domain-entity.md +80 -0
  640. package/src/hidden-skills/templates/gap-ledger.md +21 -0
  641. package/src/hidden-skills/templates/handoff.md +37 -0
  642. package/src/hidden-skills/templates/maturity.md +39 -0
  643. package/src/hidden-skills/templates/surfaces.md +207 -0
  644. package/src/skills/groundwork-check/SKILL.md +56 -0
  645. package/src/skills/groundwork-check/instructions.md +70 -0
  646. package/src/skills/groundwork-orchestrator/SKILL.md +176 -0
  647. package/src/skills/groundwork-orchestrator/workflow-index.md +50 -0
@@ -0,0 +1,100 @@
1
+ ---
2
+ title: Postgres
3
+ description: Schema design, primary keys, JSONB, expand-contract migrations, indexing, connection pooling, queues, and pgvector as a production vector store.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # Postgres
8
+
9
+ ## TL;DR
10
+
11
+ Postgres is the canonical data store for every service that needs persistence. We design schemas explicitly, choose primary keys deliberately, migrate with the expand-contract pattern, index from evidence, pool connections, and use `pgvector` as our vector store. When the question is "which database?", the answer is Postgres unless we have a specific, written reason it cannot be.
12
+
13
+ ## Why this matters
14
+
15
+ Every additional datastore in a system is a multiplier on operational complexity: another backup story, another failure mode, another skill profile to hire for, another surface to monitor. Postgres is a remarkable outlier — it does relational, JSONB document storage, full-text search, queueing, and vector similarity well enough that most workloads never need another engine. Committing to it as a default keeps the operational surface small and the engineers productive.
16
+
17
+ ## Our principles
18
+
19
+ ### 1. Schema design is a design document
20
+
21
+ Every new table begins with a schema design: what does it represent, what identifies it, what are the invariants, what queries does it need to support, what retention does it live under. This is not a formality — schema shape is the contract that outlives any service that reads or writes the table ([Data Engineering](../system-design/data-engineering.md)). Push invariants into the schema, not just the application: `NOT NULL`, `CHECK`, `FOREIGN KEY`, and `UNIQUE` constraints are enforced by the one component every writer shares. An invariant that lives only in application code is an invariant that some other writer will violate.
22
+
23
+ ### 2. Prefer columns to JSONB for stable shape
24
+
25
+ JSONB is powerful but it is not a replacement for column design. When a field is present on every row, queried often, or stable in meaning, it belongs in a column — columns get typed constraints, foreign keys, cheap statistics, and B-tree indexes the planner reasons about well. JSONB is the right call when the shape genuinely varies per row, is rarely filtered on, or is a bag of external metadata you store but do not own. When you do query inside JSONB, index it with GIN (or an expression index on the specific path you filter), and remember that you have traded away the constraint enforcement a column would have given you. The default is columns.
26
+
27
+ ### 3. Schema changes follow expand-contract; recovery is roll-forward
28
+
29
+ Backwards-incompatible change is the source of migration outages, so we never do it in one step. Every change uses expand-contract (parallel change): **expand** the schema with the new, compatible shape; deploy code that writes both old and new; **backfill** existing rows in batched background jobs, never in the migration transaction; cut reads over to the new shape; then **contract** by dropping the old shape once nothing references it. "Migrations are additive" is the easy half of this — the discipline is sequencing the destructive contract step so it lands after every reader and writer has moved.
30
+
31
+ Two rules make this safe in production, and both are non-obvious:
32
+
33
+ - **Set `lock_timeout` (and a `statement_timeout`) on the migration connection.** A bare `ALTER TABLE` queues behind any in-flight query holding a conflicting lock, and every request arriving after it then queues behind the `ALTER` — one slow query becomes a full-table stall. A short `lock_timeout` (a few seconds) makes the migration fail fast and retry instead of cascading into an outage.
34
+ - **Build indexes and validate constraints `CONCURRENTLY` / `NOT VALID` then `VALIDATE`.** These avoid the long-held `ACCESS EXCLUSIVE` lock that the naive form takes.
35
+
36
+ The contested zone is rollback. "Every migration has a pre-written down migration" sounds rigorous but is mostly theater: in production, a down migration that reverses a data-bearing change either cannot run without losing data or has never been exercised under load. Our decision rule: **recovery in production is roll-forward** — you ship a new migration that corrects the problem, because expand-contract has kept the previous shape live and compatible the whole time. Keep a tested down path for the local and CI loop, and only for changes that are provably reversible without data loss. For tables too large for an in-place `ALTER`, reach for a tool built for the job (`pgroll`, `pg_osc`) rather than hand-rolling shadow tables.
37
+
38
+ ### 4. Indexes are evidence-based
39
+
40
+ Most indexes are justified by a query pattern backed by real production data — `pg_stat_user_indexes` and `pg_stat_statements` tell us which queries are hot and which indexes are paying their cost. Unused indexes cost write throughput and disk; we remove them. Speculative indexes "in case we need them later" are the opposite of the principle.
41
+
42
+ The honest exception: some indexes are required at table creation, before any production traffic exists. Unique constraints are indexes you cannot defer. Foreign keys are not auto-indexed by Postgres, and an unindexed FK turns every parent delete or update into a full scan of the child — index the referencing side up front. Beyond that, reach for the specific index the query needs, not the generic one: partial indexes for queries that always carry the same filter, covering indexes (`INCLUDE`) to serve index-only scans, expression indexes for computed predicates. Always build them `CONCURRENTLY` on a live table.
43
+
44
+ ### 5. `pgvector` is our vector store — to a threshold we name
45
+
46
+ Semantic search, embedding similarity, RAG retrieval — all of this runs on `pgvector` in the same Postgres cluster as relational data. The payoff is real and specific: vectors live in the same transaction as the rows they describe, so you filter, join, and keep them consistent without a second system to sync, back up, and reconcile.
47
+
48
+ This is a default, not a law, and the dishonest version of it ignores scale. Vanilla `pgvector` with an HNSW index serves low-latency, high-recall queries comfortably into the low millions of vectors, while the index fits in RAM; performance degrades as the dataset outgrows memory. The decision rule by scale:
49
+
50
+ - **Up to a few million vectors:** `pgvector` + HNSW. No argument.
51
+ - **Tens of millions:** stay in Postgres but switch to `pgvectorscale` (StreamingDiskANN), which keeps the index on disk and holds high QPS at high recall well past where in-memory HNSW falls over.
52
+ - **Hundreds of millions and beyond, or hard requirements `pgvector` does not serve** (extreme-scale sharding, specialized hybrid-filtering engines): that is the written reason to run a dedicated vector store. The data and the requirement will make the case; until they do, the second system is unbought complexity.
53
+
54
+ ### 6. Connection management is explicit, and the pooler is the real answer
55
+
56
+ A Postgres backend is a full OS process with a meaningful memory footprint, so the server tops out at low thousands of connections regardless of how big the box is. The standard architecture is a transaction-mode pooler (PgBouncer or Supavisor) in front of the database: hundreds or thousands of client connections multiplexed onto a small set of server connections, each held only for the duration of a transaction. Size the server-side pool to the database's capacity (a small multiple of CPU cores), not to the number of application instances.
57
+
58
+ Transaction mode is the right default but it forbids session-scoped state — session-level `SET`, advisory locks, and `LISTEN`/`NOTIFY` break across pooled transactions; isolate those on a session-mode connection. Every service still sets explicit per-connection limits, idle timeouts, and a `statement_timeout`. "Just use the defaults" is how Postgres gets hammered into `too many connections` under load. Postgres is a shared resource; treat it like one.
59
+
60
+ ### 7. Query patterns are reviewed
61
+
62
+ Every new query is reviewed for plan shape, not just correctness. `EXPLAIN (ANALYZE, BUFFERS)` on representative data is part of the PR for any non-trivial query. N+1 queries, full-table scans, and unbounded `IN` lists are caught in review, not in production.
63
+
64
+ ### 8. Backups, retention, and disaster recovery are not afterthoughts
65
+
66
+ Automated backups run with RPO and RTO targets that the business has signed off on. We test restores — a backup we have never restored is not a backup. Retention policies are set per table at creation time and aligned with the privacy policy ([Privacy](../quality/privacy.md)).
67
+
68
+ ### 9. Primary keys are a deliberate choice, never UUIDv4
69
+
70
+ The default is a `bigint GENERATED ALWAYS AS IDENTITY` key: compact, sequential, cache-friendly, and ideal for internal tables that never leave the cluster. Choose a UUID instead when the key is generated by the client or across distributed nodes, exposed in URLs or public APIs (where a guessable sequential id leaks volume and ordering), or merged across systems that must not collide.
71
+
72
+ When you do reach for a UUID, use **UUIDv7**, never UUIDv4. A v4 key is random, so inserts scatter across the B-tree, fragmenting the index and inflating write amplification and WAL. UUIDv7 is time-ordered: it keeps the global-uniqueness and distributed-generation benefits while restoring the sequential insert locality that makes `bigint` fast — close enough to identity-key performance that the gap stops being a reason to avoid it. Postgres 18+ ships a native `uuidv7()`; on earlier versions generate it in the application or via an extension. The remaining honest cost is size — 16 bytes versus 8 — which multiplies across every secondary index that carries the key, so do not pay it without one of the reasons above.
73
+
74
+ ## How we apply this
75
+
76
+ - [Data Engineering](../system-design/data-engineering.md) — the broader treatment of data contracts.
77
+ - [Privacy](../quality/privacy.md) — the rules that shape retention and residency.
78
+
79
+ ## Anti-patterns we reject
80
+
81
+ - **JSONB-everything.** Not a schema; a confession of avoided design.
82
+ - **UUIDv4 primary keys.** Random keys fragment the index and tax every write. Use `bigint` by default, UUIDv7 when you need a UUID.
83
+ - **Indexes "just in case."** Every index is a write tax; justify it from a query or remove it — with the narrow exception of unique constraints and foreign-key indexes, which are required up front.
84
+ - **Migrations that lock a hot table.** `ALTER TABLE ... ADD COLUMN ... NOT NULL DEFAULT` on a 10M-row table with no `lock_timeout`. Add the column nullable, backfill in batches, then tighten — and fail fast on a lock you cannot get.
85
+ - **Blind down migrations as a production safety net.** They are rarely exercised and often lossy. Expand-contract plus roll-forward is the real recovery story.
86
+ - **Raw string interpolation into queries.** Parameterised queries, always. This is a security rule ([Security](../quality/security.md)) and a clarity rule.
87
+ - **A second database "just because."** Adding Redis, DynamoDB, or a dedicated vector store without a specific, documented need Postgres cannot meet. Most of the time, Postgres can.
88
+
89
+ ### On using Postgres as a queue
90
+
91
+ The reflexive "never use the database as a queue" is dated. `SELECT ... FOR UPDATE SKIP LOCKED` gives Postgres a correct, contention-free work queue, and for low-to-moderate throughput (roughly to the low thousands of jobs per second) a Postgres queue — raw `SKIP LOCKED`, or a mature layer like `pgmq` or Oban — is often the *right* call precisely because it honours the "no second datastore" principle: jobs are enqueued in the same transaction that creates the work, so you get exactly-once-with-the-write semantics for free, with one backup and one failure mode instead of two.
92
+
93
+ The decision rule: **reach for a dedicated broker when the workload outgrows what a table does well** — sustained high throughput, fan-out to many consumers, streaming and replay, or strict ordered partitions (Kafka territory). And respect the one operational tax that is real: a `SKIP LOCKED` queue churns dead tuples, so it lives or dies by autovacuum — tune aggressive autovacuum on the queue table, or partition it, before load finds the bloat for you.
94
+
95
+ ## Further reading
96
+
97
+ - *PostgreSQL: Up and Running*, Obe & Hsu — a practical, current reference.
98
+ - *The Art of PostgreSQL*, Dimitri Fontaine — advanced patterns with a teaching bent.
99
+ - *Designing Data-Intensive Applications*, Martin Kleppmann — the systems-level argument for relational-as-default.
100
+ - *pgvector documentation* ([github.com/pgvector/pgvector](https://github.com/pgvector/pgvector)) — the canonical source for vector index strategies.
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: API Design
3
+ description: Contract-first design, versioning, evolution, pagination, and AI-agent readiness.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # API Design
8
+
9
+ ## TL;DR
10
+
11
+ Every API starts as a contract — OpenAPI for HTTP, AsyncAPI for events — and the code is generated from that contract. APIs are versioned deliberately, evolved additively, and shaped so that both human developers and AI agents can consume them without surprise.
12
+
13
+ ## Why this matters
14
+
15
+ An API is the most durable commitment a service makes. Once it is in production and a client depends on it, changing it is expensive; breaking it is catastrophic. The discipline of API design is not about getting the first version "right" — it is about making the next ten versions safe to ship. In 2026, the stakes are higher still: agents read our APIs programmatically, generate clients against them, and compose them into workflows we did not design. A poorly shaped API is no longer just a developer-experience problem; it is an agent-productivity problem.
16
+
17
+ ## Our principles
18
+
19
+ ### 1. Contract-first, code-generated
20
+
21
+ A verified spec is the source of truth. Specs live in `/specs`; server handlers, typed clients, and reference docs are generated from them.
22
+
23
+ The contested part is the *authoring direction*, and serious teams genuinely split. Design-first — write the spec, then implement — makes the contract a negotiation artifact teams agree on before code exists; it is the right default for public APIs and anything crossing a team boundary. Code-first — annotate handlers, emit the spec — keeps the contract welded to the implementation and removes the design-then-diverge gap; it is legitimate for internal services where one team owns both ends. Picking a side is a distraction. What is non-negotiable is that a single spec is authoritative and that CI proves the running service matches it (principle 9). Choose the direction that fits the boundary, and never let the spec decay into documentation that trails the code.
24
+
25
+ Generated clients beat hand-rolled ones because the generator, not a human, tracks every schema change. But "generated" is not "good": stock OpenAPI Generator output is often bloated and does not validate responses at runtime, which is exactly why the modern SDK generators (Stainless, Fern, Speakeasy) exist. Generate clients — and treat the generator and its configuration as code you own and review, not a black box you run once.
26
+
27
+ ### 2. Explicit versioning, additive evolution
28
+
29
+ The best version bump is the one you never ship. Additive, expand-then-contract evolution — new optional fields, new endpoints, new response codes, never a removed or repurposed field — keeps clients on v1 for years. Existing clients must never break because we extended the schema.
30
+
31
+ When a break is truly unavoidable, it requires a new major version and a documented deprecation window for the prior one. URL path versioning (`/v2`) is the default: visible, cache-friendly, trivial to route, and obvious to a developer reading a log line. Date-pinned header versioning — Stripe's `Stripe-Version: 2026-05-20`, where each client is pinned to the API as it behaved on a given day — is more precise but demands a version-transformation layer that rewrites old request and response shapes from current internals. That machinery pays for itself only when you have many external customers you cannot coordinate; do not adopt it to serve three internal consumers you can just upgrade.
32
+
33
+ ### 3. Resources, not RPCs
34
+
35
+ Edge HTTP endpoints model resources (`POST /items`, `GET /items/{id}`), not verbs (`POST /createItem`). The resource shape forces us to think about identity, lifecycle, and composition up front. When a true verb is unavoidable (`POST /items/{id}/publish`), we name it carefully and document why a resource shape does not fit. This is a discipline for public, REST-shaped surfaces — not a ban on RPC. Internal service-to-service calls over gRPC are verb-oriented by design, and that is correct; the resource rule applies where humans and agents browse the API, not where two services exchange procedure calls behind the edge.
36
+
37
+ ### 4. Idempotency by design
38
+
39
+ HTTP already makes GET, PUT, and DELETE idempotent by definition — replaying them is safe with no extra machinery. The methods that need help are the non-idempotent ones: POST, and PATCH when it is not a full replace. Those endpoints accept an `Idempotency-Key` header. Clients that retry on failure — which includes every agent we run — depend on it.
40
+
41
+ The server owns correctness, not the client. It stores the key with the first response's status and body, returns that same response on replay, and rejects a key reused with a *different* request payload as the client bug it is (`422`) rather than silently executing twice. The header is on the IETF Standards Track (`draft-ietf-httpapi-idempotency-key-header`) and not yet a finalized RFC, but the `Idempotency-Key` spelling is already the de facto convention — use it rather than inventing your own.
42
+
43
+ ### 5. Pagination and filtering are uniform
44
+
45
+ Every collection endpoint paginates with the same shape, filters with the same query-string grammar, and returns the same `next`/`prev` link structure. Reading one collection teaches you every collection. Inconsistent pagination between endpoints is a design smell that never scales.
46
+
47
+ Cursor pagination is the default: it stays correct when rows are inserted or deleted mid-scan, where offset/limit silently skips and duplicates rows under the reader. Offset paging is acceptable only for small, bounded, slow-changing sets where jump-to-page-N is a genuine user need. Pick one per collection-class and apply it without exception — an agent that learns your pagination once should never be surprised by the next endpoint.
48
+
49
+ ### 6. Errors are structured and machine-readable
50
+
51
+ Every error response carries a stable code, a human message, and a `details` object. Clients — and especially agents — branch on the code, not on the prose. Error codes are catalogued and never renumbered.
52
+
53
+ ### 7. AI-agent readiness is a first-class concern
54
+
55
+ A rich OpenAPI spec is the substrate: descriptions on every field, enumerations for every finite domain, explicit examples on every endpoint. An agent reading the spec should be able to use the API correctly without reading the handler — the difference between a spec that compiles and a spec that teaches.
56
+
57
+ But agent-readiness in 2026 is more than good docs. The Model Context Protocol (MCP) has become the common way agents discover and invoke tools at runtime, and the live question is whether to ship REST, an MCP server, or both. REST stays the system-of-record interface for deterministic system-to-system calls; an MCP server is a thin, intent-shaped projection over that surface for agents selecting tools from natural language. It is a façade, not a second source of truth — it wraps the same handlers, enforces the same auth, and is generated and versioned alongside the spec. Building one before you have agent consumers is speculative generality; declaring a raw 200-endpoint REST surface "agent-ready" because it has an OpenAPI file is wishful.
58
+
59
+ Design for the agent's failure modes, not just its happy path. Keep response payloads lean — an agent pays tokens for every field it reads, so a 40-field object is a tax on every call. Make each operation do one nameable thing, because tool selection degrades when operations overlap. Label side effects explicitly so a planner knows which calls are safe to retry or run speculatively.
60
+
61
+ ### 8. Async events are contracts too
62
+
63
+ We treat WebSocket and message broker events with the same rigour as HTTP — an AsyncAPI spec, generated client and server models, additive evolution. Events that are "informal" today are the integration bugs of next quarter.
64
+
65
+ ### 9. The contract is enforced, not just authored
66
+
67
+ A spec no one verifies drifts the moment a provider ships. We lint specs in CI (Spectral), run consumer-driven or bi-directional contract tests behind a `can-i-deploy` gate so a provider cannot break a consumer, bind errors to **RFC 9457 Problem Details**, and choose the protocol deliberately — REST at the edge, gRPC internal, federated GraphQL for composition, tRPC only inside a TypeScript monorepo. Contract-first authoring without contract testing is half the loop.
68
+
69
+ ## Anti-patterns we reject
70
+
71
+ - **Breaking changes without a version bump.** "It is a small breaking change, no one uses that field" — the assumption is always wrong in an agent-consuming world.
72
+ - **Hand-written clients.** Clients drift, and drift causes outages. Generate.
73
+ - **Kitchen-sink endpoints.** `POST /doThing` that accepts a 40-field payload and does everything. Split it.
74
+ - **Error payloads as strings.** A 400 response body of `"invalid input"` is unusable by any automated caller. Structured errors, always.
75
+ - **Endpoint-scoped pagination conventions.** Cursor in the body here, page-number in a query string there, offset-limit somewhere else. Pick one and apply it universally.
76
+ - **"Agent-ready" by assertion.** Terse field names, no examples, and a 200-endpoint surface — declared agent-ready because an OpenAPI file exists. An agent needs the same teaching a new engineer does.
77
+
78
+ ## Further reading
79
+
80
+ - *Designing Web APIs*, Jin, Sahni, Shevat — the working bible of HTTP API design.
81
+ - *Web API Design: The Missing Link*, Apigee — the short handbook that gets the REST vocabulary right.
82
+ - *RFC 9457: Problem Details for HTTP APIs* ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc9457)) — the standard error-body format.
83
+ - *Designing robust and predictable APIs with idempotency*, Stripe ([stripe.com/blog/idempotency](https://stripe.com/blog/idempotency)) — the reference treatment of idempotency keys.
84
+ - *Model Context Protocol* ([modelcontextprotocol.io](https://modelcontextprotocol.io)) — the emerging standard for agent tool discovery and invocation.
85
+ - *AsyncAPI Specification* ([asyncapi.com](https://www.asyncapi.com)) — the canonical format for async contracts.
86
+ - *OpenAPI Specification* ([openapis.org](https://www.openapis.org)) — the canonical format for HTTP contracts.
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: Architecture Decisions
3
+ description: Architecture Decision Records as governed, re-evaluable commitments — lean, assumption-explicit, immutable as records, and readable by humans and agents alike.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # Architecture Decisions
8
+
9
+ ## TL;DR
10
+
11
+ We capture every significant architectural decision as a short record — the context that forced it, what we chose, the assumptions it rests on, and the trade-offs we accepted. The record is immutable; the decision is not. When the world changes we supersede the record rather than overwrite it, so the trail of *why* is preserved and re-evaluation is a check against named assumptions rather than a debate from scratch. A decision record is governance, not paperwork — and in an agent-led codebase it is the memory that keeps autonomous and human decisions consistent.
12
+
13
+ ## Why this matters
14
+
15
+ The reasoning behind a decision is the first thing lost and the most expensive to reconstruct. Code shows *what* was built; it cannot tell you that a boundary sits where it does because of a constraint that no longer exists. A team without decision records relitigates the same choices every year, cannot tell "we considered this and rejected it" from "nobody thought of it," and discovers its load-bearing assumptions only when one of them breaks in production.
16
+
17
+ The discipline is cheap and the payoff compounds — but only if records stay *lean enough to write* and *governed enough to trust*. Twenty-field templates die unmaintained; bare records nobody can re-evaluate rot into folklore. The craft is the narrow middle: the few fields that make a decision both fast to capture and honest to revisit.
18
+
19
+ ## Our principles
20
+
21
+ ### 1. The reasoning is the record
22
+
23
+ An ADR captures *Context* (what forced the decision, what was on the table), *Decision* (what we chose), and *Trade-offs* (what we gave up, what risk we accepted). The why is the deliverable. A record of the outcome without the reasoning is half a record — it cannot stop the next person from quietly undoing the decision because its rationale was invisible.
24
+
25
+ ### 2. Name the assumptions
26
+
27
+ Every decision rests on conditions that were true when it was made — a load profile, a team size, a vendor price, a regulatory boundary. We write them down. Assumptions are the linchpin of governance: a decision is valid exactly as long as the assumptions under it hold, and naming them turns "should we revisit this?" from a vibe into a check you can run.
28
+
29
+ ### 3. Set a review trigger
30
+
31
+ Decisions expire against reality, not the calendar. Each significant record names the condition that should bring it back for review — "when we add a second region," "if write throughput passes 50k/s," or, when nothing better exists, a date. The trigger is what makes re-evaluation *proactive*: the decision resurfaces when its world shifts, instead of when someone happens to remember it.
32
+
33
+ A trigger is not free. An unmaintained trigger is the twenty-field template in another costume — a standing promise nobody keeps — and a stale log is worse than no log, because it reads with false confidence. So a named trigger is earned, not default: reserve it for decisions whose validity hinges on a fragile, measurable assumption (a throughput ceiling, a vendor term, a single-region bet). For everything else the assumption list and the supersession chain *are* the trigger — they let any later reader re-check on demand without anyone holding a vigil.
34
+
35
+ ### 4. The record is immutable; the decision is not
36
+
37
+ Once accepted, a record's conclusions are not edited (fixing a typo or a dead link is fine). When a decision changes, we write a **new** record that supersedes the old one, and we link the two in both directions — the old marked `superseded by NNNN`, the new naming what it replaces. The original Context and Trade-offs stay true *for the world they were written in*; that is precisely why they are worth keeping. An immutable trail is what makes the set trustworthy enough to re-evaluate against. Some teams run ADRs as living documents instead, appending dated notes to the original; that is fine for anything that does not move the conclusion — a clarification, a fresh data point, a status change from proposed to accepted. The line is the conclusion itself: amend in place when the meaning holds, supersede when the meaning changes. Editing a *conclusion* in place is the single move that breaks the trail, because it rewrites history that other records and live code now depend on.
38
+
39
+ ### 5. Re-evaluation is the goal, not the exception
40
+
41
+ Records exist to make changing our minds *productive*. When circumstances shift, the record lets us argue about what actually changed — does this assumption still hold? — rather than rebuilding the whole decision from memory. Re-deciding is healthy engineering; re-deciding *without recording it* is how a team loses its memory.
42
+
43
+ ### 6. Keep it lean
44
+
45
+ Three to five sections plus a thin governance header is the whole of it. The fields earn their place because each is load-bearing — Context and Trade-offs carry the reasoning; Assumptions and the review trigger carry the governance; status, owner, and supersession links carry the history. We resist every field beyond these. A template that feels like a chore is a template that goes unwritten, and an unwritten decision is the most expensive kind.
46
+
47
+ ### 7. Owned, not orphaned
48
+
49
+ Every standing decision has a current owner — the person or team accountable for it today, who is not necessarily whoever wrote it. An orphaned decision is one nobody will revisit when its assumptions break, which is the same as having no governance at all.
50
+
51
+ ### 8. The decision log is the agent's memory
52
+
53
+ In an agent-led codebase, the record set is not just human documentation — it is the decision-context layer an agent reads before proposing or revisiting a choice. Relevance is governed by assumption-overlap and supersession status, not recency — a foundational record from the first month can bind a choice more tightly than last week's, and an agent that simply weights the newest records will miss it. So we keep the set lean, linked, assumption-explicit, and *current*, and that is what lets a human or an agent make a *consistent* next decision instead of contradicting one made last quarter. A stale log misleads an agent faster than it misleads a human: the agent reads it at full confidence and propagates a constraint that no longer holds. Records are written to be machine-consumable for the same reason every other interface is ([Agent-Native Systems](../ai-native/agent-native-systems.md)).
54
+
55
+ ### 9. Govern by advice, and pair the record with a check
56
+
57
+ A record is not a gate. "Governed" means an **advice process** — the decider seeks advice from everyone meaningfully affected *and* everyone with relevant expertise, then keeps the decision — not a central review board teams route around. The advice is mandatory; consensus is not. That trade buys speed with trust and accountability: it degrades the moment deciders skip the advice or escape the consequences of a bad call, and it presumes systems loosely coupled enough that the affected set stays small. A standing review gate is still the right tool for the rare decision that is irreversible *and* cross-cutting *and* externally constrained — a regulated boundary, a one-way data-retention choice — so reserve gates for those rather than making them the default. And where a decision is mechanically checkable, we pair it with a **fitness function** that fails the build on violation: the record documents the choice, the fitness function assures it still holds ([Evolutionary Architecture](evolutionary-architecture.md)).
58
+
59
+ ## How we apply this
60
+
61
+ Records live in `docs/architecture/decisions/NNNN-<slug>.md`, numbered sequentially. We record at the moment of the decision, not as after-the-fact paperwork — the cheapest time to capture the context is while it is still in the room. The significance test: would a new engineer or agent otherwise have to reconstruct, or relitigate, the reasoning from scratch? A reversible, low-cost, local choice does not earn a record; a durable, cross-cutting, expensive-to-reverse one does.
62
+
63
+ - [How We Structure Code](code-structure.md) — the boundary decisions most often worth recording.
64
+ - [Agent-Native Systems](../ai-native/agent-native-systems.md) — why records are written to be read by agents.
65
+
66
+ ## Anti-patterns we reject
67
+
68
+ - **Frozen decisions.** "That was decided, we don't revisit it." The record exists to make revisiting *tractable*, not to forbid it.
69
+ - **Decisions without assumptions.** A record that omits what it depended on cannot tell you when it has gone stale. It will go stale anyway — silently.
70
+ - **Overwriting a record in place.** Editing the conclusion erases the trail that made the current state trustworthy. Supersede; never rewrite.
71
+ - **The twenty-field template.** A form so heavy nobody fills it produces exactly zero records. Lean beats complete.
72
+ - **Orphaned decisions.** No owner means no one will notice when the trigger fires.
73
+ - **Paperwork after the fact.** A record written a month later, from memory, has lost the context that was the point.
74
+ - **Relitigating from scratch.** If the conversation rebuilds the whole decision instead of testing what changed, the record is not being used.
75
+
76
+ ## Further reading
77
+
78
+ - *Architecture Decision Record*, Michael Nygard (the original 2011 post) — the lightweight format everything here descends from.
79
+ - *MADR* ([adr.github.io](https://adr.github.io/)) — the modern Markdown ADR template and status lifecycle, built on Nygard's five-part structure (title, status, context, decision, consequences).
80
+ - *Facilitating Software Architecture*, Andrew Harmel-Law (O'Reilly, 2024) — the architecture advice process: decentralized, advice-bound, accountable decision-making.
81
+ - *Context Matters: Evaluating Context Strategies for Automated ADR Generation Using LLMs* (2026) — evidence that recent decision records are a high-leverage context layer for agents.
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: How We Structure Code
3
+ description: A pure core, swappable edges, no leaked implementation details, and one obvious place for everything — the structural discipline that makes a codebase legible to a human and navigable to an agent, expressed in each language's own idiom.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # How We Structure Code
8
+
9
+ ## TL;DR
10
+
11
+ Every service is a **pure core** that makes decisions, wrapped in a **thin shell** that does I/O. The core depends only on abstractions it owns; the concrete implementations — the database, the LLM, the queue — plug in at the edges and are swappable. No implementation detail leaks inward: the core never knows it is talking to Postgres or Anthropic. And the structure is **opinionated and predictable**, so a developer or an agent never has to guess where a thing lives or where a new thing goes. The discipline is the same in every language; its expression is native to each.
12
+
13
+ This is the single highest-leverage structural choice we make, and it is deliberately non-negotiable for new code.
14
+
15
+ ## Why this matters
16
+
17
+ There is one idea here, and the industry has named it five times — Dependency Inversion, Hexagonal / Ports & Adapters, Onion, Clean Architecture, Functional Core / Imperative Shell. They are restatements of a single constraint. As Robert C. Martin put it of the variants, *"they all have the same objective, which is the separation of concerns… by dividing the software into layers"* — and the rule under all of them is the **Dependency Inversion Principle** (Martin, 1996): high-level policy depends on abstractions, not on low-level detail. We state the rules directly and in plain language; the named frameworks live in Further Reading, for the lineage. What we hold is the discipline, not a label — and we keep it shallow, never the "onion with ten rings."
18
+
19
+ Holding the structure pays off twice:
20
+
21
+ - **Changeability.** Because dependencies plug in behind abstractions the core owns, swapping a database, an LLM provider, or a message broker is a configuration change at the edge, not a rewrite through the middle.
22
+ - **Testability.** A pure core has many branches and no dependencies, so it is tested exhaustively with plain input→output assertions and no mocks; the thin shell has few branches and many dependencies, so it is tested for real against the things it wraps (Gary Bernhardt's *Boundaries*). The architecture tells you what to stub and what to stand up.
23
+
24
+ And there is a payoff specific to how we work in 2026: **an opinionated structure collapses an agent's decision space.** When "where does this code live?" is already answered by the convention, the agent inherits the layout instead of inventing one, and produces code that matches the existing shape. Two cautions keep this honest, both from recent evidence: conventions help an agent only when they are **legible** — explicit and discoverable in the code — not when they are hidden framework magic the agent must already know (the *Constraint Decay* finding, that agents do worse in convention-heavy environments whose rules are implicit). And an obvious structure beats verbose prose guidance: piling requirements into instruction files measurably *reduces* success. So we make the structure speak for itself, and we keep the guidance minimal.
25
+
26
+ ## Our principles
27
+
28
+ ### 1. A functional core, an imperative shell
29
+
30
+ Decisions are pure functions over plain values — no I/O, no hidden mutation, no clock or network reached for in the middle. All side effects live in a thin shell at the edges that gathers inputs, calls the core, and acts on what it returns (Mark Seemann's *impure→pure→impure* sandwich). The core is deterministic and does not know the shell exists.
31
+
32
+ This is a discipline, not a religion, and it bends where it must: when a decision genuinely needs more data part-way through, or a filter must run database-side for performance, the shell reaches back for it. That is expected. The goal is to keep *as much branching as possible* in the dependency-free core — not to achieve a zero-effect purity the language fights. In Go, that means functions over values and errors-as-values, not a framework of structs-with-methods; in Python, injecting a clock and keeping logic in pure functions; in TypeScript, the same. Keep it native.
33
+
34
+ ### 2. Depend on abstractions you own, and make them point inward
35
+
36
+ The core declares the interface it needs, **in its own language** — `Store`, `Embedder`, `Notifier` — and the implementation conforms to it. The dependency points inward: the edge depends on the core's abstraction; the core never depends on the edge. This is not aesthetic. It is **enforceable in CI** — `depguard` in Go, `import-linter` in Python, ESLint import rules in TypeScript — and a violation fails the build. Enforcement is what turns the structure from a style we hope for into a guarantee we have.
37
+
38
+ ### 3. Never leak an implementation detail
39
+
40
+ That storage is Postgres, that the model is Anthropic, that the queue is Kafka — none of it may shape the core's types or its method signatures. No ORM entities standing in as domain models, no `IQueryable`- or SQL-shaped methods on a port, no vendor SDK type in a port's signature. An abstraction expressed in the vendor's terms is a leak wearing a costume — when the implementation changes, the "abstraction" changes with it, which is the proof it was never one. The port speaks the domain's goals: *"methods that make sense to the goals of a user in a domain, not to a database"* (Brett Schuchert).
41
+
42
+ Hold this as a direction, not a fantasy. *All non-trivial abstractions leak to some degree* (Joel Spolsky's law) — a query that is fast on an index and slow without one leaks through any storage port. We minimise leakage rigorously; we do not pretend it reaches zero.
43
+
44
+ ### 4. Abstract at the narrow seam you actually use — never speculatively
45
+
46
+ A port exposes only what the application actually calls, named in the application's language: `Orders.GetById` and `Orders.Save` over an aggregate, an `Embed(text) → vector`. Narrow, domain-named ports are cheap to carry and easy to change — effort YAGNI explicitly exempts as *making the software easier to modify*. The speculative version is the trap: an interface invented "for mocking" with exactly one implementation, a wide wrapper mirroring a vendor SDK for a swap that never comes, or a generic `Repository<T>` / `IRepository<TEntity>`.
47
+
48
+ The generic repository deserves a careful word, because it is seductive for a real reason: it *forces a clean break* between the domain and the store — a hard, uniform boundary — and that break is genuinely valuable. The lie is the word "generic." We have almost always seen exactly **one** implementation, so the generality is unproven — Brian Foote's *Speculative Generality*, "spotted when the only users are test cases." And a uniform CRUD surface (`GetAll`, `Find(query)`, an exposed `IQueryable`) ends up advertising the *store's* capabilities, not the *domain's* needs — the leak from principle 3, wearing the costume of an abstraction.
49
+
50
+ So take the clean break from where it actually comes — an owned port, a persistence-ignorant core, and adapter tests against the real database (principle 5) — not from a generic CRUD contract. The decision rule:
51
+
52
+ - **Simple CRUD or read-heavy work: no bespoke repository.** A mature ORM or query layer already *is* a unit of work and a collection-like store; wrapping it again is indirection that hides the details you need. Use it directly, or a query object.
53
+ - **A rich aggregate with invariants: one repository per aggregate root** — domain-named methods, the interface owned by the core, the implementation in the adapter. Speak the ubiquitous language, never the schema; when query variety grows, reach for a *specification*, not more methods or a leaked query type.
54
+ - **A generic base is legitimate only once the generality is proven** — three real aggregates exist (the rule of three), the base is *constrained* to enforce the aggregate-root rule, and it is reached *only* through specific domain-named ports. A generic base with one implementation is the lie; delete it.
55
+
56
+ The same logic governs every external dependency: **wrap thin clients, not rich tools** — a driver, a raw HTTP SDK, an LLM API earn a narrow owned port; a tool that already implements the pattern does not (Jimmy Bogard's heuristic; Go's *"the bigger the interface, the weaker the abstraction"*). This is the principle that most needs **judgment**, which is why it is carried in the engineer skills (principle 8) and applied consistently rather than reinvented per file. The wrong abstraction costs more than the duplication it removes (Sandi Metz) — when in doubt, wait for the third case.
57
+
58
+ ### 5. Swappable by design; integration-tested for real
59
+
60
+ Because implementations plug in behind owned abstractions, a dependency is replaceable — and we prove it the right way. Stub the port to test the core's logic exhaustively and fast; run the **real** adapter against the **real** dependency in a container, never a mock of it. We test the system, not a mock of the system. A boundary you cannot test cheaply is usually a boundary drawn in the wrong place. (See [Testing](../foundations/testing.md).)
61
+
62
+ ### 6. One obvious place for everything
63
+
64
+ The structure is opinionated on purpose: for any change there is a single predictable place it belongs, and for any feature a single place to find all of its parts. The top-level layout should **announce the domain, not the framework** — it should read as *Payments* and *Catalogue*, not as the web framework underneath (Robert C. Martin's *Screaming Architecture*). Prefer colocating what changes together — a feature owns its handler, its logic, its types — over scattering a feature across technical layers; *"minimise coupling between slices, maximise coupling within a slice"* (Jimmy Bogard). Decide the thousand trivial layout choices **once**, in the scaffold, so no one re-litigates them per service — *"a place for everything, and everything in its place; constraints liberate"* (the Rails doctrine). Python's Zen says the same: *one obvious way*, and *in the face of ambiguity, refuse the temptation to guess.* The test of the structure is that nobody — human or agent — has to guess.
65
+
66
+ ### 7. Keep it shallow
67
+
68
+ Three zones are enough: the **core**; the **abstractions plus the orchestration** that uses them; the **edge implementations**. Resist the extra rings — the five-hop DTO translation, the layer that exists only to forward a call. Indirection is not abstraction: a layer that hides no complexity is just a tax on every reader and every change, and it should be deleted. Shallow keeps cognitive load low, which is the real limit on how fast anyone moves through the code.
69
+
70
+ ### 8. The discipline is constant; the expression is native
71
+
72
+ The rules above hold identically in Go, Python, and TypeScript — but each is written the way its own community writes it, and we never impose a uniform cross-language layout (a uniform layout would itself read as a framework fingerprint, and we avoid it). Go defines narrow interfaces at the point of use and returns concrete structs; Python expresses ports as `Protocol`s — structural typing, not inheritance ceremony; TypeScript leans on structural types, where a port costs almost nothing to declare. The **engineer skill for each stack is the carrier of its native expression** — it is where these principles become a specific, idiomatic shape the agent can scaffold, extend, and recognise.
73
+
74
+ ## How we apply this
75
+
76
+ - **New services ship this shape from the scaffold** — the directory layout, the dependency-rule lint config wired into CI, and a stub core with one real adapter that demonstrates the flow. The convention is generated, not asked for.
77
+ - **The per-language engineer skill is the delivery vehicle.** `groundwork-go-engineer`, `groundwork-python-engineer`, and the surface engineers teach the agent the native expression of these rules: where to look, where new code goes, and what idiomatic looks like for that stack. An agent equipped with the skill navigates and extends any codebase built to the convention without guessing — which is the whole point of holding a convention.
78
+ - **Brownfield is guided toward the same shape.** The scan → extract → adopt path reads an existing codebase, names where it already follows these principles and where it diverges, and moves it toward the convention incrementally rather than demanding a rewrite.
79
+ - **For frontends, apply the spirit:** isolate network I/O behind a data layer and keep rendering logic free of fetching concerns; the core/edge split survives even where the file conventions differ.
80
+
81
+ ## Right-size the boundaries
82
+
83
+ Structure within a service is one decision; how many services exist is another, and the default is conservative. Start as a **modular monolith** — one deployable with strong internal module boundaries, one bounded context per module — and treat extracting a microservice as an *earned* move, justified only when multiple signals converge (the language and mental model shift, the scaling profile is incompatible, the deploy cadence is fundamentally different). The failure mode to name is the **distributed monolith**: services that deploy in lock-step, share a schema, or call each other synchronously three deep — the cost of microservices with none of the independence. Make the consolidation signal as first-class as the split: two services that always change together should be merged back. (See [Surface Architecture](surface-architecture.md) and [Evolutionary Architecture](evolutionary-architecture.md).)
84
+
85
+ ## Anti-patterns we reject
86
+
87
+ - **Framework-coupled core.** `gin.Context`, `fastapi.Request`, or an ORM entity living in the core. The moment the core imports the framework, it is no longer the core.
88
+ - **Leaked persistence.** The core knows it is Postgres; a "port" that returns `IQueryable`, a `DbSet`, or raw rows. Map at the edge, in the adapter — not by letting the storage shape reach inward.
89
+ - **Vendor-shaped ports.** A port that mirrors an SDK's methods. Swapping the vendor then means rewriting the port and everything that calls it — the opposite of what the port was for.
90
+ - **Speculative abstraction.** A generic `Repository<T>`, an interface with a single implementation added "for mocking," a wide wrapper over a mature tool to enable a swap no one will make. Indirection sold as foresight.
91
+ - **Anaemic core, god service.** Data classes with no behaviour and one application service that holds every rule. Rules belong on the entities they govern.
92
+ - **Over-layering.** Five DTO translations between the HTTP edge and the core. Adapters are thin: read the request, call the core, write the response.
93
+ - **Guessing-required structure.** No single obvious home for a change; the parts of one feature scattered so widely you have to grep to assemble it. If placement is ambiguous, the structure has failed its one job.
94
+ - **Mock-the-world testing.** Proving the system against mocks of its own dependencies. Stub the port to test the core; use the real thing to test the adapter.
95
+
96
+ ## Further reading
97
+
98
+ These are the sources behind the idea. You may know the discipline under any of their names — read one treatment in full; they converge.
99
+
100
+ - *The Dependency Inversion Principle*, Robert C. Martin (1996) — the root rule beneath every variant. Start here.
101
+ - *The Clean Architecture* (Martin, 2012), *Hexagonal / Ports & Adapters* (Alistair Cockburn, 2005), and *Onion Architecture* (Jeffrey Palermo, 2008) — three names for the inward-dependency rule. Any one of them is the same message.
102
+ - *Boundaries* / Functional Core, Imperative Shell, Gary Bernhardt (2012), and Mark Seemann's *Composition Root* and *impureim sandwich* essays on [blog.ploeh.dk](https://blog.ploeh.dk) — the pure-core framing and where it stops.
103
+ - *Screaming Architecture* (Martin, 2011) and *Vertical Slice Architecture* (Jimmy Bogard, 2018) — making placement obvious.
104
+ - The honest counter-camp, read so you abstract with judgment rather than reflex: Sandi Metz, *The Wrong Abstraction* (2016); Joel Spolsky, *The Law of Leaky Abstractions* (2002); and the repository-pattern critiques (Derek Comartin, Vladimir Khorikov) on when the abstraction is worth its cost and when it is ceremony.
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: Data Engineering
3
+ description: Events, streams, CQRS, event sourcing, and the data contracts that outlive any service.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # Data Engineering
8
+
9
+ ## TL;DR
10
+
11
+ Data outlives services. We treat every event we emit and every table we own as a long-term contract, shaped so downstream consumers — today and in three years — can work with it without archaeology. Events are append-only, schemas are versioned, and the log of what happened is preserved even when the current-state projection is rebuilt.
12
+
13
+ ## Why this matters
14
+
15
+ Services are replaced; data lives on. The data contracts we set today — table shapes, event payloads, field semantics — are the single most durable thing we will produce. Getting the contract right once is cheap; changing it retroactively after the data has multiplied is brutal.
16
+
17
+ ## Our principles
18
+
19
+ ### 1. Events are append-only and immutable
20
+
21
+ Once an event is emitted, it is never rewritten. Correction happens through *compensating* events, not through mutation of the original. This is the discipline that lets downstream consumers trust the event log as a truthful history of the system.
22
+
23
+ ### 2. Schemas are versioned and evolvable
24
+
25
+ Event payloads have explicit versions. New fields are additive; removed fields are deprecated with a deadline, not removed silently. Consumers can detect an old schema and handle it or refuse it — they are never surprised. This is the AsyncAPI discipline ([API Design](api-design.md)) applied to every stream.
26
+
27
+ ### 3. Partition keys are chosen deliberately
28
+
29
+ Partitioning forces a tradeoff with no universal answer: ordering pulls toward concentrating related records on one partition, even distribution pulls toward spreading them. The key that guarantees per-entity order — the entity's ID — also creates a hot partition the moment one entity is far busier than the rest, and the broker will not rebalance skew away for you. You size for the hottest partition and waste the rest.
30
+
31
+ Decision rule:
32
+
33
+ - Per-entity ordering needed and entities are roughly uniform → partition by entity ID.
34
+ - Ordering needed but some entities are hot → use a composite key (`tenantId|entityId`) to widen routing cardinality while preserving order for the inner entity, or salt the hot key into K buckets and accept that order is lost *across* buckets.
35
+ - No ordering requirement → leave the key null and let the producer round-robin for even load.
36
+
37
+ Choosing a partition key casually is one of the most expensive mistakes in a data system. Repartitioning a live topic is a migration, not a config change, so the key gets reviewed at design time.
38
+
39
+ ### 4. CQRS where it pays
40
+
41
+ For read-heavy surfaces with complex projections, we maintain a read model separate from the write model. The write model owns truth; the read model owns query performance. We do not apply CQRS universally; we apply it where the read load and the write load have genuinely different shapes. The tax is eventual consistency — the read model lags the write, so any surface built on it must not assume read-after-write. If a single well-indexed table serves both paths, you do not have a CQRS problem; splitting the model early buys two things to keep in sync and nothing else.
42
+
43
+ ### 5. Event sourcing is a tool, not a religion
44
+
45
+ For domains where the history of change is itself the product — audit logs, participation timelines — we store the event log as the primary artefact and derive current state from it. For domains where current state is what matters, we store current state and publish events as derivatives. Event sourcing every table "because it is purer" is overengineering.
46
+
47
+ ### 6. Data contracts are documented, versioned, and owned
48
+
49
+ Every significant table and every published event has an owner, a documented schema, a migration history, and a compatibility policy. Unowned tables and undocumented events are a ticking integration-debt clock.
50
+
51
+ ### 7. Retention is a design decision
52
+
53
+ Every dataset we store has a retention policy — deletion after N days, archival after M days, live forever. Retention is decided when the dataset is created, reviewed when the regulatory surface changes ([Privacy](../quality/privacy.md)), and enforced by automation. "We will figure it out later" is the decision that becomes a compliance incident three years later.
54
+
55
+ ### 8. Backfills are a planned operation
56
+
57
+ Changing the shape of historical data — renaming a field, re-computing a derived column — is a project with a plan, a rollback, and a measurement. We do not backfill by running a script and hoping. Backfills are rehearsed in staging and measured in production.
58
+
59
+ ### 9. Change capture, enforced schemas, and the AI-era layer
60
+
61
+ **Change capture vs. outbox.** The transactional outbox is the events we *mean* to publish, written in the same database transaction as the state change so there is no dual write to lose. CDC streams the raw row changes of a table. They are complementary, not rivals: the cleanest outbox relay *is* CDC — Debezium tailing the write-ahead log — rather than a polling loop. We reach for raw-table CDC as an integration backbone only when the source cannot give us a real contract; a CDC feed of someone else's schema is a contract we never negotiated, and they can break it without telling us.
62
+
63
+ **Enforced, not just versioned.** Versioning (principle 2) is the policy; enforcement is the mechanism. A schema registry checks compatibility at registration and fails the producer's build on a breaking change, shifting the contract left into CI instead of into a consumer's pager at 3am. The contract is owned by the producer; an unowned contract is stale documentation.
64
+
65
+ **The AI-era data layer is architecture, not a bolt-on.** Vector/embedding stores are the retrieval core of RAG, and chunking, hybrid (lexical + semantic) search, re-ranking, and metadata filtering are design decisions, not library defaults. Default to `pgvector` in the database you already run — under roughly ten million vectors it matches dedicated stores on latency and recall while saving you a whole system to operate and keep consistent. Move to a dedicated vector database when scale, recall under heavy concurrency, or index build time make Postgres the bottleneck. Embeddings are derived data: a model change invalidates them, so re-embedding is a planned backfill (principle 8) against a versioned embedding model, never an ad-hoc rerun.
66
+
67
+ **Storage: layer, don't pick a camp.** Data mesh and the lakehouse answer different questions — mesh is an org model (domain-owned data products, federated governance), the lakehouse is a technical substrate. The working pattern is layered: a platform team owns storage, catalog, and CI templates; domain teams own the products on top. For the table format there is no clean winner — Delta Lake if you are Spark/Databricks-centric, Apache Iceberg if you want engine-agnostic optionality and the broader catalog ecosystem. Pick for the engines you actually run and the vendor coupling you can tolerate, not for the benchmark of the week.
68
+
69
+ ## How we apply this
70
+
71
+ - [Postgres](../stack/postgres.md) — how we apply these principles inside our chosen database.
72
+
73
+ ## Anti-patterns we reject
74
+
75
+ - **Silent schema changes.** Renaming a column in a hot table without coordinating consumers. This is how outages start.
76
+ - **Mutable event logs.** Going back and "fixing" a past event. The event is what happened; the correction is a new event.
77
+ - **Kitchen-sink "events" table.** One table that accepts a JSON blob for every kind of event. The type system is the best friend of a data contract; do not throw it away.
78
+ - **Backfills in production without rehearsal.** See above.
79
+ - **Retention by accident.** Tables that grow forever because no one considered retention at creation time.
80
+
81
+ ## Further reading
82
+
83
+ - *Designing Data-Intensive Applications*, Martin Kleppmann — the single best survey of the territory, including the chapters on derived data, stream processing, and batch processing.
84
+ - *Data Mesh*, Zhamak Dehghani — the argument for treating data as a first-class product with owners.
85
+ - *Streaming Systems*, Akidau, Chernyak, Lax — the deep treatment of time, watermarks, and windowing in stream processing.
86
+ - *Event Sourcing and CQRS*, Vaughn Vernon (the relevant chapters of *Implementing DDD*) — a grounded, implementation-focused view.
87
+ - *Data Contracts*, Chad Sanderson, Mark Freeman & B. E. Schmidt (O'Reilly, 2025) — producer-owned, shift-left enforcement of the contracts in principles 6 and 9.
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Durable Execution
3
+ description: Workflow-as-code as a first-class primitive — moving reliability for multi-step, long-running, and must-complete processes out of application code and into the infrastructure.
4
+ status: active
5
+ last_reviewed: 2026-06-19
6
+ ---
7
+ # Durable Execution
8
+
9
+ ## TL;DR
10
+
11
+ For a process that has many steps, runs for a long time, or must either complete or compensate, we reach for **durable execution** — workflow-as-code on an engine that checkpoints progress so the process resumes where it left off after a crash, deploy, or hours-long wait. It moves the reliability guarantee out of hand-written saga, outbox, and retry glue and into the infrastructure. It is the same primitive that makes long-running agents and human-in-the-loop approvals safe. It is not free: it imposes a determinism contract on workflow code and a versioning discipline on every deploy, and below a complexity threshold an outbox or a queue is the better tool.
12
+
13
+ ## Why this matters
14
+
15
+ The classic way to make a multi-step process reliable is to hand-assemble it: an outbox here, an idempotency key there, a retry table, a state column, a cron to sweep the stuck ones. Each piece is correct and the whole is a fragile machine that every engineer reasons about differently and that fails in the gaps between the pieces. Durable execution collapses that machine into ordinary code whose execution state is automatically persisted — "a function that survives a crash and resumes." The category predates the current AI wave (it grew out of Uber's Cadence and the workflow engines before it), but agent workloads — long-running, tool-calling, occasionally-paused processes that cannot afford to restart from zero — are exactly its best fit, and they drove it into the mainstream.
16
+
17
+ ## Our principles
18
+
19
+ ### 1. Workflow-as-code is a first-class primitive
20
+
21
+ For multi-step, long-running, or compensating processes, we prefer a durable execution engine over hand-rolled saga + outbox + retry orchestration. The workflow is written as ordinary, mostly-linear code; the engine persists its progress. This sits beside choreography and orchestration as a named architectural option, not buried inside "saga."
22
+
23
+ It is not the default for every async task, and the cost is real: a determinism contract, a versioning discipline, and often a new piece of infrastructure to operate. **Decision rule:** reach for durable execution when a process has three or more steps that must survive a crash *together*, holds state across minutes-to-days, or needs compensation and progress visibility. Below that — fire-and-forget work, a single idempotent event handler, "publish this event reliably" — an outbox or a queue is correct and a workflow engine is overkill. The honest failure mode runs the other way too: a queue plus cron plus a retry table that has accreted dedup windows, scattered timers, and compensations is a workflow engine you built by accident, missing the correctness guarantees and observability you would have gotten for free.
24
+
25
+ ### 2. Durability lives in the infrastructure, not the code
26
+
27
+ The reliability guarantee — resume-where-you-left-off, durable timers, automatic retry — is provided by the engine, not re-implemented per process. Application code expresses the business steps; the platform owns crash recovery.
28
+
29
+ What the engine guarantees is **effectively-once**, not exactly-once. Activities and steps run *at-least-once*: an engine can crash after a side effect completes but before its result is recorded, and on recovery it runs the step again. The engine makes the *workflow* converge to a single logical outcome; it does not make a non-idempotent `POST` safe. Exactly-once side effects are a property the step author provides with idempotency keys — the platform turns at-least-once delivery into effectively-once *outcomes* only when the steps cooperate. Treating "exactly-once" as a free platform guarantee is the most common way teams get double-charges in a system they believed was safe.
30
+
31
+ ### 3. Choose the engine by operational shape
32
+
33
+ The category spans distinct operational shapes, and the choice is dominated by the burden you can carry, not by popularity:
34
+
35
+ - **Self-hosted replay engines** — **Temporal** (a dedicated cluster, the most mature, proven at scale) and **Restate** (the same journal/replay model with a lighter footprint and HTTP-native, serverless-friendly ergonomics). Maximum power and flexibility; you operate (or pay for) the orchestrator.
36
+ - **Managed state-machine orchestrators** — **AWS Step Functions** and the equivalents on other clouds. Workflows are declared as JSON/ASL state machines rather than code, with deep native integration to cloud services and nothing to run. The trade is expressiveness: complex branching and loops are awkward in a declarative state machine, and you are bound to one cloud. The default when the work is gluing managed services together on a single cloud.
37
+ - **Embedded / library engines** — **DBOS** and the "just Postgres" approach (a queue via `SELECT … FOR UPDATE SKIP LOCKED` plus a checkpoint table). Durable execution as a library inside your existing process, reusing Postgres as the durability layer — no separate orchestrator, minimal code change. The low-footprint option for teams already on Postgres, with a real ceiling: every step is at least one write, so a hot workflow fanning out to thousands of children breaks first on Postgres contention — lock pressure on the status table, WAL pressure, autovacuum falling behind.
38
+
39
+ **Decision rule:** already deep in one cloud and mostly orchestrating its services → managed state machine. Already on Postgres, modest scale, want to avoid new infrastructure → embedded/library. Complex long-lived workflows, high scale, or cross-environment (hybrid/on-prem) → a self-hosted replay engine.
40
+
41
+ ### 4. It complements the event log, it does not replace it
42
+
43
+ A durable workflow and an event backbone (Kafka and friends) are complementary: the log decouples producers from consumers and is the source of truth for events; durable execution orchestrates stateful, multi-step work on top. Building long workflows on the raw log alone is significant custom machinery; reaching for a workflow engine where a single event handler suffices is overkill.
44
+
45
+ ### 5. Orchestration vs choreography is a deliberate decision
46
+
47
+ Choreography (services react to each other's events, no central coordinator) keeps services loosely coupled and scales naturally; its cost is that no single place knows the state of the whole process, which makes debugging and compensation hard. Orchestration (a durable workflow drives the steps) buys you central visibility, explicit compensation, and one place to reason about the flow, at the cost of a coordinator the flow depends on.
48
+
49
+ **Decision rule:** choose by coupling and the need for visibility, not by step count. Choreography when the steps are genuinely independent reactions and no one needs to ask "where is this process right now"; orchestration the moment you need compensation, branching, a process-level SLA, or an operator who can see and intervene. Step count is a symptom, not the criterion — a two-step flow that must compensate wants orchestration; a long chain of fire-and-forget reactions does not.
50
+
51
+ ### 6. Determinism is a replay constraint, and it lives in the control flow
52
+
53
+ Replay-based engines (Temporal, Restate) recover by re-executing the workflow function and replaying recorded results for steps already done. That imposes a hard contract: the workflow's **control flow** must be deterministic, so the same history always drives the same branches. The side effects themselves need not be deterministic — a query or API call can and should return fresh data each run; they require *idempotency or duplication tolerance*, not determinism. The bug this prevents is concrete: branch on a raw `now()` or an unguarded random value and a replay can take a different path than the original, double-charging or skipping a step. Engines supply deterministic primitives — recorded timestamps, UUIDs, random — precisely so unavoidable non-determinism becomes a durable, replayable step.
54
+
55
+ The constraint is engine-specific, not a universal law of the category. Checkpoint-based engines (DBOS, the Postgres approach) re-run the function and reuse recorded step outputs, so control-flow stability still matters but the rules are looser and differently shaped than full deterministic replay. **Decision rule:** know which model your engine uses before you write the workflow — the determinism contract is the single thing most likely to bite, and it differs between replay and checkpoint engines.
56
+
57
+ ### 7. Versioning running workflows is a first-class cost, not an afterthought
58
+
59
+ A durable workflow can be in flight for days or months, so a deploy can land *while executions are mid-flight* against the old code. For a replay engine, changing the workflow's shape — reordering steps, adding a branch before an existing one — breaks replay of in-flight executions with a non-determinism error. This is the operational tax of long-lived workflows, and it must be designed for from the first deploy, not discovered in an incident.
60
+
61
+ The two mechanisms are **patching** (guard the changed code path behind a version marker so old executions take the old branch and new ones take the new) and **worker versioning** (pin in-flight executions to the worker version they started on, and let new executions adopt new code). **Decision rule:** patch small, in-place changes to a workflow's logic; pin-and-drain whole workflow versions for larger reshapes. Either way, treat "can this change replay against everything currently running?" as a release gate. Embedded/checkpoint engines soften but do not remove this concern — a function that no longer matches the steps recorded for an in-flight run is still a hazard.
62
+
63
+ ### 8. It is the substrate for long-running agents
64
+
65
+ A long-running agent — plan, act, observe, pause for a human, resume — is a durable workflow: its loop is checkpointed so it survives failure and resumes rather than repeating expensive tool calls, and a human approval is modelled as a **durable interrupt** that can wait for hours or days without holding a thread. The agent loop is also where the determinism contract is easiest to violate (the model output is non-deterministic by nature), so the LLM call belongs in a recorded step, never inline in the control flow. This is the reliability backbone of [Agentic Systems](../ai-native/agentic-systems.md).
66
+
67
+ ## How we apply this
68
+
69
+ The architect names durable execution as the pattern when a process is long-running, multi-step, or must-complete, decides the orchestration/choreography split, and confirms the cost is warranted over an outbox or queue; the engineer skills implement the workflow, choose the concrete engine by operational shape, and own the determinism and versioning discipline. Durable execution is shared infrastructure the system rides, not per-process scaffolding.
70
+
71
+ - [Integration Patterns](integration-patterns.md) — the outbox/saga/idempotency patterns durable execution subsumes.
72
+ - [Agentic Systems](../ai-native/agentic-systems.md) — long-running agents are durable workflows.
73
+
74
+ ## Anti-patterns we reject
75
+
76
+ - **Hand-rolled durability.** A retry table, a state column, and a sweeper cron re-implementing what an engine provides — fragile and bespoke per team.
77
+ - **Workflow-as-code for everything.** A heavyweight engine where a single idempotent event handler or an outbox would do.
78
+ - **Assuming exactly-once side effects.** Trusting the platform to make a non-idempotent `POST` safe; the engine gives at-least-once execution, and only idempotent steps make the outcome effectively-once.
79
+ - **Non-deterministic workflows.** Branching on wall-clock time, unguarded randomness, or an inline LLM/API call, so replay diverges.
80
+ - **Deploying without a versioning plan.** Reshaping a workflow with executions in flight and breaking their replay — versioning is a release gate, not a cleanup task.
81
+ - **The event-sourced-monolith reflex.** Reaching for full orchestration when a simple two-step choreography is correct.
82
+ - **Holding a thread for a human.** Blocking on an approval instead of a durable interrupt that resumes on decision.
83
+
84
+ ## Further reading
85
+
86
+ - *Temporal*, *Restate*, *DBOS*, *AWS Step Functions* — durable execution engines spanning self-hosted replay, embedded-library, and managed state-machine shapes.
87
+ - *Life Beyond Distributed Transactions*, Pat Helland — the reasoning the outbox and durable workflows descend from.
88
+ - *Demystifying Determinism in Durable Execution*, Jack Vanlightly (2025) — why the constraint lives in control flow, not side effects, and how it differs across engines.
89
+ - *The Rise of the Durable Execution Engine* — durable execution alongside an event backbone.