groundwork-method 0.0.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +823 -0
- package/LICENSE +21 -0
- package/README.md +44 -29
- package/bin/groundwork.js +1723 -0
- package/dist/src/generators/add-capability/generator.d.ts +8 -0
- package/dist/src/generators/add-capability/generator.js +60 -0
- package/dist/src/generators/add-capability/generator.js.map +1 -0
- package/dist/src/generators/cli-app/generator.d.ts +9 -0
- package/dist/src/generators/cli-app/generator.js +140 -0
- package/dist/src/generators/cli-app/generator.js.map +1 -0
- package/dist/src/generators/docs-site/generator.d.ts +5 -0
- package/dist/src/generators/docs-site/generator.js +441 -0
- package/dist/src/generators/docs-site/generator.js.map +1 -0
- package/dist/src/generators/electron-app/generator.d.ts +6 -0
- package/dist/src/generators/electron-app/generator.js +261 -0
- package/dist/src/generators/electron-app/generator.js.map +1 -0
- package/dist/src/generators/flutter-app/generator.d.ts +6 -0
- package/dist/src/generators/flutter-app/generator.js +314 -0
- package/dist/src/generators/flutter-app/generator.js.map +1 -0
- package/dist/src/generators/go-microservice/generator.d.ts +8 -0
- package/dist/src/generators/go-microservice/generator.js +232 -0
- package/dist/src/generators/go-microservice/generator.js.map +1 -0
- package/dist/src/generators/nextjs-app/generator.d.ts +8 -0
- package/dist/src/generators/nextjs-app/generator.js +294 -0
- package/dist/src/generators/nextjs-app/generator.js.map +1 -0
- package/dist/src/generators/python-microservice/generator.d.ts +13 -0
- package/dist/src/generators/python-microservice/generator.js +265 -0
- package/dist/src/generators/python-microservice/generator.js.map +1 -0
- package/dist/src/generators/shared/brand-tokens.d.ts +89 -0
- package/dist/src/generators/shared/brand-tokens.js +308 -0
- package/dist/src/generators/shared/brand-tokens.js.map +1 -0
- package/dist/src/generators/shared/capabilities.d.ts +101 -0
- package/dist/src/generators/shared/capabilities.js +279 -0
- package/dist/src/generators/shared/capabilities.js.map +1 -0
- package/dist/src/generators/shared/provenance.d.ts +2 -0
- package/dist/src/generators/shared/provenance.js +85 -0
- package/dist/src/generators/shared/provenance.js.map +1 -0
- package/dist/src/generators/shared/scaffold-helpers.d.ts +72 -0
- package/dist/src/generators/shared/scaffold-helpers.js +309 -0
- package/dist/src/generators/shared/scaffold-helpers.js.map +1 -0
- package/dist/src/generators/system-test-runner/generator.d.ts +23 -0
- package/dist/src/generators/system-test-runner/generator.js +173 -0
- package/dist/src/generators/system-test-runner/generator.js.map +1 -0
- package/dist/src/generators/workspace-dev-cli/generator.d.ts +7 -0
- package/dist/src/generators/workspace-dev-cli/generator.js +138 -0
- package/dist/src/generators/workspace-dev-cli/generator.js.map +1 -0
- package/generators.json +57 -0
- package/lib/repo-map/grammars/tree-sitter-c.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-cpp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-csharp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-dart.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-go.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-java.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-javascript.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-kotlin.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-lua.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-php.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-python.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-ruby.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-rust.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-scala.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-swift.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-tsx.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-typescript.wasm +0 -0
- package/lib/repo-map/index.js +386 -0
- package/lib/repo-map/languages.js +514 -0
- package/lib/repo-map/pagerank.js +59 -0
- package/migrations/README.md +60 -0
- package/migrations/_template/cli-migration.js +27 -0
- package/migrations/gw-bet-prose-redesign.js +105 -0
- package/migrations/gw-drop-test-manifest.js +37 -0
- package/migrations/gw-register-serena-mcp.js +42 -0
- package/migrations/gw-relocate-hidden-skills.js +40 -0
- package/migrations/gw-seed-config-toml.js +24 -0
- package/migrations/index.json +40 -0
- package/package.json +70 -6
- package/src/AGENTS.md +36 -0
- package/src/config/config.toml +30 -0
- package/src/config/groundwork-state.json +5 -0
- package/src/docs/llms.txt +72 -0
- package/src/docs/principles/ai-native/agent-native-systems.md +90 -0
- package/src/docs/principles/ai-native/agentic-systems.md +78 -0
- package/src/docs/principles/ai-native/ai-engineering.md +100 -0
- package/src/docs/principles/ai-native/ai-native-product.md +76 -0
- package/src/docs/principles/delivery/cost-engineering.md +89 -0
- package/src/docs/principles/delivery/day-2-operational-baseline.md +57 -0
- package/src/docs/principles/delivery/devex.md +88 -0
- package/src/docs/principles/delivery/platform.md +101 -0
- package/src/docs/principles/delivery/progressive-delivery.md +92 -0
- package/src/docs/principles/design/ai-native-design.md +73 -0
- package/src/docs/principles/design/design-foundations.md +80 -0
- package/src/docs/principles/design/design-systems-and-tokens.md +72 -0
- package/src/docs/principles/design/interaction-and-motion.md +69 -0
- package/src/docs/principles/design/layout-and-space.md +72 -0
- package/src/docs/principles/design/usability-and-ux.md +79 -0
- package/src/docs/principles/design/visual-design.md +84 -0
- package/src/docs/principles/foundations/code-craft.md +86 -0
- package/src/docs/principles/foundations/continuous-discovery.md +75 -0
- package/src/docs/principles/foundations/documentation.md +102 -0
- package/src/docs/principles/foundations/prioritization-and-appetite.md +78 -0
- package/src/docs/principles/foundations/product-engineering.md +90 -0
- package/src/docs/principles/foundations/product-risks.md +89 -0
- package/src/docs/principles/foundations/requirements-and-specs.md +80 -0
- package/src/docs/principles/foundations/success-metrics.md +66 -0
- package/src/docs/principles/foundations/testing.md +108 -0
- package/src/docs/principles/index.md +24 -0
- package/src/docs/principles/quality/accessibility.md +88 -0
- package/src/docs/principles/quality/observability.md +84 -0
- package/src/docs/principles/quality/performance.md +84 -0
- package/src/docs/principles/quality/privacy.md +92 -0
- package/src/docs/principles/quality/reliability.md +89 -0
- package/src/docs/principles/quality/security.md +78 -0
- package/src/docs/principles/stack/postgres.md +100 -0
- package/src/docs/principles/system-design/api-design.md +86 -0
- package/src/docs/principles/system-design/architecture-decisions.md +81 -0
- package/src/docs/principles/system-design/code-structure.md +104 -0
- package/src/docs/principles/system-design/data-engineering.md +87 -0
- package/src/docs/principles/system-design/durable-execution.md +89 -0
- package/src/docs/principles/system-design/evolutionary-architecture.md +81 -0
- package/src/docs/principles/system-design/identity-and-access.md +76 -0
- package/src/docs/principles/system-design/integration-patterns.md +84 -0
- package/src/docs/principles/system-design/real-time.md +83 -0
- package/src/docs/principles/system-design/surface-architecture.md +74 -0
- package/src/docs/ways-of-working/documentation.md +69 -0
- package/src/docs/ways-of-working/how-we-work.md +76 -0
- package/src/docs/ways-of-working/units-of-work.md +40 -0
- package/src/engineer-skills/groundwork-electron-engineer/SKILL.md +123 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/documentation.md +126 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/performance-and-reliability.md +80 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/process-model.md +94 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/security.md +107 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/testing-and-smoke.md +129 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/theming-and-tokens.md +74 -0
- package/src/engineer-skills/groundwork-electron-engineer/sync-anchor.md +22 -0
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +114 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/accessibility.md +92 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/architecture.md +189 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/data-and-contracts.md +136 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/documentation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/navigation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/performance-and-reliability.md +100 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/platform-channels.md +93 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/releases-and-distribution.md +84 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/security.md +96 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/state-management.md +166 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +160 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/theming-and-design-tokens.md +109 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/widgets-and-composition.md +123 -0
- package/src/engineer-skills/groundwork-flutter-engineer/sync-anchor.md +24 -0
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +174 -0
- package/src/engineer-skills/groundwork-go-engineer/references/api-design.md +82 -0
- package/src/engineer-skills/groundwork-go-engineer/references/architecture.md +42 -0
- package/src/engineer-skills/groundwork-go-engineer/references/capability-ports.md +50 -0
- package/src/engineer-skills/groundwork-go-engineer/references/code-craft-security.md +34 -0
- package/src/engineer-skills/groundwork-go-engineer/references/concurrency.md +108 -0
- package/src/engineer-skills/groundwork-go-engineer/references/documentation.md +130 -0
- package/src/engineer-skills/groundwork-go-engineer/references/go-services.md +77 -0
- package/src/engineer-skills/groundwork-go-engineer/references/http-handlers.md +172 -0
- package/src/engineer-skills/groundwork-go-engineer/references/implementation-patterns.md +156 -0
- package/src/engineer-skills/groundwork-go-engineer/references/integration-realtime-data.md +57 -0
- package/src/engineer-skills/groundwork-go-engineer/references/observability.md +49 -0
- package/src/engineer-skills/groundwork-go-engineer/references/postgres.md +41 -0
- package/src/engineer-skills/groundwork-go-engineer/references/reliability-performance.md +105 -0
- package/src/engineer-skills/groundwork-go-engineer/references/testing.md +201 -0
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +20 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +112 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/accessibility.md +111 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/architecture.md +323 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/data-fetching.md +458 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/documentation.md +324 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/error-boundaries.md +383 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/mutations-and-forms.md +396 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/observability.md +48 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/performance-and-deployment.md +947 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/routing-and-navigation.md +405 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/security.md +131 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/server-components.md +394 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/tailwind-and-styling.md +134 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/testing.md +491 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/type-system.md +368 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/ux-principles.md +230 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +16 -0
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +199 -0
- package/src/engineer-skills/groundwork-python-engineer/references/api-standards.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/architecture.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/async-patterns.md +103 -0
- package/src/engineer-skills/groundwork-python-engineer/references/capability-ports.md +44 -0
- package/src/engineer-skills/groundwork-python-engineer/references/database.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/documentation-mcp.md +167 -0
- package/src/engineer-skills/groundwork-python-engineer/references/implementation-patterns.md +166 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-pipelines.md +119 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-systems-ai-engineering.md +74 -0
- package/src/engineer-skills/groundwork-python-engineer/references/observability.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/resilience.md +126 -0
- package/src/engineer-skills/groundwork-python-engineer/references/security.md +148 -0
- package/src/engineer-skills/groundwork-python-engineer/references/testing.md +216 -0
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +20 -0
- package/src/generators/add-capability/generator.ts +70 -0
- package/src/generators/add-capability/schema.json +30 -0
- package/src/generators/capabilities/llm/capability.json +28 -0
- package/src/generators/capabilities/llm/providers/anthropic/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/python/src/__packageName__/adapters/llm.py.template +61 -0
- package/src/generators/capabilities/llm/providers/local/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/local/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/local/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/localai/footprint.json +29 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/none/footprint.json +9 -0
- package/src/generators/capabilities/llm/providers/none/stacks/go/internal/llm/llm.go.template +35 -0
- package/src/generators/capabilities/llm/providers/none/stacks/python/src/__packageName__/adapters/llm.py.template +25 -0
- package/src/generators/capabilities/llm/providers/ollama/footprint.json +20 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/openai/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/go/internal/llm/llm.go.template +98 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/python/src/__packageName__/adapters/llm.py.template +60 -0
- package/src/generators/capabilities/llm/stacks/go/internal/core/service/llm.go.template +12 -0
- package/src/generators/capabilities/llm/stacks/go/internal/llm/llm_test.go.template +33 -0
- package/src/generators/capabilities/llm/stacks/python/src/__packageName__/core/llm.py.template +15 -0
- package/src/generators/capabilities/llm/stacks/python/tests/contracts/test_llm.py.template +37 -0
- package/src/generators/cli-app/files/README.md.template +76 -0
- package/src/generators/cli-app/files/build.mjs.template +15 -0
- package/src/generators/cli-app/files/package.json.template +21 -0
- package/src/generators/cli-app/files/src/cli.ts.template +67 -0
- package/src/generators/cli-app/files/src/commands/hello.ts.template +17 -0
- package/src/generators/cli-app/files/src/commands/status.ts.template +23 -0
- package/src/generators/cli-app/files/src/core/client.test.ts.template +80 -0
- package/src/generators/cli-app/files/src/core/client.ts.template +64 -0
- package/src/generators/cli-app/files/src/registry.test.ts.template +35 -0
- package/src/generators/cli-app/files/src/registry.ts.template +31 -0
- package/src/generators/cli-app/files/tsconfig.json.template +16 -0
- package/src/generators/cli-app/files/tsconfig.test.json.template +11 -0
- package/src/generators/cli-app/generator.ts +138 -0
- package/src/generators/cli-app/schema.json +24 -0
- package/src/generators/docs-site/files/.gitignore.ejs +40 -0
- package/src/generators/docs-site/files/app/docs/__slug__/page.tsx +101 -0
- package/src/generators/docs-site/files/app/docs/layout.tsx +14 -0
- package/src/generators/docs-site/files/app/docs.css +43 -0
- package/src/generators/docs-site/files/app/layout.tsx +24 -0
- package/src/generators/docs-site/files/app/page.tsx +135 -0
- package/src/generators/docs-site/files/app/source.ts +8 -0
- package/src/generators/docs-site/files/components/mermaid.tsx +67 -0
- package/src/generators/docs-site/files/next.config.mjs +10 -0
- package/src/generators/docs-site/files/package.json +32 -0
- package/src/generators/docs-site/files/pnpm-workspace.yaml +7 -0
- package/src/generators/docs-site/files/postcss.config.mjs +6 -0
- package/src/generators/docs-site/files/source.config.ts +77 -0
- package/src/generators/docs-site/files/tailwind.config.js +10 -0
- package/src/generators/docs-site/files/tsconfig.json +27 -0
- package/src/generators/docs-site/generator.ts +476 -0
- package/src/generators/docs-site/schema.json +17 -0
- package/src/generators/electron-app/docs/principles/stack/electron/index.md +49 -0
- package/src/generators/electron-app/docs/principles/stack/electron/ipc-contracts.md +71 -0
- package/src/generators/electron-app/docs/principles/stack/electron/packaging-and-updates.md +59 -0
- package/src/generators/electron-app/docs/principles/stack/electron/process-model.md +53 -0
- package/src/generators/electron-app/docs/principles/stack/electron/security.md +70 -0
- package/src/generators/electron-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/electron-app/files/.gitignore.template +20 -0
- package/src/generators/electron-app/files/README.md.template +125 -0
- package/src/generators/electron-app/files/electron.vite.config.ts +31 -0
- package/src/generators/electron-app/files/eslint.config.mjs +92 -0
- package/src/generators/electron-app/files/forge.config.ts.template +44 -0
- package/src/generators/electron-app/files/package.json.template +54 -0
- package/src/generators/electron-app/files/playwright.config.ts +18 -0
- package/src/generators/electron-app/files/project.json.template +65 -0
- package/src/generators/electron-app/files/src/main/core-client.test.ts +81 -0
- package/src/generators/electron-app/files/src/main/core-client.ts +55 -0
- package/src/generators/electron-app/files/src/main/index.ts +157 -0
- package/src/generators/electron-app/files/src/main/ipc.ts +52 -0
- package/src/generators/electron-app/files/src/main/policy.test.ts +71 -0
- package/src/generators/electron-app/files/src/main/policy.ts +73 -0
- package/src/generators/electron-app/files/src/preload/index.ts +23 -0
- package/src/generators/electron-app/files/src/renderer/index.html.template +20 -0
- package/src/generators/electron-app/files/src/renderer/src/App.test.tsx +61 -0
- package/src/generators/electron-app/files/src/renderer/src/App.tsx.template +43 -0
- package/src/generators/electron-app/files/src/renderer/src/assets/main.css +40 -0
- package/src/generators/electron-app/files/src/renderer/src/env.d.ts +14 -0
- package/src/generators/electron-app/files/src/renderer/src/main.tsx +25 -0
- package/src/generators/electron-app/files/src/shared/ipc.ts +54 -0
- package/src/generators/electron-app/files/tests/smoke/app.spec.ts.template +133 -0
- package/src/generators/electron-app/files/tool/electron_exec.sh.template +83 -0
- package/src/generators/electron-app/files/tsconfig.json +7 -0
- package/src/generators/electron-app/files/tsconfig.node.json +27 -0
- package/src/generators/electron-app/files/tsconfig.web.json +22 -0
- package/src/generators/electron-app/files/vitest.config.ts +32 -0
- package/src/generators/electron-app/files/vitest.setup.ts +1 -0
- package/src/generators/electron-app/generator.ts +288 -0
- package/src/generators/electron-app/schema.json +23 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/architecture.md +78 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/index.md +38 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/platform-channels.md +51 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/releases-and-distribution.md +59 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/state-management.md +85 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/testing.md +86 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/widgets-and-composition.md +69 -0
- package/src/generators/flutter-app/files/.gitignore.template +30 -0
- package/src/generators/flutter-app/files/README.md.template +100 -0
- package/src/generators/flutter-app/files/analysis_options.yaml.template +18 -0
- package/src/generators/flutter-app/files/integration_test/app_test.dart.template +64 -0
- package/src/generators/flutter-app/files/lib/app.dart.template +24 -0
- package/src/generators/flutter-app/files/lib/config/app_config.dart +15 -0
- package/src/generators/flutter-app/files/lib/data/repositories/status_repository.dart +36 -0
- package/src/generators/flutter-app/files/lib/data/services/api_client.dart +71 -0
- package/src/generators/flutter-app/files/lib/domain/models/health_status.dart +23 -0
- package/src/generators/flutter-app/files/lib/main.dart +11 -0
- package/src/generators/flutter-app/files/lib/router.dart +23 -0
- package/src/generators/flutter-app/files/lib/ui/core/theme/app_theme.dart +110 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view.dart +89 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view_model.dart.template +38 -0
- package/src/generators/flutter-app/files/project.json.template +51 -0
- package/src/generators/flutter-app/files/pubspec.yaml.template +47 -0
- package/src/generators/flutter-app/files/test/api_client_test.dart.template +63 -0
- package/src/generators/flutter-app/files/test/fakes/fake_status_repository.dart.template +19 -0
- package/src/generators/flutter-app/files/test/home_view_test.dart.template +58 -0
- package/src/generators/flutter-app/files/tool/flutter_exec.sh.template +60 -0
- package/src/generators/flutter-app/generator.ts +362 -0
- package/src/generators/flutter-app/schema.json +23 -0
- package/src/generators/go-microservice/docs/principles/stack/go/concurrency.md +123 -0
- package/src/generators/go-microservice/docs/principles/stack/go/index.md +70 -0
- package/src/generators/go-microservice/docs/principles/stack/go/testing.md +168 -0
- package/src/generators/go-microservice/files/.air.toml.template +38 -0
- package/src/generators/go-microservice/files/.env.template +4 -0
- package/src/generators/go-microservice/files/.golangci.yml.template +82 -0
- package/src/generators/go-microservice/files/Dockerfile.dev.template +12 -0
- package/src/generators/go-microservice/files/asyncapi-pubsub.yaml.template +33 -0
- package/src/generators/go-microservice/files/asyncapi-ws.yaml.template +34 -0
- package/src/generators/go-microservice/files/cmd/api/main.go.template +149 -0
- package/src/generators/go-microservice/files/cmd/api/main_test.go.template +99 -0
- package/src/generators/go-microservice/files/cmd/worker/cleanup/main.go.template +39 -0
- package/src/generators/go-microservice/files/db/schema.sql.template +24 -0
- package/src/generators/go-microservice/files/go.mod.template +39 -0
- package/src/generators/go-microservice/files/internal/config/config.go.template +52 -0
- package/src/generators/go-microservice/files/internal/config/otel.go.template +93 -0
- package/src/generators/go-microservice/files/internal/core/domain/errors.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/domain/model.go.template +28 -0
- package/src/generators/go-microservice/files/internal/core/domain/user.go.template +13 -0
- package/src/generators/go-microservice/files/internal/core/pagination.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/service/app_service.go.template +79 -0
- package/src/generators/go-microservice/files/internal/core/service/event_hub.go.template +9 -0
- package/src/generators/go-microservice/files/internal/core/service/message_queue.go.template +10 -0
- package/src/generators/go-microservice/files/internal/core/service/outbox_repository.go.template +31 -0
- package/src/generators/go-microservice/files/internal/core/service/repository.go.template +23 -0
- package/src/generators/go-microservice/files/internal/core/service/user_repository.go.template +15 -0
- package/src/generators/go-microservice/files/internal/core/service/user_service.go.template +43 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/app_handler.go.template +108 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/auth_middleware_test.go.template +52 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook.go.template +202 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook_test.go.template +82 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/health_handler.go.template +80 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware.go.template +87 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware_test.go.template +76 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/repository.go.template +37 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_auth.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_loadshed.go.template +38 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_logging.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_ratelimit.go.template +48 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_test.go.template +81 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/router.go.template +105 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/types.go.template +70 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/websocket_handler.go.template +39 -0
- package/src/generators/go-microservice/files/internal/httpclient/http_client.go.template +87 -0
- package/src/generators/go-microservice/files/internal/kafka/kafka.go.template +34 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres.go.template +195 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres_test.go.template +156 -0
- package/src/generators/go-microservice/files/internal/postgres/user_repository.go.template +56 -0
- package/src/generators/go-microservice/files/internal/pubsub/gcp_pubsub.go.template +35 -0
- package/src/generators/go-microservice/files/internal/websocket/client.go.template +151 -0
- package/src/generators/go-microservice/files/internal/websocket/hub.go.template +261 -0
- package/src/generators/go-microservice/files/scripts/apply-schema.sh.template +21 -0
- package/src/generators/go-microservice/files/tools/tools.go.template +10 -0
- package/src/generators/go-microservice/generator.ts +240 -0
- package/src/generators/go-microservice/schema.json +63 -0
- package/src/generators/nextjs-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/nextjs-app/files/.dockerignore.template +7 -0
- package/src/generators/nextjs-app/files/.env.example.template +24 -0
- package/src/generators/nextjs-app/files/.gitignore.template +5 -0
- package/src/generators/nextjs-app/files/Dockerfile +53 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-in/__sign-in__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-up/__sign-up__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/api/config/route.ts.template +39 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.test.ts +15 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.ts +5 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.test.ts.template +55 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.ts.template +126 -0
- package/src/generators/nextjs-app/files/app/error.tsx +39 -0
- package/src/generators/nextjs-app/files/app/global-error.tsx +68 -0
- package/src/generators/nextjs-app/files/app/globals.css +105 -0
- package/src/generators/nextjs-app/files/app/layout.tsx +59 -0
- package/src/generators/nextjs-app/files/app/loading.tsx +13 -0
- package/src/generators/nextjs-app/files/app/not-found.tsx +30 -0
- package/src/generators/nextjs-app/files/app/page.tsx +20 -0
- package/src/generators/nextjs-app/files/components/providers/default.tsx +19 -0
- package/src/generators/nextjs-app/files/components/providers/production.tsx +32 -0
- package/src/generators/nextjs-app/files/components/providers/telemetry.tsx +76 -0
- package/src/generators/nextjs-app/files/components/render-smoke.test.tsx +29 -0
- package/src/generators/nextjs-app/files/components/theme-provider.tsx +11 -0
- package/src/generators/nextjs-app/files/components.json +21 -0
- package/src/generators/nextjs-app/files/eslint.config.mjs +120 -0
- package/src/generators/nextjs-app/files/hooks/use-toast.ts +7 -0
- package/src/generators/nextjs-app/files/instrumentation.ts +90 -0
- package/src/generators/nextjs-app/files/lib/api/fetcher.ts.template +130 -0
- package/src/generators/nextjs-app/files/lib/config.ts +21 -0
- package/src/generators/nextjs-app/files/lib/logger.ts +29 -0
- package/src/generators/nextjs-app/files/lib/schemas/index.ts +19 -0
- package/src/generators/nextjs-app/files/lib/utils.ts +6 -0
- package/src/generators/nextjs-app/files/next.config.mjs +9 -0
- package/src/generators/nextjs-app/files/package.json +70 -0
- package/src/generators/nextjs-app/files/postcss.config.mjs +8 -0
- package/src/generators/nextjs-app/files/proxy.test.ts.template +30 -0
- package/src/generators/nextjs-app/files/proxy.ts +31 -0
- package/src/generators/nextjs-app/files/public/.gitkeep +1 -0
- package/src/generators/nextjs-app/files/tsconfig.json +42 -0
- package/src/generators/nextjs-app/files/vitest.config.mts +15 -0
- package/src/generators/nextjs-app/files/vitest.setup.ts +7 -0
- package/src/generators/nextjs-app/generator.ts +307 -0
- package/src/generators/nextjs-app/schema.json +44 -0
- package/src/generators/python-microservice/docs/principles/stack/python/async.md +168 -0
- package/src/generators/python-microservice/docs/principles/stack/python/documentation.md +240 -0
- package/src/generators/python-microservice/docs/principles/stack/python/mcp.md +147 -0
- package/src/generators/python-microservice/docs/principles/stack/python/resilience.md +193 -0
- package/src/generators/python-microservice/docs/principles/stack/python/testing.md +322 -0
- package/src/generators/python-microservice/files/.env.example.template +30 -0
- package/src/generators/python-microservice/files/Dockerfile.template +36 -0
- package/src/generators/python-microservice/files/db/schema.sql.template +19 -0
- package/src/generators/python-microservice/files/pyproject.toml.template +76 -0
- package/src/generators/python-microservice/files/scripts/apply-schema.sh.template +25 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/comfyui.py.template +87 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/config.py.template +48 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/database.py.template +21 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/message_queue.py.template +29 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/repository.py.template +130 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/telemetry.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/websocket_hub.py.template +36 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/entities.py.template +22 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/exceptions.py.template +43 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/ports.py.template +42 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/service/example_service.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/dependencies.py.template +50 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/middleware.py.template +131 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/router.py.template +37 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/websocket_handler.py.template +20 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/cleanup.py.template +35 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/worker.py.template +28 -0
- package/src/generators/python-microservice/files/src/__packageName__/main.py.template +108 -0
- package/src/generators/python-microservice/files/tests/test_main.py.template +74 -0
- package/src/generators/python-microservice/files/tests/test_middleware.py.template +109 -0
- package/src/generators/python-microservice/files/tests/test_worker.py.template +16 -0
- package/src/generators/python-microservice/generator.ts +286 -0
- package/src/generators/python-microservice/schema.json +86 -0
- package/src/generators/shared/brand-tokens.ts +301 -0
- package/src/generators/shared/capabilities.ts +349 -0
- package/src/generators/shared/provenance.ts +61 -0
- package/src/generators/shared/scaffold-helpers.ts +309 -0
- package/src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md +20 -0
- package/src/generators/system-test-runner/files/tests/bets/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/bets/_archive/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/conftest.py.template +503 -0
- package/src/generators/system-test-runner/files/tests/pyproject.toml.template +20 -0
- package/src/generators/system-test-runner/files/tests/system/pages/__init__.py.template +9 -0
- package/src/generators/system-test-runner/files/tests/system/pages/base_page.py.template +36 -0
- package/src/generators/system-test-runner/files/tests/system/test_a11y_smoke.py.template +132 -0
- package/src/generators/system-test-runner/files/tests/system/test_contract_conformance.py.template +140 -0
- package/src/generators/system-test-runner/files/tests/system/test_layout_geometry.py.template +109 -0
- package/src/generators/system-test-runner/files/tests/system/test_render_smoke.py.template +257 -0
- package/src/generators/system-test-runner/files/tests/system/test_system.py.template +158 -0
- package/src/generators/system-test-runner/files/tests/system/test_token_conformance.py.template +206 -0
- package/src/generators/system-test-runner/files/tests/system/test_visual_regression.py.template +104 -0
- package/src/generators/system-test-runner/generator.ts +196 -0
- package/src/generators/system-test-runner/schema.json +24 -0
- package/src/generators/workspace-dev-cli/cli-src/build.mjs +42 -0
- package/src/generators/workspace-dev-cli/cli-src/dist/dev-bundle.js +2168 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/bet.ts +442 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/completion.ts +87 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/doctor.ts +139 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/lifecycle.ts +548 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/quality.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/surface.ts +214 -0
- package/src/generators/workspace-dev-cli/cli-src/src/index.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/registry.ts +194 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/color.ts +130 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/render.ts +158 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/tokens.ts +122 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/context.ts +43 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/extensions.ts +99 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/paths.ts +46 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/proc.ts +106 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/prompt.ts +108 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/runners.ts +70 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/services.ts +221 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/version.ts +21 -0
- package/src/generators/workspace-dev-cli/cli-src/tsconfig.json +16 -0
- package/src/generators/workspace-dev-cli/files/.agents/skills/workspace-cli/SKILL.md.template +74 -0
- package/src/generators/workspace-dev-cli/files/dev.template +16 -0
- package/src/generators/workspace-dev-cli/files/docker-compose.yml.template +20 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/milestone-test.pytmpl.template +46 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/slice-test.pytmpl.template +38 -0
- package/src/generators/workspace-dev-cli/generator.ts +136 -0
- package/src/generators/workspace-dev-cli/schema.json +22 -0
- package/src/hidden-skills/code-intelligence.md +135 -0
- package/src/hidden-skills/groundwork-architect/SKILL.md +114 -0
- package/src/hidden-skills/groundwork-architect/references/agentic-systems.md +44 -0
- package/src/hidden-skills/groundwork-architect/references/ai-native-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/api-and-contracts.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/core-and-boundaries.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/data-architecture.md +33 -0
- package/src/hidden-skills/groundwork-architect/references/decision-records.md +34 -0
- package/src/hidden-skills/groundwork-architect/references/durable-execution.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/evolutionary-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/identity-and-access.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/integration-patterns.md +39 -0
- package/src/hidden-skills/groundwork-architect/references/observability.md +36 -0
- package/src/hidden-skills/groundwork-architect/references/performance-and-scale.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/platform-and-delivery.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/realtime-and-async.md +28 -0
- package/src/hidden-skills/groundwork-architect/references/reliability.md +31 -0
- package/src/hidden-skills/groundwork-architect/references/security-and-trust.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/surface-architecture.md +40 -0
- package/src/hidden-skills/groundwork-architect/sync-anchor.md +34 -0
- package/src/hidden-skills/groundwork-architecture/architecture-template.md +50 -0
- package/src/hidden-skills/groundwork-architecture/instructions.md +139 -0
- package/src/hidden-skills/groundwork-architecture/phases/01-context-ingestion.md +18 -0
- package/src/hidden-skills/groundwork-architecture/phases/02-technical-constraints.md +27 -0
- package/src/hidden-skills/groundwork-architecture/phases/03-service-design.md +19 -0
- package/src/hidden-skills/groundwork-architecture/phases/04-data-flow-communication.md +23 -0
- package/src/hidden-skills/groundwork-architecture/phases/05-component-boundaries-contracts.md +17 -0
- package/src/hidden-skills/groundwork-architecture/phases/06-draft-review-present.md +38 -0
- package/src/hidden-skills/groundwork-architecture/phases/07-commit.md +33 -0
- package/src/hidden-skills/groundwork-architecture/templates/architecture-cache.md +43 -0
- package/src/hidden-skills/groundwork-architecture-extract/instructions.md +163 -0
- package/src/hidden-skills/groundwork-architecture-extract/templates/architecture-extract-cache.md +21 -0
- package/src/hidden-skills/groundwork-bet/briefs/acceptance-auditor.md +68 -0
- package/src/hidden-skills/groundwork-bet/briefs/blind-reviewer.md +56 -0
- package/src/hidden-skills/groundwork-bet/briefs/coverage-auditor.md +95 -0
- package/src/hidden-skills/groundwork-bet/briefs/edge-case-tracer.md +64 -0
- package/src/hidden-skills/groundwork-bet/briefs/experience-auditor.md +83 -0
- package/src/hidden-skills/groundwork-bet/briefs/slice-worker.md +257 -0
- package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +115 -0
- package/src/hidden-skills/groundwork-bet/templates/change-proposal.md +38 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/meta.json +4 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/milestone-index.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/pitch.md +45 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/01-ui-design.md +51 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/02-data-flows.md +36 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/03-api-design.md +90 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/04-data-design.md +29 -0
- package/src/hidden-skills/groundwork-bet/workflows/01-discovery.md +200 -0
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +178 -0
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +242 -0
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +226 -0
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +210 -0
- package/src/hidden-skills/groundwork-design-system/instructions.md +125 -0
- package/src/hidden-skills/groundwork-design-system/templates/brand-tokens.md +182 -0
- package/src/hidden-skills/groundwork-design-system/templates/design-system-cache.md +64 -0
- package/src/hidden-skills/groundwork-design-system/tracks/_foundation.md +136 -0
- package/src/hidden-skills/groundwork-design-system/tracks/agentic-protocol.md +269 -0
- package/src/hidden-skills/groundwork-design-system/tracks/cli.md +355 -0
- package/src/hidden-skills/groundwork-design-system/tracks/graphical-ui.md +330 -0
- package/src/hidden-skills/groundwork-design-system-extract/instructions.md +124 -0
- package/src/hidden-skills/groundwork-design-system-extract/templates/design-system-extract-cache.md +19 -0
- package/src/hidden-skills/groundwork-designer/SKILL.md +108 -0
- package/src/hidden-skills/groundwork-designer/references/accessibility.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/ai-native-design.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/design-review.md +29 -0
- package/src/hidden-skills/groundwork-designer/references/design-systems-and-tokens.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/interaction-and-motion.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/layout-and-space.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/usability-and-ux.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/visual-craft.md +49 -0
- package/src/hidden-skills/groundwork-designer/sync-anchor.md +20 -0
- package/src/hidden-skills/groundwork-doc-sync/instructions.md +100 -0
- package/src/hidden-skills/groundwork-elicit/instructions.md +66 -0
- package/src/hidden-skills/groundwork-elicit/methods.md +65 -0
- package/src/hidden-skills/groundwork-infra-adopt/instructions.md +168 -0
- package/src/hidden-skills/groundwork-infra-adopt/templates/infra-adopt-cache.md +21 -0
- package/src/hidden-skills/groundwork-mvp/instructions.md +223 -0
- package/src/hidden-skills/groundwork-mvp/templates/mvp-cache.md +9 -0
- package/src/hidden-skills/groundwork-patch/instructions.md +40 -0
- package/src/hidden-skills/groundwork-persona/instructions.md +65 -0
- package/src/hidden-skills/groundwork-product/SKILL.md +102 -0
- package/src/hidden-skills/groundwork-product/references/ai-native-product.md +45 -0
- package/src/hidden-skills/groundwork-product/references/discovery-and-opportunity.md +38 -0
- package/src/hidden-skills/groundwork-product/references/product-risks.md +52 -0
- package/src/hidden-skills/groundwork-product/references/requirements-and-specs.md +39 -0
- package/src/hidden-skills/groundwork-product/references/scope-and-sequencing.md +35 -0
- package/src/hidden-skills/groundwork-product/references/shaping-and-appetite.md +48 -0
- package/src/hidden-skills/groundwork-product/references/success-metrics-and-signals.md +37 -0
- package/src/hidden-skills/groundwork-product/sync-anchor.md +19 -0
- package/src/hidden-skills/groundwork-product-brief/instructions.md +231 -0
- package/src/hidden-skills/groundwork-product-brief-extract/instructions.md +139 -0
- package/src/hidden-skills/groundwork-product-brief-extract/templates/product-brief-extract-cache.md +17 -0
- package/src/hidden-skills/groundwork-review/checklists/architecture.md +93 -0
- package/src/hidden-skills/groundwork-review/checklists/bet-pitch.md +94 -0
- package/src/hidden-skills/groundwork-review/checklists/decomposition.md +135 -0
- package/src/hidden-skills/groundwork-review/checklists/design-system.md +85 -0
- package/src/hidden-skills/groundwork-review/checklists/domain-entity.md +66 -0
- package/src/hidden-skills/groundwork-review/checklists/implementation-readiness.md +47 -0
- package/src/hidden-skills/groundwork-review/checklists/infrastructure.md +68 -0
- package/src/hidden-skills/groundwork-review/checklists/maturity.md +71 -0
- package/src/hidden-skills/groundwork-review/checklists/product-brief.md +69 -0
- package/src/hidden-skills/groundwork-review/checklists/technical-design.md +112 -0
- package/src/hidden-skills/groundwork-review/instructions.md +181 -0
- package/src/hidden-skills/groundwork-scaffold/instructions.md +254 -0
- package/src/hidden-skills/groundwork-scaffold/phases/01-ingestion-service-mapping.md +87 -0
- package/src/hidden-skills/groundwork-scaffold/phases/02-scaffolding-execution.md +15 -0
- package/src/hidden-skills/groundwork-scaffold/phases/03-service-documentation-api-stubs.md +100 -0
- package/src/hidden-skills/groundwork-scaffold/phases/04-infrastructure-verification.md +17 -0
- package/src/hidden-skills/groundwork-scaffold/phases/05-draft-review.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/phases/06-commit.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/templates/scaffold-cache.md +23 -0
- package/src/hidden-skills/groundwork-scan/instructions.md +164 -0
- package/src/hidden-skills/groundwork-scan/references/digest-schema.md +66 -0
- package/src/hidden-skills/groundwork-scan/references/exclusions.md +44 -0
- package/src/hidden-skills/groundwork-scan/templates/architecture-findings.md +42 -0
- package/src/hidden-skills/groundwork-scan/templates/design-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/overview.md +26 -0
- package/src/hidden-skills/groundwork-scan/templates/product-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/scan-state.json +19 -0
- package/src/hidden-skills/groundwork-stack-forge/instructions.md +150 -0
- package/src/hidden-skills/groundwork-stack-forge/references/authoring-engineer-skills.md +107 -0
- package/src/hidden-skills/groundwork-surface-activation/instructions.md +138 -0
- package/src/hidden-skills/groundwork-update/briefs/reconcile-worker.md +196 -0
- package/src/hidden-skills/groundwork-update/instructions.md +200 -0
- package/src/hidden-skills/groundwork-writer/SKILL.md +278 -0
- package/src/hidden-skills/maturity-model.md +125 -0
- package/src/hidden-skills/operating-contract.md +400 -0
- package/src/hidden-skills/repo-map-schema.md +90 -0
- package/src/hidden-skills/templates/adr.md +57 -0
- package/src/hidden-skills/templates/capability-ports.md +71 -0
- package/src/hidden-skills/templates/discovery-notes.md +33 -0
- package/src/hidden-skills/templates/domain-entity.md +80 -0
- package/src/hidden-skills/templates/gap-ledger.md +21 -0
- package/src/hidden-skills/templates/handoff.md +37 -0
- package/src/hidden-skills/templates/maturity.md +39 -0
- package/src/hidden-skills/templates/surfaces.md +207 -0
- package/src/skills/groundwork-check/SKILL.md +56 -0
- package/src/skills/groundwork-check/instructions.md +70 -0
- package/src/skills/groundwork-orchestrator/SKILL.md +176 -0
- package/src/skills/groundwork-orchestrator/workflow-index.md +50 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Real, discovery-driven system tests. Parametrized over every APP service
|
|
2
|
+
found in the workspace docker-compose.yml (see conftest.services_manifest).
|
|
3
|
+
|
|
4
|
+
FAIL LOUD: the span tests are gated by GROUNDWORK_REQUIRE_TRACES=1. When that
|
|
5
|
+
flag is set (CI, `./dev test integration`), an unreachable backend or a
|
|
6
|
+
missing span is a FAILURE, not a skip.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from conftest import JAEGER_URL, _base_url, _discover_services
|
|
17
|
+
|
|
18
|
+
REQUIRE_TRACES = os.environ.get("GROUNDWORK_REQUIRE_TRACES") == "1"
|
|
19
|
+
# BatchSpanProcessor flushes ~every 5s; give spans time to land.
|
|
20
|
+
EXPORT_TIMEOUT_S = 30
|
|
21
|
+
|
|
22
|
+
# Build params at import time so pytest can show one case per service. An empty
|
|
23
|
+
# workspace yields zero params (and the suite is a no-op, which is correct).
|
|
24
|
+
_SERVICES = _discover_services()
|
|
25
|
+
_PARAMS = [pytest.param(s, id=s["name"]) for s in _SERVICES]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _fetch_trace(trace_id: str) -> dict | None:
|
|
29
|
+
deadline = time.time() + EXPORT_TIMEOUT_S
|
|
30
|
+
while time.time() < deadline:
|
|
31
|
+
resp = httpx.get(f"{JAEGER_URL}/api/traces/{trace_id}", timeout=3.0)
|
|
32
|
+
if resp.status_code == 200 and resp.json().get("data"):
|
|
33
|
+
return resp.json()["data"][0]
|
|
34
|
+
time.sleep(1)
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("svc", _PARAMS)
|
|
39
|
+
def test_every_service_is_healthy(cluster, svc):
|
|
40
|
+
"""Each discovered service answers its health endpoint. Synchronous on
|
|
41
|
+
purpose (like the trace probes below): a plain GET needs no event loop, and
|
|
42
|
+
avoiding an async fixture keeps the suite robust across pytest-asyncio /
|
|
43
|
+
Python versions."""
|
|
44
|
+
if svc["host_port"] is None:
|
|
45
|
+
pytest.skip(f"{svc['name']} has no published host port")
|
|
46
|
+
url = f"{_base_url(svc)}{svc['health_path']}"
|
|
47
|
+
resp = httpx.get(url, timeout=5.0)
|
|
48
|
+
assert resp.status_code == 200, f"{url} -> {resp.status_code}"
|
|
49
|
+
data = resp.json()
|
|
50
|
+
if svc["type"] == "go":
|
|
51
|
+
assert data.get("status") == "ok", f"{svc['name']} status: {data}"
|
|
52
|
+
assert data.get("checks", {}).get("db") == "ok", f"{svc['name']} db: {data}"
|
|
53
|
+
else:
|
|
54
|
+
# Python FastAPI returns {"status": "alive"}; Next returns its own shape.
|
|
55
|
+
assert data.get("status") in {"ok", "alive", "ready", "healthy"}, f"{svc['name']}: {data}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.mark.parametrize("svc", _PARAMS)
|
|
59
|
+
def test_instrumented_get_exports_span(cluster, svc):
|
|
60
|
+
"""A traced GET to an instrumented route produces a span that reaches jaeger.
|
|
61
|
+
|
|
62
|
+
Go and Python both export spans over OTLP: Go via otel.go, Python via the
|
|
63
|
+
FastAPI instrumentation wired in adapters/telemetry.py. Next.js server-side
|
|
64
|
+
tracing is unverified through this harness (no domain GET route to probe),
|
|
65
|
+
so it xfails here.
|
|
66
|
+
"""
|
|
67
|
+
if svc["type"] not in ("go", "python"):
|
|
68
|
+
pytest.xfail(f"{svc['type']} trace export not verified through this harness (TODO)")
|
|
69
|
+
if svc["host_port"] is None:
|
|
70
|
+
pytest.skip(f"{svc['name']} has no published host port")
|
|
71
|
+
|
|
72
|
+
trace_id = uuid.uuid4().hex
|
|
73
|
+
span_id = uuid.uuid4().hex[:16]
|
|
74
|
+
# The trailing -01 (sampled) flag is load-bearing: ParentBased(AlwaysSample)
|
|
75
|
+
# inherits it. -00 exports nothing.
|
|
76
|
+
traceparent = f"00-{trace_id}-{span_id}-01"
|
|
77
|
+
# /health is excluded from tracing by the otelhttp middleware; use a real
|
|
78
|
+
# route. Default is per-service-type (Go /api/v1/entities, Python /examples);
|
|
79
|
+
# TRACE_PROBE_PATH overrides for ad-hoc runs.
|
|
80
|
+
probe = os.environ.get("TRACE_PROBE_PATH", svc["probe_path"])
|
|
81
|
+
|
|
82
|
+
# Warm up: a just-booted service can serve liveness before its DB-backed routes
|
|
83
|
+
# are ready (e.g. a schema migration is still settling), so the first requests
|
|
84
|
+
# may transiently 5xx. Retry until the route is ready so this asserts span
|
|
85
|
+
# export, not startup ordering (test_every_service_is_healthy covers liveness).
|
|
86
|
+
resp = None
|
|
87
|
+
for attempt in range(8):
|
|
88
|
+
resp = httpx.get(
|
|
89
|
+
f"{_base_url(svc)}{probe}",
|
|
90
|
+
headers={"traceparent": traceparent},
|
|
91
|
+
timeout=5.0,
|
|
92
|
+
)
|
|
93
|
+
if resp.status_code == 200:
|
|
94
|
+
break
|
|
95
|
+
time.sleep(2)
|
|
96
|
+
assert resp.status_code == 200, f"probe failed after warmup retries: {resp.status_code}"
|
|
97
|
+
|
|
98
|
+
trace = _fetch_trace(trace_id)
|
|
99
|
+
if trace is None:
|
|
100
|
+
msg = (
|
|
101
|
+
f"no span with trace_id={trace_id} reached jaeger within "
|
|
102
|
+
f"{EXPORT_TIMEOUT_S}s — the OTLP export pipeline is broken"
|
|
103
|
+
)
|
|
104
|
+
if REQUIRE_TRACES:
|
|
105
|
+
pytest.fail(msg)
|
|
106
|
+
pytest.skip(msg)
|
|
107
|
+
|
|
108
|
+
services = {p["serviceName"] for p in trace["processes"].values()}
|
|
109
|
+
assert svc["otel_name"] in services, (
|
|
110
|
+
f"expected span from '{svc['otel_name']}', got {services}"
|
|
111
|
+
)
|
|
112
|
+
assert trace["spans"], "trace arrived but contains no spans"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.parametrize("svc", _PARAMS)
|
|
116
|
+
def test_trace_context_propagates_unchanged(cluster, svc):
|
|
117
|
+
"""The service continues the incoming trace id rather than starting a new one."""
|
|
118
|
+
if svc["type"] not in ("go", "python"):
|
|
119
|
+
pytest.xfail(f"{svc['type']} trace export not verified through this harness (TODO)")
|
|
120
|
+
if svc["host_port"] is None:
|
|
121
|
+
pytest.skip(f"{svc['name']} has no published host port")
|
|
122
|
+
|
|
123
|
+
trace_id = uuid.uuid4().hex
|
|
124
|
+
span_id = uuid.uuid4().hex[:16]
|
|
125
|
+
traceparent = f"00-{trace_id}-{span_id}-01"
|
|
126
|
+
probe = os.environ.get("TRACE_PROBE_PATH", svc["probe_path"])
|
|
127
|
+
|
|
128
|
+
httpx.get(
|
|
129
|
+
f"{_base_url(svc)}{probe}",
|
|
130
|
+
headers={"traceparent": traceparent},
|
|
131
|
+
timeout=5.0,
|
|
132
|
+
)
|
|
133
|
+
trace = _fetch_trace(trace_id)
|
|
134
|
+
if trace is None:
|
|
135
|
+
msg = "injected trace context was not propagated/exported"
|
|
136
|
+
if REQUIRE_TRACES:
|
|
137
|
+
pytest.fail(msg)
|
|
138
|
+
pytest.skip(msg)
|
|
139
|
+
# Compare numerically so leading-zero normalisation doesn't cause a false fail.
|
|
140
|
+
assert int(trace["traceID"], 16) == int(trace_id, 16)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@pytest.mark.skip(reason="real CRUD lands in Phase 4")
|
|
144
|
+
@pytest.mark.asyncio
|
|
145
|
+
@pytest.mark.parametrize("svc", _PARAMS)
|
|
146
|
+
async def test_crud_round_trip(cluster, api_client: httpx.AsyncClient, svc):
|
|
147
|
+
"""POST then GET an entity and assert the round-trip through the real DB."""
|
|
148
|
+
if svc["type"] != "go":
|
|
149
|
+
pytest.skip("CRUD round-trip targets Go services for now")
|
|
150
|
+
base = _base_url(svc)
|
|
151
|
+
payload = {"name": "phase0-roundtrip"}
|
|
152
|
+
created = await api_client.post(f"{base}/api/v1/entities", json=payload, timeout=5.0)
|
|
153
|
+
assert created.status_code in (200, 201), created.text
|
|
154
|
+
entity_id = created.json().get("id")
|
|
155
|
+
assert entity_id, "POST did not return an id"
|
|
156
|
+
fetched = await api_client.get(f"{base}/api/v1/entities/{entity_id}", timeout=5.0)
|
|
157
|
+
assert fetched.status_code == 200
|
|
158
|
+
assert fetched.json().get("name") == payload["name"]
|
package/src/generators/system-test-runner/files/tests/system/test_token_conformance.py.template
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<% if (!surfaces) { -%>
|
|
2
|
+
"""Token-conformance gate — Tier 1 of the visual verification loop.
|
|
3
|
+
|
|
4
|
+
The deterministic answer to "did the polish actually land?" A behavioural test
|
|
5
|
+
passes while the atmosphere has silently degraded to a flat default — the build
|
|
6
|
+
dropped the design system's surface treatment, or the projected tokens never
|
|
7
|
+
reached the page. This gate reads computed styles and asserts the opposite:
|
|
8
|
+
the atmosphere tokens resolve, the elevation is a multi-layer stack (not one
|
|
9
|
+
default drop shadow), and any surface treatment in the DOM renders with its
|
|
10
|
+
backdrop blur and layered shadow rather than collapsing to a framework default.
|
|
11
|
+
|
|
12
|
+
It is deliberately structural, not aesthetic: it cannot judge taste (that is the
|
|
13
|
+
designer's spec-conformance pass), but it makes "the tokens are applied" a fact,
|
|
14
|
+
not an opinion. Skips cleanly when no frontend service is discovered.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import pathlib
|
|
19
|
+
|
|
20
|
+
from playwright.sync_api import Page
|
|
21
|
+
|
|
22
|
+
# Atmosphere tokens the nextjs-app generator projects into brand.css from the
|
|
23
|
+
# design system's visual block. Their presence proves the projection landed.
|
|
24
|
+
_PROBE = """
|
|
25
|
+
() => {
|
|
26
|
+
const root = getComputedStyle(document.documentElement);
|
|
27
|
+
const tok = (n) => (root.getPropertyValue(n) || '').trim();
|
|
28
|
+
const countLayers = (s) => {
|
|
29
|
+
s = (s || '').trim();
|
|
30
|
+
if (!s || s === 'none') return 0;
|
|
31
|
+
let depth = 0, n = 1;
|
|
32
|
+
for (const ch of s) {
|
|
33
|
+
if (ch === '(') depth++;
|
|
34
|
+
else if (ch === ')') depth--;
|
|
35
|
+
else if (ch === ',' && depth === 0) n++;
|
|
36
|
+
}
|
|
37
|
+
return n;
|
|
38
|
+
};
|
|
39
|
+
const surfaces = [];
|
|
40
|
+
for (const el of document.querySelectorAll('.surface-glass, .surface-elevated, .surface-hero')) {
|
|
41
|
+
const cs = getComputedStyle(el);
|
|
42
|
+
const bf = (cs.backdropFilter || cs.webkitBackdropFilter || '').trim();
|
|
43
|
+
surfaces.push({
|
|
44
|
+
cls: el.className,
|
|
45
|
+
hasBlur: /blur\\(/.test(bf),
|
|
46
|
+
shadowLayers: countLayers(cs.boxShadow),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
shadowMid: tok('--gw-shadow-mid'),
|
|
51
|
+
shadowMidLayers: countLayers(tok('--gw-shadow-mid')),
|
|
52
|
+
blurStandard: tok('--gw-blur-standard'),
|
|
53
|
+
surfaces,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load_routes() -> tuple[str, ...]:
|
|
60
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
61
|
+
if manifest.exists():
|
|
62
|
+
try:
|
|
63
|
+
routes = json.loads(manifest.read_text())
|
|
64
|
+
if isinstance(routes, list) and routes:
|
|
65
|
+
return tuple(routes)
|
|
66
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
67
|
+
pass
|
|
68
|
+
return ("/",)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
ROUTES = _load_routes()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _assert_token_conformance(page: Page, surface_slug: str, base_url: str | None) -> None:
|
|
75
|
+
for route in ROUTES:
|
|
76
|
+
target = (base_url.rstrip("/") + route) if base_url else route
|
|
77
|
+
page.goto(target, wait_until="load")
|
|
78
|
+
page.wait_for_load_state("networkidle")
|
|
79
|
+
r = page.evaluate(_PROBE)
|
|
80
|
+
|
|
81
|
+
# The atmosphere layer projected into :root.
|
|
82
|
+
assert r["shadowMid"], (
|
|
83
|
+
f"{surface_slug} {route}: --gw-shadow-mid is undefined — the brand.css "
|
|
84
|
+
f"projection did not reach the page (atmosphere tokens missing)."
|
|
85
|
+
)
|
|
86
|
+
assert r["blurStandard"], (
|
|
87
|
+
f"{surface_slug} {route}: --gw-blur-standard is undefined — blur tokens "
|
|
88
|
+
f"did not project."
|
|
89
|
+
)
|
|
90
|
+
assert r["shadowMidLayers"] >= 2, (
|
|
91
|
+
f"{surface_slug} {route}: --gw-shadow-mid is a single-layer shadow "
|
|
92
|
+
f"({r['shadowMidLayers']} layer) — the multi-layer elevation stack "
|
|
93
|
+
f"degraded to a default drop shadow."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Any surface treatment in the DOM rendered its atmosphere, not a flat default.
|
|
97
|
+
for s in r["surfaces"]:
|
|
98
|
+
assert s["hasBlur"], (
|
|
99
|
+
f"{surface_slug} {route}: '{s['cls']}' rendered with no backdrop "
|
|
100
|
+
f"blur — the glass treatment was stripped to a flat fill."
|
|
101
|
+
)
|
|
102
|
+
assert s["shadowLayers"] >= 2, (
|
|
103
|
+
f"{surface_slug} {route}: '{s['cls']}' rendered with "
|
|
104
|
+
f"{s['shadowLayers']} shadow layer(s) — the elevation stack was lost."
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_frontend_token_conformance(cluster, frontend_base_url, page: Page):
|
|
109
|
+
_assert_token_conformance(page, "frontend", frontend_base_url)
|
|
110
|
+
<% } else { -%>
|
|
111
|
+
"""Token-conformance gate, one test per graphical surface — Tier 1 of the visual loop.
|
|
112
|
+
|
|
113
|
+
The deterministic answer to "did the polish actually land?" A behavioural test
|
|
114
|
+
passes while the atmosphere has silently degraded to a flat default. This gate
|
|
115
|
+
reads computed styles and asserts the atmosphere tokens resolve, the elevation is
|
|
116
|
+
a multi-layer stack, and any surface treatment in the DOM renders with its
|
|
117
|
+
backdrop blur and layered shadow rather than a framework default. Structural, not
|
|
118
|
+
aesthetic — taste is the designer's spec-conformance pass. Skips cleanly when a
|
|
119
|
+
surface is not reachable.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
import json
|
|
123
|
+
import pathlib
|
|
124
|
+
|
|
125
|
+
from playwright.sync_api import Page
|
|
126
|
+
|
|
127
|
+
_PROBE = """
|
|
128
|
+
() => {
|
|
129
|
+
const root = getComputedStyle(document.documentElement);
|
|
130
|
+
const tok = (n) => (root.getPropertyValue(n) || '').trim();
|
|
131
|
+
const countLayers = (s) => {
|
|
132
|
+
s = (s || '').trim();
|
|
133
|
+
if (!s || s === 'none') return 0;
|
|
134
|
+
let depth = 0, n = 1;
|
|
135
|
+
for (const ch of s) {
|
|
136
|
+
if (ch === '(') depth++;
|
|
137
|
+
else if (ch === ')') depth--;
|
|
138
|
+
else if (ch === ',' && depth === 0) n++;
|
|
139
|
+
}
|
|
140
|
+
return n;
|
|
141
|
+
};
|
|
142
|
+
const surfaces = [];
|
|
143
|
+
for (const el of document.querySelectorAll('.surface-glass, .surface-elevated, .surface-hero')) {
|
|
144
|
+
const cs = getComputedStyle(el);
|
|
145
|
+
const bf = (cs.backdropFilter || cs.webkitBackdropFilter || '').trim();
|
|
146
|
+
surfaces.push({
|
|
147
|
+
cls: el.className,
|
|
148
|
+
hasBlur: /blur\\(/.test(bf),
|
|
149
|
+
shadowLayers: countLayers(cs.boxShadow),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
shadowMid: tok('--gw-shadow-mid'),
|
|
154
|
+
shadowMidLayers: countLayers(tok('--gw-shadow-mid')),
|
|
155
|
+
blurStandard: tok('--gw-blur-standard'),
|
|
156
|
+
surfaces,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _load_routes() -> tuple[str, ...]:
|
|
163
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
164
|
+
if manifest.exists():
|
|
165
|
+
try:
|
|
166
|
+
routes = json.loads(manifest.read_text())
|
|
167
|
+
if isinstance(routes, list) and routes:
|
|
168
|
+
return tuple(routes)
|
|
169
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
170
|
+
pass
|
|
171
|
+
return ("/",)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
ROUTES = _load_routes()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _assert_token_conformance(page: Page, surface_slug: str) -> None:
|
|
178
|
+
for route in ROUTES:
|
|
179
|
+
page.goto(route, wait_until="load")
|
|
180
|
+
page.wait_for_load_state("networkidle")
|
|
181
|
+
r = page.evaluate(_PROBE)
|
|
182
|
+
|
|
183
|
+
assert r["shadowMid"], (
|
|
184
|
+
f"{surface_slug} {route}: --gw-shadow-mid is undefined — the brand.css "
|
|
185
|
+
f"projection did not reach the page."
|
|
186
|
+
)
|
|
187
|
+
assert r["blurStandard"], (
|
|
188
|
+
f"{surface_slug} {route}: --gw-blur-standard is undefined."
|
|
189
|
+
)
|
|
190
|
+
assert r["shadowMidLayers"] >= 2, (
|
|
191
|
+
f"{surface_slug} {route}: --gw-shadow-mid degraded to a single-layer "
|
|
192
|
+
f"shadow ({r['shadowMidLayers']} layer)."
|
|
193
|
+
)
|
|
194
|
+
for s in r["surfaces"]:
|
|
195
|
+
assert s["hasBlur"], (
|
|
196
|
+
f"{surface_slug} {route}: '{s['cls']}' rendered with no backdrop blur."
|
|
197
|
+
)
|
|
198
|
+
assert s["shadowLayers"] >= 2, (
|
|
199
|
+
f"{surface_slug} {route}: '{s['cls']}' rendered with "
|
|
200
|
+
f"{s['shadowLayers']} shadow layer(s) — elevation stack lost."
|
|
201
|
+
)
|
|
202
|
+
<% graphicalSurfaces.forEach((s) => { %>
|
|
203
|
+
|
|
204
|
+
def test_<%= s.ident %>_token_conformance(cluster, <%= s.ident %>_page: Page):
|
|
205
|
+
_assert_token_conformance(<%= s.ident %>_page, "<%= s.slug %>")
|
|
206
|
+
<% }) %><% } -%>
|
package/src/generators/system-test-runner/files/tests/system/test_visual_regression.py.template
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Visual regression gate — Tier 1 of the visual verification loop, OPT-IN.
|
|
2
|
+
|
|
3
|
+
Screenshot-baseline diffing catches *unintended* visual change against a known-good
|
|
4
|
+
baseline. It is off by default (decision D8): it carries real baseline-management and
|
|
5
|
+
flakiness cost, and it does nothing for a screen's *first* render — that is what the
|
|
6
|
+
render-smoke, a11y, and geometry gates plus the agent's own inspection cover. Turn it
|
|
7
|
+
on only once a surface is visually stable and worth pinning.
|
|
8
|
+
|
|
9
|
+
Enable: set GROUNDWORK_VISUAL_REGRESSION=1. Baselines live in
|
|
10
|
+
tests/system/visual-baselines/<surface>/<route>.png and are committed to the repo.
|
|
11
|
+
Update protocol: review the diff, then re-run with GROUNDWORK_VISUAL_REGRESSION=update
|
|
12
|
+
to overwrite the baseline deliberately — never auto-update in CI.
|
|
13
|
+
|
|
14
|
+
Determinism note: when enabled the gate pins the viewport, disables animations, and
|
|
15
|
+
masks nothing by default; add per-project masks for dynamic regions (timestamps,
|
|
16
|
+
avatars) before trusting it. Requires Pillow for the pixel diff; skips if absent.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import pathlib
|
|
22
|
+
|
|
23
|
+
import pytest
|
|
24
|
+
from playwright.sync_api import Page
|
|
25
|
+
|
|
26
|
+
_MODE = os.environ.get("GROUNDWORK_VISUAL_REGRESSION", "")
|
|
27
|
+
_BASELINE_DIR = pathlib.Path("tests/system/visual-baselines")
|
|
28
|
+
# Fraction of differing pixels tolerated before a route is considered changed.
|
|
29
|
+
_DIFF_THRESHOLD = 0.01
|
|
30
|
+
DESKTOP = (1280, 800)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _load_routes() -> tuple[str, ...]:
|
|
34
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
35
|
+
if manifest.exists():
|
|
36
|
+
try:
|
|
37
|
+
routes = json.loads(manifest.read_text())
|
|
38
|
+
if isinstance(routes, list) and routes:
|
|
39
|
+
return tuple(routes)
|
|
40
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
41
|
+
pass
|
|
42
|
+
return ("/",)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
ROUTES = _load_routes()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _diff_fraction(a_bytes: bytes, b_bytes: bytes) -> float:
|
|
49
|
+
from io import BytesIO
|
|
50
|
+
|
|
51
|
+
from PIL import Image, ImageChops
|
|
52
|
+
|
|
53
|
+
a = Image.open(BytesIO(a_bytes)).convert("RGB")
|
|
54
|
+
b = Image.open(BytesIO(b_bytes)).convert("RGB")
|
|
55
|
+
if a.size != b.size:
|
|
56
|
+
return 1.0
|
|
57
|
+
diff = ImageChops.difference(a, b)
|
|
58
|
+
bbox = diff.getbbox()
|
|
59
|
+
if bbox is None:
|
|
60
|
+
return 0.0
|
|
61
|
+
changed = sum(1 for px in diff.crop(bbox).getdata() if any(px))
|
|
62
|
+
return changed / float(a.size[0] * a.size[1])
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _visual_regression(page: Page, surface_slug: str, base_url: str | None) -> None:
|
|
66
|
+
if not _MODE:
|
|
67
|
+
pytest.skip("visual regression is opt-in; set GROUNDWORK_VISUAL_REGRESSION=1")
|
|
68
|
+
try:
|
|
69
|
+
import PIL # noqa: F401
|
|
70
|
+
except ImportError:
|
|
71
|
+
pytest.skip("visual regression needs Pillow (pip install Pillow)")
|
|
72
|
+
|
|
73
|
+
page.set_viewport_size({"width": DESKTOP[0], "height": DESKTOP[1]})
|
|
74
|
+
out_dir = _BASELINE_DIR / surface_slug
|
|
75
|
+
for route in ROUTES:
|
|
76
|
+
target = (base_url.rstrip("/") + route) if base_url else route
|
|
77
|
+
page.goto(target, wait_until="load")
|
|
78
|
+
page.wait_for_load_state("networkidle")
|
|
79
|
+
shot = page.screenshot(animations="disabled", full_page=True)
|
|
80
|
+
|
|
81
|
+
slug = route.strip("/").replace("/", "_") or "root"
|
|
82
|
+
baseline = out_dir / f"{slug}.png"
|
|
83
|
+
if _MODE == "update" or not baseline.exists():
|
|
84
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
baseline.write_bytes(shot)
|
|
86
|
+
if _MODE != "update":
|
|
87
|
+
pytest.skip(f"no baseline for {surface_slug} {route}; wrote one — review and commit it")
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
frac = _diff_fraction(baseline.read_bytes(), shot)
|
|
91
|
+
assert frac <= _DIFF_THRESHOLD, (
|
|
92
|
+
f"{surface_slug} {route} changed visually: {frac:.1%} of pixels differ "
|
|
93
|
+
f"from the baseline (threshold {_DIFF_THRESHOLD:.0%}). Review the change; "
|
|
94
|
+
f"re-run with GROUNDWORK_VISUAL_REGRESSION=update to accept it."
|
|
95
|
+
)
|
|
96
|
+
<% if (!surfaces) { -%>
|
|
97
|
+
|
|
98
|
+
def test_frontend_visual_regression(cluster, frontend_base_url, page: Page):
|
|
99
|
+
_visual_regression(page, "frontend", frontend_base_url)
|
|
100
|
+
<% } else { -%><% graphicalSurfaces.forEach((s) => { %>
|
|
101
|
+
|
|
102
|
+
def test_<%= s.ident %>_visual_regression(cluster, <%= s.ident %>_page: Page):
|
|
103
|
+
_visual_regression(<%= s.ident %>_page, "<%= s.slug %>", None)
|
|
104
|
+
<% }) %><% } -%>
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatFiles,
|
|
3
|
+
generateFiles,
|
|
4
|
+
Tree,
|
|
5
|
+
} from '@nx/devkit';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { recordGeneratorProvenance } from '../shared/provenance';
|
|
8
|
+
|
|
9
|
+
/** One surface from the registry (`.groundwork/surfaces.json`). The slug is the
|
|
10
|
+
* join key everywhere — ledger cells, fixture map keys, generated fixture
|
|
11
|
+
* names. `medium` is the registry's `testMedium`; `reach` is an optional
|
|
12
|
+
* static base URL (playwright / protocol-client), launch command
|
|
13
|
+
* (subprocess-cli), or test-harness command (flutter-integration /
|
|
14
|
+
* playwright-electron) — omitted, URL mediums are discovered at test time
|
|
15
|
+
* from the docker-compose service named after the slug, and harness mediums
|
|
16
|
+
* resolve to `npx nx run <slug>:<target>` when services/<slug> exists. */
|
|
17
|
+
export interface SurfaceSpec {
|
|
18
|
+
slug: string;
|
|
19
|
+
medium: string;
|
|
20
|
+
reach?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SystemTestRunnerGeneratorSchema {
|
|
24
|
+
projectPrefix?: string;
|
|
25
|
+
/** Deprecated single-surface alias — `surfaces` supersedes it. */
|
|
26
|
+
interfaceMedium?: 'graphical-ui' | 'cli' | 'agentic-protocol';
|
|
27
|
+
/** JSON array of SurfaceSpec (string form when invoked from the CLI). */
|
|
28
|
+
surfaces?: string | SurfaceSpec[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Parsed spec plus the python-identifier form of the slug, for fixture names. */
|
|
32
|
+
interface SurfaceTemplateSpec extends SurfaceSpec {
|
|
33
|
+
ident: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseSurfaces(
|
|
37
|
+
raw: string | SurfaceSpec[] | undefined
|
|
38
|
+
): SurfaceTemplateSpec[] | null {
|
|
39
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
let specs: unknown;
|
|
43
|
+
if (typeof raw === 'string') {
|
|
44
|
+
try {
|
|
45
|
+
specs = JSON.parse(raw);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
throw new Error(`--surfaces is not valid JSON: ${(e as Error).message}`);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
specs = raw;
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(specs) || specs.length === 0) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
'--surfaces must be a non-empty JSON array of {slug, medium, reach?} objects'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
for (const spec of specs as SurfaceSpec[]) {
|
|
58
|
+
if (
|
|
59
|
+
!spec ||
|
|
60
|
+
typeof spec.slug !== 'string' ||
|
|
61
|
+
spec.slug.length === 0 ||
|
|
62
|
+
typeof spec.medium !== 'string' ||
|
|
63
|
+
spec.medium.length === 0
|
|
64
|
+
) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`every --surfaces entry needs a slug and a medium: ${JSON.stringify(spec)}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return (specs as SurfaceSpec[]).map((s) => ({
|
|
71
|
+
...s,
|
|
72
|
+
ident: s.slug.replace(/-/g, '_'),
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A fail-closed pytest stub for a graphical surface with no runnable UI check.
|
|
77
|
+
* It fails (never skips) so the surface's UI proof is an honest red until a
|
|
78
|
+
* platform check is implemented per NATIVE-CHECK-CONTRACT.md. Deleting it to go
|
|
79
|
+
* green is the silent-skip this exists to prevent. */
|
|
80
|
+
function uiCheckPlaceholder(s: SurfaceTemplateSpec): string {
|
|
81
|
+
return `import pytest
|
|
82
|
+
|
|
83
|
+
# AUTO-GENERATED fail-closed placeholder — do not delete to go green.
|
|
84
|
+
# Surface "${s.slug}" (test medium "${s.medium}") is a surface GroundWork has no
|
|
85
|
+
# UI check runner for. A milestone cannot be proven on a surface nothing checks,
|
|
86
|
+
# so this placeholder FAILS until a platform UI check is implemented for it per
|
|
87
|
+
# src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md (render,
|
|
88
|
+
# navigation / no dead ends, the named states, design-system token match).
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_${s.ident}_ui_check_not_implemented():
|
|
92
|
+
pytest.fail(
|
|
93
|
+
"No UI check runner for surface '${s.slug}' (medium '${s.medium}'). "
|
|
94
|
+
"Implement a platform UI check per "
|
|
95
|
+
"system-test-runner/NATIVE-CHECK-CONTRACT.md, then replace this "
|
|
96
|
+
"fail-closed placeholder. A graphical surface must not ship unverified."
|
|
97
|
+
)
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function systemTestRunnerGenerator(
|
|
102
|
+
tree: Tree,
|
|
103
|
+
options: SystemTestRunnerGeneratorSchema
|
|
104
|
+
) {
|
|
105
|
+
const projectPrefix = options.projectPrefix || 'groundwork';
|
|
106
|
+
const interfaceMedium = options.interfaceMedium || 'graphical-ui';
|
|
107
|
+
// Registry mode: `surfaces` carries the full set of test mediums and wins
|
|
108
|
+
// over the deprecated single-medium alias when both are given.
|
|
109
|
+
const surfaces = parseSurfaces(options.surfaces);
|
|
110
|
+
|
|
111
|
+
const graphicalSurfaces = (surfaces ?? []).filter((s) => s.medium === 'playwright');
|
|
112
|
+
const cliSurfaces = (surfaces ?? []).filter((s) => s.medium === 'subprocess-cli');
|
|
113
|
+
const protocolSurfaces = (surfaces ?? []).filter((s) => s.medium === 'protocol-client');
|
|
114
|
+
// App-harness mediums: the surface ships its own test harness (Flutter
|
|
115
|
+
// integration_test / Playwright _electron smoke); the runner fixture drives
|
|
116
|
+
// it as a subprocess through the app's Nx target.
|
|
117
|
+
const flutterSurfaces = (surfaces ?? []).filter((s) => s.medium === 'flutter-integration');
|
|
118
|
+
const electronSurfaces = (surfaces ?? []).filter((s) => s.medium === 'playwright-electron');
|
|
119
|
+
|
|
120
|
+
// Every test medium GroundWork knows how to run a check for. A graphical
|
|
121
|
+
// surface registered with a medium outside this set has no UI check runner —
|
|
122
|
+
// and a milestone cannot be proven on a surface nothing checks. We refuse to
|
|
123
|
+
// silently leave it unverified: each such surface gets a fail-closed
|
|
124
|
+
// placeholder check (below) naming the gap, never a silent no-op.
|
|
125
|
+
const KNOWN_MEDIA = new Set([
|
|
126
|
+
'playwright',
|
|
127
|
+
'subprocess-cli',
|
|
128
|
+
'protocol-client',
|
|
129
|
+
'flutter-integration',
|
|
130
|
+
'playwright-electron',
|
|
131
|
+
]);
|
|
132
|
+
const unsupportedSurfaces = (surfaces ?? []).filter((s) => !KNOWN_MEDIA.has(s.medium));
|
|
133
|
+
|
|
134
|
+
// Playwright structure follows graphical surfaces: any playwright surface in
|
|
135
|
+
// registry mode, the graphical-ui value in single-medium mode. pexpect ships
|
|
136
|
+
// alongside the subprocess runners so interactive (REPL) CLI flows are testable.
|
|
137
|
+
const includePlaywright = surfaces
|
|
138
|
+
? graphicalSurfaces.length > 0
|
|
139
|
+
: interfaceMedium === 'graphical-ui';
|
|
140
|
+
const includePexpect = cliSurfaces.length > 0;
|
|
141
|
+
|
|
142
|
+
// Generate files into the workspace root
|
|
143
|
+
generateFiles(
|
|
144
|
+
tree,
|
|
145
|
+
path.join(__dirname, '..', '..', '..', '..', 'src', 'generators', 'system-test-runner', 'files'),
|
|
146
|
+
'.',
|
|
147
|
+
{
|
|
148
|
+
...options,
|
|
149
|
+
projectPrefix,
|
|
150
|
+
interfaceMedium,
|
|
151
|
+
surfaces,
|
|
152
|
+
graphicalSurfaces,
|
|
153
|
+
cliSurfaces,
|
|
154
|
+
protocolSurfaces,
|
|
155
|
+
flutterSurfaces,
|
|
156
|
+
electronSurfaces,
|
|
157
|
+
includePlaywright,
|
|
158
|
+
includePexpect,
|
|
159
|
+
tmpl: ''
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Playwright structure ships only with a graphical web surface: the
|
|
164
|
+
// page-object package, the axe-core a11y smoke, and the render-smoke gate
|
|
165
|
+
// depend on pytest-playwright, which the pyproject template declares only when
|
|
166
|
+
// includePlaywright is set. Removing the web-specific gates here is correct —
|
|
167
|
+
// they genuinely cannot run without a web surface. What must never happen is a
|
|
168
|
+
// graphical surface left with no check at all; the placeholder below closes
|
|
169
|
+
// that gap fail-closed instead of silently.
|
|
170
|
+
if (!includePlaywright) {
|
|
171
|
+
tree.delete('tests/system/pages');
|
|
172
|
+
tree.delete('tests/system/test_a11y_smoke.py');
|
|
173
|
+
tree.delete('tests/system/test_render_smoke.py');
|
|
174
|
+
tree.delete('tests/system/test_layout_geometry.py');
|
|
175
|
+
tree.delete('tests/system/test_visual_regression.py');
|
|
176
|
+
tree.delete('tests/system/test_token_conformance.py');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Fail-closed: a graphical surface whose test medium GroundWork cannot run a
|
|
180
|
+
// check for gets a placeholder that FAILS naming the gap, never a silent skip.
|
|
181
|
+
// The scaffold still generates and its other tests run; this surface's UI
|
|
182
|
+
// proof is an honest red until a platform check is implemented per
|
|
183
|
+
// NATIVE-CHECK-CONTRACT.md — the follow-on that turns it green.
|
|
184
|
+
for (const s of unsupportedSurfaces) {
|
|
185
|
+
tree.write(
|
|
186
|
+
`tests/system/test_${s.ident}_ui_check_missing.py`,
|
|
187
|
+
uiCheckPlaceholder(s)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await formatFiles(tree);
|
|
192
|
+
|
|
193
|
+
recordGeneratorProvenance(tree, 'system-test-runner', options as unknown as Record<string, unknown>);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default systemTestRunnerGenerator;
|