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,503 @@
|
|
|
1
|
+
"""Shared fixtures for the system and bet-progress test suites.
|
|
2
|
+
|
|
3
|
+
This file is the single source of truth for service discovery and state
|
|
4
|
+
management. Both tests/system/ and tests/bets/ import from here.
|
|
5
|
+
|
|
6
|
+
Services and their ports/DBs are DISCOVERED at runtime by parsing the
|
|
7
|
+
workspace docker-compose.yml — there are no hardcoded ports and no
|
|
8
|
+
manifest file. Adding a service to the workspace automatically brings
|
|
9
|
+
it under test.
|
|
10
|
+
|
|
11
|
+
FAIL LOUD, NOT SILENT: when GROUNDWORK_REQUIRE_SERVICES=1 (set by CI and
|
|
12
|
+
by `./dev test integration`), an unreachable service is a FAILURE. Locally
|
|
13
|
+
(flag unset) the same condition skips, so the inner loop stays ergonomic.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
<% if (flutterSurfaces.length) { -%>
|
|
17
|
+
import json
|
|
18
|
+
<% } -%>
|
|
19
|
+
import os
|
|
20
|
+
<% if (cliSurfaces.length || flutterSurfaces.length || electronSurfaces.length) { -%>
|
|
21
|
+
import shlex
|
|
22
|
+
<% } -%>
|
|
23
|
+
<% if (flutterSurfaces.length) { -%>
|
|
24
|
+
import shutil
|
|
25
|
+
<% } -%>
|
|
26
|
+
<% if (cliSurfaces.length || flutterSurfaces.length || electronSurfaces.length) { -%>
|
|
27
|
+
import subprocess
|
|
28
|
+
<% } -%>
|
|
29
|
+
import uuid
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
import httpx
|
|
33
|
+
import psycopg
|
|
34
|
+
import pytest
|
|
35
|
+
import yaml
|
|
36
|
+
from tenacity import RetryError, retry, stop_after_delay, wait_exponential
|
|
37
|
+
|
|
38
|
+
# tests/ -> workspace root (conftest.py sits at tests/, one level above system/)
|
|
39
|
+
WORKSPACE_ROOT = Path(__file__).resolve().parent.parent
|
|
40
|
+
COMPOSE_PATH = WORKSPACE_ROOT / "docker-compose.yml"
|
|
41
|
+
SERVICES_DIR = WORKSPACE_ROOT / "services"
|
|
42
|
+
|
|
43
|
+
# Postgres on the dev stack is published to host localhost:5432.
|
|
44
|
+
PG_HOST = "localhost"
|
|
45
|
+
PG_PORT = 5432
|
|
46
|
+
PG_USER = "postgres"
|
|
47
|
+
PG_PASSWORD = "postgres"
|
|
48
|
+
|
|
49
|
+
# Jaeger query API (UI + /api/services + /api/traces) on the dev stack.
|
|
50
|
+
JAEGER_URL = os.environ.get("TRACE_BACKEND_URL", "http://localhost:16686")
|
|
51
|
+
|
|
52
|
+
REQUIRE_SERVICES = os.environ.get("GROUNDWORK_REQUIRE_SERVICES") == "1"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _infer_type(name: str) -> str:
|
|
56
|
+
"""Infer service type from on-disk markers under services/<name>/."""
|
|
57
|
+
svc = SERVICES_DIR / name
|
|
58
|
+
if (svc / "go.mod").exists():
|
|
59
|
+
return "go"
|
|
60
|
+
if (svc / "pyproject.toml").exists():
|
|
61
|
+
return "python"
|
|
62
|
+
if (svc / "package.json").exists():
|
|
63
|
+
return "next"
|
|
64
|
+
return "unknown"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _health_path_for(svc_type: str) -> str:
|
|
68
|
+
if svc_type == "next":
|
|
69
|
+
return "/api/healthz"
|
|
70
|
+
# Go (huma) and Python (FastAPI) both expose /health.
|
|
71
|
+
return "/health"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _probe_path_for(svc_type: str) -> str:
|
|
75
|
+
"""A real (traced) GET route per service type, used by the span tests.
|
|
76
|
+
/health is excluded from tracing by the otelhttp middleware, so use a
|
|
77
|
+
domain route. Go ships /api/v1/entities; the Python scaffold's example
|
|
78
|
+
router is mounted at /examples."""
|
|
79
|
+
if svc_type == "python":
|
|
80
|
+
return "/examples"
|
|
81
|
+
return "/api/v1/entities"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _discover_services() -> list[dict]:
|
|
85
|
+
"""Parse the workspace docker-compose.yml and return APP services only.
|
|
86
|
+
|
|
87
|
+
Discriminator: an APP service has `build.context` starting with
|
|
88
|
+
`./services/`; infra services use `image:` and are skipped.
|
|
89
|
+
"""
|
|
90
|
+
if not COMPOSE_PATH.exists():
|
|
91
|
+
return []
|
|
92
|
+
doc = yaml.safe_load(COMPOSE_PATH.read_text()) or {}
|
|
93
|
+
services = doc.get("services", {}) or {}
|
|
94
|
+
discovered: list[dict] = []
|
|
95
|
+
for name, spec in services.items():
|
|
96
|
+
spec = spec or {}
|
|
97
|
+
build = spec.get("build")
|
|
98
|
+
context = build.get("context") if isinstance(build, dict) else None
|
|
99
|
+
if not (isinstance(context, str) and context.startswith("./services/")):
|
|
100
|
+
continue # infra (image:-based) service
|
|
101
|
+
|
|
102
|
+
# host_port = left side of the first "X:Y" ports mapping.
|
|
103
|
+
host_port = None
|
|
104
|
+
for mapping in spec.get("ports", []) or []:
|
|
105
|
+
left = str(mapping).split(":")[0]
|
|
106
|
+
if left.isdigit():
|
|
107
|
+
host_port = int(left)
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
# db_name from environment DB_NAME (supports list or dict form),
|
|
111
|
+
# default to the service key.
|
|
112
|
+
db_name = name
|
|
113
|
+
env = spec.get("environment")
|
|
114
|
+
env_pairs: dict[str, str] = {}
|
|
115
|
+
if isinstance(env, list):
|
|
116
|
+
for item in env:
|
|
117
|
+
if "=" in str(item):
|
|
118
|
+
k, v = str(item).split("=", 1)
|
|
119
|
+
env_pairs[k] = v
|
|
120
|
+
elif isinstance(env, dict):
|
|
121
|
+
env_pairs = {k: str(v) for k, v in env.items()}
|
|
122
|
+
if "DB_NAME" in env_pairs:
|
|
123
|
+
# value may be "${DB_NAME:-svc}"; take the default after :- if present.
|
|
124
|
+
raw = env_pairs["DB_NAME"]
|
|
125
|
+
if ":-" in raw:
|
|
126
|
+
db_name = raw.split(":-", 1)[1].rstrip("}")
|
|
127
|
+
elif not raw.startswith("$"):
|
|
128
|
+
db_name = raw
|
|
129
|
+
|
|
130
|
+
svc_type = _infer_type(name)
|
|
131
|
+
discovered.append(
|
|
132
|
+
{
|
|
133
|
+
"name": name,
|
|
134
|
+
"host_port": host_port,
|
|
135
|
+
"db_name": db_name,
|
|
136
|
+
# otel_name == compose service key == Go otelhttp.NewMiddleware name.
|
|
137
|
+
"otel_name": name,
|
|
138
|
+
"health_path": _health_path_for(svc_type),
|
|
139
|
+
"probe_path": _probe_path_for(svc_type),
|
|
140
|
+
"type": svc_type,
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
return discovered
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@pytest.fixture(scope="session")
|
|
147
|
+
def services_manifest() -> list[dict]:
|
|
148
|
+
"""Discovered APP services. Empty list is allowed (no services yet)."""
|
|
149
|
+
return _discover_services()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _base_url(svc: dict) -> str:
|
|
153
|
+
return f"http://localhost:{svc['host_port']}"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@pytest.fixture(scope="session")
|
|
157
|
+
def cluster(services_manifest):
|
|
158
|
+
"""Health-gate the dev stack: poll every discovered service's health
|
|
159
|
+
endpoint plus the jaeger query API. FAIL if REQUIRE_SERVICES and anything
|
|
160
|
+
is unreachable after the timeout; otherwise skip (local inner loop)."""
|
|
161
|
+
|
|
162
|
+
@retry(stop=stop_after_delay(300), wait=wait_exponential(multiplier=1, min=1, max=8))
|
|
163
|
+
def _wait():
|
|
164
|
+
unreachable = []
|
|
165
|
+
for svc in services_manifest:
|
|
166
|
+
if svc["host_port"] is None:
|
|
167
|
+
continue
|
|
168
|
+
url = f"{_base_url(svc)}{svc['health_path']}"
|
|
169
|
+
try:
|
|
170
|
+
r = httpx.get(url, timeout=2.0)
|
|
171
|
+
if r.status_code != 200:
|
|
172
|
+
unreachable.append(url)
|
|
173
|
+
except httpx.HTTPError:
|
|
174
|
+
unreachable.append(url)
|
|
175
|
+
try:
|
|
176
|
+
j = httpx.get(f"{JAEGER_URL}/api/services", timeout=2.0)
|
|
177
|
+
if j.status_code != 200:
|
|
178
|
+
unreachable.append(f"{JAEGER_URL}/api/services")
|
|
179
|
+
except httpx.HTTPError:
|
|
180
|
+
unreachable.append(f"{JAEGER_URL}/api/services")
|
|
181
|
+
if unreachable:
|
|
182
|
+
raise RuntimeError(f"unreachable: {unreachable}")
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
_wait()
|
|
186
|
+
except (RetryError, RuntimeError) as exc:
|
|
187
|
+
if REQUIRE_SERVICES:
|
|
188
|
+
pytest.fail(
|
|
189
|
+
f"dev stack not healthy and GROUNDWORK_REQUIRE_SERVICES=1: {exc}"
|
|
190
|
+
)
|
|
191
|
+
pytest.skip(f"dev stack not reachable (run `./dev start`): {exc}")
|
|
192
|
+
return services_manifest
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@pytest.fixture
|
|
196
|
+
def trace_id():
|
|
197
|
+
"""A W3C trace id (32 hex chars)."""
|
|
198
|
+
return uuid.uuid4().hex
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@pytest.fixture
|
|
202
|
+
async def api_client(trace_id):
|
|
203
|
+
"""Async HTTPX client that stamps a W3C traceparent so test traffic is
|
|
204
|
+
identifiable in jaeger."""
|
|
205
|
+
span_id = uuid.uuid4().hex[:16]
|
|
206
|
+
traceparent = f"00-{trace_id}-{span_id}-01"
|
|
207
|
+
headers = {"traceparent": traceparent, "x-test-run": "system-test"}
|
|
208
|
+
async with httpx.AsyncClient(headers=headers, timeout=10.0) as client:
|
|
209
|
+
yield client
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@pytest.fixture(scope="function", autouse=True)
|
|
213
|
+
def pure_state_reset(services_manifest):
|
|
214
|
+
"""Truncate every table in each discovered service's database before each
|
|
215
|
+
test. Runs for BOTH system/ and bets/ suites (autouse is rootdir-scoped).
|
|
216
|
+
Tolerates a missing DB (not yet created) and an empty table set."""
|
|
217
|
+
for svc in services_manifest:
|
|
218
|
+
dsn = (
|
|
219
|
+
f"postgresql://{PG_USER}:{PG_PASSWORD}@{PG_HOST}:{PG_PORT}/{svc['db_name']}"
|
|
220
|
+
)
|
|
221
|
+
try:
|
|
222
|
+
with psycopg.connect(dsn, autocommit=True) as conn:
|
|
223
|
+
with conn.cursor() as cur:
|
|
224
|
+
cur.execute(
|
|
225
|
+
"SELECT tablename FROM pg_tables WHERE schemaname='public'"
|
|
226
|
+
)
|
|
227
|
+
tables = [row[0] for row in cur.fetchall()]
|
|
228
|
+
if tables:
|
|
229
|
+
joined = ", ".join(f'"{t}"' for t in tables)
|
|
230
|
+
cur.execute(
|
|
231
|
+
f"TRUNCATE {joined} RESTART IDENTITY CASCADE"
|
|
232
|
+
)
|
|
233
|
+
except psycopg.OperationalError:
|
|
234
|
+
pass # DB may not exist yet (pre-migrate); tolerate it.
|
|
235
|
+
yield
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
<% if (!surfaces && interfaceMedium === 'graphical-ui') { %>
|
|
239
|
+
@pytest.fixture(scope="session")
|
|
240
|
+
def frontend_base_url(services_manifest):
|
|
241
|
+
"""Base URL for the Next.js frontend service. Used by graphical-ui bet-progress tests.
|
|
242
|
+
|
|
243
|
+
Discovers the first service of type 'next' from the workspace docker-compose.yml
|
|
244
|
+
and returns its host URL. Skips cleanly when no Next.js service is present.
|
|
245
|
+
"""
|
|
246
|
+
next_svcs = [s for s in services_manifest if s.get("type") == "next"]
|
|
247
|
+
if not next_svcs:
|
|
248
|
+
pytest.skip("No Next.js service found in docker-compose.yml — frontend_base_url unavailable")
|
|
249
|
+
svc = next_svcs[0]
|
|
250
|
+
if svc["host_port"] is None:
|
|
251
|
+
pytest.skip(f"Next.js service '{svc['name']}' has no published host port")
|
|
252
|
+
return f"http://localhost:{svc['host_port']}"
|
|
253
|
+
<% } %><% if (surfaces) { %>
|
|
254
|
+
# ---------------------------------------------------------------------------
|
|
255
|
+
# Surface fixtures
|
|
256
|
+
#
|
|
257
|
+
# `surfaces` is the canonical per-surface reach map, derived from the surface
|
|
258
|
+
# registry (docs/surfaces.md / .groundwork/surfaces.json) at scaffold time.
|
|
259
|
+
# Per-surface runner fixtures are generated by test medium:
|
|
260
|
+
# playwright -> <slug>_page (Playwright page scoped to that surface)
|
|
261
|
+
# subprocess-cli -> <slug>_runner (invokes the binary, returns CompletedProcess)
|
|
262
|
+
# protocol-client -> <slug>_client (httpx client bound to the endpoint)
|
|
263
|
+
# flutter-integration -> <slug>_runner (drives the app's own integration_test
|
|
264
|
+
# suite as a subprocess via its Nx target)
|
|
265
|
+
# playwright-electron -> <slug>_runner (drives the app's own Playwright
|
|
266
|
+
# _electron smoke as a subprocess via its Nx target)
|
|
267
|
+
# A surface whose medium has no generated family (e.g. a scaffold: manual
|
|
268
|
+
# surface on bespoke tooling) is still registered in `surfaces` so tests can name it; the
|
|
269
|
+
# implementation must keep this registration honest when it lands — a health
|
|
270
|
+
# endpoint, ./dev integration, and a reach value tests can use.
|
|
271
|
+
# ---------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
# Scaffold-time surface specs. reach=None means "discover at runtime": URL
|
|
274
|
+
# mediums resolve the docker-compose service named after the slug.
|
|
275
|
+
_SURFACE_SPECS: dict[str, dict] = {
|
|
276
|
+
<% surfaces.forEach((s) => { %> "<%= s.slug %>": {"medium": "<%= s.medium %>", "reach": <%- s.reach ? JSON.stringify(s.reach) : 'None' %>},
|
|
277
|
+
<% }) %>}
|
|
278
|
+
|
|
279
|
+
_URL_MEDIUMS = {"playwright", "protocol-client"}
|
|
280
|
+
<% if (flutterSurfaces.length || electronSurfaces.length) { %>
|
|
281
|
+
# App-harness mediums -> the Nx target that runs the surface's OWN test suite.
|
|
282
|
+
# Their reach is a command (like subprocess-cli), not a URL: the suite, not
|
|
283
|
+
# this conftest, holds the Dart/Electron driver.
|
|
284
|
+
_HARNESS_MEDIUMS = {
|
|
285
|
+
"flutter-integration": "test-integration",
|
|
286
|
+
"playwright-electron": "smoke",
|
|
287
|
+
}
|
|
288
|
+
<% } %>
|
|
289
|
+
|
|
290
|
+
def _surface_reach(slug: str, spec: dict, manifest: list[dict]) -> str | None:
|
|
291
|
+
"""Resolve a surface's reach: the scaffold-time value when set, otherwise
|
|
292
|
+
the compose service whose name matches the slug (URL mediums) or the
|
|
293
|
+
scaffolded app's Nx test target (app-harness mediums)."""
|
|
294
|
+
if spec["reach"] is not None:
|
|
295
|
+
return spec["reach"]
|
|
296
|
+
<% if (flutterSurfaces.length || electronSurfaces.length) { %> if spec["medium"] in _HARNESS_MEDIUMS:
|
|
297
|
+
if (SERVICES_DIR / slug / "project.json").exists():
|
|
298
|
+
return f"npx nx run {slug}:{_HARNESS_MEDIUMS[spec['medium']]}"
|
|
299
|
+
return None
|
|
300
|
+
<% } %> if spec["medium"] not in _URL_MEDIUMS:
|
|
301
|
+
return None
|
|
302
|
+
for svc in manifest:
|
|
303
|
+
if svc["name"] == slug and svc["host_port"] is not None:
|
|
304
|
+
return f"http://localhost:{svc['host_port']}"
|
|
305
|
+
<% if (graphicalSurfaces.length === 1) { %> # Single graphical surface: fall back to the first Next.js service — the
|
|
306
|
+
# same discovery the deprecated frontend_base_url fixture used.
|
|
307
|
+
if spec["medium"] == "playwright":
|
|
308
|
+
for svc in manifest:
|
|
309
|
+
if svc.get("type") == "next" and svc["host_port"] is not None:
|
|
310
|
+
return f"http://localhost:{svc['host_port']}"
|
|
311
|
+
<% } %> return None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@pytest.fixture(scope="session")
|
|
315
|
+
def surfaces(services_manifest) -> dict[str, dict]:
|
|
316
|
+
"""Surface slug -> {"slug", "medium", "reach"} for every registered surface.
|
|
317
|
+
|
|
318
|
+
reach is a base URL for playwright/protocol-client surfaces, a launch
|
|
319
|
+
command for subprocess-cli surfaces, the test-harness command for
|
|
320
|
+
flutter-integration/playwright-electron surfaces, and None when the
|
|
321
|
+
surface is not reachable yet (its runner fixture skips).
|
|
322
|
+
"""
|
|
323
|
+
return {
|
|
324
|
+
slug: {
|
|
325
|
+
"slug": slug,
|
|
326
|
+
"medium": spec["medium"],
|
|
327
|
+
"reach": _surface_reach(slug, spec, services_manifest),
|
|
328
|
+
}
|
|
329
|
+
for slug, spec in _SURFACE_SPECS.items()
|
|
330
|
+
}
|
|
331
|
+
<% graphicalSurfaces.forEach((s) => { %>
|
|
332
|
+
|
|
333
|
+
@pytest.fixture
|
|
334
|
+
def <%= s.ident %>_page(browser, surfaces):
|
|
335
|
+
"""Playwright page for the `<%= s.slug %>` surface. Relative page.goto()
|
|
336
|
+
paths resolve against the surface's base URL."""
|
|
337
|
+
base = surfaces["<%= s.slug %>"]["reach"]
|
|
338
|
+
if base is None:
|
|
339
|
+
pytest.skip("surface '<%= s.slug %>' has no reachable base URL (is its service in docker-compose.yml?)")
|
|
340
|
+
context = browser.new_context(base_url=base)
|
|
341
|
+
page = context.new_page()
|
|
342
|
+
yield page
|
|
343
|
+
context.close()
|
|
344
|
+
<% }) %><% cliSurfaces.forEach((s) => { %>
|
|
345
|
+
|
|
346
|
+
@pytest.fixture
|
|
347
|
+
def <%= s.ident %>_runner(surfaces):
|
|
348
|
+
"""Subprocess runner for the `<%= s.slug %>` CLI surface: run(args, ...) ->
|
|
349
|
+
subprocess.CompletedProcess. Drive interactive (REPL) flows with pexpect
|
|
350
|
+
against the same launch command."""
|
|
351
|
+
command = surfaces["<%= s.slug %>"]["reach"]
|
|
352
|
+
if command is None:
|
|
353
|
+
pytest.skip("surface '<%= s.slug %>' has no launch command registered")
|
|
354
|
+
|
|
355
|
+
def run(args, input=None, timeout=30, env=None):
|
|
356
|
+
cmd = [*shlex.split(command), *[str(a) for a in args]]
|
|
357
|
+
return subprocess.run(
|
|
358
|
+
cmd,
|
|
359
|
+
input=input,
|
|
360
|
+
capture_output=True,
|
|
361
|
+
text=True,
|
|
362
|
+
timeout=timeout,
|
|
363
|
+
env={**os.environ, **(env or {})},
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
return run
|
|
367
|
+
<% }) %><% protocolSurfaces.forEach((s) => { %>
|
|
368
|
+
|
|
369
|
+
@pytest.fixture
|
|
370
|
+
async def <%= s.ident %>_client(surfaces):
|
|
371
|
+
"""Async httpx client bound to the `<%= s.slug %>` protocol endpoint."""
|
|
372
|
+
endpoint = surfaces["<%= s.slug %>"]["reach"]
|
|
373
|
+
if endpoint is None:
|
|
374
|
+
pytest.skip("surface '<%= s.slug %>' has no reachable endpoint (is its service in docker-compose.yml?)")
|
|
375
|
+
async with httpx.AsyncClient(base_url=endpoint, timeout=10.0) as client:
|
|
376
|
+
yield client
|
|
377
|
+
<% }) %><% if (flutterSurfaces.length || electronSurfaces.length) { %>
|
|
378
|
+
|
|
379
|
+
def _core_base_url(manifest: list[dict]) -> str | None:
|
|
380
|
+
"""The capability core's gateway URL for app-harness surfaces: the compose
|
|
381
|
+
service named (or suffixed) `gateway` when one exists, otherwise the first
|
|
382
|
+
discovered service with a published port — the same booted topology the
|
|
383
|
+
API tests hit."""
|
|
384
|
+
candidates = [s for s in manifest if s["host_port"] is not None]
|
|
385
|
+
for svc in candidates:
|
|
386
|
+
if svc["name"] == "gateway" or svc["name"].endswith("-gateway"):
|
|
387
|
+
return _base_url(svc)
|
|
388
|
+
return _base_url(candidates[0]) if candidates else None
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def _harness_runner(slug: str, command: str, *, default_args=(), default_env=None):
|
|
392
|
+
"""Build a run() callable that drives a surface's OWN test harness as a
|
|
393
|
+
subprocess — the only honest integration this conftest can offer a Dart or
|
|
394
|
+
Electron suite, and what keeps the surface loop thin (prove-once: the
|
|
395
|
+
harness asserts wiring and rendering; capability behaviour is already
|
|
396
|
+
proven headless at the core's contract). Running through the app's Nx
|
|
397
|
+
target reuses its tool/*_exec.sh toolchain guard; a guard exit ("tier
|
|
398
|
+
skipped") becomes pytest.skip with the guard's reason — skipped with
|
|
399
|
+
reason, never silently green."""
|
|
400
|
+
|
|
401
|
+
def run(extra_args=(), timeout=900, env=None):
|
|
402
|
+
cmd = [*shlex.split(command), *[str(a) for a in (*default_args, *extra_args)]]
|
|
403
|
+
proc = subprocess.run(
|
|
404
|
+
cmd,
|
|
405
|
+
cwd=WORKSPACE_ROOT,
|
|
406
|
+
capture_output=True,
|
|
407
|
+
text=True,
|
|
408
|
+
timeout=timeout,
|
|
409
|
+
env={**os.environ, **(default_env or {}), **(env or {})},
|
|
410
|
+
)
|
|
411
|
+
if proc.returncode != 0 and "tier skipped" in (proc.stderr or ""):
|
|
412
|
+
pytest.skip(
|
|
413
|
+
f"surface '{slug}' harness unavailable — skipped, not silently "
|
|
414
|
+
f"green: {proc.stderr.strip()}"
|
|
415
|
+
)
|
|
416
|
+
return proc
|
|
417
|
+
|
|
418
|
+
return run
|
|
419
|
+
<% } %><% flutterSurfaces.forEach((s) => { %>
|
|
420
|
+
|
|
421
|
+
@pytest.fixture
|
|
422
|
+
def <%= s.ident %>_runner(surfaces, services_manifest):
|
|
423
|
+
"""Flutter integration_test runner for the `<%= s.slug %>` mobile surface:
|
|
424
|
+
run(extra_args, ...) -> subprocess.CompletedProcess, driving the app's own
|
|
425
|
+
integration_test suite as a subprocess via its `test-integration` Nx
|
|
426
|
+
target. Prove-once: that suite asserts wiring and rendering only —
|
|
427
|
+
capability behaviour is already proven headless at the core's contract,
|
|
428
|
+
which is what keeps the expensive emulator loop thin. The core's gateway
|
|
429
|
+
URL is forwarded via --dart-define=API_BASE_URL so surface tests hit the
|
|
430
|
+
same booted topology the API tests use. Skips with reason when the Flutter
|
|
431
|
+
SDK or a device/emulator is unavailable — never silently green. Patrol is
|
|
432
|
+
the escalation path when a flow crosses the Flutter/OS boundary
|
|
433
|
+
(permission dialogs, push, system sheets); it is not wired here."""
|
|
434
|
+
command = surfaces["<%= s.slug %>"]["reach"]
|
|
435
|
+
if command is None:
|
|
436
|
+
pytest.skip("surface '<%= s.slug %>' has no test-harness command (is services/<%= s.slug %> scaffolded?)")
|
|
437
|
+
if shutil.which("flutter") is None:
|
|
438
|
+
pytest.skip(
|
|
439
|
+
"flutter SDK not found — '<%= s.slug %>' surface tier skipped, not "
|
|
440
|
+
"silently green (install the Flutter SDK, then `npx nx run <%= s.slug %>:bootstrap`)"
|
|
441
|
+
)
|
|
442
|
+
try:
|
|
443
|
+
probe = subprocess.run(
|
|
444
|
+
["flutter", "devices", "--machine"],
|
|
445
|
+
capture_output=True,
|
|
446
|
+
text=True,
|
|
447
|
+
timeout=120,
|
|
448
|
+
)
|
|
449
|
+
devices = json.loads(probe.stdout or "[]")
|
|
450
|
+
except (OSError, subprocess.SubprocessError, ValueError):
|
|
451
|
+
devices = []
|
|
452
|
+
# Only platforms this app project supports count: a host machine usually
|
|
453
|
+
# reports desktop/web "devices" (macOS, Chrome) that `flutter test
|
|
454
|
+
# integration_test` cannot target for an android/ios scaffold — counting
|
|
455
|
+
# them turns the intended skip into a hard no-devices failure.
|
|
456
|
+
mobile_devices = [
|
|
457
|
+
d
|
|
458
|
+
for d in devices
|
|
459
|
+
if str(d.get("targetPlatform", "")).startswith(("android", "ios"))
|
|
460
|
+
]
|
|
461
|
+
if not mobile_devices:
|
|
462
|
+
pytest.skip(
|
|
463
|
+
"no Android/iOS device or emulator available — '<%= s.slug %>' surface tier "
|
|
464
|
+
"skipped, not silently green (boot a headless Android emulator; "
|
|
465
|
+
"docs/principles/stack/flutter/testing.md)"
|
|
466
|
+
)
|
|
467
|
+
api_base = _core_base_url(services_manifest)
|
|
468
|
+
# `--` so Nx forwards the define verbatim to tool/flutter_exec.sh.
|
|
469
|
+
dart_defines = ["--", f"--dart-define=API_BASE_URL={api_base}"] if api_base else []
|
|
470
|
+
return _harness_runner("<%= s.slug %>", command, default_args=dart_defines)
|
|
471
|
+
<% }) %><% electronSurfaces.forEach((s) => { %>
|
|
472
|
+
|
|
473
|
+
@pytest.fixture
|
|
474
|
+
def <%= s.ident %>_runner(surfaces, services_manifest):
|
|
475
|
+
"""Playwright _electron smoke runner for the `<%= s.slug %>` desktop surface:
|
|
476
|
+
run(extra_args, ...) -> subprocess.CompletedProcess, driving the app's own
|
|
477
|
+
Playwright _electron suite as a subprocess via its `smoke` Nx target.
|
|
478
|
+
Prove-once: that suite asserts boot, wiring, and rendering only —
|
|
479
|
+
capability behaviour is already proven headless at the core's contract.
|
|
480
|
+
tool/electron_exec.sh owns the toolchain question (Electron binary, xvfb
|
|
481
|
+
on displayless Linux); its guard exits are translated here into
|
|
482
|
+
pytest.skip with the guard's reason — never silently green. The core's
|
|
483
|
+
gateway URL is passed via the API_BASE_URL environment variable."""
|
|
484
|
+
command = surfaces["<%= s.slug %>"]["reach"]
|
|
485
|
+
if command is None:
|
|
486
|
+
pytest.skip("surface '<%= s.slug %>' has no test-harness command (is services/<%= s.slug %> scaffolded?)")
|
|
487
|
+
api_base = _core_base_url(services_manifest)
|
|
488
|
+
env = {"API_BASE_URL": api_base} if api_base else {}
|
|
489
|
+
return _harness_runner("<%= s.slug %>", command, default_env=env)
|
|
490
|
+
<% }) %><% if (graphicalSurfaces.length === 1) { %>
|
|
491
|
+
|
|
492
|
+
@pytest.fixture(scope="session")
|
|
493
|
+
def frontend_base_url(surfaces):
|
|
494
|
+
"""DEPRECATED alias for surfaces["<%= graphicalSurfaces[0].slug %>"]["reach"].
|
|
495
|
+
|
|
496
|
+
Generated only while exactly one graphical surface exists; new tests read
|
|
497
|
+
the `surfaces` fixture instead.
|
|
498
|
+
"""
|
|
499
|
+
base = surfaces["<%= graphicalSurfaces[0].slug %>"]["reach"]
|
|
500
|
+
if base is None:
|
|
501
|
+
pytest.skip("surface '<%= graphicalSurfaces[0].slug %>' has no reachable base URL — frontend_base_url unavailable")
|
|
502
|
+
return base
|
|
503
|
+
<% } %><% } %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tests"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "System Test Runner for <%= projectPrefix %>"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"pytest>=8.0.0",
|
|
8
|
+
"pytest-asyncio>=0.23.0",
|
|
9
|
+
"pytest-timeout>=2.3.1",
|
|
10
|
+
"httpx>=0.27.0",
|
|
11
|
+
"tenacity>=8.2.3",
|
|
12
|
+
"psycopg[binary]>=3.1.18",
|
|
13
|
+
"PyYAML>=6.0",<% if (includePlaywright) { %>
|
|
14
|
+
"pytest-playwright>=0.5",<% } %><% if (includePexpect) { %>
|
|
15
|
+
"pexpect>=4.9",<% } %>
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.pytest.ini_options]
|
|
19
|
+
asyncio_mode = "auto"
|
|
20
|
+
testpaths = ["system", "bets"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Minimal page-object base for graphical-ui system tests.
|
|
2
|
+
|
|
3
|
+
Subclass per screen and add intent-named methods (e.g. submit_login) on top of
|
|
4
|
+
the shared navigation/assertion helpers here. <% if (!surfaces) { %>The frontend base URL comes from
|
|
5
|
+
the conftest `frontend_base_url` fixture, which discovers the Next.js service
|
|
6
|
+
from the workspace docker-compose.yml.<% } else { %>Each graphical surface's base URL
|
|
7
|
+
comes from the conftest `surfaces` fixture (slug -> reach).<% } %>
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from playwright.sync_api import Page, expect
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BasePage:
|
|
14
|
+
"""Holds the Playwright page plus the frontend base URL.
|
|
15
|
+
|
|
16
|
+
Helpers return self so page interactions chain:
|
|
17
|
+
<% if (!surfaces) { -%>
|
|
18
|
+
HomePage(page, frontend_base_url).goto("/").expect_visible("main")
|
|
19
|
+
<% } else { -%>
|
|
20
|
+
HomePage(page, surfaces["<%= graphicalSurfaces.length ? graphicalSurfaces[0].slug : 'web-app' %>"]["reach"]).goto("/").expect_visible("main")
|
|
21
|
+
<% } -%>
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, page: Page, base_url: str):
|
|
25
|
+
self.page = page
|
|
26
|
+
self.base_url = base_url.rstrip("/")
|
|
27
|
+
|
|
28
|
+
def goto(self, path: str = "/") -> "BasePage":
|
|
29
|
+
"""Navigate to a path under the frontend base URL and wait for load."""
|
|
30
|
+
self.page.goto(f"{self.base_url}{path}", wait_until="load")
|
|
31
|
+
return self
|
|
32
|
+
|
|
33
|
+
def expect_visible(self, selector: str, timeout_ms: float = 10_000) -> "BasePage":
|
|
34
|
+
"""Assert the first element matching the selector is visible."""
|
|
35
|
+
expect(self.page.locator(selector).first).to_be_visible(timeout=timeout_ms)
|
|
36
|
+
return self
|