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,132 @@
|
|
|
1
|
+
<% if (!surfaces) { -%>
|
|
2
|
+
"""Accessibility + console-hygiene smoke for the graphical UI.
|
|
3
|
+
|
|
4
|
+
Loads the frontend root (discovered via conftest.frontend_base_url) and asserts:
|
|
5
|
+
|
|
6
|
+
1. No severe console errors during initial load — uncaught page errors and
|
|
7
|
+
console.error output both fail the smoke.
|
|
8
|
+
2. An axe-core scan (injected from CDN — no extra pip dependency) reports
|
|
9
|
+
ZERO critical violations. Lower-impact findings are reported as warnings
|
|
10
|
+
so they surface without blocking the loop.
|
|
11
|
+
|
|
12
|
+
Skips cleanly when no frontend service is discovered (frontend_base_url skips)
|
|
13
|
+
and when the axe-core CDN is unreachable (offline runs must not false-fail).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import warnings
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
from playwright.sync_api import Page
|
|
20
|
+
|
|
21
|
+
AXE_CDN_URL = "https://cdn.jsdelivr.net/npm/axe-core@4.10.2/axe.min.js"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_frontend_root_a11y_smoke(cluster, frontend_base_url, page: Page):
|
|
25
|
+
console_errors: list[str] = []
|
|
26
|
+
page.on(
|
|
27
|
+
"console",
|
|
28
|
+
lambda msg: console_errors.append(msg.text) if msg.type == "error" else None,
|
|
29
|
+
)
|
|
30
|
+
page.on("pageerror", lambda exc: console_errors.append(f"pageerror: {exc}"))
|
|
31
|
+
|
|
32
|
+
page.goto(frontend_base_url, wait_until="load")
|
|
33
|
+
# Let hydration and deferred requests settle so late errors are captured.
|
|
34
|
+
page.wait_for_load_state("networkidle")
|
|
35
|
+
|
|
36
|
+
assert not console_errors, (
|
|
37
|
+
f"frontend root emitted severe console errors on load:\n "
|
|
38
|
+
+ "\n ".join(console_errors)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
page.add_script_tag(url=AXE_CDN_URL)
|
|
43
|
+
except Exception as exc: # noqa: BLE001 — any injection failure means no scan
|
|
44
|
+
pytest.skip(f"axe-core CDN unreachable, a11y scan skipped: {exc}")
|
|
45
|
+
|
|
46
|
+
results = page.evaluate(
|
|
47
|
+
"() => axe.run(document, { resultTypes: ['violations'] })"
|
|
48
|
+
)
|
|
49
|
+
violations = results.get("violations", [])
|
|
50
|
+
|
|
51
|
+
non_critical = [v for v in violations if v.get("impact") != "critical"]
|
|
52
|
+
if non_critical:
|
|
53
|
+
summary = ", ".join(f"{v['id']} ({v.get('impact')})" for v in non_critical)
|
|
54
|
+
warnings.warn(f"non-critical a11y violations on frontend root: {summary}")
|
|
55
|
+
|
|
56
|
+
critical = [v for v in violations if v.get("impact") == "critical"]
|
|
57
|
+
assert not critical, (
|
|
58
|
+
"axe-core found CRITICAL accessibility violations on the frontend root:\n "
|
|
59
|
+
+ "\n ".join(
|
|
60
|
+
f"{v['id']}: {v['help']} ({len(v.get('nodes', []))} node(s))"
|
|
61
|
+
for v in critical
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
<% } else { -%>
|
|
65
|
+
"""Accessibility + console-hygiene smoke, one test per graphical surface.
|
|
66
|
+
|
|
67
|
+
Each test loads the surface's root via its generated per-surface page fixture
|
|
68
|
+
(see conftest's surface fixtures) and asserts:
|
|
69
|
+
|
|
70
|
+
1. No severe console errors during initial load — uncaught page errors and
|
|
71
|
+
console.error output both fail the smoke.
|
|
72
|
+
2. An axe-core scan (injected from CDN — no extra pip dependency) reports
|
|
73
|
+
ZERO critical violations. Lower-impact findings are reported as warnings
|
|
74
|
+
so they surface without blocking the loop.
|
|
75
|
+
|
|
76
|
+
Skips cleanly when a surface is not reachable (its page fixture skips) and
|
|
77
|
+
when the axe-core CDN is unreachable (offline runs must not false-fail).
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
import warnings
|
|
81
|
+
|
|
82
|
+
import pytest
|
|
83
|
+
from playwright.sync_api import Page
|
|
84
|
+
|
|
85
|
+
AXE_CDN_URL = "https://cdn.jsdelivr.net/npm/axe-core@4.10.2/axe.min.js"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _root_a11y_smoke(page: Page, surface_slug: str) -> None:
|
|
89
|
+
console_errors: list[str] = []
|
|
90
|
+
page.on(
|
|
91
|
+
"console",
|
|
92
|
+
lambda msg: console_errors.append(msg.text) if msg.type == "error" else None,
|
|
93
|
+
)
|
|
94
|
+
page.on("pageerror", lambda exc: console_errors.append(f"pageerror: {exc}"))
|
|
95
|
+
|
|
96
|
+
page.goto("/", wait_until="load")
|
|
97
|
+
# Let hydration and deferred requests settle so late errors are captured.
|
|
98
|
+
page.wait_for_load_state("networkidle")
|
|
99
|
+
|
|
100
|
+
assert not console_errors, (
|
|
101
|
+
f"{surface_slug} root emitted severe console errors on load:\n "
|
|
102
|
+
+ "\n ".join(console_errors)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
page.add_script_tag(url=AXE_CDN_URL)
|
|
107
|
+
except Exception as exc: # noqa: BLE001 — any injection failure means no scan
|
|
108
|
+
pytest.skip(f"axe-core CDN unreachable, a11y scan skipped: {exc}")
|
|
109
|
+
|
|
110
|
+
results = page.evaluate(
|
|
111
|
+
"() => axe.run(document, { resultTypes: ['violations'] })"
|
|
112
|
+
)
|
|
113
|
+
violations = results.get("violations", [])
|
|
114
|
+
|
|
115
|
+
non_critical = [v for v in violations if v.get("impact") != "critical"]
|
|
116
|
+
if non_critical:
|
|
117
|
+
summary = ", ".join(f"{v['id']} ({v.get('impact')})" for v in non_critical)
|
|
118
|
+
warnings.warn(f"non-critical a11y violations on {surface_slug} root: {summary}")
|
|
119
|
+
|
|
120
|
+
critical = [v for v in violations if v.get("impact") == "critical"]
|
|
121
|
+
assert not critical, (
|
|
122
|
+
f"axe-core found CRITICAL accessibility violations on the {surface_slug} root:\n "
|
|
123
|
+
+ "\n ".join(
|
|
124
|
+
f"{v['id']}: {v['help']} ({len(v.get('nodes', []))} node(s))"
|
|
125
|
+
for v in critical
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
<% graphicalSurfaces.forEach((s) => { %>
|
|
129
|
+
|
|
130
|
+
def test_<%= s.ident %>_root_a11y_smoke(cluster, <%= s.ident %>_page: Page):
|
|
131
|
+
_root_a11y_smoke(<%= s.ident %>_page, "<%= s.slug %>")
|
|
132
|
+
<% }) %><% } -%>
|
package/src/generators/system-test-runner/files/tests/system/test_contract_conformance.py.template
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Contract conformance: the served API vs the promoted design spec.
|
|
2
|
+
|
|
3
|
+
For each discovered APP service (see conftest.services_manifest) with a
|
|
4
|
+
canonical promoted spec at docs/architecture/api/<service>/openapi.yaml, fetch the spec the
|
|
5
|
+
RUNNING service serves (Huma on Go and FastAPI both expose /openapi.json) and
|
|
6
|
+
structurally compare paths + methods + response codes.
|
|
7
|
+
|
|
8
|
+
Drift taxonomy:
|
|
9
|
+
BREAKING — surface present in the design spec but missing from the served
|
|
10
|
+
spec (a path, method, or response code the design promises and
|
|
11
|
+
the implementation does not deliver). Fails the test.
|
|
12
|
+
ADDITIVE — surface the service serves that the design spec does not declare.
|
|
13
|
+
Reported as a warning: no consumer breaks, but the canonical spec
|
|
14
|
+
is stale and should be re-promoted (see the bet validation phase).
|
|
15
|
+
|
|
16
|
+
Skips cleanly when no design spec is committed for a service, or the service
|
|
17
|
+
does not expose a machine-readable spec.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
import warnings
|
|
22
|
+
|
|
23
|
+
import httpx
|
|
24
|
+
import pytest
|
|
25
|
+
import yaml
|
|
26
|
+
|
|
27
|
+
from conftest import WORKSPACE_ROOT, _base_url, _discover_services
|
|
28
|
+
|
|
29
|
+
DESIGN_SPEC_ROOT = WORKSPACE_ROOT / "docs" / "api"
|
|
30
|
+
# Huma serves both; FastAPI serves /openapi.json. Try JSON first.
|
|
31
|
+
SERVED_SPEC_PATHS = ("/openapi.json", "/openapi.yaml")
|
|
32
|
+
HTTP_METHODS = {"get", "put", "post", "delete", "options", "head", "patch", "trace"}
|
|
33
|
+
|
|
34
|
+
# Build params at import time so pytest shows one case per service.
|
|
35
|
+
_SERVICES = _discover_services()
|
|
36
|
+
_PARAMS = [pytest.param(s, id=s["name"]) for s in _SERVICES]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _normalize_path(path: str) -> str:
|
|
40
|
+
"""Collapse path-parameter names so /users/{id} == /users/{userId}.
|
|
41
|
+
|
|
42
|
+
Parameter naming is a spec-authoring choice, not an API surface change —
|
|
43
|
+
comparing it would report false breaking drift.
|
|
44
|
+
"""
|
|
45
|
+
return re.sub(r"\{[^}]*\}", "{}", path)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _surface(spec: dict) -> dict[tuple[str, str], set[str]]:
|
|
49
|
+
"""Flatten an OpenAPI document to {(path, method): {response codes}}."""
|
|
50
|
+
surface: dict[tuple[str, str], set[str]] = {}
|
|
51
|
+
for raw_path, item in (spec.get("paths") or {}).items():
|
|
52
|
+
if not isinstance(item, dict):
|
|
53
|
+
continue
|
|
54
|
+
for method, op in item.items():
|
|
55
|
+
if method.lower() not in HTTP_METHODS or not isinstance(op, dict):
|
|
56
|
+
continue
|
|
57
|
+
codes = {str(c) for c in (op.get("responses") or {})}
|
|
58
|
+
surface[(_normalize_path(raw_path), method.lower())] = codes
|
|
59
|
+
return surface
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _load_design_spec(service_name: str) -> dict | None:
|
|
63
|
+
spec_path = DESIGN_SPEC_ROOT / service_name / "openapi.yaml"
|
|
64
|
+
if not spec_path.exists():
|
|
65
|
+
return None
|
|
66
|
+
return yaml.safe_load(spec_path.read_text()) or {}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _fetch_served_spec(svc: dict) -> dict | None:
|
|
70
|
+
for path in SERVED_SPEC_PATHS:
|
|
71
|
+
try:
|
|
72
|
+
resp = httpx.get(f"{_base_url(svc)}{path}", timeout=5.0)
|
|
73
|
+
except httpx.HTTPError:
|
|
74
|
+
continue
|
|
75
|
+
if resp.status_code != 200:
|
|
76
|
+
continue
|
|
77
|
+
if path.endswith(".json"):
|
|
78
|
+
return resp.json()
|
|
79
|
+
return yaml.safe_load(resp.text)
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _diff(design: dict, served: dict) -> tuple[list[str], list[str]]:
|
|
84
|
+
"""Compare flattened surfaces. Returns (breaking, additive) drift lines."""
|
|
85
|
+
breaking: list[str] = []
|
|
86
|
+
additive: list[str] = []
|
|
87
|
+
for (path, method), designed_codes in design.items():
|
|
88
|
+
label = f"{method.upper()} {path}"
|
|
89
|
+
served_codes = served.get((path, method))
|
|
90
|
+
if served_codes is None:
|
|
91
|
+
breaking.append(f"{label} — designed but not served")
|
|
92
|
+
continue
|
|
93
|
+
missing = designed_codes - served_codes
|
|
94
|
+
if missing:
|
|
95
|
+
breaking.append(
|
|
96
|
+
f"{label} — designed response codes not served: {sorted(missing)}"
|
|
97
|
+
)
|
|
98
|
+
extra = served_codes - designed_codes
|
|
99
|
+
if extra:
|
|
100
|
+
additive.append(f"{label} — served response codes not in design: {sorted(extra)}")
|
|
101
|
+
for path, method in sorted(served.keys() - design.keys()):
|
|
102
|
+
additive.append(f"{method.upper()} {path} — served but not in design spec")
|
|
103
|
+
return breaking, additive
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@pytest.mark.parametrize("svc", _PARAMS)
|
|
107
|
+
def test_served_api_conforms_to_design_spec(cluster, svc):
|
|
108
|
+
"""The running service implements at least the surface its promoted spec declares."""
|
|
109
|
+
design_doc = _load_design_spec(svc["name"])
|
|
110
|
+
if design_doc is None:
|
|
111
|
+
pytest.skip(
|
|
112
|
+
f"no promoted design spec at docs/architecture/api/{svc['name']}/openapi.yaml — "
|
|
113
|
+
"promote one in bet validation to bring this service under contract test"
|
|
114
|
+
)
|
|
115
|
+
if svc["host_port"] is None:
|
|
116
|
+
pytest.skip(f"{svc['name']} has no published host port")
|
|
117
|
+
|
|
118
|
+
served_doc = _fetch_served_spec(svc)
|
|
119
|
+
if served_doc is None:
|
|
120
|
+
pytest.skip(f"{svc['name']} does not expose a served spec at {SERVED_SPEC_PATHS}")
|
|
121
|
+
|
|
122
|
+
design = _surface(design_doc)
|
|
123
|
+
if not design:
|
|
124
|
+
pytest.skip(f"design spec for {svc['name']} declares no paths — nothing to conform to")
|
|
125
|
+
served = _surface(served_doc)
|
|
126
|
+
|
|
127
|
+
breaking, additive = _diff(design, served)
|
|
128
|
+
|
|
129
|
+
if additive:
|
|
130
|
+
warnings.warn(
|
|
131
|
+
f"ADDITIVE drift for {svc['name']} — the served API exceeds "
|
|
132
|
+
f"docs/architecture/api/{svc['name']}/openapi.yaml; re-promote the spec:\n "
|
|
133
|
+
+ "\n ".join(additive)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
assert not breaking, (
|
|
137
|
+
f"BREAKING drift for {svc['name']} — the design spec at "
|
|
138
|
+
f"docs/architecture/api/{svc['name']}/openapi.yaml promises surface the running "
|
|
139
|
+
"service does not deliver:\n " + "\n ".join(breaking)
|
|
140
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<% if (!surfaces) { -%>
|
|
2
|
+
"""Layout geometry gate — Tier 1 of the visual verification loop.
|
|
3
|
+
|
|
4
|
+
A deliberately small handful of deterministic layout invariants that responsive
|
|
5
|
+
breakage violates — kept narrow to avoid heuristic flakiness. Today it asserts the
|
|
6
|
+
highest-signal one: no horizontal overflow at the mobile breakpoint, the defect that
|
|
7
|
+
ships a sideways-scrolling phone layout unseen. Grow the invariant set per project
|
|
8
|
+
(landmark-in-viewport, no-clipped-primary-content) only where it stays deterministic.
|
|
9
|
+
|
|
10
|
+
Skips cleanly when no frontend service is discovered (frontend_base_url skips).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import pathlib
|
|
15
|
+
|
|
16
|
+
from playwright.sync_api import Page
|
|
17
|
+
|
|
18
|
+
MOBILE = (375, 812)
|
|
19
|
+
# Sub-pixel rounding can add a fraction; a small tolerance avoids false failures.
|
|
20
|
+
_OVERFLOW_TOLERANCE_PX = 2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _load_routes() -> tuple[str, ...]:
|
|
24
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
25
|
+
if manifest.exists():
|
|
26
|
+
try:
|
|
27
|
+
routes = json.loads(manifest.read_text())
|
|
28
|
+
if isinstance(routes, list) and routes:
|
|
29
|
+
return tuple(routes)
|
|
30
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
31
|
+
pass
|
|
32
|
+
return ("/",)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
ROUTES = _load_routes()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _no_horizontal_overflow(page: Page, surface_slug: str, base_url: str | None) -> None:
|
|
39
|
+
page.set_viewport_size({"width": MOBILE[0], "height": MOBILE[1]})
|
|
40
|
+
for route in ROUTES:
|
|
41
|
+
target = (base_url.rstrip("/") + route) if base_url else route
|
|
42
|
+
page.goto(target, wait_until="load")
|
|
43
|
+
page.wait_for_load_state("networkidle")
|
|
44
|
+
widths = page.evaluate(
|
|
45
|
+
"() => ({ scroll: document.documentElement.scrollWidth,"
|
|
46
|
+
" client: document.documentElement.clientWidth })"
|
|
47
|
+
)
|
|
48
|
+
assert widths["scroll"] <= widths["client"] + _OVERFLOW_TOLERANCE_PX, (
|
|
49
|
+
f"{surface_slug} {route} overflows horizontally at {MOBILE[0]}px "
|
|
50
|
+
f"(scrollWidth {widths['scroll']} > clientWidth {widths['client']})"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_frontend_no_horizontal_overflow(cluster, frontend_base_url, page: Page):
|
|
55
|
+
_no_horizontal_overflow(page, "frontend", frontend_base_url)
|
|
56
|
+
<% } else { -%>
|
|
57
|
+
"""Layout geometry gate, one test per graphical surface — Tier 1 of the visual loop.
|
|
58
|
+
|
|
59
|
+
A deliberately small handful of deterministic layout invariants that responsive
|
|
60
|
+
breakage violates — kept narrow to avoid heuristic flakiness. Today it asserts the
|
|
61
|
+
highest-signal one: no horizontal overflow at the mobile breakpoint, the defect that
|
|
62
|
+
ships a sideways-scrolling phone layout unseen. Grow the invariant set per project
|
|
63
|
+
only where it stays deterministic.
|
|
64
|
+
|
|
65
|
+
Skips cleanly when a surface is not reachable (its page fixture skips).
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
import json
|
|
69
|
+
import pathlib
|
|
70
|
+
|
|
71
|
+
from playwright.sync_api import Page
|
|
72
|
+
|
|
73
|
+
MOBILE = (375, 812)
|
|
74
|
+
_OVERFLOW_TOLERANCE_PX = 2
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _load_routes() -> tuple[str, ...]:
|
|
78
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
79
|
+
if manifest.exists():
|
|
80
|
+
try:
|
|
81
|
+
routes = json.loads(manifest.read_text())
|
|
82
|
+
if isinstance(routes, list) and routes:
|
|
83
|
+
return tuple(routes)
|
|
84
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
85
|
+
pass
|
|
86
|
+
return ("/",)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
ROUTES = _load_routes()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _no_horizontal_overflow(page: Page, surface_slug: str) -> None:
|
|
93
|
+
page.set_viewport_size({"width": MOBILE[0], "height": MOBILE[1]})
|
|
94
|
+
for route in ROUTES:
|
|
95
|
+
page.goto(route, wait_until="load")
|
|
96
|
+
page.wait_for_load_state("networkidle")
|
|
97
|
+
widths = page.evaluate(
|
|
98
|
+
"() => ({ scroll: document.documentElement.scrollWidth,"
|
|
99
|
+
" client: document.documentElement.clientWidth })"
|
|
100
|
+
)
|
|
101
|
+
assert widths["scroll"] <= widths["client"] + _OVERFLOW_TOLERANCE_PX, (
|
|
102
|
+
f"{surface_slug} {route} overflows horizontally at {MOBILE[0]}px "
|
|
103
|
+
f"(scrollWidth {widths['scroll']} > clientWidth {widths['client']})"
|
|
104
|
+
)
|
|
105
|
+
<% graphicalSurfaces.forEach((s) => { %>
|
|
106
|
+
|
|
107
|
+
def test_<%= s.ident %>_no_horizontal_overflow(cluster, <%= s.ident %>_page: Page):
|
|
108
|
+
_no_horizontal_overflow(<%= s.ident %>_page, "<%= s.slug %>")
|
|
109
|
+
<% }) %><% } -%>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<% if (!surfaces) { -%>
|
|
2
|
+
"""Render-smoke gate for the graphical UI — Tier 1 of the visual verification loop.
|
|
3
|
+
|
|
4
|
+
A behavioural test asserting a selector exists passes while the page is a blank
|
|
5
|
+
screen, throwing a hydration error, unstyled because the CSS 404'd, or showing an
|
|
6
|
+
error-boundary fallback. This gate catches that class: it boots the app and
|
|
7
|
+
asserts the objective facts a broken page violates, across a viewport x theme
|
|
8
|
+
matrix, and writes a screenshot of each (route, viewport, theme) so the delivery
|
|
9
|
+
agent (Tier 2) and the fidelity critic (Tier 3) can read what actually rendered.
|
|
10
|
+
|
|
11
|
+
What this gate sees: render correctness, console/asset hygiene, blank-render, and
|
|
12
|
+
basic theme/viewport breakage. What it does NOT see: motion (easing, durations,
|
|
13
|
+
press physics) and perceived latency — those stay behaviour-tested, never asserted
|
|
14
|
+
from a static screenshot.
|
|
15
|
+
|
|
16
|
+
Skips cleanly when no frontend service is discovered (frontend_base_url skips).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import pathlib
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _load_routes() -> tuple[str, ...]:
|
|
24
|
+
"""Route inventory — the screens the route-driven gates sweep. A bet declares
|
|
25
|
+
the routes it touched in tests/system/routes.json (a JSON array of paths);
|
|
26
|
+
absent that file, the gate covers the app root."""
|
|
27
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
28
|
+
if manifest.exists():
|
|
29
|
+
try:
|
|
30
|
+
routes = json.loads(manifest.read_text())
|
|
31
|
+
if isinstance(routes, list) and routes:
|
|
32
|
+
return tuple(routes)
|
|
33
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
34
|
+
pass
|
|
35
|
+
return ("/",)
|
|
36
|
+
|
|
37
|
+
import pytest
|
|
38
|
+
from playwright.sync_api import Page
|
|
39
|
+
|
|
40
|
+
# The design system commits dual themes and responsive breakpoints; the matrix is
|
|
41
|
+
# the test projection of those commitments. Extend per project as the spec grows.
|
|
42
|
+
VIEWPORTS = {"mobile": (375, 812), "desktop": (1280, 800)}
|
|
43
|
+
THEMES = ("light", "dark")
|
|
44
|
+
# Route inventory: the bet's declared routes (tests/system/routes.json) or the root.
|
|
45
|
+
ROUTES = _load_routes()
|
|
46
|
+
# DOM node floor a non-blank page clears — catches the blank-screen and the
|
|
47
|
+
# dark-mode-invisible-text cases.
|
|
48
|
+
_MIN_DOM_NODES = 8
|
|
49
|
+
_VISUAL_DIR = pathlib.Path(".groundwork/cache/visual/_smoke")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _render_smoke(page: Page, surface_slug: str, base_url: str | None) -> None:
|
|
53
|
+
console_errors: list[str] = []
|
|
54
|
+
failed_requests: list[str] = []
|
|
55
|
+
page.on(
|
|
56
|
+
"console",
|
|
57
|
+
lambda msg: console_errors.append(msg.text) if msg.type == "error" else None,
|
|
58
|
+
)
|
|
59
|
+
page.on("pageerror", lambda exc: console_errors.append(f"pageerror: {exc}"))
|
|
60
|
+
page.on(
|
|
61
|
+
"response",
|
|
62
|
+
lambda r: failed_requests.append(f"{r.status} {r.url}")
|
|
63
|
+
if r.status >= 400 and (base_url is None or r.url.startswith(base_url))
|
|
64
|
+
else None,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
for vp_name, (w, h) in VIEWPORTS.items():
|
|
68
|
+
for theme in THEMES:
|
|
69
|
+
page.set_viewport_size({"width": w, "height": h})
|
|
70
|
+
page.emulate_media(color_scheme=theme)
|
|
71
|
+
for route in ROUTES:
|
|
72
|
+
console_errors.clear()
|
|
73
|
+
failed_requests.clear()
|
|
74
|
+
target = (base_url.rstrip("/") + route) if base_url else route
|
|
75
|
+
resp = page.goto(target, wait_until="load")
|
|
76
|
+
page.wait_for_load_state("networkidle")
|
|
77
|
+
|
|
78
|
+
ctx = f"{surface_slug} {route} [{vp_name}/{theme}]"
|
|
79
|
+
|
|
80
|
+
# 1. Navigation succeeded (never a 4xx/5xx document response).
|
|
81
|
+
assert resp is not None and resp.status < 400, (
|
|
82
|
+
f"{ctx}: navigation returned "
|
|
83
|
+
f"{resp.status if resp else 'no response'}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# 2. No severe console output and no uncaught exceptions.
|
|
87
|
+
assert not console_errors, (
|
|
88
|
+
f"{ctx}: severe console output on load:\n "
|
|
89
|
+
+ "\n ".join(console_errors)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# 3. No failed same-origin asset/API requests (the unstyled-CSS-404 case).
|
|
93
|
+
assert not failed_requests, (
|
|
94
|
+
f"{ctx}: failed same-origin requests:\n "
|
|
95
|
+
+ "\n ".join(failed_requests)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# 4. No framework error overlay or app error-boundary fallback.
|
|
99
|
+
overlay = page.locator(
|
|
100
|
+
"nextjs-portal, [data-nextjs-dialog], [data-testid='error-boundary']"
|
|
101
|
+
)
|
|
102
|
+
assert overlay.count() == 0, f"{ctx}: an error overlay/boundary is present"
|
|
103
|
+
|
|
104
|
+
# 5. The page rendered meaningful content (blank-screen case).
|
|
105
|
+
metrics = page.evaluate(
|
|
106
|
+
"() => ({ nodes: document.querySelectorAll('*').length,"
|
|
107
|
+
" text: (document.body.innerText || '').trim().length })"
|
|
108
|
+
)
|
|
109
|
+
assert metrics["nodes"] >= _MIN_DOM_NODES and metrics["text"] > 0, (
|
|
110
|
+
f"{ctx}: page rendered blank "
|
|
111
|
+
f"({metrics['nodes']} nodes, {metrics['text']} chars of text)"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# 6. Not a dead end. When the app has more than one route, every
|
|
115
|
+
# screen offers a same-origin way onward or back, so a user is
|
|
116
|
+
# never stranded (the "you can reach the library but never leave
|
|
117
|
+
# it" class). Single-route apps are exempt.
|
|
118
|
+
if len(ROUTES) > 1:
|
|
119
|
+
nav_links = page.evaluate(
|
|
120
|
+
"() => Array.from(document.querySelectorAll('a[href]'))"
|
|
121
|
+
".filter(a => { const h = a.getAttribute('href') || '';"
|
|
122
|
+
" return h.startsWith('/') || h.startsWith(location.origin);"
|
|
123
|
+
" }).length"
|
|
124
|
+
)
|
|
125
|
+
assert nav_links > 0, (
|
|
126
|
+
f"{ctx}: dead-end screen — no in-app navigation link to "
|
|
127
|
+
f"leave this route"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# On pass, persist the screenshot for Tiers 2-3 to read.
|
|
131
|
+
out = _VISUAL_DIR / surface_slug
|
|
132
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
slug = route.strip("/").replace("/", "_") or "root"
|
|
134
|
+
page.screenshot(path=str(out / f"{slug}__{vp_name}__{theme}.png"))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_frontend_root_render_smoke(cluster, frontend_base_url, page: Page):
|
|
138
|
+
_render_smoke(page, "frontend", frontend_base_url)
|
|
139
|
+
<% } else { -%>
|
|
140
|
+
"""Render-smoke gate, one test per graphical surface — Tier 1 of the visual loop.
|
|
141
|
+
|
|
142
|
+
A behavioural test asserting a selector exists passes while the page is a blank
|
|
143
|
+
screen, throwing a hydration error, unstyled because the CSS 404'd, or showing an
|
|
144
|
+
error-boundary fallback. This gate catches that class: it boots each surface and
|
|
145
|
+
asserts the objective facts a broken page violates, across a viewport x theme
|
|
146
|
+
matrix, and writes a screenshot of each (route, viewport, theme) so the delivery
|
|
147
|
+
agent (Tier 2) and the fidelity critic (Tier 3) can read what actually rendered.
|
|
148
|
+
|
|
149
|
+
What this gate sees: render correctness, console/asset hygiene, blank-render, and
|
|
150
|
+
basic theme/viewport breakage. What it does NOT see: motion (easing, durations,
|
|
151
|
+
press physics) and perceived latency — those stay behaviour-tested, never asserted
|
|
152
|
+
from a static screenshot.
|
|
153
|
+
|
|
154
|
+
Skips cleanly when a surface is not reachable (its page fixture skips).
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
import json
|
|
158
|
+
import pathlib
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _load_routes() -> tuple[str, ...]:
|
|
162
|
+
"""Route inventory — the screens the route-driven gates sweep. A bet declares
|
|
163
|
+
the routes it touched in tests/system/routes.json (a JSON array of paths);
|
|
164
|
+
absent that file, the gate covers the app root."""
|
|
165
|
+
manifest = pathlib.Path("tests/system/routes.json")
|
|
166
|
+
if manifest.exists():
|
|
167
|
+
try:
|
|
168
|
+
routes = json.loads(manifest.read_text())
|
|
169
|
+
if isinstance(routes, list) and routes:
|
|
170
|
+
return tuple(routes)
|
|
171
|
+
except Exception: # noqa: BLE001 — a malformed manifest falls back to root
|
|
172
|
+
pass
|
|
173
|
+
return ("/",)
|
|
174
|
+
|
|
175
|
+
from playwright.sync_api import Page
|
|
176
|
+
|
|
177
|
+
VIEWPORTS = {"mobile": (375, 812), "desktop": (1280, 800)}
|
|
178
|
+
THEMES = ("light", "dark")
|
|
179
|
+
ROUTES = _load_routes()
|
|
180
|
+
_MIN_DOM_NODES = 8
|
|
181
|
+
_VISUAL_DIR = pathlib.Path(".groundwork/cache/visual/_smoke")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _render_smoke(page: Page, surface_slug: str) -> None:
|
|
185
|
+
console_errors: list[str] = []
|
|
186
|
+
failed_requests: list[str] = []
|
|
187
|
+
page.on(
|
|
188
|
+
"console",
|
|
189
|
+
lambda msg: console_errors.append(msg.text) if msg.type == "error" else None,
|
|
190
|
+
)
|
|
191
|
+
page.on("pageerror", lambda exc: console_errors.append(f"pageerror: {exc}"))
|
|
192
|
+
page.on(
|
|
193
|
+
"response",
|
|
194
|
+
lambda r: failed_requests.append(f"{r.status} {r.url}")
|
|
195
|
+
if r.status >= 400
|
|
196
|
+
else None,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
for vp_name, (w, h) in VIEWPORTS.items():
|
|
200
|
+
for theme in THEMES:
|
|
201
|
+
page.set_viewport_size({"width": w, "height": h})
|
|
202
|
+
page.emulate_media(color_scheme=theme)
|
|
203
|
+
for route in ROUTES:
|
|
204
|
+
console_errors.clear()
|
|
205
|
+
failed_requests.clear()
|
|
206
|
+
resp = page.goto(route, wait_until="load")
|
|
207
|
+
page.wait_for_load_state("networkidle")
|
|
208
|
+
|
|
209
|
+
ctx = f"{surface_slug} {route} [{vp_name}/{theme}]"
|
|
210
|
+
|
|
211
|
+
assert resp is not None and resp.status < 400, (
|
|
212
|
+
f"{ctx}: navigation returned "
|
|
213
|
+
f"{resp.status if resp else 'no response'}"
|
|
214
|
+
)
|
|
215
|
+
assert not console_errors, (
|
|
216
|
+
f"{ctx}: severe console output on load:\n "
|
|
217
|
+
+ "\n ".join(console_errors)
|
|
218
|
+
)
|
|
219
|
+
assert not failed_requests, (
|
|
220
|
+
f"{ctx}: failed requests:\n " + "\n ".join(failed_requests)
|
|
221
|
+
)
|
|
222
|
+
overlay = page.locator(
|
|
223
|
+
"nextjs-portal, [data-nextjs-dialog], [data-testid='error-boundary']"
|
|
224
|
+
)
|
|
225
|
+
assert overlay.count() == 0, f"{ctx}: an error overlay/boundary is present"
|
|
226
|
+
metrics = page.evaluate(
|
|
227
|
+
"() => ({ nodes: document.querySelectorAll('*').length,"
|
|
228
|
+
" text: (document.body.innerText || '').trim().length })"
|
|
229
|
+
)
|
|
230
|
+
assert metrics["nodes"] >= _MIN_DOM_NODES and metrics["text"] > 0, (
|
|
231
|
+
f"{ctx}: page rendered blank "
|
|
232
|
+
f"({metrics['nodes']} nodes, {metrics['text']} chars of text)"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Not a dead end: a multi-route surface offers a same-origin way
|
|
236
|
+
# off every screen, so a user is never stranded.
|
|
237
|
+
if len(ROUTES) > 1:
|
|
238
|
+
nav_links = page.evaluate(
|
|
239
|
+
"() => Array.from(document.querySelectorAll('a[href]'))"
|
|
240
|
+
".filter(a => { const h = a.getAttribute('href') || '';"
|
|
241
|
+
" return h.startsWith('/') || h.startsWith(location.origin);"
|
|
242
|
+
" }).length"
|
|
243
|
+
)
|
|
244
|
+
assert nav_links > 0, (
|
|
245
|
+
f"{ctx}: dead-end screen — no in-app navigation link to "
|
|
246
|
+
f"leave this route"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
out = _VISUAL_DIR / surface_slug
|
|
250
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
251
|
+
slug = route.strip("/").replace("/", "_") or "root"
|
|
252
|
+
page.screenshot(path=str(out / f"{slug}__{vp_name}__{theme}.png"))
|
|
253
|
+
<% graphicalSurfaces.forEach((s) => { %>
|
|
254
|
+
|
|
255
|
+
def test_<%= s.ident %>_render_smoke(cluster, <%= s.ident %>_page: Page):
|
|
256
|
+
_render_smoke(<%= s.ident %>_page, "<%= s.slug %>")
|
|
257
|
+
<% }) %><% } -%>
|