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,288 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatFiles,
|
|
3
|
+
generateFiles,
|
|
4
|
+
Tree,
|
|
5
|
+
names,
|
|
6
|
+
} from '@nx/devkit';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { recordGeneratorProvenance } from '../shared/provenance';
|
|
9
|
+
import {
|
|
10
|
+
promoteEngineerSkill,
|
|
11
|
+
deployStackDocs,
|
|
12
|
+
registerRunner,
|
|
13
|
+
} from '../shared/scaffold-helpers';
|
|
14
|
+
|
|
15
|
+
export interface ElectronAppGeneratorSchema {
|
|
16
|
+
name: string;
|
|
17
|
+
org?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const BRAND_TOKENS_PATH = '.groundwork/config/brand-tokens.json';
|
|
21
|
+
|
|
22
|
+
/** Palette roles the visual block commits to (templates/brand-tokens.md). */
|
|
23
|
+
const PALETTE_ROLES = [
|
|
24
|
+
'primary',
|
|
25
|
+
'accent',
|
|
26
|
+
'surface',
|
|
27
|
+
'surfaceAlt',
|
|
28
|
+
'textBody',
|
|
29
|
+
'success',
|
|
30
|
+
'error',
|
|
31
|
+
'warning',
|
|
32
|
+
'info',
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
/** camelCase palette role → kebab-case CSS custom property suffix. */
|
|
36
|
+
function cssRoleName(role: string): string {
|
|
37
|
+
return role.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Neutral defaults used when no visual block (or no tokens at all) exists.
|
|
41
|
+
* Mirrors how cli-app and flutter-app degrade: the app still themes
|
|
42
|
+
* coherently, it just wears a restrained default brand. */
|
|
43
|
+
const NEUTRAL_PALETTE: Record<string, { light: string; dark: string }> = {
|
|
44
|
+
primary: { light: '#3b6fd4', dark: '#7da7ef' },
|
|
45
|
+
accent: { light: '#7a5fd4', dark: '#b3a1f0' },
|
|
46
|
+
surface: { light: '#fafafa', dark: '#17181c' },
|
|
47
|
+
surfaceAlt: { light: '#f0f1f4', dark: '#1f2127' },
|
|
48
|
+
textBody: { light: '#26282e', dark: '#e4e6eb' },
|
|
49
|
+
success: { light: '#2f9e6e', dark: '#5fc398' },
|
|
50
|
+
error: { light: '#cf4444', dark: '#ef7f7f' },
|
|
51
|
+
warning: { light: '#c08a1f', dark: '#e0b35f' },
|
|
52
|
+
info: { light: '#3b6fd4', dark: '#7da7ef' },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const NEUTRAL_TYPOGRAPHY = {
|
|
56
|
+
display: { family: 'Inter', weight: 600 },
|
|
57
|
+
body: { family: 'Inter', weight: 400 },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const NEUTRAL_RADIUS = '8px';
|
|
61
|
+
|
|
62
|
+
/** The renderer is CSS, so colour values pass through verbatim — the browser
|
|
63
|
+
* resolves OKLCH natively (unlike the Dart projection, which converts at
|
|
64
|
+
* generation time). Validation still gates what enters the file: a value the
|
|
65
|
+
* regexes cannot recognise falls back to the neutral default instead of
|
|
66
|
+
* emitting broken CSS. */
|
|
67
|
+
function validCssColor(value: unknown): string | null {
|
|
68
|
+
if (typeof value !== 'string') return null;
|
|
69
|
+
const v = value.trim();
|
|
70
|
+
if (/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v)) return v.toLowerCase();
|
|
71
|
+
if (/^oklch\(\s*[\d.]+%?\s+[\d.]+\s+[\d.]+(?:deg)?\s*\)$/i.test(v)) return v;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Validate a radius descriptor like "8px" / "0.5rem" / "12"; bare numbers are
|
|
76
|
+
* treated as logical pixels. */
|
|
77
|
+
function validCssRadius(value: unknown): string | null {
|
|
78
|
+
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
|
79
|
+
const m = String(value)
|
|
80
|
+
.trim()
|
|
81
|
+
.match(/^([\d.]+)\s*(px|rem)?$/i);
|
|
82
|
+
if (!m || !Number.isFinite(parseFloat(m[1]))) return null;
|
|
83
|
+
return `${m[1]}${(m[2] || 'px').toLowerCase()}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface ResolvedBrand {
|
|
87
|
+
palette: Record<string, { light: string; dark: string }>; // CSS color values
|
|
88
|
+
typography: {
|
|
89
|
+
display: { family: string; weight: number };
|
|
90
|
+
body: { family: string; weight: number };
|
|
91
|
+
};
|
|
92
|
+
radius: string;
|
|
93
|
+
source: 'visual-block' | 'identity-only' | 'default';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Project the design system's brand tokens into theme inputs. Three cases,
|
|
97
|
+
* mirroring the cli-app/flutter-app token handling:
|
|
98
|
+
* - a Tier 2 `visual` block → full projection (palette roles, typography, shape)
|
|
99
|
+
* - Tier-1-only tokens → palette seeded from identity.primary/accent
|
|
100
|
+
* - no tokens → the neutral default */
|
|
101
|
+
function resolveBrand(tree: Tree): ResolvedBrand {
|
|
102
|
+
const palette: Record<string, { light: string; dark: string }> = {};
|
|
103
|
+
for (const role of PALETTE_ROLES) {
|
|
104
|
+
palette[role] = { ...NEUTRAL_PALETTE[role] };
|
|
105
|
+
}
|
|
106
|
+
const typography = {
|
|
107
|
+
display: { ...NEUTRAL_TYPOGRAPHY.display },
|
|
108
|
+
body: { ...NEUTRAL_TYPOGRAPHY.body },
|
|
109
|
+
};
|
|
110
|
+
let radius = NEUTRAL_RADIUS;
|
|
111
|
+
let source: ResolvedBrand['source'] = 'default';
|
|
112
|
+
|
|
113
|
+
if (!tree.exists(BRAND_TOKENS_PATH)) {
|
|
114
|
+
return { palette, typography, radius, source };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let raw: any = null;
|
|
118
|
+
try {
|
|
119
|
+
raw = JSON.parse(tree.read(BRAND_TOKENS_PATH, 'utf-8') || '{}');
|
|
120
|
+
} catch {
|
|
121
|
+
return { palette, typography, radius, source }; // malformed — neutral
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const visual = raw?.visual;
|
|
125
|
+
if (visual && typeof visual === 'object') {
|
|
126
|
+
source = 'visual-block';
|
|
127
|
+
for (const role of PALETTE_ROLES) {
|
|
128
|
+
const entry = visual.palette?.[role];
|
|
129
|
+
const light = validCssColor(entry?.light);
|
|
130
|
+
const dark = validCssColor(entry?.dark);
|
|
131
|
+
if (light) palette[role].light = light;
|
|
132
|
+
if (dark) palette[role].dark = dark;
|
|
133
|
+
}
|
|
134
|
+
if (visual.typography?.display?.family) {
|
|
135
|
+
typography.display.family = String(visual.typography.display.family);
|
|
136
|
+
typography.display.weight =
|
|
137
|
+
Number(visual.typography.display.weight) || typography.display.weight;
|
|
138
|
+
}
|
|
139
|
+
if (visual.typography?.body?.family) {
|
|
140
|
+
typography.body.family = String(visual.typography.body.family);
|
|
141
|
+
typography.body.weight =
|
|
142
|
+
Number(visual.typography.body.weight) || typography.body.weight;
|
|
143
|
+
}
|
|
144
|
+
radius = validCssRadius(visual.shape?.radiusBase) ?? NEUTRAL_RADIUS;
|
|
145
|
+
return { palette, typography, radius, source };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Tier-1-only tokens: derive the brand colours from identity, keep the
|
|
149
|
+
// neutral chrome. This is the mechanical Tier-1 projection the brand-tokens
|
|
150
|
+
// contract requires of every consumer.
|
|
151
|
+
const identity = raw?.identity;
|
|
152
|
+
if (identity && typeof identity === 'object') {
|
|
153
|
+
source = 'identity-only';
|
|
154
|
+
const primary = validCssColor(identity.primary);
|
|
155
|
+
const accent = validCssColor(identity.accent);
|
|
156
|
+
if (primary) {
|
|
157
|
+
palette.primary = { light: primary, dark: primary };
|
|
158
|
+
palette.info = { light: primary, dark: primary };
|
|
159
|
+
}
|
|
160
|
+
if (accent) palette.accent = { light: accent, dark: accent };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { palette, typography, radius, source };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Render the generated brand stylesheet. The CSS custom properties are the
|
|
167
|
+
* projection artifact (regenerate, never hand-edit); the static main.css maps
|
|
168
|
+
* them into Tailwind theme tokens via `@theme inline`, so components consume
|
|
169
|
+
* utilities, never these variables directly. */
|
|
170
|
+
function renderBrandCss(brand: ResolvedBrand): string {
|
|
171
|
+
const lines: string[] = [];
|
|
172
|
+
lines.push('/* GENERATED by the GroundWork electron-app generator from');
|
|
173
|
+
lines.push(
|
|
174
|
+
' * .groundwork/config/brand-tokens.json — do not hand-edit this file.',
|
|
175
|
+
);
|
|
176
|
+
lines.push(' *');
|
|
177
|
+
lines.push(
|
|
178
|
+
' * Re-run the design system to evolve the brand, then regenerate (or update',
|
|
179
|
+
);
|
|
180
|
+
lines.push(
|
|
181
|
+
' * this file to match brand-tokens.json). Components never read these',
|
|
182
|
+
);
|
|
183
|
+
lines.push(
|
|
184
|
+
' * variables directly: main.css maps them into Tailwind theme tokens, and',
|
|
185
|
+
);
|
|
186
|
+
lines.push(
|
|
187
|
+
' * components consume the utilities (docs/principles/stack/typescript/frontend.md).',
|
|
188
|
+
);
|
|
189
|
+
lines.push(` * Projection source: ${brand.source}.`);
|
|
190
|
+
lines.push(' */');
|
|
191
|
+
lines.push('');
|
|
192
|
+
lines.push('/* Palette roles — light theme. */');
|
|
193
|
+
lines.push(':root {');
|
|
194
|
+
for (const role of PALETTE_ROLES) {
|
|
195
|
+
lines.push(` --gw-${cssRoleName(role)}: ${brand.palette[role].light};`);
|
|
196
|
+
}
|
|
197
|
+
lines.push('');
|
|
198
|
+
lines.push(' /* Typography (bundle or system-load the families to take effect). */');
|
|
199
|
+
lines.push(
|
|
200
|
+
` --gw-font-display: '${brand.typography.display.family.replace(/'/g, '')}';`,
|
|
201
|
+
);
|
|
202
|
+
lines.push(` --gw-font-display-weight: ${brand.typography.display.weight};`);
|
|
203
|
+
lines.push(
|
|
204
|
+
` --gw-font-body: '${brand.typography.body.family.replace(/'/g, '')}';`,
|
|
205
|
+
);
|
|
206
|
+
lines.push(` --gw-font-body-weight: ${brand.typography.body.weight};`);
|
|
207
|
+
lines.push('');
|
|
208
|
+
lines.push(' /* Shape. */');
|
|
209
|
+
lines.push(` --gw-radius-base: ${brand.radius};`);
|
|
210
|
+
lines.push('}');
|
|
211
|
+
lines.push('');
|
|
212
|
+
lines.push('/* Palette roles — dark theme. The attribute is set from the');
|
|
213
|
+
lines.push(' * nativeTheme broadcast (src/renderer/src/main.tsx). */');
|
|
214
|
+
lines.push(":root[data-theme='dark'] {");
|
|
215
|
+
for (const role of PALETTE_ROLES) {
|
|
216
|
+
lines.push(` --gw-${cssRoleName(role)}: ${brand.palette[role].dark};`);
|
|
217
|
+
}
|
|
218
|
+
lines.push('}');
|
|
219
|
+
lines.push('');
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export default async function (tree: Tree, options: ElectronAppGeneratorSchema) {
|
|
224
|
+
const serviceNames = names(options.name);
|
|
225
|
+
const projectRoot = `services/${serviceNames.fileName}`;
|
|
226
|
+
const org = options.org || 'com.example';
|
|
227
|
+
// Reverse-domain application id (macOS bundle id, Windows AppUserModelID).
|
|
228
|
+
const appId = `${org}.${serviceNames.fileName.replace(/-/g, '')}`;
|
|
229
|
+
|
|
230
|
+
const templateOptions = {
|
|
231
|
+
...options,
|
|
232
|
+
...serviceNames,
|
|
233
|
+
org,
|
|
234
|
+
appId,
|
|
235
|
+
tmpl: '', // required by generateFiles
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
generateFiles(
|
|
239
|
+
tree,
|
|
240
|
+
path.join(__dirname, '..', '..', '..', '..', 'src', 'generators', 'electron-app', 'files'),
|
|
241
|
+
projectRoot,
|
|
242
|
+
templateOptions,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Project brand-tokens.json's visual block into the renderer's CSS custom
|
|
246
|
+
// properties — the same Tailwind path nextjs-app uses, generated instead of
|
|
247
|
+
// hand-authored.
|
|
248
|
+
tree.write(
|
|
249
|
+
`${projectRoot}/src/renderer/src/assets/brand.css`,
|
|
250
|
+
renderBrandCss(resolveBrand(tree)),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
deployStackDocs(tree, path.join(__dirname, '..', '..', '..', '..', 'src', 'generators', 'electron-app', 'docs'));
|
|
254
|
+
|
|
255
|
+
// An Electron app has no Docker boot — but it IS a managed unit. It never
|
|
256
|
+
// joins docker-compose.yml or the package.json workspaces list; instead it
|
|
257
|
+
// registers as a native runner so `./dev` start/stop/status/logs manage it
|
|
258
|
+
// like any other process. autostart: the desktop app comes up with `./dev
|
|
259
|
+
// start`. Its verification tiers are defined by the multi-surface contract:
|
|
260
|
+
// generation (snapshot), compilation (tsc + lint), boot (Playwright _electron
|
|
261
|
+
// under xvfb).
|
|
262
|
+
registerRunner(tree, {
|
|
263
|
+
name: serviceNames.fileName,
|
|
264
|
+
kind: 'surface',
|
|
265
|
+
cmd: 'bash tool/electron_exec.sh run',
|
|
266
|
+
cwd: projectRoot,
|
|
267
|
+
autostart: true,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
promoteEngineerSkill(tree, 'groundwork-electron-engineer');
|
|
271
|
+
|
|
272
|
+
await formatFiles(tree);
|
|
273
|
+
|
|
274
|
+
recordGeneratorProvenance(tree, 'electron-app', options as unknown as Record<string, unknown>);
|
|
275
|
+
|
|
276
|
+
return () => {
|
|
277
|
+
// Deliberate deviation from the nextjs-app callback (which runs an install
|
|
278
|
+
// immediately): `npm install` here would pull the ~100 MB Electron binary
|
|
279
|
+
// into every scaffold run and every generation-test sandbox. Bootstrap is
|
|
280
|
+
// an explicit, guarded target instead — the same shape flutter-app uses
|
|
281
|
+
// for its SDK-dependent steps.
|
|
282
|
+
console.log(
|
|
283
|
+
`Scaffolded ${projectRoot}. Next steps:\n` +
|
|
284
|
+
` npx nx run ${serviceNames.fileName}:bootstrap # npm install (downloads the Electron binary)\n` +
|
|
285
|
+
` npx nx run ${serviceNames.fileName}:smoke # build + Playwright _electron boot smoke`,
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"id": "electron-app",
|
|
4
|
+
"title": "Electron App",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "The name of the Electron application (surface slug)",
|
|
10
|
+
"$default": {
|
|
11
|
+
"$source": "argv",
|
|
12
|
+
"index": 0
|
|
13
|
+
},
|
|
14
|
+
"x-prompt": "What name would you like to use for the app?"
|
|
15
|
+
},
|
|
16
|
+
"org": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Reverse-domain organization identifier used for the macOS bundle id and the Windows AppUserModelID",
|
|
19
|
+
"default": "com.example"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": ["name"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture
|
|
3
|
+
description: MVVM over UI/Data layers, repositories and services, the core-access seam, freezed immutability, and the Riverpod default.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-06-12
|
|
6
|
+
---
|
|
7
|
+
# Architecture
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
We follow the official Flutter architecture guide: a UI layer of MVVM features over a data layer of repositories and services, with a Domain layer added only when it earns its place. The capability core is reached through a typed contract client that lives in the data layer as a service — nothing above the data layer knows the transport. State management is Riverpod 3.x; models are immutable via `freezed`.
|
|
12
|
+
|
|
13
|
+
## Why this matters
|
|
14
|
+
|
|
15
|
+
Flutter spent years without an official architecture, and the vacuum filled with cargo-culted "Clean Architecture" folder mazes and state-management package wars. The Flutter team now ships a first-party, opinionated guide (MVVM + repositories), and we adopt it wholesale because an agent writing against the documented official pattern produces code every Flutter engineer and every future agent recognises — the training-set fluency axis applied within a stack.
|
|
16
|
+
|
|
17
|
+
## The layers
|
|
18
|
+
|
|
19
|
+
### UI and Data are mandatory; Domain is earned
|
|
20
|
+
|
|
21
|
+
Two layers by default. The **UI layer** holds features; the **data layer** holds repositories and services. A **Domain layer** (use-cases) is added per-case only when logic merges multiple repositories, is genuinely complex, or is reused across view models — never preemptively, because empty pass-through use-cases are pure indirection tax.
|
|
22
|
+
|
|
23
|
+
### MVVM in the UI layer
|
|
24
|
+
|
|
25
|
+
Every feature is exactly one **View** paired with exactly one **ViewModel**:
|
|
26
|
+
|
|
27
|
+
- The **View** is widgets only — layout, animation, conditional rendering, simple routing. No business logic, no direct repository access.
|
|
28
|
+
- The **ViewModel** transforms repository data into UI state and exposes **commands** for user events. It is the only thing the View talks to.
|
|
29
|
+
|
|
30
|
+
Naming follows the official convention: `HomeView`/`HomeViewModel`, `UserRepository`, `AuthService`. Predictable names are how an agent finds the right file on the first try.
|
|
31
|
+
|
|
32
|
+
### Repositories and services in the data layer
|
|
33
|
+
|
|
34
|
+
**Repositories** are the source of truth: they own caching, retry, polling, and the transformation of raw payloads into domain models. **Services** are stateless wrappers over external interfaces — API clients, platform APIs, local storage — exposing `Future`/`Stream`. Repositories consume services; they are many-to-many with view models and never aware of each other. View models depend on **abstract repository classes**, so tests substitute fakes without mocking frameworks.
|
|
35
|
+
|
|
36
|
+
### The core-access seam
|
|
37
|
+
|
|
38
|
+
A GroundWork Flutter app is a surface adapter over a headless capability core (see [capability core and surfaces](../../capability-core-and-surfaces.md)). The seam is precise:
|
|
39
|
+
|
|
40
|
+
- A **typed client generated from the promoted contracts** (dio-based, via OpenAPI codegen — `openapi_generator`'s `dart-dio` path when sharing one HTTP stack) lives in the data layer **as a service**.
|
|
41
|
+
- **Repositories consume that client** and translate contract types into domain models.
|
|
42
|
+
- **Nothing above the data layer knows the transport.** A view model cannot tell whether the core is hosted or embedded, REST or gRPC — which is exactly what lets capability logic be proven once, headless, while the surface proves only wiring.
|
|
43
|
+
|
|
44
|
+
Hand-rolled JSON mapping against an existing contract spec is a defect: the contract is promoted precisely so clients are generated from it.
|
|
45
|
+
|
|
46
|
+
## Project organisation
|
|
47
|
+
|
|
48
|
+
`ui/` is organised **by feature** (one folder per feature holding its view and view model); `data/` is organised **by type** (repositories and services are shared infrastructure, not feature property). For large apps, split features into local packages using Dart pub workspaces (`resolution: workspace`), with Melos 7.x — now configured under the `melos:` key in the root pubspec — for scripts and versioning. Layer-first roots (`/screens`, `/widgets`, `/models`) are legacy for anything non-trivial.
|
|
49
|
+
|
|
50
|
+
## Immutability
|
|
51
|
+
|
|
52
|
+
Data and domain models are immutable, generated with `freezed`. Mutable models invite state changes that bypass the unidirectional flow; `freezed` gives value equality, `copyWith`, and exhaustive pattern matching for the price of a build_runner step we already pay for contract codegen.
|
|
53
|
+
|
|
54
|
+
## State management: the GroundWork default
|
|
55
|
+
|
|
56
|
+
**Riverpod 3.x** for new builds. The reasoning: it is the converging ecosystem default for new projects in 2026, it is compile-safe and testable without widget-tree gymnastics, and its provider graph doubles as the app's dependency-injection mechanism — one tool for state and DI. Riverpod 3.0 added Mutations (action lifecycle without codegen). Pin conservatively: the maintainer flags a possible near-term 4.0 and the offline-persistence API is explicitly experimental — do not build on experimental Riverpod APIs.
|
|
57
|
+
|
|
58
|
+
- **Bloc (flutter_bloc 9.x)** is the deliberate alternative for large, regulated, audit-trail-heavy teams — stable and slow-moving by design. Choosing it is a recorded decision, not a drift.
|
|
59
|
+
- **GetX** is an anti-recommendation, full stop.
|
|
60
|
+
|
|
61
|
+
DI stance: Riverpod providers are the composition mechanism — repositories and services are providers, view models read them. The official guide's `provider`-for-DI recommendation is what Riverpod subsumes; do not run both.
|
|
62
|
+
|
|
63
|
+
See [State management](state-management.md) for the patterns in depth.
|
|
64
|
+
|
|
65
|
+
## Anti-patterns we reject
|
|
66
|
+
|
|
67
|
+
- **Business logic in widgets.** A `build` method that branches on domain rules belongs in a view model.
|
|
68
|
+
- **Preemptive Domain layers.** Use-case classes that forward one repository call add a file and remove nothing.
|
|
69
|
+
- **Repositories aware of each other.** Cross-repository orchestration is a view model or use-case concern.
|
|
70
|
+
- **Hand-written API clients against a promoted contract.** Generate the client; the contract is the source of truth.
|
|
71
|
+
- **Surface-side business rules.** If a rule is proven at the core's contract, the surface re-implementing it is divergence waiting to happen.
|
|
72
|
+
- **Clean-Architecture folder cargo-culting.** entities/usecases/datasources scaffolding imported from blog folklore — the official two-layer guide replaced it.
|
|
73
|
+
|
|
74
|
+
## Further reading
|
|
75
|
+
|
|
76
|
+
- [Flutter architecture guide](https://docs.flutter.dev/app-architecture/guide) — the canonical source for this document.
|
|
77
|
+
- [Architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations) — the cross-cutting recommendations table.
|
|
78
|
+
- The **Compass app** — the Flutter team's reference implementation of this architecture.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Flutter
|
|
3
|
+
description: Architecture, state management, widgets, testing, native interop, and release engineering for Flutter mobile surfaces.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-06-12
|
|
6
|
+
---
|
|
7
|
+
# Flutter
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
A GroundWork mobile surface is a Flutter app: MVVM over a two-layer architecture, Riverpod for state, go_router for navigation, widget tests as the bulk of coverage, Pigeon for native interop, and a CI pipeline that signs, versions, and ships to both stores without a human in the loop. The surface is a thin adapter — business logic lives in the capability core and is reached through a typed client over promoted contracts.
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
This set is named for the framework, not the language, because the engineering knowledge that matters is platform knowledge: widget lifecycles, store signing, emulator CI, platform channels. Dart idioms appear only where they carry a platform decision (immutability, codegen). The set covers mobile surfaces; Flutter-desktop and Flutter-web are out of scope — GroundWork's standard picks route those platforms elsewhere (see [surface stack selection](../../surface-stack-selection.md)).
|
|
16
|
+
|
|
17
|
+
## Toolchain currency
|
|
18
|
+
|
|
19
|
+
**Flutter 3.44.0 stable / Dart 3.12.0** (May 2026). Notable for planning: Material and Cupertino are frozen in the core framework as of 3.44 and are moving to standalone `material_ui`/`cupertino_ui` packages with independent versioning — pin them deliberately when they land.
|
|
20
|
+
|
|
21
|
+
## The set
|
|
22
|
+
|
|
23
|
+
- **[Architecture](architecture.md)** — the two-layer (UI/Data, optional Domain) model from the official Flutter architecture guide: MVVM in the UI layer, repositories and services in the data layer, feature-first `ui/` with type-first `data/`, immutable models via `freezed`, and the core-access seam — how the surface reaches the capability core through a typed contract client. States the GroundWork state-management default (Riverpod 3.x) and why.
|
|
24
|
+
- **[State management](state-management.md)** — Riverpod 3.x in depth: provider and Notifier patterns, Mutations for action lifecycle, what belongs in providers versus ephemeral widget state, and the testing seams (`ProviderContainer`, overrides). Names the legacy patterns — provider-as-state-management, setState-as-architecture, GetX — so they are recognised and refused.
|
|
25
|
+
- **[Widgets and composition](widgets-and-composition.md)** — composition over inheritance, `const` discipline, build-method purity, keys, theming as a projection of `brand-tokens.json` into a generated `ThemeData` module, go_router navigation with typed routes, and the accessibility baseline.
|
|
26
|
+
- **[Testing](testing.md)** — the unit/widget/integration taxonomy: pure-Dart unit tests with fakes, widget tests as the bulk, `integration_test` happy paths on a headless Android emulator as the CI-canonical loop, Patrol only where flows cross the Flutter/OS boundary, and golden tests via alchemist. Surface tests prove wiring, never re-prove core logic.
|
|
27
|
+
- **[Platform channels](platform-channels.md)** — the native escape hatch: Pigeon for structured APIs today, ffigen/jnigen with build hooks as the tracked destination, raw `MethodChannel` only for trivial one-offs, and when dropping to Swift/Kotlin is justified.
|
|
28
|
+
- **[Releases and distribution](releases-and-distribution.md)** — signing discipline on both stores, CI/CD with GitHub Actions + fastlane (Codemagic as the Flutter-native alternative), pubspec versioning, the backend-driven forced-upgrade floor that makes contract compatibility enforceable on a lagging mobile fleet, and Shorebird code push for Dart-only OTA patches.
|
|
29
|
+
|
|
30
|
+
## Survey provenance
|
|
31
|
+
|
|
32
|
+
This set was authored from a dated ecosystem survey, not from prior assumption. A future refresh supersedes this provenance block.
|
|
33
|
+
|
|
34
|
+
**Survey date: 2026-06-12.** Load-bearing sources:
|
|
35
|
+
|
|
36
|
+
- Official: [docs.flutter.dev/app-architecture/guide](https://docs.flutter.dev/app-architecture/guide) and [/recommendations](https://docs.flutter.dev/app-architecture/recommendations) (both updated 2026-05-05); Flutter 3.44 release notes and announcement (May 2026); [Flutter's path towards seamless interop](https://blog.flutter.dev/flutters-path-towards-seamless-interop-4bf7d4579d9a); [dart.dev/tools/pub/workspaces](https://dart.dev/tools/pub/workspaces) and [dart.dev/tools/hooks](https://dart.dev/tools/hooks); docs.flutter.dev deployment, integration-test, and bind-native-code pages.
|
|
37
|
+
- Packages (versions checked 2026-06-12 on pub.dev): go_router 17.3.0 and pigeon 27.1.0 (flutter.dev verified publisher), patrol 4.6.1 (LeanCode), riverpod 3.0.x changelog + riverpod.dev "what's new", flutter_bloc 9.1.1, alchemist, upgrader, shorebird_code_push, openapi_generator.
|
|
38
|
+
- Ecosystem: Very Good Ventures (flutter_bloc rationale, Very Good Core, alchemist tutorial); Codemagic docs (code signing, Shorebird publishing); ReactiveCircus android-emulator-runner; Code With Andrea project-structure guidance; 2026 state-management comparisons (foresightmobile, startdebugging.net, dasroot.net).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Platform Channels
|
|
3
|
+
description: Pigeon for structured native APIs, ffigen/jnigen as the tracked destination, when to drop to Swift/Kotlin, and SwiftPM as the iOS default.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-06-12
|
|
6
|
+
---
|
|
7
|
+
# Platform Channels
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
Native interop is Pigeon: define the API once in Dart, generate type-safe Kotlin and Swift. The ecosystem is mid-migration to channel-free codegen interop — ffigen/jnigen on build hooks — which we track as the destination but do not adopt for app code until it settles. Raw `MethodChannel` is for trivial one-offs only. Dropping to native is a capability decision, not a performance reflex. iOS native wiring uses Swift Package Manager; CocoaPods is legacy.
|
|
12
|
+
|
|
13
|
+
## Why this matters
|
|
14
|
+
|
|
15
|
+
The platform capability ceiling axis says a stack's reach is measured *with* its native escape hatches, because AI labor makes them nearly free — dropping to Swift or Kotlin for one capability is a slice, not a staffing decision. That only holds if the hatch is disciplined: a typed, generated boundary an agent can regenerate and test, not a pile of stringly-typed channel calls that fail at runtime on a misspelled key.
|
|
16
|
+
|
|
17
|
+
## The interop ladder
|
|
18
|
+
|
|
19
|
+
### Pigeon — today's default for structured APIs
|
|
20
|
+
|
|
21
|
+
**Pigeon** (flutter.dev verified publisher) is the production answer for any real native API surface: declare the interface once in a Dart file, generate matching type-safe Kotlin/Swift (and Java/Obj-C/C++) stubs on both sides. Misspelled method names and mistyped arguments become compile errors instead of runtime channel exceptions. Every structured Flutter↔native API in a GroundWork app goes through Pigeon.
|
|
22
|
+
|
|
23
|
+
### ffigen / jnigen — the tracked destination
|
|
24
|
+
|
|
25
|
+
The Flutter team's declared direction is **direct codegen interop without channels**: `ffigen` for C/Obj-C/Swift bindings (stable; synchronous calls, tree-shaking), `jnigen` for Java/Kotlin via JNI (pre-1.0, production-usable), built on **build hooks / native assets** (stable since Dart 3.10, default-enabled since Flutter 3.38). The ecosystem is mid-migration: plugin authors are rebuilding on these tools. We name the destination so refactors aim at it, and we adopt it where a dependency already ships it — but Pigeon remains the app-code default until the jnigen path reaches 1.0. Re-evaluate at the next survey.
|
|
26
|
+
|
|
27
|
+
### Raw `MethodChannel` — trivial one-offs only
|
|
28
|
+
|
|
29
|
+
A raw channel is acceptable for a single fire-and-forget call with no evolving surface — toggling a platform flag, reading one value. It is not type-safe and not refactorable; the moment a channel grows a second method or a structured payload, it becomes a Pigeon definition. Hand-written channels for any real API surface are legacy.
|
|
30
|
+
|
|
31
|
+
## When to drop to native
|
|
32
|
+
|
|
33
|
+
Write Swift/Kotlin when Flutter **cannot reach the capability**: a platform API with no maintained plugin (HealthKit details, platform-specific background modes, a vendor SDK), OS-integrated UI Flutter cannot render (widgets/complications, App Intents), or hardware access below the plugin ecosystem. Do **not** drop to native as a performance reflex — profile first; Flutter's rendering and isolates cover almost every performance complaint, and a native detour taken for imagined speed buys two codebases for one feature.
|
|
34
|
+
|
|
35
|
+
Check pub.dev before writing native code: a maintained, verified-publisher plugin is a dependency decision, not an interop project. Write native code when the plugin shelf is empty or unmaintained — and structure it as a small, Pigeon-fronted module the agent can regenerate, with the Dart side exposed as a **service** in the data layer like any other platform API (see [Architecture](architecture.md)).
|
|
36
|
+
|
|
37
|
+
## iOS build wiring
|
|
38
|
+
|
|
39
|
+
**Swift Package Manager is the iOS/macOS default as of Flutter 3.44.** New native modules and plugin dependencies use SwiftPM; CocoaPods is the legacy path, kept only where a required dependency has not migrated. Do not add new Podfile-based wiring.
|
|
40
|
+
|
|
41
|
+
## Testing the boundary
|
|
42
|
+
|
|
43
|
+
The Pigeon-fronted service is faked in Dart for unit and widget tests like any other service — the native side does not run in those tiers. The native implementation itself is exercised by Patrol or `integration_test` on the emulator (see [Testing](testing.md)) only where the capability is observable; pure pass-through wrappers over OS APIs get a thin smoke test, not a re-proof of the OS.
|
|
44
|
+
|
|
45
|
+
## Anti-patterns we reject
|
|
46
|
+
|
|
47
|
+
- **Hand-written channels for evolving APIs.** Stringly-typed, runtime-failing, agent-hostile. Pigeon exists.
|
|
48
|
+
- **Native-by-reflex for performance.** Profile in Dart first; the ceiling axis funds capability gaps, not superstition.
|
|
49
|
+
- **Skipping the plugin shelf.** Rewriting a maintained verified-publisher plugin is negative work.
|
|
50
|
+
- **New CocoaPods wiring.** SwiftPM is the default; Pods are a compatibility tail.
|
|
51
|
+
- **Native logic that grows business rules.** The native module is transport to an OS capability; rules live in the core or the view model, where they are proven.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Releases and Distribution
|
|
3
|
+
description: Store signing discipline, CI/CD with GitHub Actions + fastlane or Codemagic, pubspec versioning, the forced-upgrade floor, and Shorebird code push.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-06-12
|
|
6
|
+
---
|
|
7
|
+
# Releases and Distribution
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
Both stores are shipped from CI: GitHub Actions + fastlane by default, Codemagic as the Flutter-native alternative. Signing material never touches git. Versions are `x.y.z+build` in pubspec with the build number CI-incremented. A backend-driven minimum-version floor with the `upgrader` package is mandatory — it is the mechanism that makes contract compatibility enforceable on a fleet that lags releases by months. Shorebird code push is the Dart-only OTA lane.
|
|
12
|
+
|
|
13
|
+
## Why this matters
|
|
14
|
+
|
|
15
|
+
A mobile surface cannot be rolled back and cannot be force-refreshed: every version ever shipped is potentially still running, store review adds days of latency, and a signing mistake can lock you out of your own listing permanently. Release engineering is therefore not a deploy script — it is the half of the architecture's compatibility story that lives outside the repo. The agent-closable loop axis also bites here: a release process with a human clicking through Xcode is a process the delivery loop stalls on, so everything below is CI-driven.
|
|
16
|
+
|
|
17
|
+
## Signing
|
|
18
|
+
|
|
19
|
+
**Android.** One upload keystore per app, generated once, stored in a secret manager with an independent secure copy — the official docs warn it is unrecoverable, and losing it means losing the update path. `key.properties` and the keystore are never in git; CI injects them from encrypted secrets (or Codemagic code-signing identities). **Play App Signing** holds the actual app signing key, so a compromised upload key is rotatable.
|
|
20
|
+
|
|
21
|
+
**iOS.** **App Store Connect API keys** drive CI authentication — no human Apple ID sessions in automation. Certificates and provisioning profiles are managed fastlane-match-style (encrypted, centrally stored, CI-fetched); Codemagic automates the same with managed code signing.
|
|
22
|
+
|
|
23
|
+
A keystore or service-account JSON committed to a repo is an incident, not a finding.
|
|
24
|
+
|
|
25
|
+
## CI/CD
|
|
26
|
+
|
|
27
|
+
Two converging stacks; both are sound, pick one per product and record it:
|
|
28
|
+
|
|
29
|
+
- **GitHub Actions + fastlane** (default — it shares the runner platform every other GroundWork surface uses): `supply` publishes to Play, `deliver`/`pilot` to App Store Connect/TestFlight.
|
|
30
|
+
- **Codemagic** — Flutter-native CI with built-in store publishing and managed signing; fastlane is preinstalled, so the stacks compose rather than compete.
|
|
31
|
+
|
|
32
|
+
The 2026-standard flow: PRs run the test gate (see [Testing](testing.md) — widget tests plus the headless-emulator integration lane); merges to main build signed release artifacts; artifacts publish to the **internal track / TestFlight first**, then promote to production after staged rollout. No artifact reaches a store that CI did not build, sign, and test.
|
|
33
|
+
|
|
34
|
+
## Versioning
|
|
35
|
+
|
|
36
|
+
pubspec carries `version: x.y.z+buildNumber`. Humans own `x.y.z` (it is the user-visible and compatibility-relevant part); **CI owns the build number**, auto-incrementing per release build via `--build-number` (`--build-name` overrides the version when needed). Hand-bumped build numbers collide; collided build numbers are store-upload rejections.
|
|
37
|
+
|
|
38
|
+
## Forced upgrade — the contract-compatibility floor
|
|
39
|
+
|
|
40
|
+
The architecture's stance is that a published contract field is never broken — because the fleet consuming it lags releases by months and cannot be recalled. Forced upgrade is the other half of that bargain: the floor below which the core stops accommodating old clients.
|
|
41
|
+
|
|
42
|
+
- The core (or a config endpoint / Firebase Remote Config) publishes a **minimum supported version**; the app compares at startup.
|
|
43
|
+
- The **`upgrader`** package implements the prompt: `minAppVersion` below the floor auto-hides "Later"/"Ignore".
|
|
44
|
+
- Two tiers, used deliberately: **soft prompt** (dismissible — new features, gentle migration pressure) and **hard force** (blocking — security issues, a contract the core can no longer serve).
|
|
45
|
+
|
|
46
|
+
The floor advances slowly and is a recorded product decision each time, because a hard force on a user mid-task is the worst interaction the product will ever ship. Between "never break a field" and "hard floor," every fleet version in the window is served correctly; outside it, the app says so honestly. Shipping a contract-breaking core change without first raising the floor and waiting out adoption is a sequencing defect.
|
|
47
|
+
|
|
48
|
+
## Shorebird code push
|
|
49
|
+
|
|
50
|
+
**Shorebird** is the OTA lane: stable on Android and iOS, store-policy compliant, Dart-only patches with `shorebird_code_push` for in-app patch checks. Use it for what it is — a hotfix channel that skips store latency for Dart-level defects — not as a release process. Native-code changes (plugins, Pigeon modules, SDK bumps) still require a store release, so OTA never substitutes for the version floor; a patched 1.4.2 is still 1.4.2 to the contract.
|
|
51
|
+
|
|
52
|
+
## Anti-patterns we reject
|
|
53
|
+
|
|
54
|
+
- **Signing material in the repo.** Keystores, `key.properties`, ASC keys, service-account JSON — secrets infrastructure only.
|
|
55
|
+
- **Manual store uploads.** A human with Xcode or the Play console in the loop is an unrepeatable release.
|
|
56
|
+
- **Breaking a published contract field because "the new app is out."** The fleet lags by months; the floor, not the release date, defines what you may drop.
|
|
57
|
+
- **Hard-forcing upgrades as a convenience.** Hard force is for security and served-contract limits; everything else is a soft prompt.
|
|
58
|
+
- **Treating Shorebird as the release process.** OTA patches Dart defects; features, native changes, and version floors move through the stores.
|
|
59
|
+
- **CodePush-era expectations.** Flutter never had Microsoft CodePush; Shorebird is the ecosystem answer, with Dart-only limits respected.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: State Management
|
|
3
|
+
description: Riverpod 3.x providers, Notifiers, Mutations, ephemeral vs app state, testing seams, and the legacy patterns we name and refuse.
|
|
4
|
+
status: active
|
|
5
|
+
last_reviewed: 2026-06-12
|
|
6
|
+
---
|
|
7
|
+
# State Management
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
Riverpod 3.x is the default. Providers hold shared and derived state, `Notifier` classes hold state with behaviour, Mutations carry the lifecycle of user actions, and `setState` survives only for ephemeral widget-local state. Tests exercise providers in a `ProviderContainer` with overrides — no widget tree required. Provider-as-state-management, setState-as-architecture, and GetX are legacy.
|
|
12
|
+
|
|
13
|
+
## Why this matters
|
|
14
|
+
|
|
15
|
+
State management is where Flutter codebases historically rotted: a package chosen by fashion, state scattered between widgets and singletons, and tests that need a full widget pump to assert a counter incremented. Riverpod's compile-safe provider graph fixes the structural problems — no `BuildContext` dependency for reads, no runtime lookup failures, and a DI story for free — which is why it is the converging 2026 default for new builds and our pick. Pin 3.x APIs conservatively: persistence is experimental and the maintainer flags a possible 4.0.
|
|
16
|
+
|
|
17
|
+
## The patterns
|
|
18
|
+
|
|
19
|
+
### Providers are the graph
|
|
20
|
+
|
|
21
|
+
Everything shared is a provider: repositories, services, the contract client, derived view state. The provider graph **is** the dependency-injection graph — a view model provider `ref.watch`es its repository provider, the repository provider reads the client provider. Construction order, disposal, and test substitution all fall out of the graph; there is no separate DI container.
|
|
22
|
+
|
|
23
|
+
```dart
|
|
24
|
+
final userRepositoryProvider = Provider<UserRepository>(
|
|
25
|
+
(ref) => UserRepository(ref.watch(apiClientProvider)),
|
|
26
|
+
);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Notifier for state with behaviour
|
|
30
|
+
|
|
31
|
+
A view model is a `Notifier` (or `AsyncNotifier` when the initial state is asynchronous) exposing an immutable state object and methods as the MVVM commands:
|
|
32
|
+
|
|
33
|
+
```dart
|
|
34
|
+
class ProfileViewModel extends AsyncNotifier<ProfileState> {
|
|
35
|
+
@override
|
|
36
|
+
Future<ProfileState> build() async =>
|
|
37
|
+
ProfileState(user: await ref.watch(userRepositoryProvider).current());
|
|
38
|
+
|
|
39
|
+
Future<void> rename(String name) async { /* command */ }
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`build` declares dependencies via `ref.watch`; when a dependency changes, the state recomputes. Derived read-only state uses plain providers over Notifiers — do not write a class where a function suffices.
|
|
44
|
+
|
|
45
|
+
### Mutations for action lifecycle
|
|
46
|
+
|
|
47
|
+
User actions that need pending/success/error surfacing — submit, delete, retry — use Riverpod 3 **Mutations** (idle/pending/success/error, no codegen required). They replace the hand-rolled `isLoading` boolean and the per-action `AsyncValue` juggling; the view watches the mutation state and renders the spinner or the error.
|
|
48
|
+
|
|
49
|
+
### What is state, and what is ephemeral
|
|
50
|
+
|
|
51
|
+
A provider holds state that is **shared, derived from the core, or must survive the widget's lifetime**: session, fetched data, feature view state. `setState` in a `StatefulWidget` is correct for **ephemeral** state — text-field focus, animation progress, whether a tile is expanded — where lifting it into a provider adds graph noise for state nobody else reads. The boundary question: does anything outside this widget care? No → ephemeral.
|
|
52
|
+
|
|
53
|
+
Client state must also be recoverable: anything not rebuildable from the core or the route is state the app loses on process death, and mobile processes die constantly. The route (go_router's URL) is a first-class state container.
|
|
54
|
+
|
|
55
|
+
### Testing seams
|
|
56
|
+
|
|
57
|
+
Providers test without widgets. A `ProviderContainer` with overrides substitutes fakes at any node of the graph:
|
|
58
|
+
|
|
59
|
+
```dart
|
|
60
|
+
final container = ProviderContainer(overrides: [
|
|
61
|
+
userRepositoryProvider.overrideWithValue(FakeUserRepository()),
|
|
62
|
+
]);
|
|
63
|
+
final state = await container.read(profileViewModelProvider.future);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Fakes over mocks, per the official guide: an abstract repository with an in-memory fake survives refactors that a stubbed mock breaks on. Widget tests override the same providers via `ProviderScope(overrides: ...)` — the same seam serves both tiers.
|
|
67
|
+
|
|
68
|
+
## Legacy patterns — named so they are refused
|
|
69
|
+
|
|
70
|
+
- **provider-as-state-management.** The 2019–2022 default. Riverpod is its designated successor by the same author; `provider` survives officially only as a DI mechanism, and with Riverpod even that role is subsumed.
|
|
71
|
+
- **setState-as-architecture.** `StatefulWidget`s holding app state, passed down constructors, mutated in callbacks. Legacy for anything beyond ephemeral state.
|
|
72
|
+
- **GetX.** Uniformly an anti-recommendation in serious 2026 comparisons — global mutable service locators and context-free magic that defeats both the type system and testability. Never.
|
|
73
|
+
- **Riverpod experimental persistence.** Not legacy but forbidden for now: explicitly experimental, and we do not put experimental APIs in load-bearing paths.
|
|
74
|
+
|
|
75
|
+
## Anti-patterns
|
|
76
|
+
|
|
77
|
+
- **Reading providers in `build` methods imperatively.** `ref.watch` in build, `ref.read` only inside callbacks. A `ref.read` in build silently opts out of reactivity.
|
|
78
|
+
- **Provider state that mirrors the route.** If go_router already encodes it (selected id, tab), the provider duplicating it will drift.
|
|
79
|
+
- **One god-provider per screen.** Split by concern; the graph dedupes and recomputes at the right granularity only if you let it.
|
|
80
|
+
- **Mocking your own Notifier.** Test the real Notifier with fake repositories; mocking the unit under test proves nothing.
|
|
81
|
+
|
|
82
|
+
## Further reading
|
|
83
|
+
|
|
84
|
+
- [riverpod.dev](https://riverpod.dev) — the official docs; the "what's new in 3.0" page covers Mutations.
|
|
85
|
+
- [Flutter architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations) — fakes-over-mocks and the command pattern this document assumes.
|