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.
- package/CHANGELOG.md +823 -0
- package/LICENSE +21 -0
- package/README.md +44 -29
- package/bin/groundwork.js +1723 -0
- package/dist/src/generators/add-capability/generator.d.ts +8 -0
- package/dist/src/generators/add-capability/generator.js +60 -0
- package/dist/src/generators/add-capability/generator.js.map +1 -0
- package/dist/src/generators/cli-app/generator.d.ts +9 -0
- package/dist/src/generators/cli-app/generator.js +140 -0
- package/dist/src/generators/cli-app/generator.js.map +1 -0
- package/dist/src/generators/docs-site/generator.d.ts +5 -0
- package/dist/src/generators/docs-site/generator.js +441 -0
- package/dist/src/generators/docs-site/generator.js.map +1 -0
- package/dist/src/generators/electron-app/generator.d.ts +6 -0
- package/dist/src/generators/electron-app/generator.js +261 -0
- package/dist/src/generators/electron-app/generator.js.map +1 -0
- package/dist/src/generators/flutter-app/generator.d.ts +6 -0
- package/dist/src/generators/flutter-app/generator.js +314 -0
- package/dist/src/generators/flutter-app/generator.js.map +1 -0
- package/dist/src/generators/go-microservice/generator.d.ts +8 -0
- package/dist/src/generators/go-microservice/generator.js +232 -0
- package/dist/src/generators/go-microservice/generator.js.map +1 -0
- package/dist/src/generators/nextjs-app/generator.d.ts +8 -0
- package/dist/src/generators/nextjs-app/generator.js +294 -0
- package/dist/src/generators/nextjs-app/generator.js.map +1 -0
- package/dist/src/generators/python-microservice/generator.d.ts +13 -0
- package/dist/src/generators/python-microservice/generator.js +265 -0
- package/dist/src/generators/python-microservice/generator.js.map +1 -0
- package/dist/src/generators/shared/brand-tokens.d.ts +89 -0
- package/dist/src/generators/shared/brand-tokens.js +308 -0
- package/dist/src/generators/shared/brand-tokens.js.map +1 -0
- package/dist/src/generators/shared/capabilities.d.ts +101 -0
- package/dist/src/generators/shared/capabilities.js +279 -0
- package/dist/src/generators/shared/capabilities.js.map +1 -0
- package/dist/src/generators/shared/provenance.d.ts +2 -0
- package/dist/src/generators/shared/provenance.js +85 -0
- package/dist/src/generators/shared/provenance.js.map +1 -0
- package/dist/src/generators/shared/scaffold-helpers.d.ts +72 -0
- package/dist/src/generators/shared/scaffold-helpers.js +309 -0
- package/dist/src/generators/shared/scaffold-helpers.js.map +1 -0
- package/dist/src/generators/system-test-runner/generator.d.ts +23 -0
- package/dist/src/generators/system-test-runner/generator.js +173 -0
- package/dist/src/generators/system-test-runner/generator.js.map +1 -0
- package/dist/src/generators/workspace-dev-cli/generator.d.ts +7 -0
- package/dist/src/generators/workspace-dev-cli/generator.js +138 -0
- package/dist/src/generators/workspace-dev-cli/generator.js.map +1 -0
- package/generators.json +57 -0
- package/lib/repo-map/grammars/tree-sitter-c.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-cpp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-csharp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-dart.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-go.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-java.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-javascript.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-kotlin.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-lua.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-php.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-python.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-ruby.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-rust.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-scala.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-swift.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-tsx.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-typescript.wasm +0 -0
- package/lib/repo-map/index.js +386 -0
- package/lib/repo-map/languages.js +514 -0
- package/lib/repo-map/pagerank.js +59 -0
- package/migrations/README.md +60 -0
- package/migrations/_template/cli-migration.js +27 -0
- package/migrations/gw-bet-prose-redesign.js +105 -0
- package/migrations/gw-drop-test-manifest.js +37 -0
- package/migrations/gw-register-serena-mcp.js +42 -0
- package/migrations/gw-relocate-hidden-skills.js +40 -0
- package/migrations/gw-seed-config-toml.js +24 -0
- package/migrations/index.json +40 -0
- package/package.json +70 -6
- package/src/AGENTS.md +36 -0
- package/src/config/config.toml +30 -0
- package/src/config/groundwork-state.json +5 -0
- package/src/docs/llms.txt +72 -0
- package/src/docs/principles/ai-native/agent-native-systems.md +90 -0
- package/src/docs/principles/ai-native/agentic-systems.md +78 -0
- package/src/docs/principles/ai-native/ai-engineering.md +100 -0
- package/src/docs/principles/ai-native/ai-native-product.md +76 -0
- package/src/docs/principles/delivery/cost-engineering.md +89 -0
- package/src/docs/principles/delivery/day-2-operational-baseline.md +57 -0
- package/src/docs/principles/delivery/devex.md +88 -0
- package/src/docs/principles/delivery/platform.md +101 -0
- package/src/docs/principles/delivery/progressive-delivery.md +92 -0
- package/src/docs/principles/design/ai-native-design.md +73 -0
- package/src/docs/principles/design/design-foundations.md +80 -0
- package/src/docs/principles/design/design-systems-and-tokens.md +72 -0
- package/src/docs/principles/design/interaction-and-motion.md +69 -0
- package/src/docs/principles/design/layout-and-space.md +72 -0
- package/src/docs/principles/design/usability-and-ux.md +79 -0
- package/src/docs/principles/design/visual-design.md +84 -0
- package/src/docs/principles/foundations/code-craft.md +86 -0
- package/src/docs/principles/foundations/continuous-discovery.md +75 -0
- package/src/docs/principles/foundations/documentation.md +102 -0
- package/src/docs/principles/foundations/prioritization-and-appetite.md +78 -0
- package/src/docs/principles/foundations/product-engineering.md +90 -0
- package/src/docs/principles/foundations/product-risks.md +89 -0
- package/src/docs/principles/foundations/requirements-and-specs.md +80 -0
- package/src/docs/principles/foundations/success-metrics.md +66 -0
- package/src/docs/principles/foundations/testing.md +108 -0
- package/src/docs/principles/index.md +24 -0
- package/src/docs/principles/quality/accessibility.md +88 -0
- package/src/docs/principles/quality/observability.md +84 -0
- package/src/docs/principles/quality/performance.md +84 -0
- package/src/docs/principles/quality/privacy.md +92 -0
- package/src/docs/principles/quality/reliability.md +89 -0
- package/src/docs/principles/quality/security.md +78 -0
- package/src/docs/principles/stack/postgres.md +100 -0
- package/src/docs/principles/system-design/api-design.md +86 -0
- package/src/docs/principles/system-design/architecture-decisions.md +81 -0
- package/src/docs/principles/system-design/code-structure.md +104 -0
- package/src/docs/principles/system-design/data-engineering.md +87 -0
- package/src/docs/principles/system-design/durable-execution.md +89 -0
- package/src/docs/principles/system-design/evolutionary-architecture.md +81 -0
- package/src/docs/principles/system-design/identity-and-access.md +76 -0
- package/src/docs/principles/system-design/integration-patterns.md +84 -0
- package/src/docs/principles/system-design/real-time.md +83 -0
- package/src/docs/principles/system-design/surface-architecture.md +74 -0
- package/src/docs/ways-of-working/documentation.md +69 -0
- package/src/docs/ways-of-working/how-we-work.md +76 -0
- package/src/docs/ways-of-working/units-of-work.md +40 -0
- package/src/engineer-skills/groundwork-electron-engineer/SKILL.md +123 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/documentation.md +126 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/performance-and-reliability.md +80 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/process-model.md +94 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/security.md +107 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/testing-and-smoke.md +129 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/theming-and-tokens.md +74 -0
- package/src/engineer-skills/groundwork-electron-engineer/sync-anchor.md +22 -0
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +114 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/accessibility.md +92 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/architecture.md +189 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/data-and-contracts.md +136 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/documentation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/navigation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/performance-and-reliability.md +100 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/platform-channels.md +93 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/releases-and-distribution.md +84 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/security.md +96 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/state-management.md +166 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +160 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/theming-and-design-tokens.md +109 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/widgets-and-composition.md +123 -0
- package/src/engineer-skills/groundwork-flutter-engineer/sync-anchor.md +24 -0
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +174 -0
- package/src/engineer-skills/groundwork-go-engineer/references/api-design.md +82 -0
- package/src/engineer-skills/groundwork-go-engineer/references/architecture.md +42 -0
- package/src/engineer-skills/groundwork-go-engineer/references/capability-ports.md +50 -0
- package/src/engineer-skills/groundwork-go-engineer/references/code-craft-security.md +34 -0
- package/src/engineer-skills/groundwork-go-engineer/references/concurrency.md +108 -0
- package/src/engineer-skills/groundwork-go-engineer/references/documentation.md +130 -0
- package/src/engineer-skills/groundwork-go-engineer/references/go-services.md +77 -0
- package/src/engineer-skills/groundwork-go-engineer/references/http-handlers.md +172 -0
- package/src/engineer-skills/groundwork-go-engineer/references/implementation-patterns.md +156 -0
- package/src/engineer-skills/groundwork-go-engineer/references/integration-realtime-data.md +57 -0
- package/src/engineer-skills/groundwork-go-engineer/references/observability.md +49 -0
- package/src/engineer-skills/groundwork-go-engineer/references/postgres.md +41 -0
- package/src/engineer-skills/groundwork-go-engineer/references/reliability-performance.md +105 -0
- package/src/engineer-skills/groundwork-go-engineer/references/testing.md +201 -0
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +20 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +112 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/accessibility.md +111 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/architecture.md +323 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/data-fetching.md +458 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/documentation.md +324 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/error-boundaries.md +383 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/mutations-and-forms.md +396 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/observability.md +48 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/performance-and-deployment.md +947 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/routing-and-navigation.md +405 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/security.md +131 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/server-components.md +394 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/tailwind-and-styling.md +134 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/testing.md +491 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/type-system.md +368 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/ux-principles.md +230 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +16 -0
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +199 -0
- package/src/engineer-skills/groundwork-python-engineer/references/api-standards.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/architecture.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/async-patterns.md +103 -0
- package/src/engineer-skills/groundwork-python-engineer/references/capability-ports.md +44 -0
- package/src/engineer-skills/groundwork-python-engineer/references/database.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/documentation-mcp.md +167 -0
- package/src/engineer-skills/groundwork-python-engineer/references/implementation-patterns.md +166 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-pipelines.md +119 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-systems-ai-engineering.md +74 -0
- package/src/engineer-skills/groundwork-python-engineer/references/observability.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/resilience.md +126 -0
- package/src/engineer-skills/groundwork-python-engineer/references/security.md +148 -0
- package/src/engineer-skills/groundwork-python-engineer/references/testing.md +216 -0
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +20 -0
- package/src/generators/add-capability/generator.ts +70 -0
- package/src/generators/add-capability/schema.json +30 -0
- package/src/generators/capabilities/llm/capability.json +28 -0
- package/src/generators/capabilities/llm/providers/anthropic/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/python/src/__packageName__/adapters/llm.py.template +61 -0
- package/src/generators/capabilities/llm/providers/local/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/local/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/local/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/localai/footprint.json +29 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/none/footprint.json +9 -0
- package/src/generators/capabilities/llm/providers/none/stacks/go/internal/llm/llm.go.template +35 -0
- package/src/generators/capabilities/llm/providers/none/stacks/python/src/__packageName__/adapters/llm.py.template +25 -0
- package/src/generators/capabilities/llm/providers/ollama/footprint.json +20 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/openai/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/go/internal/llm/llm.go.template +98 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/python/src/__packageName__/adapters/llm.py.template +60 -0
- package/src/generators/capabilities/llm/stacks/go/internal/core/service/llm.go.template +12 -0
- package/src/generators/capabilities/llm/stacks/go/internal/llm/llm_test.go.template +33 -0
- package/src/generators/capabilities/llm/stacks/python/src/__packageName__/core/llm.py.template +15 -0
- package/src/generators/capabilities/llm/stacks/python/tests/contracts/test_llm.py.template +37 -0
- package/src/generators/cli-app/files/README.md.template +76 -0
- package/src/generators/cli-app/files/build.mjs.template +15 -0
- package/src/generators/cli-app/files/package.json.template +21 -0
- package/src/generators/cli-app/files/src/cli.ts.template +67 -0
- package/src/generators/cli-app/files/src/commands/hello.ts.template +17 -0
- package/src/generators/cli-app/files/src/commands/status.ts.template +23 -0
- package/src/generators/cli-app/files/src/core/client.test.ts.template +80 -0
- package/src/generators/cli-app/files/src/core/client.ts.template +64 -0
- package/src/generators/cli-app/files/src/registry.test.ts.template +35 -0
- package/src/generators/cli-app/files/src/registry.ts.template +31 -0
- package/src/generators/cli-app/files/tsconfig.json.template +16 -0
- package/src/generators/cli-app/files/tsconfig.test.json.template +11 -0
- package/src/generators/cli-app/generator.ts +138 -0
- package/src/generators/cli-app/schema.json +24 -0
- package/src/generators/docs-site/files/.gitignore.ejs +40 -0
- package/src/generators/docs-site/files/app/docs/__slug__/page.tsx +101 -0
- package/src/generators/docs-site/files/app/docs/layout.tsx +14 -0
- package/src/generators/docs-site/files/app/docs.css +43 -0
- package/src/generators/docs-site/files/app/layout.tsx +24 -0
- package/src/generators/docs-site/files/app/page.tsx +135 -0
- package/src/generators/docs-site/files/app/source.ts +8 -0
- package/src/generators/docs-site/files/components/mermaid.tsx +67 -0
- package/src/generators/docs-site/files/next.config.mjs +10 -0
- package/src/generators/docs-site/files/package.json +32 -0
- package/src/generators/docs-site/files/pnpm-workspace.yaml +7 -0
- package/src/generators/docs-site/files/postcss.config.mjs +6 -0
- package/src/generators/docs-site/files/source.config.ts +77 -0
- package/src/generators/docs-site/files/tailwind.config.js +10 -0
- package/src/generators/docs-site/files/tsconfig.json +27 -0
- package/src/generators/docs-site/generator.ts +476 -0
- package/src/generators/docs-site/schema.json +17 -0
- package/src/generators/electron-app/docs/principles/stack/electron/index.md +49 -0
- package/src/generators/electron-app/docs/principles/stack/electron/ipc-contracts.md +71 -0
- package/src/generators/electron-app/docs/principles/stack/electron/packaging-and-updates.md +59 -0
- package/src/generators/electron-app/docs/principles/stack/electron/process-model.md +53 -0
- package/src/generators/electron-app/docs/principles/stack/electron/security.md +70 -0
- package/src/generators/electron-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/electron-app/files/.gitignore.template +20 -0
- package/src/generators/electron-app/files/README.md.template +125 -0
- package/src/generators/electron-app/files/electron.vite.config.ts +31 -0
- package/src/generators/electron-app/files/eslint.config.mjs +92 -0
- package/src/generators/electron-app/files/forge.config.ts.template +44 -0
- package/src/generators/electron-app/files/package.json.template +54 -0
- package/src/generators/electron-app/files/playwright.config.ts +18 -0
- package/src/generators/electron-app/files/project.json.template +65 -0
- package/src/generators/electron-app/files/src/main/core-client.test.ts +81 -0
- package/src/generators/electron-app/files/src/main/core-client.ts +55 -0
- package/src/generators/electron-app/files/src/main/index.ts +157 -0
- package/src/generators/electron-app/files/src/main/ipc.ts +52 -0
- package/src/generators/electron-app/files/src/main/policy.test.ts +71 -0
- package/src/generators/electron-app/files/src/main/policy.ts +73 -0
- package/src/generators/electron-app/files/src/preload/index.ts +23 -0
- package/src/generators/electron-app/files/src/renderer/index.html.template +20 -0
- package/src/generators/electron-app/files/src/renderer/src/App.test.tsx +61 -0
- package/src/generators/electron-app/files/src/renderer/src/App.tsx.template +43 -0
- package/src/generators/electron-app/files/src/renderer/src/assets/main.css +40 -0
- package/src/generators/electron-app/files/src/renderer/src/env.d.ts +14 -0
- package/src/generators/electron-app/files/src/renderer/src/main.tsx +25 -0
- package/src/generators/electron-app/files/src/shared/ipc.ts +54 -0
- package/src/generators/electron-app/files/tests/smoke/app.spec.ts.template +133 -0
- package/src/generators/electron-app/files/tool/electron_exec.sh.template +83 -0
- package/src/generators/electron-app/files/tsconfig.json +7 -0
- package/src/generators/electron-app/files/tsconfig.node.json +27 -0
- package/src/generators/electron-app/files/tsconfig.web.json +22 -0
- package/src/generators/electron-app/files/vitest.config.ts +32 -0
- package/src/generators/electron-app/files/vitest.setup.ts +1 -0
- package/src/generators/electron-app/generator.ts +288 -0
- package/src/generators/electron-app/schema.json +23 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/architecture.md +78 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/index.md +38 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/platform-channels.md +51 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/releases-and-distribution.md +59 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/state-management.md +85 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/testing.md +86 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/widgets-and-composition.md +69 -0
- package/src/generators/flutter-app/files/.gitignore.template +30 -0
- package/src/generators/flutter-app/files/README.md.template +100 -0
- package/src/generators/flutter-app/files/analysis_options.yaml.template +18 -0
- package/src/generators/flutter-app/files/integration_test/app_test.dart.template +64 -0
- package/src/generators/flutter-app/files/lib/app.dart.template +24 -0
- package/src/generators/flutter-app/files/lib/config/app_config.dart +15 -0
- package/src/generators/flutter-app/files/lib/data/repositories/status_repository.dart +36 -0
- package/src/generators/flutter-app/files/lib/data/services/api_client.dart +71 -0
- package/src/generators/flutter-app/files/lib/domain/models/health_status.dart +23 -0
- package/src/generators/flutter-app/files/lib/main.dart +11 -0
- package/src/generators/flutter-app/files/lib/router.dart +23 -0
- package/src/generators/flutter-app/files/lib/ui/core/theme/app_theme.dart +110 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view.dart +89 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view_model.dart.template +38 -0
- package/src/generators/flutter-app/files/project.json.template +51 -0
- package/src/generators/flutter-app/files/pubspec.yaml.template +47 -0
- package/src/generators/flutter-app/files/test/api_client_test.dart.template +63 -0
- package/src/generators/flutter-app/files/test/fakes/fake_status_repository.dart.template +19 -0
- package/src/generators/flutter-app/files/test/home_view_test.dart.template +58 -0
- package/src/generators/flutter-app/files/tool/flutter_exec.sh.template +60 -0
- package/src/generators/flutter-app/generator.ts +362 -0
- package/src/generators/flutter-app/schema.json +23 -0
- package/src/generators/go-microservice/docs/principles/stack/go/concurrency.md +123 -0
- package/src/generators/go-microservice/docs/principles/stack/go/index.md +70 -0
- package/src/generators/go-microservice/docs/principles/stack/go/testing.md +168 -0
- package/src/generators/go-microservice/files/.air.toml.template +38 -0
- package/src/generators/go-microservice/files/.env.template +4 -0
- package/src/generators/go-microservice/files/.golangci.yml.template +82 -0
- package/src/generators/go-microservice/files/Dockerfile.dev.template +12 -0
- package/src/generators/go-microservice/files/asyncapi-pubsub.yaml.template +33 -0
- package/src/generators/go-microservice/files/asyncapi-ws.yaml.template +34 -0
- package/src/generators/go-microservice/files/cmd/api/main.go.template +149 -0
- package/src/generators/go-microservice/files/cmd/api/main_test.go.template +99 -0
- package/src/generators/go-microservice/files/cmd/worker/cleanup/main.go.template +39 -0
- package/src/generators/go-microservice/files/db/schema.sql.template +24 -0
- package/src/generators/go-microservice/files/go.mod.template +39 -0
- package/src/generators/go-microservice/files/internal/config/config.go.template +52 -0
- package/src/generators/go-microservice/files/internal/config/otel.go.template +93 -0
- package/src/generators/go-microservice/files/internal/core/domain/errors.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/domain/model.go.template +28 -0
- package/src/generators/go-microservice/files/internal/core/domain/user.go.template +13 -0
- package/src/generators/go-microservice/files/internal/core/pagination.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/service/app_service.go.template +79 -0
- package/src/generators/go-microservice/files/internal/core/service/event_hub.go.template +9 -0
- package/src/generators/go-microservice/files/internal/core/service/message_queue.go.template +10 -0
- package/src/generators/go-microservice/files/internal/core/service/outbox_repository.go.template +31 -0
- package/src/generators/go-microservice/files/internal/core/service/repository.go.template +23 -0
- package/src/generators/go-microservice/files/internal/core/service/user_repository.go.template +15 -0
- package/src/generators/go-microservice/files/internal/core/service/user_service.go.template +43 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/app_handler.go.template +108 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/auth_middleware_test.go.template +52 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook.go.template +202 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook_test.go.template +82 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/health_handler.go.template +80 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware.go.template +87 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware_test.go.template +76 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/repository.go.template +37 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_auth.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_loadshed.go.template +38 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_logging.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_ratelimit.go.template +48 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_test.go.template +81 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/router.go.template +105 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/types.go.template +70 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/websocket_handler.go.template +39 -0
- package/src/generators/go-microservice/files/internal/httpclient/http_client.go.template +87 -0
- package/src/generators/go-microservice/files/internal/kafka/kafka.go.template +34 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres.go.template +195 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres_test.go.template +156 -0
- package/src/generators/go-microservice/files/internal/postgres/user_repository.go.template +56 -0
- package/src/generators/go-microservice/files/internal/pubsub/gcp_pubsub.go.template +35 -0
- package/src/generators/go-microservice/files/internal/websocket/client.go.template +151 -0
- package/src/generators/go-microservice/files/internal/websocket/hub.go.template +261 -0
- package/src/generators/go-microservice/files/scripts/apply-schema.sh.template +21 -0
- package/src/generators/go-microservice/files/tools/tools.go.template +10 -0
- package/src/generators/go-microservice/generator.ts +240 -0
- package/src/generators/go-microservice/schema.json +63 -0
- package/src/generators/nextjs-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/nextjs-app/files/.dockerignore.template +7 -0
- package/src/generators/nextjs-app/files/.env.example.template +24 -0
- package/src/generators/nextjs-app/files/.gitignore.template +5 -0
- package/src/generators/nextjs-app/files/Dockerfile +53 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-in/__sign-in__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-up/__sign-up__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/api/config/route.ts.template +39 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.test.ts +15 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.ts +5 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.test.ts.template +55 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.ts.template +126 -0
- package/src/generators/nextjs-app/files/app/error.tsx +39 -0
- package/src/generators/nextjs-app/files/app/global-error.tsx +68 -0
- package/src/generators/nextjs-app/files/app/globals.css +105 -0
- package/src/generators/nextjs-app/files/app/layout.tsx +59 -0
- package/src/generators/nextjs-app/files/app/loading.tsx +13 -0
- package/src/generators/nextjs-app/files/app/not-found.tsx +30 -0
- package/src/generators/nextjs-app/files/app/page.tsx +20 -0
- package/src/generators/nextjs-app/files/components/providers/default.tsx +19 -0
- package/src/generators/nextjs-app/files/components/providers/production.tsx +32 -0
- package/src/generators/nextjs-app/files/components/providers/telemetry.tsx +76 -0
- package/src/generators/nextjs-app/files/components/render-smoke.test.tsx +29 -0
- package/src/generators/nextjs-app/files/components/theme-provider.tsx +11 -0
- package/src/generators/nextjs-app/files/components.json +21 -0
- package/src/generators/nextjs-app/files/eslint.config.mjs +120 -0
- package/src/generators/nextjs-app/files/hooks/use-toast.ts +7 -0
- package/src/generators/nextjs-app/files/instrumentation.ts +90 -0
- package/src/generators/nextjs-app/files/lib/api/fetcher.ts.template +130 -0
- package/src/generators/nextjs-app/files/lib/config.ts +21 -0
- package/src/generators/nextjs-app/files/lib/logger.ts +29 -0
- package/src/generators/nextjs-app/files/lib/schemas/index.ts +19 -0
- package/src/generators/nextjs-app/files/lib/utils.ts +6 -0
- package/src/generators/nextjs-app/files/next.config.mjs +9 -0
- package/src/generators/nextjs-app/files/package.json +70 -0
- package/src/generators/nextjs-app/files/postcss.config.mjs +8 -0
- package/src/generators/nextjs-app/files/proxy.test.ts.template +30 -0
- package/src/generators/nextjs-app/files/proxy.ts +31 -0
- package/src/generators/nextjs-app/files/public/.gitkeep +1 -0
- package/src/generators/nextjs-app/files/tsconfig.json +42 -0
- package/src/generators/nextjs-app/files/vitest.config.mts +15 -0
- package/src/generators/nextjs-app/files/vitest.setup.ts +7 -0
- package/src/generators/nextjs-app/generator.ts +307 -0
- package/src/generators/nextjs-app/schema.json +44 -0
- package/src/generators/python-microservice/docs/principles/stack/python/async.md +168 -0
- package/src/generators/python-microservice/docs/principles/stack/python/documentation.md +240 -0
- package/src/generators/python-microservice/docs/principles/stack/python/mcp.md +147 -0
- package/src/generators/python-microservice/docs/principles/stack/python/resilience.md +193 -0
- package/src/generators/python-microservice/docs/principles/stack/python/testing.md +322 -0
- package/src/generators/python-microservice/files/.env.example.template +30 -0
- package/src/generators/python-microservice/files/Dockerfile.template +36 -0
- package/src/generators/python-microservice/files/db/schema.sql.template +19 -0
- package/src/generators/python-microservice/files/pyproject.toml.template +76 -0
- package/src/generators/python-microservice/files/scripts/apply-schema.sh.template +25 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/comfyui.py.template +87 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/config.py.template +48 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/database.py.template +21 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/message_queue.py.template +29 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/repository.py.template +130 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/telemetry.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/websocket_hub.py.template +36 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/entities.py.template +22 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/exceptions.py.template +43 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/ports.py.template +42 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/service/example_service.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/dependencies.py.template +50 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/middleware.py.template +131 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/router.py.template +37 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/websocket_handler.py.template +20 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/cleanup.py.template +35 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/worker.py.template +28 -0
- package/src/generators/python-microservice/files/src/__packageName__/main.py.template +108 -0
- package/src/generators/python-microservice/files/tests/test_main.py.template +74 -0
- package/src/generators/python-microservice/files/tests/test_middleware.py.template +109 -0
- package/src/generators/python-microservice/files/tests/test_worker.py.template +16 -0
- package/src/generators/python-microservice/generator.ts +286 -0
- package/src/generators/python-microservice/schema.json +86 -0
- package/src/generators/shared/brand-tokens.ts +301 -0
- package/src/generators/shared/capabilities.ts +349 -0
- package/src/generators/shared/provenance.ts +61 -0
- package/src/generators/shared/scaffold-helpers.ts +309 -0
- package/src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md +20 -0
- package/src/generators/system-test-runner/files/tests/bets/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/bets/_archive/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/conftest.py.template +503 -0
- package/src/generators/system-test-runner/files/tests/pyproject.toml.template +20 -0
- package/src/generators/system-test-runner/files/tests/system/pages/__init__.py.template +9 -0
- package/src/generators/system-test-runner/files/tests/system/pages/base_page.py.template +36 -0
- package/src/generators/system-test-runner/files/tests/system/test_a11y_smoke.py.template +132 -0
- package/src/generators/system-test-runner/files/tests/system/test_contract_conformance.py.template +140 -0
- package/src/generators/system-test-runner/files/tests/system/test_layout_geometry.py.template +109 -0
- package/src/generators/system-test-runner/files/tests/system/test_render_smoke.py.template +257 -0
- package/src/generators/system-test-runner/files/tests/system/test_system.py.template +158 -0
- package/src/generators/system-test-runner/files/tests/system/test_token_conformance.py.template +206 -0
- package/src/generators/system-test-runner/files/tests/system/test_visual_regression.py.template +104 -0
- package/src/generators/system-test-runner/generator.ts +196 -0
- package/src/generators/system-test-runner/schema.json +24 -0
- package/src/generators/workspace-dev-cli/cli-src/build.mjs +42 -0
- package/src/generators/workspace-dev-cli/cli-src/dist/dev-bundle.js +2168 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/bet.ts +442 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/completion.ts +87 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/doctor.ts +139 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/lifecycle.ts +548 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/quality.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/surface.ts +214 -0
- package/src/generators/workspace-dev-cli/cli-src/src/index.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/registry.ts +194 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/color.ts +130 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/render.ts +158 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/tokens.ts +122 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/context.ts +43 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/extensions.ts +99 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/paths.ts +46 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/proc.ts +106 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/prompt.ts +108 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/runners.ts +70 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/services.ts +221 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/version.ts +21 -0
- package/src/generators/workspace-dev-cli/cli-src/tsconfig.json +16 -0
- package/src/generators/workspace-dev-cli/files/.agents/skills/workspace-cli/SKILL.md.template +74 -0
- package/src/generators/workspace-dev-cli/files/dev.template +16 -0
- package/src/generators/workspace-dev-cli/files/docker-compose.yml.template +20 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/milestone-test.pytmpl.template +46 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/slice-test.pytmpl.template +38 -0
- package/src/generators/workspace-dev-cli/generator.ts +136 -0
- package/src/generators/workspace-dev-cli/schema.json +22 -0
- package/src/hidden-skills/code-intelligence.md +135 -0
- package/src/hidden-skills/groundwork-architect/SKILL.md +114 -0
- package/src/hidden-skills/groundwork-architect/references/agentic-systems.md +44 -0
- package/src/hidden-skills/groundwork-architect/references/ai-native-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/api-and-contracts.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/core-and-boundaries.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/data-architecture.md +33 -0
- package/src/hidden-skills/groundwork-architect/references/decision-records.md +34 -0
- package/src/hidden-skills/groundwork-architect/references/durable-execution.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/evolutionary-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/identity-and-access.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/integration-patterns.md +39 -0
- package/src/hidden-skills/groundwork-architect/references/observability.md +36 -0
- package/src/hidden-skills/groundwork-architect/references/performance-and-scale.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/platform-and-delivery.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/realtime-and-async.md +28 -0
- package/src/hidden-skills/groundwork-architect/references/reliability.md +31 -0
- package/src/hidden-skills/groundwork-architect/references/security-and-trust.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/surface-architecture.md +40 -0
- package/src/hidden-skills/groundwork-architect/sync-anchor.md +34 -0
- package/src/hidden-skills/groundwork-architecture/architecture-template.md +50 -0
- package/src/hidden-skills/groundwork-architecture/instructions.md +139 -0
- package/src/hidden-skills/groundwork-architecture/phases/01-context-ingestion.md +18 -0
- package/src/hidden-skills/groundwork-architecture/phases/02-technical-constraints.md +27 -0
- package/src/hidden-skills/groundwork-architecture/phases/03-service-design.md +19 -0
- package/src/hidden-skills/groundwork-architecture/phases/04-data-flow-communication.md +23 -0
- package/src/hidden-skills/groundwork-architecture/phases/05-component-boundaries-contracts.md +17 -0
- package/src/hidden-skills/groundwork-architecture/phases/06-draft-review-present.md +38 -0
- package/src/hidden-skills/groundwork-architecture/phases/07-commit.md +33 -0
- package/src/hidden-skills/groundwork-architecture/templates/architecture-cache.md +43 -0
- package/src/hidden-skills/groundwork-architecture-extract/instructions.md +163 -0
- package/src/hidden-skills/groundwork-architecture-extract/templates/architecture-extract-cache.md +21 -0
- package/src/hidden-skills/groundwork-bet/briefs/acceptance-auditor.md +68 -0
- package/src/hidden-skills/groundwork-bet/briefs/blind-reviewer.md +56 -0
- package/src/hidden-skills/groundwork-bet/briefs/coverage-auditor.md +95 -0
- package/src/hidden-skills/groundwork-bet/briefs/edge-case-tracer.md +64 -0
- package/src/hidden-skills/groundwork-bet/briefs/experience-auditor.md +83 -0
- package/src/hidden-skills/groundwork-bet/briefs/slice-worker.md +257 -0
- package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +115 -0
- package/src/hidden-skills/groundwork-bet/templates/change-proposal.md +38 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/meta.json +4 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/milestone-index.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/pitch.md +45 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/01-ui-design.md +51 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/02-data-flows.md +36 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/03-api-design.md +90 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/04-data-design.md +29 -0
- package/src/hidden-skills/groundwork-bet/workflows/01-discovery.md +200 -0
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +178 -0
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +242 -0
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +226 -0
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +210 -0
- package/src/hidden-skills/groundwork-design-system/instructions.md +125 -0
- package/src/hidden-skills/groundwork-design-system/templates/brand-tokens.md +182 -0
- package/src/hidden-skills/groundwork-design-system/templates/design-system-cache.md +64 -0
- package/src/hidden-skills/groundwork-design-system/tracks/_foundation.md +136 -0
- package/src/hidden-skills/groundwork-design-system/tracks/agentic-protocol.md +269 -0
- package/src/hidden-skills/groundwork-design-system/tracks/cli.md +355 -0
- package/src/hidden-skills/groundwork-design-system/tracks/graphical-ui.md +330 -0
- package/src/hidden-skills/groundwork-design-system-extract/instructions.md +124 -0
- package/src/hidden-skills/groundwork-design-system-extract/templates/design-system-extract-cache.md +19 -0
- package/src/hidden-skills/groundwork-designer/SKILL.md +108 -0
- package/src/hidden-skills/groundwork-designer/references/accessibility.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/ai-native-design.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/design-review.md +29 -0
- package/src/hidden-skills/groundwork-designer/references/design-systems-and-tokens.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/interaction-and-motion.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/layout-and-space.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/usability-and-ux.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/visual-craft.md +49 -0
- package/src/hidden-skills/groundwork-designer/sync-anchor.md +20 -0
- package/src/hidden-skills/groundwork-doc-sync/instructions.md +100 -0
- package/src/hidden-skills/groundwork-elicit/instructions.md +66 -0
- package/src/hidden-skills/groundwork-elicit/methods.md +65 -0
- package/src/hidden-skills/groundwork-infra-adopt/instructions.md +168 -0
- package/src/hidden-skills/groundwork-infra-adopt/templates/infra-adopt-cache.md +21 -0
- package/src/hidden-skills/groundwork-mvp/instructions.md +223 -0
- package/src/hidden-skills/groundwork-mvp/templates/mvp-cache.md +9 -0
- package/src/hidden-skills/groundwork-patch/instructions.md +40 -0
- package/src/hidden-skills/groundwork-persona/instructions.md +65 -0
- package/src/hidden-skills/groundwork-product/SKILL.md +102 -0
- package/src/hidden-skills/groundwork-product/references/ai-native-product.md +45 -0
- package/src/hidden-skills/groundwork-product/references/discovery-and-opportunity.md +38 -0
- package/src/hidden-skills/groundwork-product/references/product-risks.md +52 -0
- package/src/hidden-skills/groundwork-product/references/requirements-and-specs.md +39 -0
- package/src/hidden-skills/groundwork-product/references/scope-and-sequencing.md +35 -0
- package/src/hidden-skills/groundwork-product/references/shaping-and-appetite.md +48 -0
- package/src/hidden-skills/groundwork-product/references/success-metrics-and-signals.md +37 -0
- package/src/hidden-skills/groundwork-product/sync-anchor.md +19 -0
- package/src/hidden-skills/groundwork-product-brief/instructions.md +231 -0
- package/src/hidden-skills/groundwork-product-brief-extract/instructions.md +139 -0
- package/src/hidden-skills/groundwork-product-brief-extract/templates/product-brief-extract-cache.md +17 -0
- package/src/hidden-skills/groundwork-review/checklists/architecture.md +93 -0
- package/src/hidden-skills/groundwork-review/checklists/bet-pitch.md +94 -0
- package/src/hidden-skills/groundwork-review/checklists/decomposition.md +135 -0
- package/src/hidden-skills/groundwork-review/checklists/design-system.md +85 -0
- package/src/hidden-skills/groundwork-review/checklists/domain-entity.md +66 -0
- package/src/hidden-skills/groundwork-review/checklists/implementation-readiness.md +47 -0
- package/src/hidden-skills/groundwork-review/checklists/infrastructure.md +68 -0
- package/src/hidden-skills/groundwork-review/checklists/maturity.md +71 -0
- package/src/hidden-skills/groundwork-review/checklists/product-brief.md +69 -0
- package/src/hidden-skills/groundwork-review/checklists/technical-design.md +112 -0
- package/src/hidden-skills/groundwork-review/instructions.md +181 -0
- package/src/hidden-skills/groundwork-scaffold/instructions.md +254 -0
- package/src/hidden-skills/groundwork-scaffold/phases/01-ingestion-service-mapping.md +87 -0
- package/src/hidden-skills/groundwork-scaffold/phases/02-scaffolding-execution.md +15 -0
- package/src/hidden-skills/groundwork-scaffold/phases/03-service-documentation-api-stubs.md +100 -0
- package/src/hidden-skills/groundwork-scaffold/phases/04-infrastructure-verification.md +17 -0
- package/src/hidden-skills/groundwork-scaffold/phases/05-draft-review.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/phases/06-commit.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/templates/scaffold-cache.md +23 -0
- package/src/hidden-skills/groundwork-scan/instructions.md +164 -0
- package/src/hidden-skills/groundwork-scan/references/digest-schema.md +66 -0
- package/src/hidden-skills/groundwork-scan/references/exclusions.md +44 -0
- package/src/hidden-skills/groundwork-scan/templates/architecture-findings.md +42 -0
- package/src/hidden-skills/groundwork-scan/templates/design-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/overview.md +26 -0
- package/src/hidden-skills/groundwork-scan/templates/product-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/scan-state.json +19 -0
- package/src/hidden-skills/groundwork-stack-forge/instructions.md +150 -0
- package/src/hidden-skills/groundwork-stack-forge/references/authoring-engineer-skills.md +107 -0
- package/src/hidden-skills/groundwork-surface-activation/instructions.md +138 -0
- package/src/hidden-skills/groundwork-update/briefs/reconcile-worker.md +196 -0
- package/src/hidden-skills/groundwork-update/instructions.md +200 -0
- package/src/hidden-skills/groundwork-writer/SKILL.md +278 -0
- package/src/hidden-skills/maturity-model.md +125 -0
- package/src/hidden-skills/operating-contract.md +400 -0
- package/src/hidden-skills/repo-map-schema.md +90 -0
- package/src/hidden-skills/templates/adr.md +57 -0
- package/src/hidden-skills/templates/capability-ports.md +71 -0
- package/src/hidden-skills/templates/discovery-notes.md +33 -0
- package/src/hidden-skills/templates/domain-entity.md +80 -0
- package/src/hidden-skills/templates/gap-ledger.md +21 -0
- package/src/hidden-skills/templates/handoff.md +37 -0
- package/src/hidden-skills/templates/maturity.md +39 -0
- package/src/hidden-skills/templates/surfaces.md +207 -0
- package/src/skills/groundwork-check/SKILL.md +56 -0
- package/src/skills/groundwork-check/instructions.md +70 -0
- package/src/skills/groundwork-orchestrator/SKILL.md +176 -0
- package/src/skills/groundwork-orchestrator/workflow-index.md +50 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Testing
|
|
3
|
+
description: Honeycomb testing for Python microservices — service-perimeter tests as the default, unit tests reserved for complex isolated logic.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-05-26
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Testing
|
|
9
|
+
|
|
10
|
+
## TL;DR
|
|
11
|
+
|
|
12
|
+
We test from the perimeter of the service inward. A request goes in through the HTTP entrypoint, traverses real service and provider logic, and comes back out — with real infrastructure running in containers. Unit tests are reserved for logic so complex it earns isolation. Most tests never see a mock.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The Model: Why Honeycomb, Not Pyramid
|
|
17
|
+
|
|
18
|
+
The classical test pyramid (many unit tests → some integration tests → few E2E tests) made sense when running a database in a test was expensive and slow. That constraint no longer exists. Testcontainers spins up a real Postgres or Pub/Sub emulator in seconds, and the confidence it provides is categorically different from any mock.
|
|
19
|
+
|
|
20
|
+
The failure mode of a mock-heavy suite is insidious: tests pass while production breaks. Mocked adapters drift from real behaviour. SQL queries are never exercised. Serialisation boundaries are never crossed. The suite is green and the system is broken.
|
|
21
|
+
|
|
22
|
+
The **honeycomb model** inverts the priority:
|
|
23
|
+
|
|
24
|
+
- **Service tests are the default.** The bulk of test coverage comes from tests that exercise the full vertical slice — HTTP in, real infrastructure out.
|
|
25
|
+
- **Unit tests are reserved.** Complex isolated algorithms, domain computations, and pure validation logic earn a unit test. Everything else does not.
|
|
26
|
+
- **System tests are minimal.** A bootstrap test verifies the DI container wires correctly. A small number of golden-path tests exercise the service end-to-end against a live stack.
|
|
27
|
+
|
|
28
|
+
This gives us a suite where a passing run is a meaningful signal. The bugs we care about — boundary mismatches, SQL correctness, serialisation errors, provider contract violations — are caught before they reach production.
|
|
29
|
+
|
|
30
|
+
This is the Python idiom of the framework testing canon (`docs/principles/foundations/testing.md`); the canon is the parent principle and wins on any disagreement.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## The Three Tiers
|
|
35
|
+
|
|
36
|
+
### Tier 1 — Service Tests (the default)
|
|
37
|
+
|
|
38
|
+
A service test drives the FastAPI app through its HTTP interface using `httpx.AsyncClient` with `ASGITransport`. Infrastructure dependencies (Pub/Sub, storage, databases) run in containers. Provider dependencies that cannot be containerised are replaced at the FastAPI dependency boundary using `dependency_overrides` — not mocked in the interior of the code.
|
|
39
|
+
|
|
40
|
+
This is the tier where most new tests belong. If you are testing a use case, an endpoint, a pipeline step, or a provider interaction with infrastructure — write a service test.
|
|
41
|
+
|
|
42
|
+
**Structure:**
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
tests/
|
|
46
|
+
integration/
|
|
47
|
+
entrypoints/ # HTTP-level service tests
|
|
48
|
+
providers/ # Provider tests against real containers or live APIs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Fixture setup — app + async client:**
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# tests/integration/conftest.py
|
|
55
|
+
import pytest
|
|
56
|
+
import httpx
|
|
57
|
+
from app.main import create_app
|
|
58
|
+
|
|
59
|
+
@pytest.fixture(scope="session")
|
|
60
|
+
def app():
|
|
61
|
+
return create_app()
|
|
62
|
+
|
|
63
|
+
@pytest.fixture
|
|
64
|
+
async def client(app):
|
|
65
|
+
async with httpx.AsyncClient(
|
|
66
|
+
transport=httpx.ASGITransport(app=app),
|
|
67
|
+
base_url="http://test",
|
|
68
|
+
) as c:
|
|
69
|
+
yield c
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Fixture setup — containerised infrastructure:**
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# tests/integration/conftest.py (continued)
|
|
76
|
+
import pytest
|
|
77
|
+
from testcontainers.postgres import PostgresContainer
|
|
78
|
+
from testcontainers.google import PubSubContainer
|
|
79
|
+
|
|
80
|
+
@pytest.fixture(scope="session")
|
|
81
|
+
def postgres():
|
|
82
|
+
with PostgresContainer("postgres:16-alpine") as container:
|
|
83
|
+
yield container
|
|
84
|
+
|
|
85
|
+
@pytest.fixture(scope="session")
|
|
86
|
+
def pubsub():
|
|
87
|
+
with PubSubContainer() as container:
|
|
88
|
+
yield container
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Scope containers at `session` when startup cost is high. Reset state between tests using transactions or truncation, not by restarting the container.
|
|
92
|
+
|
|
93
|
+
**Replacing non-containerisable providers:**
|
|
94
|
+
|
|
95
|
+
When a provider calls a live third-party API, replace it at the FastAPI boundary — not by patching the internals:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# tests/integration/entrypoints/test_transcribe_api.py
|
|
99
|
+
import pytest
|
|
100
|
+
from unittest.mock import AsyncMock
|
|
101
|
+
from app.entrypoints.api.dependencies import get_transcriber
|
|
102
|
+
from app.core.domain.models import Transcript
|
|
103
|
+
|
|
104
|
+
@pytest.fixture
|
|
105
|
+
def fake_transcriber():
|
|
106
|
+
port = AsyncMock()
|
|
107
|
+
port.transcribe.return_value = Transcript(...)
|
|
108
|
+
return port
|
|
109
|
+
|
|
110
|
+
async def test_transcribe_returns_structured_result(client, app, fake_transcriber):
|
|
111
|
+
app.dependency_overrides[get_transcriber] = lambda: fake_transcriber
|
|
112
|
+
|
|
113
|
+
response = await client.post("/transcribe", json={"audio_url": "gs://bucket/file.mp3"})
|
|
114
|
+
|
|
115
|
+
assert response.status_code == 200
|
|
116
|
+
assert response.json()["segments"][0]["confidence"] >= 0.0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The key distinction: we are replacing the provider at the dependency injection boundary. The route handler, service, and validation logic all run for real. Only the external API call is substituted.
|
|
120
|
+
|
|
121
|
+
**Live provider tests** (requiring real API keys) live in `tests/integration/providers/` and are marked `@pytest.mark.live` so they can be excluded from normal runs with pytest's `-m` flag:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
@pytest.mark.live
|
|
125
|
+
async def test_assemblyai_transcribes_real_audio(transcriber, audio_fixture):
|
|
126
|
+
result = await transcriber.transcribe(audio_fixture)
|
|
127
|
+
assert len(result.segments) > 0
|
|
128
|
+
assert result.metadata.duration > 0
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Run with: `uv run pytest tests/integration -m live`
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Tier 2 — Unit Tests (reserved)
|
|
136
|
+
|
|
137
|
+
A unit test is appropriate when the logic is genuinely complex, has many branches, operates on pure data, and would be painful to exercise through the full HTTP stack. Domain model invariants, audio codec transformations, confidence score calculations, and parsing algorithms are good candidates.
|
|
138
|
+
|
|
139
|
+
The test for "does this service method call the adapter and return the result" is not a unit test. That behaviour is validated by a service test.
|
|
140
|
+
|
|
141
|
+
**What earns a unit test:**
|
|
142
|
+
|
|
143
|
+
- Domain models: immutability, field validation, business rules
|
|
144
|
+
- Complex transformations: audio resampling, segment merging, transcript normalisation
|
|
145
|
+
- Pure algorithms: confidence thresholding, speaker attribution logic, chunking strategies
|
|
146
|
+
- Edge cases too costly to set up through a real HTTP path
|
|
147
|
+
|
|
148
|
+
**What does not earn a unit test:**
|
|
149
|
+
|
|
150
|
+
- Service methods that orchestrate calls between an adapter and a domain object
|
|
151
|
+
- Endpoint handlers that validate input and delegate
|
|
152
|
+
- Providers that translate SDK responses into domain types
|
|
153
|
+
|
|
154
|
+
**Pattern — domain model:**
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
# tests/unit/core/domain/test_transcript.py
|
|
158
|
+
class TestTranscriptSegment:
|
|
159
|
+
def test_rejects_confidence_above_one(self):
|
|
160
|
+
with pytest.raises(ValidationError):
|
|
161
|
+
TranscriptSegment(text="hello", confidence=1.5, start=0.0, end=1.0)
|
|
162
|
+
|
|
163
|
+
def test_is_immutable(self):
|
|
164
|
+
segment = TranscriptSegment(text="hello", confidence=0.9, start=0.0, end=1.0)
|
|
165
|
+
with pytest.raises(ValidationError):
|
|
166
|
+
segment.text = "goodbye"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Pattern — complex algorithm:**
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
# tests/unit/providers/test_audio_codec.py
|
|
173
|
+
class TestResample:
|
|
174
|
+
def test_output_matches_target_sample_rate(self, codec, wav_16khz):
|
|
175
|
+
result = codec.resample(wav_16khz, target_hz=8000)
|
|
176
|
+
assert result.sample_rate == 8000
|
|
177
|
+
|
|
178
|
+
def test_duration_is_preserved_within_tolerance(self, codec, wav_16khz):
|
|
179
|
+
result = codec.resample(wav_16khz, target_hz=8000)
|
|
180
|
+
assert abs(result.duration - wav_16khz.duration) < 0.01
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### Tier 3 — System Tests (minimal)
|
|
186
|
+
|
|
187
|
+
System tests answer one question: does the fully-wired application start and serve requests correctly? There are two of them.
|
|
188
|
+
|
|
189
|
+
**Bootstrap test** — verifies the DI container initialises without error. Catches missing environment variables, wiring mistakes, and import-time failures before they hit production:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# tests/system/test_bootstrap.py
|
|
193
|
+
def test_application_starts_without_error():
|
|
194
|
+
app = create_app()
|
|
195
|
+
assert app is not None
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Golden path test** — one end-to-end test per critical user journey, run against the full live stack in CI. Not a substitute for service tests; a smoke check that the seams hold.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Async Configuration
|
|
203
|
+
|
|
204
|
+
All test functions are async by default. `asyncio_mode = "auto"` in `pyproject.toml` means no decorator is needed:
|
|
205
|
+
|
|
206
|
+
```toml
|
|
207
|
+
[tool.pytest.ini_options]
|
|
208
|
+
asyncio_mode = "auto"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Write async tests as plain `async def` functions:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
async def test_streaming_session_emits_events(client):
|
|
215
|
+
response = await client.post("/streaming/start", json={"meeting_id": "m-1"})
|
|
216
|
+
assert response.status_code == 200
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Do not use `@pytest.mark.asyncio` — it is redundant under auto mode and adds noise.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Test Isolation
|
|
224
|
+
|
|
225
|
+
**Container-backed tests** should not share state. Use one of:
|
|
226
|
+
|
|
227
|
+
- **Transaction rollback** — wrap each test in a transaction and roll back on teardown. Zero teardown cost, guaranteed clean state.
|
|
228
|
+
- **Truncation fixture** — truncate relevant tables in a `function`-scoped fixture. Slower but simpler when transactions interfere with the code under test.
|
|
229
|
+
|
|
230
|
+
Never rely on test ordering to manage state. A test that requires a previous test to have run is a broken test.
|
|
231
|
+
|
|
232
|
+
**`dependency_overrides` cleanup** — always reset overrides after a test to prevent state leaking between tests:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
@pytest.fixture(autouse=True)
|
|
236
|
+
def reset_dependency_overrides(app):
|
|
237
|
+
yield
|
|
238
|
+
app.dependency_overrides.clear()
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Naming
|
|
244
|
+
|
|
245
|
+
Every test name describes observable behaviour, not implementation:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
# Good
|
|
249
|
+
async def test_transcribe_returns_422_when_audio_url_is_missing(client): ...
|
|
250
|
+
async def test_streaming_session_assigns_unique_id_per_meeting(client): ...
|
|
251
|
+
|
|
252
|
+
# Bad
|
|
253
|
+
async def test_transcribe_success(client): ...
|
|
254
|
+
async def test_streaming(client): ...
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
A failing test name should give an on-call engineer enough information to form a hypothesis without opening the test file.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Trace Assertions — observability is a test surface
|
|
262
|
+
|
|
263
|
+
A critical-path request must emit an unbroken trace; a missing span is a test failure, not an instrumentation TODO. Register an OTel **`InMemorySpanExporter`** in the test process, exercise the endpoint, and assert on the finished spans — the entry span exists, the trace stays connected across the service hop, and the attributes a dashboard query depends on are present.
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
267
|
+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
268
|
+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
|
269
|
+
|
|
270
|
+
@pytest.fixture
|
|
271
|
+
def spans():
|
|
272
|
+
exporter = InMemorySpanExporter()
|
|
273
|
+
provider = TracerProvider()
|
|
274
|
+
provider.add_span_processor(SimpleSpanProcessor(exporter))
|
|
275
|
+
# install provider for the test, exercise the system, then:
|
|
276
|
+
return exporter
|
|
277
|
+
|
|
278
|
+
async def test_transcribe_emits_connected_trace(client, spans):
|
|
279
|
+
await client.post("/transcribe", json={"audio_url": "gs://b/f.mp3"})
|
|
280
|
+
names = [s.name for s in spans.get_finished_spans()]
|
|
281
|
+
assert "POST /transcribe" in names
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Assert the spans the contract promises and let the rest float — pinning the whole tree couples the test to implementation.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Mutation Testing — the assertion-quality read-out
|
|
289
|
+
|
|
290
|
+
A service test drives many branches through one HTTP call and can *execute* them all while only asserting on the response body. Mutation testing proves the suite checks what it runs: inject a fault, confirm a test fails; a surviving mutant is a covered-but-unchecked line. Python's tooling is production-grade — **`mutmut`** and **`cosmic-ray`** — but it is expensive, so it is a **signal, never a gate**: run it incrementally on the high-risk modules the risk matrix flags and on changed code only. A surviving mutant on changed high-risk code is the missing assertion to add — the same read-out catches AI-generated tests whose oracle was lifted from the implementation. (Note: `mutmut` 3 no longer mutates module-level code.)
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Generate the Inputs You Can't Enumerate
|
|
295
|
+
|
|
296
|
+
Example-based tests check the cases you thought of; the bugs live in the cases you didn't. For pure logic with a real invariant — a round-trip (`decode ∘ encode = id`), a parser that must never raise, a domain calculation with an algebraic law — state the property and let **`Hypothesis`** generate and shrink counterexamples. One property covers an infinity of examples, and most caught faults surface on a single generated input. At the API boundary, **Schemathesis** derives a semantics-aware fuzzer straight from the OpenAPI schema and finds materially more defects than example-based API tests for the cost of pointing it at the spec. Reach for these where invariants are real; the authoring cost (a meaningful property needs a generator) is why they are not everywhere.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Running Tests
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
uv run pytest tests/unit # Unit tests — no infrastructure required
|
|
304
|
+
uv run pytest tests/integration -m "not live" # Service tests — requires Docker; skips live APIs
|
|
305
|
+
uv run pytest tests/integration -m live # Live API provider tests — requires real keys
|
|
306
|
+
uv run pytest tests/system # Bootstrap + golden path
|
|
307
|
+
uv run pytest # Everything
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Anti-Patterns
|
|
313
|
+
|
|
314
|
+
**Mocking the interior of a service.** Replacing an adapter with `MagicMock` inside a service test asserts nothing about real behaviour. If you are mocking something that could run in a container, use a container.
|
|
315
|
+
|
|
316
|
+
**Testing that delegation happens.** `assert transcriber.transcribe.called_once_with(segment)` is an assertion about the implementation, not the behaviour. Test what comes back out of the system, not which internal methods were invoked.
|
|
317
|
+
|
|
318
|
+
**Fixture factories that mirror the domain.** A fixture that constructs a complex domain object with many defaults trains engineers to not think about what data matters for the test. Construct only what the test needs; leave everything else visible.
|
|
319
|
+
|
|
320
|
+
**`scope="session"` on mutable state.** Session-scoped fixtures that hold mutable objects leak state between tests. Containers are safe at session scope. Application state is not.
|
|
321
|
+
|
|
322
|
+
**Skipping the async client.** `TestClient` is a synchronous wrapper that hides async behaviour. Use `httpx.AsyncClient` with `ASGITransport` for all entrypoint tests so the lifespan and async middleware execute correctly.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Environment Configuration
|
|
2
|
+
# Copy this file to .env and fill in the values.
|
|
3
|
+
|
|
4
|
+
# HTTP server port for this service.
|
|
5
|
+
SERVER_PORT=<%= assignedPort %>
|
|
6
|
+
<% if (postgres) { %>
|
|
7
|
+
# Postgres connection (composed into the async DSN by the service config).
|
|
8
|
+
DB_HOST=localhost
|
|
9
|
+
DB_PORT=5432
|
|
10
|
+
DB_USER=postgres
|
|
11
|
+
DB_PASSWORD=postgres
|
|
12
|
+
DB_NAME=<%= fileName %>
|
|
13
|
+
<% } %>
|
|
14
|
+
<% if (messaging === 'redis' || websockets) { %>
|
|
15
|
+
# Redis (message bus and/or WebSocket backplane).
|
|
16
|
+
REDIS_URL=redis://localhost:6379
|
|
17
|
+
<% } %>
|
|
18
|
+
<% if (messaging === 'kafka') { %>
|
|
19
|
+
# Kafka broker list (comma-separated host:port).
|
|
20
|
+
KAFKA_BROKERS=localhost:9092
|
|
21
|
+
<% } %>
|
|
22
|
+
<% if (messaging === 'gcp-pubsub') { %>
|
|
23
|
+
# GCP Pub/Sub project id (use any value when running against the emulator).
|
|
24
|
+
PUBSUB_PROJECT_ID=local-project
|
|
25
|
+
<% } %>
|
|
26
|
+
# OpenTelemetry OTLP gRPC endpoint for trace export. Docker compose injects the
|
|
27
|
+
# in-cluster Jaeger address; this default covers native (`./dev start`) runs.
|
|
28
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
|
29
|
+
<%# LLM env vars are written by the llm capability footprint (plan WS-F) so they
|
|
30
|
+
match the chosen provider's defaults — see the "# llm capability" block. %>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install system dependencies (e.g. for psycopg2, if needed)
|
|
6
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
7
|
+
build-essential \
|
|
8
|
+
libpq-dev \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
10
|
+
|
|
11
|
+
# Install uv for fast dependency resolution
|
|
12
|
+
RUN pip install uv
|
|
13
|
+
|
|
14
|
+
COPY pyproject.toml .
|
|
15
|
+
# Compile locked requirements for layer caching, then install
|
|
16
|
+
RUN uv pip compile pyproject.toml -o requirements.txt \
|
|
17
|
+
&& uv pip install --system -r requirements.txt
|
|
18
|
+
|
|
19
|
+
COPY src/ ./src/
|
|
20
|
+
# src-layout: the importable package is src/<%= packageName %>, so the source root
|
|
21
|
+
# (not the app root) is what goes on the import path.
|
|
22
|
+
ENV PYTHONPATH=/app/src
|
|
23
|
+
<% if (postgres) { %>
|
|
24
|
+
# Declarative schema (db/schema.sql) + apply-schema.sh, applied by `./dev migrate`.
|
|
25
|
+
COPY db/ ./db/
|
|
26
|
+
COPY scripts/ ./scripts/
|
|
27
|
+
<% } %>
|
|
28
|
+
|
|
29
|
+
<% if (runpod) { %>
|
|
30
|
+
# Runpod serverless entrypoint
|
|
31
|
+
CMD ["python", "-m", "<%= packageName %>.entrypoints.worker.worker"]
|
|
32
|
+
<% } else { %>
|
|
33
|
+
# FastAPI entrypoint
|
|
34
|
+
EXPOSE <%= assignedPort %>
|
|
35
|
+
CMD ["uvicorn", "<%= packageName %>.main:app", "--host", "0.0.0.0", "--port", "<%= assignedPort %>"]
|
|
36
|
+
<% } %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Declarative Schema definition
|
|
2
|
+
-- Use a diffing engine (e.g. pg-schema-diff or atlas) to apply this to the database.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE examples (
|
|
5
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
6
|
+
name VARCHAR(255) NOT NULL,
|
|
7
|
+
description TEXT
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE TABLE idempotency_keys (
|
|
11
|
+
key VARCHAR(255) PRIMARY KEY,
|
|
12
|
+
user_id VARCHAR(255) NOT NULL,
|
|
13
|
+
status VARCHAR(50) NOT NULL,
|
|
14
|
+
response_code INT,
|
|
15
|
+
response_body TEXT,
|
|
16
|
+
response_headers TEXT,
|
|
17
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
19
|
+
);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "<%= name %>"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A GroundWork python microservice"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"pydantic>=2.0.0",
|
|
8
|
+
"pydantic-settings>=2.0.0",
|
|
9
|
+
"tenacity>=8.0.0",
|
|
10
|
+
"circuitbreaker>=2.0.0",
|
|
11
|
+
"httpx>=0.27.0",
|
|
12
|
+
"opentelemetry-api",
|
|
13
|
+
"opentelemetry-sdk",
|
|
14
|
+
"opentelemetry-instrumentation-fastapi",
|
|
15
|
+
"opentelemetry-exporter-otlp-proto-grpc",
|
|
16
|
+
"python-json-logger>=2.0.0",
|
|
17
|
+
"fastapi>=0.110.0",
|
|
18
|
+
"uvicorn[standard]>=0.29.0",
|
|
19
|
+
<% if (postgres) { %>
|
|
20
|
+
"sqlalchemy>=2.0.0",
|
|
21
|
+
"asyncpg>=0.29.0",
|
|
22
|
+
"alembic>=1.13.0",
|
|
23
|
+
# SQLAlchemy's async engine requires greenlet at runtime; it is not always
|
|
24
|
+
# pulled in transitively (notably on newer Python), so pin it explicitly —
|
|
25
|
+
# without it every async DB call raises "the greenlet library is required".
|
|
26
|
+
"greenlet>=3.0.0",
|
|
27
|
+
<% } %>
|
|
28
|
+
<% if (messaging === 'redis' || websockets) { %>
|
|
29
|
+
"redis>=5.0.0",
|
|
30
|
+
<% } %>
|
|
31
|
+
<% if (runpod) { %>
|
|
32
|
+
"runpod>=1.5.0",
|
|
33
|
+
<% } %>
|
|
34
|
+
]<%# The LLM provider dependency (anthropic/openai/none) is injected by the llm
|
|
35
|
+
capability footprint, plan WS-F — not declared inline. %>
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8.0.0",
|
|
40
|
+
"pytest-asyncio>=0.23.0",
|
|
41
|
+
"testcontainers[postgres,redis]>=3.7.1",
|
|
42
|
+
"httpx>=0.27.0",
|
|
43
|
+
"import-linter>=2.0",
|
|
44
|
+
"ruff>=0.6.0",
|
|
45
|
+
"black>=24.0.0"
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[build-system]
|
|
49
|
+
requires = ["hatchling"]
|
|
50
|
+
build-backend = "hatchling.build"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/<%= packageName %>"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
asyncio_mode = "auto"
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
|
|
59
|
+
# import-linter enforces the inward dependency rule structurally: the core
|
|
60
|
+
# (domain + service ports) must never import an adapter, an entrypoint, or a
|
|
61
|
+
# web/db framework. Run `lint-imports` to check it (CI + pre-commit).
|
|
62
|
+
[tool.importlinter]
|
|
63
|
+
root_package = "<%= packageName %>"
|
|
64
|
+
|
|
65
|
+
[[tool.importlinter.contracts]]
|
|
66
|
+
name = "core depends on nothing outward"
|
|
67
|
+
type = "forbidden"
|
|
68
|
+
source_modules = [
|
|
69
|
+
"<%= packageName %>.core",
|
|
70
|
+
]
|
|
71
|
+
forbidden_modules = [
|
|
72
|
+
"<%= packageName %>.adapters",
|
|
73
|
+
"<%= packageName %>.entrypoints",
|
|
74
|
+
"fastapi",
|
|
75
|
+
"sqlalchemy",
|
|
76
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Loads environment variables if .env exists
|
|
5
|
+
if [ -f .env ]; then
|
|
6
|
+
export $(cat .env | xargs)
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
# Ensure database URL is available
|
|
10
|
+
DB_URL=${DATABASE_URL:-"postgres://postgres:postgres@localhost:5432/<%= name %>?sslmode=disable"}
|
|
11
|
+
|
|
12
|
+
echo "Applying declarative schema to database..."
|
|
13
|
+
|
|
14
|
+
# Uses Stripe's pg-schema-diff to declaratively migrate the database to match
|
|
15
|
+
# db/schema.sql. This is a Python service (no go.mod), so the tool is pinned with
|
|
16
|
+
# an explicit @version — `go run <pkg>@<ver>` runs module-independently, whereas a
|
|
17
|
+
# bare `go run <pkg>` would require a surrounding go.mod. Version matches the Go
|
|
18
|
+
# scaffold's pinned pg-schema-diff for parity. Requires the Go toolchain on PATH
|
|
19
|
+
# (./dev doctor checks for it when a Python service is present).
|
|
20
|
+
go run github.com/stripe/pg-schema-diff/cmd/pg-schema-diff@v1.0.5 apply \
|
|
21
|
+
--from-dsn "$DB_URL" \
|
|
22
|
+
--to-dir ./db \
|
|
23
|
+
--skip-confirm-prompt
|
|
24
|
+
|
|
25
|
+
echo "Schema applied successfully!"
|
package/src/generators/python-microservice/files/src/__packageName__/adapters/comfyui.py.template
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import websockets
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
from circuitbreaker import circuit
|
|
7
|
+
from <%= packageName %>.adapters.config import settings
|
|
8
|
+
from <%= packageName %>.core.domain.exceptions import TransientInferenceError, PermanentInferenceError, CircuitBreakerOpenError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
def comfyui_fallback(e):
|
|
13
|
+
logger.error(f"ComfyUI circuit breaker tripped! {e}")
|
|
14
|
+
raise CircuitBreakerOpenError("ComfyUI server is currently unavailable.")
|
|
15
|
+
|
|
16
|
+
class ComfyUIClient:
|
|
17
|
+
"""
|
|
18
|
+
Adapter to interact with a remote ComfyUI instance over REST and WebSockets.
|
|
19
|
+
"""
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.http_client = httpx.AsyncClient(base_url=settings.comfyui_base_url, timeout=60.0)
|
|
22
|
+
# ComfyUI websocket uses ws:// instead of http://
|
|
23
|
+
self.ws_url = settings.comfyui_base_url.replace("http://", "ws://").replace("https://", "wss://") + "/ws"
|
|
24
|
+
self.client_id = "media-service-client-1"
|
|
25
|
+
|
|
26
|
+
@circuit(failure_threshold=3, recovery_timeout=60, fallback_function=comfyui_fallback, expected_exception=TransientInferenceError)
|
|
27
|
+
async def queue_prompt(self, workflow_json: Dict[str, Any]) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Submits a workflow to ComfyUI for execution and returns the Prompt ID.
|
|
30
|
+
"""
|
|
31
|
+
payload = {
|
|
32
|
+
"prompt": workflow_json,
|
|
33
|
+
"client_id": self.client_id
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = await self.http_client.post("/prompt", json=payload)
|
|
38
|
+
|
|
39
|
+
if response.status_code >= 500:
|
|
40
|
+
raise TransientInferenceError(f"ComfyUI internal server error: {response.text}")
|
|
41
|
+
elif response.status_code >= 400:
|
|
42
|
+
raise PermanentInferenceError(f"Invalid workflow request: {response.text}")
|
|
43
|
+
|
|
44
|
+
data = response.json()
|
|
45
|
+
prompt_id = data.get("prompt_id")
|
|
46
|
+
if not prompt_id:
|
|
47
|
+
raise PermanentInferenceError("ComfyUI response missing prompt_id")
|
|
48
|
+
|
|
49
|
+
return prompt_id
|
|
50
|
+
|
|
51
|
+
except httpx.RequestError as e:
|
|
52
|
+
raise TransientInferenceError(f"Network error connecting to ComfyUI: {e}")
|
|
53
|
+
|
|
54
|
+
async def poll_execution_progress(self, prompt_id: str) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Connects via WebSocket to listen for progress updates for a specific prompt_id.
|
|
57
|
+
In a real application, you would yield progress events or store them in Redis.
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
url = f"{self.ws_url}?clientId={self.client_id}"
|
|
61
|
+
async with websockets.connect(url) as ws:
|
|
62
|
+
while True:
|
|
63
|
+
msg = await ws.recv()
|
|
64
|
+
if isinstance(msg, str):
|
|
65
|
+
data = json.loads(msg)
|
|
66
|
+
if data['type'] == 'executing':
|
|
67
|
+
node = data['data'].get('node')
|
|
68
|
+
if node is None and data['data'].get('prompt_id') == prompt_id:
|
|
69
|
+
# Execution is finished
|
|
70
|
+
break
|
|
71
|
+
elif data['type'] == 'progress':
|
|
72
|
+
# Log or publish progress
|
|
73
|
+
pass
|
|
74
|
+
except websockets.exceptions.WebSocketException as e:
|
|
75
|
+
raise TransientInferenceError(f"WebSocket disconnected from ComfyUI: {e}")
|
|
76
|
+
|
|
77
|
+
async def get_history(self, prompt_id: str) -> Dict[str, Any]:
|
|
78
|
+
"""Fetches the generated artifacts/outputs from ComfyUI history."""
|
|
79
|
+
try:
|
|
80
|
+
response = await self.http_client.get(f"/history/{prompt_id}")
|
|
81
|
+
if response.status_code == 200:
|
|
82
|
+
history = response.json()
|
|
83
|
+
return history.get(prompt_id, {})
|
|
84
|
+
else:
|
|
85
|
+
raise TransientInferenceError("Failed to fetch history")
|
|
86
|
+
except httpx.RequestError as e:
|
|
87
|
+
raise TransientInferenceError(f"Network error fetching history: {e}")
|
package/src/generators/python-microservice/files/src/__packageName__/adapters/config.py.template
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8', extra='ignore')
|
|
6
|
+
|
|
7
|
+
server_port: int = <%= assignedPort %>
|
|
8
|
+
|
|
9
|
+
# OTLP gRPC endpoint for trace export. Compose injects
|
|
10
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317 for docker; this default
|
|
11
|
+
# covers native (`./dev start`) runs. BaseSettings maps the env var
|
|
12
|
+
# case-insensitively onto this field.
|
|
13
|
+
otel_exporter_otlp_endpoint: str = "http://localhost:4317"
|
|
14
|
+
<% if (postgres) { %>
|
|
15
|
+
db_host: str = "localhost"
|
|
16
|
+
db_port: int = 5432
|
|
17
|
+
db_user: str = "postgres"
|
|
18
|
+
db_password: str = "postgres"
|
|
19
|
+
db_name: str = "<%= fileName %>"
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def database_url(self) -> Optional[str]:
|
|
23
|
+
return f"postgresql+asyncpg://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}"
|
|
24
|
+
<% } else { %>
|
|
25
|
+
@property
|
|
26
|
+
def database_url(self) -> Optional[str]:
|
|
27
|
+
return None
|
|
28
|
+
<% } %>
|
|
29
|
+
|
|
30
|
+
<% if (messaging === 'redis' || websockets) { %>
|
|
31
|
+
redis_url: str = "redis://localhost:6379"
|
|
32
|
+
<% } %>
|
|
33
|
+
|
|
34
|
+
<% if (messaging === 'kafka') { %>
|
|
35
|
+
kafka_brokers: str = "localhost:9092"
|
|
36
|
+
<% } %>
|
|
37
|
+
|
|
38
|
+
<% if (messaging === 'gcp-pubsub') { %>
|
|
39
|
+
pubsub_project_id: str = "local-project"
|
|
40
|
+
<% } %>
|
|
41
|
+
|
|
42
|
+
<% if (llm) { %>
|
|
43
|
+
llm_api_key: Optional[str] = None
|
|
44
|
+
llm_base_url: Optional[str] = None
|
|
45
|
+
llm_model: str = "<% if (llmProvider === 'anthropic') { %>claude-sonnet-4-6<% } else if (llmProvider === 'local') { %>llama3.1<% } else if (llmProvider === 'none') { %><% } else { %>gpt-4o<% } %>"
|
|
46
|
+
<% } %>
|
|
47
|
+
|
|
48
|
+
settings = Settings()
|
package/src/generators/python-microservice/files/src/__packageName__/adapters/database.py.template
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
|
2
|
+
from sqlalchemy.orm import declarative_base
|
|
3
|
+
from <%= packageName %>.adapters.config import settings
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
Base = declarative_base()
|
|
7
|
+
|
|
8
|
+
engine = create_async_engine(
|
|
9
|
+
settings.database_url,
|
|
10
|
+
echo=False,
|
|
11
|
+
future=True,
|
|
12
|
+
pool_pre_ping=True
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
async_session_maker = async_sessionmaker(
|
|
16
|
+
engine, expire_on_commit=False
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
async def get_db_session():
|
|
20
|
+
async with async_session_maker() as session:
|
|
21
|
+
yield session
|