groundwork-method 0.0.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +781 -0
- package/LICENSE +21 -0
- package/README.md +44 -29
- package/bin/groundwork.js +1654 -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 +125 -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 +68 -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 +82 -0
- package/src/docs/principles/index.md +23 -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 +118 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -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 +107 -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 +14 -0
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +108 -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/navigation.md +122 -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/state-management.md +166 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +135 -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 +15 -0
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +171 -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/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 +139 -0
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +11 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +107 -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/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/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 +433 -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 +278 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +9 -0
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +196 -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/testing.md +177 -0
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +13 -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 +47 -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 +68 -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 +74 -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 +30 -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 +152 -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 +281 -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/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 +227 -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 +142 -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 +129 -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/slice-worker.md +191 -0
- package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +126 -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 +35 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +35 -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 +198 -0
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +168 -0
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +246 -0
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +193 -0
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +199 -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 +54 -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 +46 -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,126 @@
|
|
|
1
|
+
# Resilience
|
|
2
|
+
|
|
3
|
+
## Timeouts
|
|
4
|
+
|
|
5
|
+
Set explicit timeouts on every outbound HTTP call:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
client = httpx.AsyncClient(
|
|
11
|
+
timeout=httpx.Timeout(
|
|
12
|
+
connect=5.0, # TCP connection
|
|
13
|
+
read=30.0, # Full response body
|
|
14
|
+
write=10.0, # Request body
|
|
15
|
+
pool=5.0, # Connection from pool
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Own the timeout at the provider level. The `read` timeout is critical for ML/API providers — a streaming response that hangs mid-way waits indefinitely without it.
|
|
21
|
+
|
|
22
|
+
## Retries
|
|
23
|
+
|
|
24
|
+
Use `tenacity` for retry logic:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from tenacity import (
|
|
28
|
+
retry,
|
|
29
|
+
retry_if_exception_type,
|
|
30
|
+
stop_after_attempt,
|
|
31
|
+
wait_exponential_jitter,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@retry(
|
|
35
|
+
retry=retry_if_exception_type(TransientError),
|
|
36
|
+
wait=wait_exponential_jitter(initial=1, max=30),
|
|
37
|
+
stop=stop_after_attempt(3),
|
|
38
|
+
)
|
|
39
|
+
async def _call_with_retry(self, payload: dict) -> dict:
|
|
40
|
+
return await self._client.post(payload)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Jitter is not optional.** Backoff without jitter causes thundering herd.
|
|
44
|
+
|
|
45
|
+
**Only retry transient errors.** A `400` is permanent — retrying wastes budget. Map errors to domain types before retrying.
|
|
46
|
+
|
|
47
|
+
## Circuit Breakers
|
|
48
|
+
|
|
49
|
+
Prevent cascading failure from a down dependency:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from circuitbreaker import circuit
|
|
53
|
+
|
|
54
|
+
@circuit(
|
|
55
|
+
failure_threshold=5,
|
|
56
|
+
recovery_timeout=30,
|
|
57
|
+
expected_exception=TransientError,
|
|
58
|
+
)
|
|
59
|
+
async def complete(self, prompt: str) -> str:
|
|
60
|
+
return await self._client.complete(prompt)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Set `expected_exception` explicitly.** A breaker that trips on all exceptions produces false opens.
|
|
64
|
+
|
|
65
|
+
## Graceful Shutdown
|
|
66
|
+
|
|
67
|
+
FastAPI + Uvicorn handle `SIGTERM` correctly when `lifespan` is used:
|
|
68
|
+
|
|
69
|
+
1. `SIGTERM` received
|
|
70
|
+
2. Stop accepting new connections
|
|
71
|
+
3. In-flight requests and `BackgroundTasks` complete
|
|
72
|
+
4. `lifespan` cleanup runs
|
|
73
|
+
5. Process exits
|
|
74
|
+
|
|
75
|
+
Keep the shutdown path fast:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
@asynccontextmanager
|
|
79
|
+
async def lifespan(app: FastAPI):
|
|
80
|
+
resources = await initialise_resources()
|
|
81
|
+
yield
|
|
82
|
+
await asyncio.wait_for(resources.close(), timeout=4.0)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Health Probes
|
|
86
|
+
|
|
87
|
+
**`/healthz` (liveness):** Returns `200` unconditionally. No external dependency checks.
|
|
88
|
+
|
|
89
|
+
**`/readyz` (readiness):** Returns `200` only after startup completes. Returns `503` during startup and shutdown.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
@router.get("/healthz")
|
|
93
|
+
async def liveness() -> dict:
|
|
94
|
+
return {"status": "ok"}
|
|
95
|
+
|
|
96
|
+
@router.get("/readyz")
|
|
97
|
+
async def readiness(state: AppState = Depends(get_app_state)) -> dict:
|
|
98
|
+
if not state.ready:
|
|
99
|
+
raise HTTPException(status_code=503, detail="not ready")
|
|
100
|
+
return {"status": "ready"}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Graceful Degradation
|
|
104
|
+
|
|
105
|
+
Return partial results rather than hard errors when non-critical steps fail:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
async def process_with_enrichment(self, input_uri: str) -> Result:
|
|
109
|
+
base = await self.processor.process(input_uri)
|
|
110
|
+
try:
|
|
111
|
+
enrichment = await self.enricher.enrich(base)
|
|
112
|
+
return base.with_enrichment(enrichment)
|
|
113
|
+
except EnrichmentUnavailableError:
|
|
114
|
+
logger.warning("enrichment.unavailable", item_id=base.id)
|
|
115
|
+
return base
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Log every degraded response with structured context. Silent degradation is invisible in production.
|
|
119
|
+
|
|
120
|
+
## Anti-Patterns
|
|
121
|
+
|
|
122
|
+
- **No timeout on provider calls.** Hung connections hold indefinitely.
|
|
123
|
+
- **Retrying non-transient errors.** A `400` never succeeds on retry.
|
|
124
|
+
- **Circuit breaker on all exceptions.** False opens from caller errors.
|
|
125
|
+
- **Long-running lifespan shutdown.** Force-killed by orchestrator.
|
|
126
|
+
- **Silent degradation.** Partial results without structured logs.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
## The Model: Honeycomb, Not Pyramid
|
|
4
|
+
|
|
5
|
+
Testcontainers spins up real Postgres/Pub/Sub in seconds. The confidence it provides is categorically different from any mock. Mock-heavy suites pass while production breaks.
|
|
6
|
+
|
|
7
|
+
**Service tests are the default.** Unit tests are reserved for genuinely complex logic. System tests are minimal.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Tier 1 — Service Tests (Default)
|
|
12
|
+
|
|
13
|
+
Drive the FastAPI app through HTTP using `httpx.AsyncClient` with `ASGITransport`. Infrastructure runs in containers. Non-containerisable providers (LLM APIs) are replaced at the FastAPI `dependency_overrides` boundary.
|
|
14
|
+
|
|
15
|
+
### Fixture Setup — App + Async Client
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import pytest
|
|
19
|
+
import httpx
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(scope="session")
|
|
22
|
+
def app():
|
|
23
|
+
return create_app()
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
async def client(app):
|
|
27
|
+
async with httpx.AsyncClient(
|
|
28
|
+
transport=httpx.ASGITransport(app=app),
|
|
29
|
+
base_url="http://test",
|
|
30
|
+
) as c:
|
|
31
|
+
yield c
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Fixture Setup — Containerised Infrastructure
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from testcontainers.postgres import PostgresContainer
|
|
38
|
+
|
|
39
|
+
@pytest.fixture(scope="session")
|
|
40
|
+
def postgres():
|
|
41
|
+
with PostgresContainer("postgres:16-alpine") as container:
|
|
42
|
+
yield container
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Scope containers at `session`. Reset state via transactions or truncation between tests.
|
|
46
|
+
|
|
47
|
+
### Replacing Non-Containerisable Providers
|
|
48
|
+
|
|
49
|
+
Replace at the FastAPI boundary, not by patching internals:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from unittest.mock import AsyncMock
|
|
53
|
+
|
|
54
|
+
@pytest.fixture
|
|
55
|
+
def fake_processor():
|
|
56
|
+
proc = AsyncMock()
|
|
57
|
+
proc.process.return_value = DomainResult(...)
|
|
58
|
+
return proc
|
|
59
|
+
|
|
60
|
+
async def test_endpoint_returns_result(client, app, fake_processor):
|
|
61
|
+
app.dependency_overrides[get_processor] = lambda: fake_processor
|
|
62
|
+
response = await client.post("/process", json={"input": "test"})
|
|
63
|
+
assert response.status_code == 200
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Route handler, service, and validation all run for real. Only the external API call is substituted.
|
|
67
|
+
|
|
68
|
+
### Live Provider Tests
|
|
69
|
+
|
|
70
|
+
Marked `@pytest.mark.live` and selected explicitly with pytest's `-m` flag (excluded from normal runs with `-m "not live"`):
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
@pytest.mark.live
|
|
74
|
+
async def test_adapter_against_real_api(processor, fixture_data):
|
|
75
|
+
result = await processor.process(fixture_data)
|
|
76
|
+
assert result.confidence > 0
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Tier 2 — Unit Tests (Reserved)
|
|
82
|
+
|
|
83
|
+
Appropriate when logic is genuinely complex, has many branches, operates on pure data, and is painful to exercise through HTTP.
|
|
84
|
+
|
|
85
|
+
**What earns a unit test:**
|
|
86
|
+
- Domain model immutability, field validation, business rules
|
|
87
|
+
- Complex transformations (resampling, merging, normalisation)
|
|
88
|
+
- Pure algorithms (confidence thresholding, chunking strategies)
|
|
89
|
+
|
|
90
|
+
**What does NOT earn a unit test:**
|
|
91
|
+
- Service methods that orchestrate adapter calls
|
|
92
|
+
- Endpoint handlers that validate and delegate
|
|
93
|
+
- Adapters that translate SDK responses
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
class TestDomainModel:
|
|
97
|
+
def test_rejects_invalid_confidence(self):
|
|
98
|
+
with pytest.raises(ValidationError):
|
|
99
|
+
Segment(text="hello", confidence=1.5, start=0.0, end=1.0)
|
|
100
|
+
|
|
101
|
+
def test_is_immutable(self):
|
|
102
|
+
segment = Segment(text="hello", confidence=0.9, start=0.0, end=1.0)
|
|
103
|
+
with pytest.raises(ValidationError):
|
|
104
|
+
segment.text = "goodbye"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Tier 3 — System Tests (Minimal)
|
|
110
|
+
|
|
111
|
+
**Bootstrap test** — verifies the DI container initialises:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
def test_application_starts():
|
|
115
|
+
app = create_app()
|
|
116
|
+
assert app is not None
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Golden path test** — one per critical journey, against the full live stack.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Async Configuration
|
|
124
|
+
|
|
125
|
+
```toml
|
|
126
|
+
[tool.pytest.ini_options]
|
|
127
|
+
asyncio_mode = "auto"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Write `async def` tests directly. No `@pytest.mark.asyncio` needed.
|
|
131
|
+
|
|
132
|
+
## Test Isolation
|
|
133
|
+
|
|
134
|
+
- **Transaction rollback** per test, or **truncation fixture**.
|
|
135
|
+
- Always reset `dependency_overrides` after each test:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
@pytest.fixture(autouse=True)
|
|
139
|
+
def reset_overrides(app):
|
|
140
|
+
yield
|
|
141
|
+
app.dependency_overrides.clear()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Naming
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
# Good
|
|
148
|
+
async def test_process_returns_422_when_input_missing(client): ...
|
|
149
|
+
# Bad
|
|
150
|
+
async def test_process_success(client): ...
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Running Tests
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
uv run pytest tests/unit # No infrastructure
|
|
157
|
+
uv run pytest tests/integration -m "not live" # Requires Docker; skips live APIs
|
|
158
|
+
uv run pytest tests/integration -m live # Live API tests — requires real keys
|
|
159
|
+
uv run pytest tests/system # Bootstrap + golden path
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Anti-Patterns
|
|
163
|
+
|
|
164
|
+
- **Mocking the interior.** Test what comes out, not which methods were called.
|
|
165
|
+
- **Testing delegation.** `assert adapter.called_once_with(...)` tests the mock, not the code.
|
|
166
|
+
- **Fixture factories that mirror domain.** Construct only what the test needs.
|
|
167
|
+
- **`scope="session"` on mutable state.** Containers: safe. Application state: not.
|
|
168
|
+
- **Skipping the async client.** `TestClient` hides async behaviour. Use `httpx.AsyncClient`.
|
|
169
|
+
|
|
170
|
+
## Bet Slice Rollout — the permanent tests a slice owes
|
|
171
|
+
|
|
172
|
+
When a bet slice's progress tests go green, the slice rolls out permanent coverage before it closes (bet workflow, Delivery step 5). The bet-progress tests prove the capability once and are archived; these stay.
|
|
173
|
+
|
|
174
|
+
- **Service perimeter test (always).** One test per capability the slice delivered, through `httpx.AsyncClient` against the real app with a real database — the coverage that survives refactors.
|
|
175
|
+
- **Unit tests (when logic earned them).** Pure-function tests for branching business logic the slice introduced — validation rules, transformations, state machines. Plumbing does not earn unit tests; the perimeter test already covers it.
|
|
176
|
+
- **Property-based tests (when invariants exist).** A slice that introduced an invariant — round-trip serialization, idempotent consumers, order-independent merges — pins it with Hypothesis, because example-based tests sample invariants instead of stating them.
|
|
177
|
+
- **Contract conformance (when the slice changed an API).** FastAPI's served `/openapi.json` must match the promoted spec in `docs/architecture/api/<service>/openapi.yaml`; the generated system suite checks this — the slice's job is to keep the spec promotion current.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Sync Anchor
|
|
2
|
+
|
|
3
|
+
This file pins the principle files this skill embeds. When any listed file
|
|
4
|
+
changes, this skill must be reviewed in the same commit. CI verifies the
|
|
5
|
+
hashes match.
|
|
6
|
+
|
|
7
|
+
| Principle file | SHA-256 | Last reviewed |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| src/generators/python-microservice/docs/principles/stack/python/async.md | 6fdd399fb3052381020ff6e792a724d72bdabe674817794093853cbf24fa9f97 | 2026-05-26 |
|
|
10
|
+
| src/generators/python-microservice/docs/principles/stack/python/resilience.md | d5a7b8f089acdb71d64c1bd4fc9ce80e6947504b01b0ace695ac5ee66554a1b1 | 2026-06-19 |
|
|
11
|
+
| src/generators/python-microservice/docs/principles/stack/python/testing.md | b596a4281825349c627dca17e671052ef64a371ff66c50b66d11ceab5ee7b5f2 | 2026-06-19 |
|
|
12
|
+
| src/generators/python-microservice/docs/principles/stack/python/documentation.md | ac58228ba22435bf9bad2ea5bf924bdf9e3674e9967515d5c82aaf3b7825214d | 2026-05-26 |
|
|
13
|
+
| src/generators/python-microservice/docs/principles/stack/python/mcp.md | 1e6deab0b45c7271e0038e9b3d51bc30cb2917488f608e847d566739ac6caeba | 2026-06-19 |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { formatFiles, Tree } from '@nx/devkit';
|
|
2
|
+
import { recordGeneratorProvenance } from '../shared/provenance';
|
|
3
|
+
import { applyCapability, loadCapability } from '../shared/capabilities';
|
|
4
|
+
|
|
5
|
+
export interface AddCapabilityGeneratorSchema {
|
|
6
|
+
service: string;
|
|
7
|
+
capability: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Locate the service root in the tree. Accepts a bare name (resolved under
|
|
13
|
+
* services/) or an explicit path. */
|
|
14
|
+
function resolveServiceRoot(tree: Tree, service: string): string {
|
|
15
|
+
const candidates = service.includes('/') ? [service] : [`services/${service}`, service];
|
|
16
|
+
for (const c of candidates) {
|
|
17
|
+
if (tree.exists(`${c}/pyproject.toml`) || tree.exists(`${c}/go.mod`) || tree.exists(`${c}/package.json`)) {
|
|
18
|
+
return c;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Could not find service "${service}" (looked for ${candidates.join(', ')} with a pyproject.toml / go.mod / package.json).`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Infer the stack from the marker file present in the service root. */
|
|
27
|
+
function detectStack(tree: Tree, serviceRoot: string): string {
|
|
28
|
+
if (tree.exists(`${serviceRoot}/pyproject.toml`)) return 'python';
|
|
29
|
+
if (tree.exists(`${serviceRoot}/go.mod`)) return 'go';
|
|
30
|
+
if (tree.exists(`${serviceRoot}/package.json`)) return 'typescript';
|
|
31
|
+
throw new Error(`Could not detect the stack for ${serviceRoot}.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default async function (tree: Tree, options: AddCapabilityGeneratorSchema) {
|
|
35
|
+
const capability = options.capability || 'llm';
|
|
36
|
+
const cap = loadCapability(capability);
|
|
37
|
+
const provider = options.provider || cap.default;
|
|
38
|
+
const serviceRoot = resolveServiceRoot(tree, options.service);
|
|
39
|
+
const stack = options.stack || detectStack(tree, serviceRoot);
|
|
40
|
+
|
|
41
|
+
const result = applyCapability(tree, { capability, provider, stack, serviceRoot });
|
|
42
|
+
|
|
43
|
+
await formatFiles(tree);
|
|
44
|
+
recordGeneratorProvenance(tree, 'add-capability', options as unknown as Record<string, unknown>);
|
|
45
|
+
|
|
46
|
+
// Guidance — the injector writes the port, adapter, contract test, deps and
|
|
47
|
+
// env, but composition-root wiring is service-specific and a compose/runner
|
|
48
|
+
// footprint is workspace-level, so we surface what the engineer must finish.
|
|
49
|
+
console.log(`\nAdded capability "${capability}" (provider: ${provider}, footprint: ${result.footprint}) to ${serviceRoot}`);
|
|
50
|
+
console.log(` port: ${result.files.port}`);
|
|
51
|
+
console.log(` adapter: ${result.files.adapter}`);
|
|
52
|
+
console.log(` contract test: ${result.files.contractTest}`);
|
|
53
|
+
if (result.wiring) console.log(` wiring: ${result.wiring}`);
|
|
54
|
+
if (provider === 'none') {
|
|
55
|
+
console.log(` bet: the contract test is strict-xfail — implement the adapter to cash it.`);
|
|
56
|
+
}
|
|
57
|
+
if (result.footprint === 'compose-service') {
|
|
58
|
+
console.log(
|
|
59
|
+
result.materialized
|
|
60
|
+
? ` footprint: injected its container into docker-compose.yml — run \`./dev start\` to boot it.`
|
|
61
|
+
: ` footprint: needs a docker-compose service, but no docker-compose.yml was found — run workspace-dev-cli first.`,
|
|
62
|
+
);
|
|
63
|
+
} else if (result.footprint === 'runner') {
|
|
64
|
+
console.log(
|
|
65
|
+
result.materialized
|
|
66
|
+
? ` footprint: registered as a native runner in .dev/dev.config.json — \`./dev start\` will launch it.`
|
|
67
|
+
: ` footprint: runs as a process, but no .dev/dev.config.json was found — run workspace-dev-cli first.`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"id": "add-capability",
|
|
4
|
+
"title": "Add Capability",
|
|
5
|
+
"description": "Add a capability interface + a chosen provider implementation (or a bare `none` interface) to an existing service. The footprint — env / compose-service / runner / none — follows from the provider.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"service": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The service to add the capability to (directory name under services/, or a path to the service root).",
|
|
11
|
+
"$default": { "$source": "argv", "index": 0 },
|
|
12
|
+
"x-prompt": "Which service should the capability be added to?"
|
|
13
|
+
},
|
|
14
|
+
"capability": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"default": "llm",
|
|
17
|
+
"description": "The capability to add (must exist in the capability registry).",
|
|
18
|
+
"x-prompt": "Which capability do you want to add?"
|
|
19
|
+
},
|
|
20
|
+
"provider": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "The provider that satisfies the capability. Defaults to the capability's default provider. Use `none` for a bare interface (interface + stub + red contract test, to be built as a bet)."
|
|
23
|
+
},
|
|
24
|
+
"stack": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Language stack of the target service. Auto-detected from the service when omitted (e.g. pyproject.toml -> python)."
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"required": ["service"]
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "llm",
|
|
3
|
+
"title": "LLM inference",
|
|
4
|
+
"description": "Text generation via a large language model. The interface is fixed; the provider (and therefore the operational footprint) is a scaffold-time choice.",
|
|
5
|
+
"default": "anthropic",
|
|
6
|
+
"providers": ["anthropic", "openai", "local", "ollama", "localai", "none"],
|
|
7
|
+
"stacks": {
|
|
8
|
+
"python": {
|
|
9
|
+
"port": "src/__packageName__/core/llm.py",
|
|
10
|
+
"portSymbol": "TextGenerator",
|
|
11
|
+
"adapter": "src/__packageName__/adapters/llm.py",
|
|
12
|
+
"adapterSymbol": "LLMClient",
|
|
13
|
+
"contractTest": "tests/contracts/test_llm.py",
|
|
14
|
+
"envFile": ".env.example",
|
|
15
|
+
"wiring": "Provide LLMClient() to your composition root (e.g. dependencies.py get_text_generator) typed as the TextGenerator protocol from core.llm, and call generate_text() from your service."
|
|
16
|
+
},
|
|
17
|
+
"go": {
|
|
18
|
+
"port": "internal/core/service/llm.go",
|
|
19
|
+
"portSymbol": "TextGenerator",
|
|
20
|
+
"adapter": "internal/llm/llm.go",
|
|
21
|
+
"adapterSymbol": "Client",
|
|
22
|
+
"contractTest": "internal/llm/llm_test.go",
|
|
23
|
+
"envFile": ".env",
|
|
24
|
+
"module": true,
|
|
25
|
+
"wiring": "Construct llm.NewClient() in cmd/api/main.go and pass it (typed as the service.TextGenerator interface) into your service. Run `go mod tidy` if a provider added a dependency."
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "env",
|
|
3
|
+
"summary": "Anthropic Claude over HTTPS. API key in env; no infrastructure.",
|
|
4
|
+
"env": [
|
|
5
|
+
{ "name": "LLM_API_KEY", "required": true, "default": "" },
|
|
6
|
+
{ "name": "LLM_BASE_URL", "required": false, "default": "" },
|
|
7
|
+
{ "name": "LLM_MODEL", "required": false, "default": "claude-sonnet-4-6" }
|
|
8
|
+
],
|
|
9
|
+
"stacks": {
|
|
10
|
+
"python": { "dependencies": ["anthropic>=0.40.0"] },
|
|
11
|
+
"go": { "dependencies": [] }
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/generators/capabilities/llm/providers/anthropic/stacks/go/internal/llm/llm.go.template
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"net/http"
|
|
10
|
+
"os"
|
|
11
|
+
"time"
|
|
12
|
+
|
|
13
|
+
"<%= moduleName %>/internal/core/service"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// Ensure Client implements the service.TextGenerator interface.
|
|
17
|
+
var _ service.TextGenerator = (*Client)(nil)
|
|
18
|
+
|
|
19
|
+
const anthropicVersion = "2023-06-01"
|
|
20
|
+
|
|
21
|
+
// Client calls the Anthropic Messages API over HTTPS. It is a minimal
|
|
22
|
+
// net/http adapter (no SDK dependency) — a transparent starting point you can
|
|
23
|
+
// extend (streaming, tool use, prompt caching) or swap for the official SDK
|
|
24
|
+
// behind this same interface. Reads LLM_API_KEY / LLM_BASE_URL / LLM_MODEL from env.
|
|
25
|
+
type Client struct {
|
|
26
|
+
apiKey string
|
|
27
|
+
baseURL string
|
|
28
|
+
model string
|
|
29
|
+
http *http.Client
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func NewClient() *Client {
|
|
33
|
+
base := os.Getenv("LLM_BASE_URL")
|
|
34
|
+
if base == "" {
|
|
35
|
+
base = "https://api.anthropic.com"
|
|
36
|
+
}
|
|
37
|
+
model := os.Getenv("LLM_MODEL")
|
|
38
|
+
if model == "" {
|
|
39
|
+
model = "claude-sonnet-4-6"
|
|
40
|
+
}
|
|
41
|
+
return &Client{
|
|
42
|
+
apiKey: os.Getenv("LLM_API_KEY"),
|
|
43
|
+
baseURL: base,
|
|
44
|
+
model: model,
|
|
45
|
+
http: &http.Client{Timeout: 60 * time.Second},
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func (a *Client) GenerateText(ctx context.Context, prompt string, maxTokens int) (string, error) {
|
|
50
|
+
reqBody, err := json.Marshal(map[string]any{
|
|
51
|
+
"model": a.model,
|
|
52
|
+
"max_tokens": maxTokens,
|
|
53
|
+
"messages": []map[string]string{{"role": "user", "content": prompt}},
|
|
54
|
+
})
|
|
55
|
+
if err != nil {
|
|
56
|
+
return "", fmt.Errorf("llm: marshal request: %w", err)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var lastErr error
|
|
60
|
+
for attempt := 0; attempt < 3; attempt++ {
|
|
61
|
+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.baseURL+"/v1/messages", bytes.NewReader(reqBody))
|
|
62
|
+
if err != nil {
|
|
63
|
+
return "", fmt.Errorf("llm: build request: %w", err)
|
|
64
|
+
}
|
|
65
|
+
req.Header.Set("content-type", "application/json")
|
|
66
|
+
req.Header.Set("x-api-key", a.apiKey)
|
|
67
|
+
req.Header.Set("anthropic-version", anthropicVersion)
|
|
68
|
+
|
|
69
|
+
resp, err := a.http.Do(req)
|
|
70
|
+
if err != nil {
|
|
71
|
+
lastErr = fmt.Errorf("llm: connection error: %w", err)
|
|
72
|
+
continue // transient — retry
|
|
73
|
+
}
|
|
74
|
+
body, _ := io.ReadAll(resp.Body)
|
|
75
|
+
resp.Body.Close()
|
|
76
|
+
|
|
77
|
+
if resp.StatusCode >= 500 || resp.StatusCode == http.StatusTooManyRequests {
|
|
78
|
+
lastErr = fmt.Errorf("llm: transient provider error (status %d): %s", resp.StatusCode, body)
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
if resp.StatusCode >= 400 {
|
|
82
|
+
return "", fmt.Errorf("llm: provider request error (status %d): %s", resp.StatusCode, body)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
var parsed struct {
|
|
86
|
+
Content []struct {
|
|
87
|
+
Type string `json:"type"`
|
|
88
|
+
Text string `json:"text"`
|
|
89
|
+
} `json:"content"`
|
|
90
|
+
}
|
|
91
|
+
if err := json.Unmarshal(body, &parsed); err != nil {
|
|
92
|
+
return "", fmt.Errorf("llm: decode response: %w", err)
|
|
93
|
+
}
|
|
94
|
+
for _, block := range parsed.Content {
|
|
95
|
+
if block.Type == "text" && block.Text != "" {
|
|
96
|
+
return block.Text, nil
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return "", fmt.Errorf("llm: empty response from provider")
|
|
100
|
+
}
|
|
101
|
+
return "", fmt.Errorf("llm: exhausted retries: %w", lastErr)
|
|
102
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from <%= packageName %>.core.domain.exceptions import TransientInferenceError, PermanentInferenceError, CircuitBreakerOpenError
|
|
2
|
+
from tenacity import retry, wait_exponential_jitter, stop_after_attempt, retry_if_exception_type
|
|
3
|
+
from circuitbreaker import circuit
|
|
4
|
+
from <%= packageName %>.adapters.config import settings
|
|
5
|
+
import anthropic
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
def fallback_circuit_breaker(e):
|
|
11
|
+
logger.error(f"Circuit breaker tripped or open! {e}")
|
|
12
|
+
raise CircuitBreakerOpenError(f"External LLM API is unavailable. Please try again later.")
|
|
13
|
+
|
|
14
|
+
class LLMClient:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
# Defer building the provider client until the first generate_text call so
|
|
17
|
+
# the service boots and serves its non-LLM routes even when no API key is
|
|
18
|
+
# configured — the Anthropic client raises at construction on a missing key,
|
|
19
|
+
# which would otherwise 500 every route that depends on this service.
|
|
20
|
+
self._client: anthropic.AsyncAnthropic | None = None
|
|
21
|
+
self.model = settings.llm_model
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def client(self) -> anthropic.AsyncAnthropic:
|
|
25
|
+
if self._client is None:
|
|
26
|
+
# base_url is optional — leave LLM_BASE_URL blank to use Anthropic's default endpoint.
|
|
27
|
+
self._client = anthropic.AsyncAnthropic(
|
|
28
|
+
api_key=settings.llm_api_key,
|
|
29
|
+
base_url=settings.llm_base_url or None,
|
|
30
|
+
)
|
|
31
|
+
return self._client
|
|
32
|
+
|
|
33
|
+
@circuit(failure_threshold=5, recovery_timeout=30, fallback_function=fallback_circuit_breaker, expected_exception=TransientInferenceError)
|
|
34
|
+
@retry(
|
|
35
|
+
wait=wait_exponential_jitter(initial=1, max=30),
|
|
36
|
+
stop=stop_after_attempt(5),
|
|
37
|
+
retry=retry_if_exception_type(TransientInferenceError)
|
|
38
|
+
)
|
|
39
|
+
async def generate_text(self, prompt: str, max_tokens: int = 100) -> str:
|
|
40
|
+
try:
|
|
41
|
+
response = await self.client.messages.create(
|
|
42
|
+
model=self.model,
|
|
43
|
+
max_tokens=max_tokens,
|
|
44
|
+
messages=[{"role": "user", "content": prompt}]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Boundary validation: ensure we got a valid text response
|
|
48
|
+
content = next((block.text for block in response.content if block.type == "text"), None)
|
|
49
|
+
if not content:
|
|
50
|
+
raise PermanentInferenceError("Received empty response from LLM provider")
|
|
51
|
+
|
|
52
|
+
return content
|
|
53
|
+
|
|
54
|
+
except anthropic.APIConnectionError as e:
|
|
55
|
+
raise TransientInferenceError(f"Connection error: {e}")
|
|
56
|
+
except anthropic.RateLimitError as e:
|
|
57
|
+
raise TransientInferenceError(f"Rate limited: {e}")
|
|
58
|
+
except anthropic.APIStatusError as e:
|
|
59
|
+
if e.status_code >= 500:
|
|
60
|
+
raise TransientInferenceError(f"Provider server error: {e}")
|
|
61
|
+
raise PermanentInferenceError(f"Provider request error: {e}")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "env",
|
|
3
|
+
"summary": "A self-hosted, OpenAI-compatible endpoint (Ollama, vLLM, llama.cpp, LM Studio). Points at a base URL you run; no cloud key, no infrastructure this scaffold provisions. If you containerise the model server, add it as a compose service; if it is a host process, register it as a runner.",
|
|
4
|
+
"env": [
|
|
5
|
+
{ "name": "LLM_API_KEY", "required": false, "default": "not-needed" },
|
|
6
|
+
{ "name": "LLM_BASE_URL", "required": true, "default": "http://localhost:11434/v1" },
|
|
7
|
+
{ "name": "LLM_MODEL", "required": false, "default": "llama3.1" }
|
|
8
|
+
],
|
|
9
|
+
"stacks": {
|
|
10
|
+
"python": { "dependencies": ["openai>=1.14.0"] },
|
|
11
|
+
"go": { "dependencies": [] }
|
|
12
|
+
}
|
|
13
|
+
}
|