@caddis/cli 0.1.0 → 0.2.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/README.md +29 -16
- package/bundles/antigravity-plugin-extras/plugin.json +5 -0
- package/bundles/antigravity-plugin-extras/skills/_registry.md +154 -0
- package/bundles/antigravity-plugin-extras/skills/agent-md-refactor/SKILL.md +356 -0
- package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +517 -0
- package/bundles/antigravity-plugin-extras/skills/algorithmic-art/SKILL.md +293 -0
- package/bundles/antigravity-plugin-extras/skills/anchor-review/SKILL.md +135 -0
- package/bundles/antigravity-plugin-extras/skills/api-client-patterns/SKILL.md +367 -0
- package/bundles/antigravity-plugin-extras/skills/architecture-design/SKILL.md +244 -0
- package/bundles/antigravity-plugin-extras/skills/architecture-diagram/SKILL.md +156 -0
- package/bundles/antigravity-plugin-extras/skills/architecture-document/SKILL.md +321 -0
- package/bundles/antigravity-plugin-extras/skills/artifacts-builder/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/artifacts-builder/SKILL.md +74 -0
- package/bundles/antigravity-plugin-extras/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/bundles/antigravity-plugin-extras/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
- package/bundles/antigravity-plugin-extras/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/bundles/antigravity-plugin-extras/skills/ascii-art/SKILL.md +144 -0
- package/bundles/antigravity-plugin-extras/skills/asking-questions/SKILL.md +81 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/SKILL.md +118 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/cross-service/credential-management.md +450 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/browser/README.md +363 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/code-interpreter/README.md +232 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/gateway/README.md +100 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/gateway/deploy-template.sh +117 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/gateway/deployment-strategies.md +416 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/gateway/troubleshooting-guide.md +384 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/gateway/validate-deployment.sh +186 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/identity/README.md +186 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/memory/README.md +253 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/observability/README.md +517 -0
- package/bundles/antigravity-plugin-extras/skills/aws-agentic-ai/services/runtime/README.md +215 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cdk-development/SKILL.md +279 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cdk-development/references/cdk-patterns.md +431 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cdk-development/scripts/validate-stack.sh +240 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cost-operations/SKILL.md +318 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cost-operations/references/cloudwatch-alarms.md +567 -0
- package/bundles/antigravity-plugin-extras/skills/aws-cost-operations/references/operations-patterns.md +394 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/SKILL.md +758 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/deployment-best-practices.md +830 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/eda-patterns.md +1002 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/observability-best-practices.md +770 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/performance-optimization.md +671 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/security-best-practices.md +625 -0
- package/bundles/antigravity-plugin-extras/skills/aws-serverless-eda/references/serverless-patterns.md +838 -0
- package/bundles/antigravity-plugin-extras/skills/backend-to-frontend-handoff/SKILL.md +139 -0
- package/bundles/antigravity-plugin-extras/skills/banner-design/SKILL.md +188 -0
- package/bundles/antigravity-plugin-extras/skills/brand-design/SKILL.md +298 -0
- package/bundles/antigravity-plugin-extras/skills/brand-guidelines/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/brand-guidelines/SKILL.md +118 -0
- package/bundles/antigravity-plugin-extras/skills/brand-voice/SKILL.md +93 -0
- package/bundles/antigravity-plugin-extras/skills/building-ai-agent-on-cloudflare/SKILL.md +392 -0
- package/bundles/antigravity-plugin-extras/skills/building-ai-agent-on-cloudflare/references/agent-patterns.md +461 -0
- package/bundles/antigravity-plugin-extras/skills/building-ai-agent-on-cloudflare/references/examples.md +188 -0
- package/bundles/antigravity-plugin-extras/skills/building-ai-agent-on-cloudflare/references/state-patterns.md +360 -0
- package/bundles/antigravity-plugin-extras/skills/building-ai-agent-on-cloudflare/references/troubleshooting.md +362 -0
- package/bundles/antigravity-plugin-extras/skills/building-mcp-server-on-cloudflare/SKILL.md +266 -0
- package/bundles/antigravity-plugin-extras/skills/building-mcp-server-on-cloudflare/references/examples.md +115 -0
- package/bundles/antigravity-plugin-extras/skills/building-mcp-server-on-cloudflare/references/oauth-setup.md +338 -0
- package/bundles/antigravity-plugin-extras/skills/building-mcp-server-on-cloudflare/references/troubleshooting.md +317 -0
- package/bundles/antigravity-plugin-extras/skills/caching-patterns/SKILL.md +385 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/SKILL.md +91 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/changelog-generator/SKILL.md +105 -0
- package/bundles/antigravity-plugin-extras/skills/code-explainer/SKILL.md +91 -0
- package/bundles/antigravity-plugin-extras/skills/competitive-ads-extractor/SKILL.md +294 -0
- package/bundles/antigravity-plugin-extras/skills/component-testing/SKILL.md +310 -0
- package/bundles/antigravity-plugin-extras/skills/content-research-writer/SKILL.md +539 -0
- package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +51 -0
- package/bundles/antigravity-plugin-extras/skills/data-analysis/SKILL.md +111 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/README.md +248 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/SKILL.md +203 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/adapter.py.template +44 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/api-router.py.template +43 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/contract-test.py.template +384 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/display-dto.py.template +39 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/frontend-service.ts.template +46 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/frontend-types.ts.template +38 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/ingestion-model.py.template +42 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/mapping-doc.md.template +46 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/normalizer.py.template +48 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/prompts/extract_requirements.md +36 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/prompts/extract_ui_demand.md +35 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/prompts/reconcile_mapping.md +66 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/assets/query-catalog.md.template +62 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/references/consumer-driven-contracts.md +131 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/references/drift-check-catalog.md +305 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/references/layer-responsibilities.md +333 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/discover_connections.py +170 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/discover_sources.py +161 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/drift_check.py +171 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/extract_schema.py +1363 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/generate_gap_report.py +81 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/generate_mapping_doc.py +294 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/ts_dto_compare.py +214 -0
- package/bundles/antigravity-plugin-extras/skills/data-contract-pipeline/scripts/validate_lineage.py +51 -0
- package/bundles/antigravity-plugin-extras/skills/data-loader/SKILL.md +753 -0
- package/bundles/antigravity-plugin-extras/skills/data-validation/SKILL.md +220 -0
- package/bundles/antigravity-plugin-extras/skills/db-testing/SKILL.md +117 -0
- package/bundles/antigravity-plugin-extras/skills/deploy-local/SKILL.md +259 -0
- package/bundles/antigravity-plugin-extras/skills/design-md/SKILL.md +167 -0
- package/bundles/antigravity-plugin-extras/skills/design-system-tokens/SKILL.md +240 -0
- package/bundles/antigravity-plugin-extras/skills/design-system-tokens/references/DESIGN-SYSTEM-TEMPLATE.md +577 -0
- package/bundles/antigravity-plugin-extras/skills/developer-growth-analysis/SKILL.md +323 -0
- package/bundles/antigravity-plugin-extras/skills/doc-coauthoring/SKILL.md +378 -0
- package/bundles/antigravity-plugin-extras/skills/documentation-analyzer/SKILL.md +78 -0
- package/bundles/antigravity-plugin-extras/skills/domain-name-brainstormer/SKILL.md +213 -0
- package/bundles/antigravity-plugin-extras/skills/enterprise-dashboard-aesthetic-system/SKILL.md +120 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/SKILL.md +193 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/references/element-types.md +497 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/references/excalidraw-schema.md +356 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/references/reliable-diagrams.md +115 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/scripts/README.md +60 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/scripts/add-arrow.py +354 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/scripts/add-icon-to-diagram.py +412 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/scripts/split-excalidraw-library.py +183 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/business-flow-swimlane-template.excalidraw +334 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/class-diagram-template.excalidraw +558 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/data-flow-diagram-template.excalidraw +279 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/er-diagram-template.excalidraw +662 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/flowchart-template.excalidraw +179 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/mindmap-template.excalidraw +244 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/relationship-template.excalidraw +145 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/reliable-flowchart.excalidraw +1 -0
- package/bundles/antigravity-plugin-extras/skills/excalidraw/templates/sequence-diagram-template.excalidraw +509 -0
- package/bundles/antigravity-plugin-extras/skills/file-organizer/SKILL.md +434 -0
- package/bundles/antigravity-plugin-extras/skills/game-changing-features/README.md +221 -0
- package/bundles/antigravity-plugin-extras/skills/game-changing-features/SKILL.md +265 -0
- package/bundles/antigravity-plugin-extras/skills/github-issues/SKILL.md +359 -0
- package/bundles/antigravity-plugin-extras/skills/github-issues/references/templates.md +90 -0
- package/bundles/antigravity-plugin-extras/skills/golden-nuggets/SKILL.md +192 -0
- package/bundles/antigravity-plugin-extras/skills/humanizer/SKILL.md +194 -0
- package/bundles/antigravity-plugin-extras/skills/image-enhancer/SKILL.md +100 -0
- package/bundles/antigravity-plugin-extras/skills/intent-writer/SKILL.md +201 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/SKILL.md +124 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/examples/3p-updates.md +47 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/examples/company-newsletter.md +65 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/examples/faq-answers.md +30 -0
- package/bundles/antigravity-plugin-extras/skills/internal-comms/examples/general-comms.md +16 -0
- package/bundles/antigravity-plugin-extras/skills/invoice-organizer/SKILL.md +447 -0
- package/bundles/antigravity-plugin-extras/skills/jira-issues/SKILL.md +182 -0
- package/bundles/antigravity-plugin-extras/skills/job-application/SKILL.md +188 -0
- package/bundles/antigravity-plugin-extras/skills/lead-research-assistant/SKILL.md +200 -0
- package/bundles/antigravity-plugin-extras/skills/llm-application-dev/SKILL.md +217 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/SKILL.md +100 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/references/evaluation.md +602 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/references/mcp_best_practices.md +915 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/references/node_mcp_server.md +916 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/references/python_mcp_server.md +752 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/scripts/connections.py +151 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/bundles/antigravity-plugin-extras/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/bundles/antigravity-plugin-extras/skills/meeting-insights-analyzer/SKILL.md +328 -0
- package/bundles/antigravity-plugin-extras/skills/monorepo/SKILL.md +267 -0
- package/bundles/antigravity-plugin-extras/skills/naming-analyzer/SKILL.md +376 -0
- package/bundles/antigravity-plugin-extras/skills/nextjs-app-router/SKILL.md +271 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/AUTHENTICATION.md +154 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/SKILL.md +270 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/references/api_reference.md +309 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/references/troubleshooting.md +376 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/references/usage_patterns.md +338 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/__init__.py +81 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/ask_question.py +256 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/auth_manager.py +358 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/browser_session.py +255 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/browser_utils.py +107 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/cleanup_manager.py +302 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/config.py +44 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/notebook_manager.py +410 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/run.py +102 -0
- package/bundles/antigravity-plugin-extras/skills/notebooklm/scripts/setup_environment.py +204 -0
- package/bundles/antigravity-plugin-extras/skills/observability/SKILL.md +371 -0
- package/bundles/antigravity-plugin-extras/skills/onboard-project/SKILL.md +192 -0
- package/bundles/antigravity-plugin-extras/skills/performance-testing/SKILL.md +226 -0
- package/bundles/antigravity-plugin-extras/skills/pipeline-state/SKILL.md +208 -0
- package/bundles/antigravity-plugin-extras/skills/plantuml/SKILL.md +228 -0
- package/bundles/antigravity-plugin-extras/skills/popular-web-designs/SKILL.md +142 -0
- package/bundles/antigravity-plugin-extras/skills/popular-web-designs/templates/linear.app.md +196 -0
- package/bundles/antigravity-plugin-extras/skills/popular-web-designs/templates/stripe.md +214 -0
- package/bundles/antigravity-plugin-extras/skills/popular-web-designs/templates/vercel.md +218 -0
- package/bundles/antigravity-plugin-extras/skills/prd-to-code/SKILL.md +127 -0
- package/bundles/antigravity-plugin-extras/skills/raffle-winner-picker/SKILL.md +159 -0
- package/bundles/antigravity-plugin-extras/skills/react-useeffect/README.md +320 -0
- package/bundles/antigravity-plugin-extras/skills/react-useeffect/SKILL.md +53 -0
- package/bundles/antigravity-plugin-extras/skills/react-useeffect/alternatives.md +258 -0
- package/bundles/antigravity-plugin-extras/skills/react-useeffect/anti-patterns.md +290 -0
- package/bundles/antigravity-plugin-extras/skills/receiving-code-review/SKILL.md +213 -0
- package/bundles/antigravity-plugin-extras/skills/relay/SKILL.md +246 -0
- package/bundles/antigravity-plugin-extras/skills/requesting-code-review/SKILL.md +105 -0
- package/bundles/antigravity-plugin-extras/skills/requesting-code-review/code-reviewer.md +146 -0
- package/bundles/antigravity-plugin-extras/skills/responsive-mobile-native/SKILL.md +350 -0
- package/bundles/antigravity-plugin-extras/skills/responsive-mobile-native/references/pwa-checklist.md +226 -0
- package/bundles/antigravity-plugin-extras/skills/responsive-mobile-native/references/touch-and-interaction.md +265 -0
- package/bundles/antigravity-plugin-extras/skills/responsive-mobile-native/references/transformation-catalogue.md +326 -0
- package/bundles/antigravity-plugin-extras/skills/schema-migration/SKILL.md +275 -0
- package/bundles/antigravity-plugin-extras/skills/setup-project-ai/SKILL.md +120 -0
- package/bundles/antigravity-plugin-extras/skills/shadcn-radix/SKILL.md +330 -0
- package/bundles/antigravity-plugin-extras/skills/sketch/SKILL.md +104 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/SKILL.md +646 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/color_palettes.py +302 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/easing.py +230 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/frame_composer.py +469 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/gif_builder.py +246 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/typography.py +357 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/validators.py +264 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/core/visual_effects.py +494 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/requirements.txt +4 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/bounce.py +106 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/explode.py +331 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/fade.py +329 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/flip.py +291 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/kaleidoscope.py +211 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/morph.py +329 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/move.py +293 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/pulse.py +268 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/shake.py +127 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/slide.py +291 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/spin.py +269 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/wiggle.py +300 -0
- package/bundles/antigravity-plugin-extras/skills/slack-gif-creator/templates/zoom.py +312 -0
- package/bundles/antigravity-plugin-extras/skills/slides/SKILL.md +38 -0
- package/bundles/antigravity-plugin-extras/skills/state-tracking/SKILL.md +131 -0
- package/bundles/antigravity-plugin-extras/skills/streamlit-dev/SKILL.md +268 -0
- package/bundles/antigravity-plugin-extras/skills/svg-create/SKILL.md +251 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/SKILL.md +59 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/theme-showcase.pdf +0 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/desert-rose.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/golden-hour.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/bundles/antigravity-plugin-extras/skills/ui-styling-patterns/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin-extras/skills/ui-styling-patterns/SKILL.md +320 -0
- package/bundles/antigravity-plugin-extras/skills/ui-testing/SKILL.md +660 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/SKILL.md +660 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/_sync_all.py +414 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/app-interface.csv +31 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/charts.csv +26 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/colors.csv +162 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/design.csv +1776 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/draft.csv +1779 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/google-fonts.csv +1924 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/icons.csv +106 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/landing.csv +35 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/products.csv +162 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/react-performance.csv +45 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/styles.csv +85 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/typography.csv +74 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/ui-reasoning.csv +162 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/data/ux-guidelines.csv +100 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/scripts/core.py +247 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/scripts/design_system.py +1067 -0
- package/bundles/antigravity-plugin-extras/skills/ui-ux-intelligence/scripts/search.py +114 -0
- package/bundles/antigravity-plugin-extras/skills/understand-anything/SKILL.md +140 -0
- package/bundles/antigravity-plugin-extras/skills/understand-anything/references/commands.md +252 -0
- package/bundles/antigravity-plugin-extras/skills/understand-anything/references/graph-schema.md +116 -0
- package/bundles/antigravity-plugin-extras/skills/understand-anything/references/pipeline.md +331 -0
- package/bundles/antigravity-plugin-extras/skills/update-readme/SKILL.md +74 -0
- package/bundles/antigravity-plugin-extras/skills/verification-loop/SKILL.md +209 -0
- package/bundles/antigravity-plugin-extras/skills/video-downloader/SKILL.md +107 -0
- package/bundles/antigravity-plugin-extras/skills/webapp-development/SKILL.md +288 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/SKILL.md +339 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/agents/agent-interface.md +175 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/html.md +384 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/nlp-keywords.md +127 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/palettes.md +149 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/python.md +249 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/react.md +427 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/shapes.md +135 -0
- package/bundles/antigravity-plugin-extras/skills/word-cloud/references/stopwords.md +115 -0
- package/bundles/antigravity-plugin-extras/skills/youtube-transcript/SKILL.md +416 -0
- package/bundles/manifest.json +2 -1
- package/dist/cli.js +166 -37
- package/package.json +1 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{SOURCE_NAME}} Contract Tests
|
|
3
|
+
|
|
4
|
+
Validates alignment between the golden sample payload, ingestion model,
|
|
5
|
+
display DTO, and (optionally) TypeScript types.
|
|
6
|
+
|
|
7
|
+
Supports two modes for schema drift detection:
|
|
8
|
+
- SOFT (default while schema evolves): warnings.warn — CI passes
|
|
9
|
+
- HARD (after schema freeze): assert — CI blocks on drift
|
|
10
|
+
|
|
11
|
+
Freeze mode resolution precedence:
|
|
12
|
+
1) CONTRACT_SCHEMA_FROZEN env var (explicit override)
|
|
13
|
+
2) Marker file tests/fixtures/payloads/{{source_slug}}/_contract_state.json
|
|
14
|
+
3) CI fallback (if CI=true and sample exists): HARD mode
|
|
15
|
+
4) Default: SOFT mode
|
|
16
|
+
|
|
17
|
+
Accepted explicit values: 1/true/yes/on/frozen and 0/false/no/off/evolving.
|
|
18
|
+
|
|
19
|
+
Run: pytest tests/unit/test_contract_{{source_slug}}.py -v
|
|
20
|
+
|
|
21
|
+
Part of the data contract pipeline — these tests ARE the contract.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import warnings
|
|
29
|
+
from fnmatch import fnmatch
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Union, get_args, get_origin, get_type_hints
|
|
32
|
+
|
|
33
|
+
import pytest
|
|
34
|
+
from pydantic import BaseModel
|
|
35
|
+
|
|
36
|
+
# --- Update these imports to match your project ---
|
|
37
|
+
# from src.models.ingestion.{{source_slug}} import {{ClassName}}Payload
|
|
38
|
+
# from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
39
|
+
# from src.services.normalizers.{{source_slug}} import normalize
|
|
40
|
+
|
|
41
|
+
PAYLOAD_DIR = Path(__file__).parent.parent / "fixtures" / "payloads"
|
|
42
|
+
SAMPLE_PATH = PAYLOAD_DIR / "{{source_slug}}" / "sample.json"
|
|
43
|
+
STATE_PATH = PAYLOAD_DIR / "{{source_slug}}" / "_contract_state.json"
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Schema evolution toggle
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# Set to True once the upstream schema is stable. While False, drift
|
|
49
|
+
# detection emits warnings; when True, drift causes hard test failures.
|
|
50
|
+
#
|
|
51
|
+
# Prefer explicit env override in CI:
|
|
52
|
+
# CONTRACT_SCHEMA_FROZEN=1 -> hard mode
|
|
53
|
+
# CONTRACT_SCHEMA_FROZEN=0 -> soft mode
|
|
54
|
+
#
|
|
55
|
+
# If env is missing, _resolve_schema_frozen() applies fallback logic.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _truthy_env(value: str) -> bool | None:
|
|
59
|
+
"""Parse common truthy/falsey strings; return None if unknown."""
|
|
60
|
+
normalized = value.strip().lower()
|
|
61
|
+
if normalized in {"1", "true", "yes", "on", "frozen", "hard"}:
|
|
62
|
+
return True
|
|
63
|
+
if normalized in {"0", "false", "no", "off", "evolving", "soft"}:
|
|
64
|
+
return False
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _load_state_file_flag() -> bool | None:
|
|
69
|
+
"""Read optional schema-frozen flag from _contract_state.json.
|
|
70
|
+
|
|
71
|
+
Expected shape:
|
|
72
|
+
{
|
|
73
|
+
"schema_frozen": true,
|
|
74
|
+
"source": "agent",
|
|
75
|
+
"note": "optional"
|
|
76
|
+
}
|
|
77
|
+
"""
|
|
78
|
+
if not STATE_PATH.exists():
|
|
79
|
+
return None
|
|
80
|
+
try:
|
|
81
|
+
state = json.loads(STATE_PATH.read_text(encoding="utf-8"))
|
|
82
|
+
value = state.get("schema_frozen")
|
|
83
|
+
if isinstance(value, bool):
|
|
84
|
+
return value
|
|
85
|
+
if isinstance(value, str):
|
|
86
|
+
return _truthy_env(value)
|
|
87
|
+
except Exception as exc: # pragma: no cover - defensive fallback
|
|
88
|
+
warnings.warn(
|
|
89
|
+
f"Could not parse contract state file {STATE_PATH}: {exc}. "
|
|
90
|
+
"Falling back to next freeze resolution rule.",
|
|
91
|
+
stacklevel=2,
|
|
92
|
+
)
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _resolve_schema_frozen() -> bool:
|
|
97
|
+
"""Resolve drift mode using explicit override + deterministic fallbacks."""
|
|
98
|
+
env_value = os.getenv("CONTRACT_SCHEMA_FROZEN")
|
|
99
|
+
if env_value is not None:
|
|
100
|
+
parsed = _truthy_env(env_value)
|
|
101
|
+
if parsed is not None:
|
|
102
|
+
return parsed
|
|
103
|
+
warnings.warn(
|
|
104
|
+
"CONTRACT_SCHEMA_FROZEN has unrecognized value "
|
|
105
|
+
f"'{env_value}'. Expected one of: 1/0, true/false, frozen/evolving. "
|
|
106
|
+
"Continuing with fallback resolution.",
|
|
107
|
+
stacklevel=2,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
state_flag = _load_state_file_flag()
|
|
111
|
+
if state_flag is not None:
|
|
112
|
+
return state_flag
|
|
113
|
+
|
|
114
|
+
ci_value = os.getenv("CI", "")
|
|
115
|
+
ci_mode = _truthy_env(ci_value) is True
|
|
116
|
+
if ci_mode and SAMPLE_PATH.exists():
|
|
117
|
+
warnings.warn(
|
|
118
|
+
"Auto-freeze fallback enabled (CI=true, sample.json present, no explicit "
|
|
119
|
+
"CONTRACT_SCHEMA_FROZEN/state flag). Running HARD drift mode. "
|
|
120
|
+
"Set CONTRACT_SCHEMA_FROZEN explicitly to override.",
|
|
121
|
+
stacklevel=2,
|
|
122
|
+
)
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
SCHEMA_FROZEN = _resolve_schema_frozen()
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
# Exclusion allow-list (Layer 3 of the three-layer defence)
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
# Keys that are intentionally NOT typed in the DTO. Use dot-path globs
|
|
134
|
+
# (fnmatch syntax). Always add a comment with a ticket ref or justification.
|
|
135
|
+
# Review periodically — remove entries once the key is typed or removed upstream.
|
|
136
|
+
KNOWN_EXCLUDED_KEYS: dict[str, set[str]] = {
|
|
137
|
+
# Example entries (delete and replace with real ones):
|
|
138
|
+
# "root.section.*": {"experimental_field"}, # TICKET-123: upstream experiment
|
|
139
|
+
# "surveys.*": {"internal_metadata"}, # TICKET-456: not consumer-facing
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ====================== helpers =============================================
|
|
144
|
+
|
|
145
|
+
def _unwrap_optional(tp):
|
|
146
|
+
"""Extract T from Optional[T] / T | None."""
|
|
147
|
+
origin = get_origin(tp)
|
|
148
|
+
if origin is Union:
|
|
149
|
+
args = [a for a in get_args(tp) if a is not type(None)]
|
|
150
|
+
return args[0] if len(args) == 1 else tp
|
|
151
|
+
return tp
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _resolve_child(tp):
|
|
155
|
+
"""If tp is list[X] return X; if BaseModel subclass, return it; else None."""
|
|
156
|
+
tp = _unwrap_optional(tp)
|
|
157
|
+
if isinstance(tp, type) and issubclass(tp, BaseModel):
|
|
158
|
+
return tp
|
|
159
|
+
origin = get_origin(tp)
|
|
160
|
+
if origin is list:
|
|
161
|
+
inner = get_args(tp)[0]
|
|
162
|
+
inner = _unwrap_optional(inner)
|
|
163
|
+
if isinstance(inner, type) and issubclass(inner, BaseModel):
|
|
164
|
+
return inner
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _collect_dropped_keys(
|
|
169
|
+
data,
|
|
170
|
+
model_cls: type[BaseModel],
|
|
171
|
+
prefix: str = "",
|
|
172
|
+
dropped: list[str] | None = None,
|
|
173
|
+
) -> list[str]:
|
|
174
|
+
"""Recursively walk JSON data vs model fields; return unmatched key paths.
|
|
175
|
+
|
|
176
|
+
Walks nested dicts and lists-of-dicts at every depth level, not just
|
|
177
|
+
top-level keys. Keys matching KNOWN_EXCLUDED_KEYS are silently skipped.
|
|
178
|
+
"""
|
|
179
|
+
if dropped is None:
|
|
180
|
+
dropped = []
|
|
181
|
+
|
|
182
|
+
if isinstance(data, list):
|
|
183
|
+
for item in data:
|
|
184
|
+
if isinstance(item, dict):
|
|
185
|
+
_collect_dropped_keys(item, model_cls, prefix, dropped)
|
|
186
|
+
return dropped
|
|
187
|
+
|
|
188
|
+
if not isinstance(data, dict):
|
|
189
|
+
return dropped
|
|
190
|
+
|
|
191
|
+
hints = get_type_hints(model_cls, include_extras=True)
|
|
192
|
+
alias_map: dict[str, tuple[str, type | None]] = {}
|
|
193
|
+
for field_name, field_info in model_cls.model_fields.items():
|
|
194
|
+
alias = field_info.alias or field_name
|
|
195
|
+
alias_map[alias] = (field_name, hints.get(field_name))
|
|
196
|
+
|
|
197
|
+
for key in data:
|
|
198
|
+
full_path = f"{prefix}.{key}" if prefix else key
|
|
199
|
+
|
|
200
|
+
# Check exclusion allow-list
|
|
201
|
+
if any(
|
|
202
|
+
fnmatch(full_path, pattern) and key in excl
|
|
203
|
+
for pattern, excl in KNOWN_EXCLUDED_KEYS.items()
|
|
204
|
+
):
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
if key not in alias_map:
|
|
208
|
+
dropped.append(full_path)
|
|
209
|
+
else:
|
|
210
|
+
_field_name, type_hint = alias_map[key]
|
|
211
|
+
if type_hint is not None:
|
|
212
|
+
child_model = _resolve_child(type_hint)
|
|
213
|
+
if child_model is not None:
|
|
214
|
+
_collect_dropped_keys(
|
|
215
|
+
data[key], child_model, full_path, dropped,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return dropped
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _report_drift(dropped: list[str], context: str) -> None:
|
|
222
|
+
"""Emit soft warning or hard assertion depending on SCHEMA_FROZEN."""
|
|
223
|
+
if not dropped:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
msg = (
|
|
227
|
+
f"Coverage gap in {context} — "
|
|
228
|
+
f"{len(dropped)} key(s) forwarded via model_extra:\n"
|
|
229
|
+
+ "\n".join(f" - {k}" for k in dropped)
|
|
230
|
+
+ "\nData is NOT lost (extra='allow'), but typed fields "
|
|
231
|
+
"give IDE support and validation."
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if SCHEMA_FROZEN:
|
|
235
|
+
raise AssertionError(msg)
|
|
236
|
+
else:
|
|
237
|
+
warnings.warn(msg, stacklevel=2)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ====================== fixtures ============================================
|
|
241
|
+
|
|
242
|
+
@pytest.fixture
|
|
243
|
+
def golden_sample() -> dict:
|
|
244
|
+
"""Load the golden sample payload."""
|
|
245
|
+
assert SAMPLE_PATH.exists(), (
|
|
246
|
+
f"Golden sample missing: {SAMPLE_PATH}\n"
|
|
247
|
+
"Create one by copying a real payload and sanitizing PII."
|
|
248
|
+
)
|
|
249
|
+
return json.loads(SAMPLE_PATH.read_text(encoding="utf-8"))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ============================================================
|
|
253
|
+
# D8 — Golden Sample Exists
|
|
254
|
+
# ============================================================
|
|
255
|
+
|
|
256
|
+
class TestGoldenSampleExists:
|
|
257
|
+
"""Verify the golden sample fixture is present and loadable."""
|
|
258
|
+
|
|
259
|
+
def test_sample_file_exists(self):
|
|
260
|
+
assert SAMPLE_PATH.exists(), f"Golden sample not found: {SAMPLE_PATH}"
|
|
261
|
+
|
|
262
|
+
def test_sample_is_valid_json(self):
|
|
263
|
+
data = json.loads(SAMPLE_PATH.read_text(encoding="utf-8"))
|
|
264
|
+
assert isinstance(data, (dict, list)), "Golden sample must be a JSON object or array"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ============================================================
|
|
268
|
+
# D1 — Payload Keys Covered by Ingestion Model (Recursive)
|
|
269
|
+
# ============================================================
|
|
270
|
+
|
|
271
|
+
class TestPayloadToIngestionModel:
|
|
272
|
+
"""Every key in the golden sample must map to an ingestion model field.
|
|
273
|
+
|
|
274
|
+
Uses _collect_dropped_keys for recursive depth comparison, not just
|
|
275
|
+
top-level set difference.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
279
|
+
def test_all_payload_keys_covered(self, golden_sample):
|
|
280
|
+
"""No payload key should be missing from the ingestion model."""
|
|
281
|
+
# from src.models.ingestion.{{source_slug}} import {{ClassName}}Payload
|
|
282
|
+
|
|
283
|
+
dropped = _collect_dropped_keys(golden_sample, {{ClassName}}Payload)
|
|
284
|
+
_report_drift(dropped, "payload → ingestion model")
|
|
285
|
+
|
|
286
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
287
|
+
def test_payload_validates_against_ingestion_model(self, golden_sample):
|
|
288
|
+
"""Golden sample must parse into the ingestion model without error."""
|
|
289
|
+
# from src.models.ingestion.{{source_slug}} import {{ClassName}}Payload
|
|
290
|
+
|
|
291
|
+
result = {{ClassName}}Payload.model_validate(golden_sample)
|
|
292
|
+
assert result is not None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# ============================================================
|
|
296
|
+
# D3 — Display DTO Extra Config
|
|
297
|
+
# ============================================================
|
|
298
|
+
|
|
299
|
+
class TestDtoConfig:
|
|
300
|
+
"""Display DTO must not use extra='ignore'."""
|
|
301
|
+
|
|
302
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
303
|
+
def test_dto_does_not_ignore_extras(self):
|
|
304
|
+
"""extra='ignore' silently drops fields — use 'forbid' or 'allow'."""
|
|
305
|
+
# from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
306
|
+
|
|
307
|
+
extra_config = {{ClassName}}Response.model_config.get("extra", "ignore")
|
|
308
|
+
assert extra_config != "ignore", (
|
|
309
|
+
f"Display DTO uses extra='ignore' — this silently drops fields. "
|
|
310
|
+
f"Use extra='forbid' (strict) or extra='allow' (migration)."
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# ============================================================
|
|
315
|
+
# D2 — Display DTO Covers Ingestion Fields (Recursive)
|
|
316
|
+
# ============================================================
|
|
317
|
+
|
|
318
|
+
class TestIngestionToDto:
|
|
319
|
+
"""Ingestion model fields should map to display DTO fields.
|
|
320
|
+
|
|
321
|
+
Uses _collect_dropped_keys for recursive depth comparison.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
325
|
+
def test_golden_sample_validates_against_dto(self, golden_sample):
|
|
326
|
+
"""Golden sample must parse into the display DTO without error."""
|
|
327
|
+
# from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
328
|
+
|
|
329
|
+
result = {{ClassName}}Response.model_validate(golden_sample)
|
|
330
|
+
assert result is not None
|
|
331
|
+
|
|
332
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
333
|
+
def test_no_uncovered_payload_keys_in_dto(self, golden_sample):
|
|
334
|
+
"""Every payload key must have a corresponding DTO field."""
|
|
335
|
+
# from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
336
|
+
|
|
337
|
+
dropped = _collect_dropped_keys(golden_sample, {{ClassName}}Response)
|
|
338
|
+
_report_drift(dropped, "payload → display DTO")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ============================================================
|
|
342
|
+
# D10 — Normalizer Produces Valid DTO
|
|
343
|
+
# ============================================================
|
|
344
|
+
|
|
345
|
+
class TestNormalizer:
|
|
346
|
+
"""Normalizer output must validate against the display DTO."""
|
|
347
|
+
|
|
348
|
+
@pytest.mark.skip(reason="Uncomment imports and remove skip to activate")
|
|
349
|
+
def test_normalizer_output_is_valid_dto(self, golden_sample):
|
|
350
|
+
"""Pass golden sample through normalizer → must produce valid DTO."""
|
|
351
|
+
# from src.models.ingestion.{{source_slug}} import {{ClassName}}Payload
|
|
352
|
+
# from src.services.normalizers.{{source_slug}} import normalize
|
|
353
|
+
|
|
354
|
+
ingestion = {{ClassName}}Payload.model_validate(golden_sample)
|
|
355
|
+
dto = normalize(ingestion)
|
|
356
|
+
assert dto is not None
|
|
357
|
+
# Verify it's actually the right type
|
|
358
|
+
# assert isinstance(dto, {{ClassName}}Response)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
# ============================================================
|
|
362
|
+
# D4 — DTO Aliases Match TypeScript (Manual Check)
|
|
363
|
+
# ============================================================
|
|
364
|
+
|
|
365
|
+
class TestTypeScriptAlignment:
|
|
366
|
+
"""
|
|
367
|
+
Cross-layer check: DTO aliases should match TypeScript interface properties.
|
|
368
|
+
|
|
369
|
+
This is a structural reminder test. For automated checking,
|
|
370
|
+
use the ts_dto_compare.py script.
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
@pytest.mark.skip(reason="Run scripts/ts_dto_compare.py for automated check")
|
|
374
|
+
def test_dto_aliases_documented(self):
|
|
375
|
+
"""Ensure DTO aliases are listed for manual comparison."""
|
|
376
|
+
# from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
377
|
+
|
|
378
|
+
aliases = {
|
|
379
|
+
info.alias or name
|
|
380
|
+
for name, info in {{ClassName}}Response.model_fields.items()
|
|
381
|
+
}
|
|
382
|
+
# Print for manual comparison with TypeScript interface
|
|
383
|
+
print(f"DTO aliases: {sorted(aliases)}")
|
|
384
|
+
assert len(aliases) > 0, "DTO has no fields"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{SOURCE_NAME}} Display DTO
|
|
3
|
+
|
|
4
|
+
Frozen Pydantic model defining the API boundary contract for {{source_name}} data.
|
|
5
|
+
This is what the frontend receives — changes here require frontend coordination.
|
|
6
|
+
|
|
7
|
+
Part of the data contract pipeline:
|
|
8
|
+
Source → Adapter → Ingestion Model → Normalizer → **Display DTO** → API Router
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class {{ClassName}}Response(BaseModel):
|
|
17
|
+
"""
|
|
18
|
+
API response DTO for {{source_name}}.
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
- frozen=True: immutable after construction
|
|
22
|
+
- populate_by_name=True: can construct with Python names or aliases
|
|
23
|
+
- extra="forbid": unknown fields cause ValidationError (no silent drops)
|
|
24
|
+
- Every field has Field(alias="camelCaseName") for frontend consumption
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(
|
|
28
|
+
frozen=True,
|
|
29
|
+
populate_by_name=True,
|
|
30
|
+
extra="forbid",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# --- Replace these with actual display fields ---
|
|
34
|
+
# Example fields (delete and replace):
|
|
35
|
+
# record_id: str = Field(alias="recordId")
|
|
36
|
+
# score: float = Field(alias="score")
|
|
37
|
+
# total_responses: int = Field(alias="totalResponses")
|
|
38
|
+
# categories: list[str] = Field(default_factory=list, alias="categories")
|
|
39
|
+
{{FIELDS}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{SOURCE_NAME}} Frontend Service
|
|
3
|
+
*
|
|
4
|
+
* Typed fetch functions for {{source_name}} API endpoints.
|
|
5
|
+
* Return types match the TypeScript interfaces in types/{{source-slug}}.ts.
|
|
6
|
+
*
|
|
7
|
+
* Part of the data contract pipeline:
|
|
8
|
+
* Backend API Router → **Frontend Service** → UI Components
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { {{ClassName}}Response } from '@/types/{{source-slug}}';
|
|
12
|
+
// Use your project's API client — adapt the import to match:
|
|
13
|
+
// import { apiClient } from '@/lib/api-client';
|
|
14
|
+
// import axios from 'axios';
|
|
15
|
+
|
|
16
|
+
const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetch {{source_name}} data from the API.
|
|
20
|
+
*
|
|
21
|
+
* @returns Typed {{ClassName}}Response from the backend.
|
|
22
|
+
* @throws Error if the request fails.
|
|
23
|
+
*/
|
|
24
|
+
export async function fetch{{ClassName}}Data(): Promise<{{ClassName}}Response> {
|
|
25
|
+
const response = await fetch(`${BASE_URL}/api/{{url-slug}}`);
|
|
26
|
+
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Failed to fetch {{source_name}} data: ${response.status} ${response.statusText}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data: {{ClassName}}Response = await response.json();
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- React Query hook example (uncomment if using TanStack Query) ---
|
|
38
|
+
//
|
|
39
|
+
// import { useQuery } from '@tanstack/react-query';
|
|
40
|
+
//
|
|
41
|
+
// export function use{{ClassName}}Data() {
|
|
42
|
+
// return useQuery({
|
|
43
|
+
// queryKey: ['{{source-slug}}'],
|
|
44
|
+
// queryFn: fetch{{ClassName}}Data,
|
|
45
|
+
// });
|
|
46
|
+
// }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{SOURCE_NAME}} TypeScript Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript interfaces mirroring the backend display DTO.
|
|
5
|
+
* Field names match the DTO's camelCase aliases exactly.
|
|
6
|
+
*
|
|
7
|
+
* Part of the data contract pipeline:
|
|
8
|
+
* Backend Display DTO → **Frontend Types** → Frontend Service
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: When adding/removing fields in the Python DTO,
|
|
11
|
+
* update this file in the same PR.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// --- Replace with actual interface matching the display DTO ---
|
|
15
|
+
|
|
16
|
+
export interface {{ClassName}}Response {
|
|
17
|
+
// Each property name matches the Python Field(alias="...") value.
|
|
18
|
+
// Types follow the Python→TypeScript mapping:
|
|
19
|
+
// str → string
|
|
20
|
+
// int/float → number
|
|
21
|
+
// bool → boolean
|
|
22
|
+
// list[T] → T[]
|
|
23
|
+
// dict[str, T] → Record<string, T>
|
|
24
|
+
// Optional[T] → T | null
|
|
25
|
+
|
|
26
|
+
// Example fields (delete and replace):
|
|
27
|
+
// recordId: string;
|
|
28
|
+
// score: number;
|
|
29
|
+
// totalResponses: number;
|
|
30
|
+
// categories: string[];
|
|
31
|
+
{{FIELDS}}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// --- Nested interfaces (if the DTO has nested objects) ---
|
|
35
|
+
|
|
36
|
+
// export interface {{NestedName}} {
|
|
37
|
+
// nestedField: string;
|
|
38
|
+
// }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{SOURCE_NAME}} Ingestion Model
|
|
3
|
+
|
|
4
|
+
1:1 Pydantic model matching the raw {{source_name}} payload shape.
|
|
5
|
+
This model exists to validate raw data structure — no business logic.
|
|
6
|
+
|
|
7
|
+
Part of the data contract pipeline:
|
|
8
|
+
Source → [Adapter] → **Ingestion Model** → [Normalizer] → Display DTO
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
# Shared config for all data-ingest DTOs. extra="allow" means unknown JSON
|
|
16
|
+
# keys land in model_extra and flow through to .model_dump() output — nothing
|
|
17
|
+
# is silently dropped while the upstream schema is still evolving.
|
|
18
|
+
# Switch individual models to extra="forbid" only AFTER the schema is frozen.
|
|
19
|
+
_FLEX = ConfigDict(populate_by_name=True, extra="allow")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class {{ClassName}}Payload(BaseModel):
|
|
23
|
+
"""
|
|
24
|
+
1:1 representation of the raw {{source_name}} payload.
|
|
25
|
+
|
|
26
|
+
- Every key in the source payload has a corresponding field here.
|
|
27
|
+
- Field types match the source data types exactly.
|
|
28
|
+
- Uses _FLEX (extra="allow") so new upstream keys pass through
|
|
29
|
+
via model_extra instead of being silently dropped.
|
|
30
|
+
- When the schema is frozen, swap _FLEX → ConfigDict(extra="forbid").
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
model_config = _FLEX
|
|
34
|
+
|
|
35
|
+
# --- Replace these with actual fields from the source payload ---
|
|
36
|
+
# Example fields (delete and replace):
|
|
37
|
+
# record_id: str
|
|
38
|
+
# score: float
|
|
39
|
+
# responses: int
|
|
40
|
+
# categories: list[str] = Field(default_factory=list)
|
|
41
|
+
# metadata: dict | None = None
|
|
42
|
+
{{FIELDS}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# {{SOURCE_NAME}} — UI ↔ JSON Data Mapping
|
|
2
|
+
|
|
3
|
+
> **Auto-generated reference** — do not hand-edit.
|
|
4
|
+
> Regenerate with: `python scripts/generate_mapping_doc.py`
|
|
5
|
+
> Source of truth: contract tests in `tests/unit/test_contract_{{source_slug}}.py`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Data Source
|
|
10
|
+
|
|
11
|
+
| Property | Value |
|
|
12
|
+
|----------|-------|
|
|
13
|
+
| Source Type | {{source_type}} |
|
|
14
|
+
| Source Location | {{source_location}} |
|
|
15
|
+
| Golden Sample | `tests/fixtures/payloads/{{source_slug}}/sample.json` |
|
|
16
|
+
| Ingestion Model | `src/models/ingestion/{{source_slug}}.py` |
|
|
17
|
+
| Display DTO | `src/models/responses/{{source_slug}}.py` |
|
|
18
|
+
| API Endpoint | `GET /api/{{url-slug}}` |
|
|
19
|
+
| Frontend Types | `frontend/src/types/{{source-slug}}.ts` |
|
|
20
|
+
| Frontend Service | `frontend/src/services/{{source-slug}}.ts` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Field Mapping
|
|
25
|
+
|
|
26
|
+
| JSON Key | Python Field | DTO Alias | TypeScript Property | Type | Notes |
|
|
27
|
+
|----------|-------------|-----------|--------------------:|------|-------|
|
|
28
|
+
{{FIELD_ROWS}}
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Intentionally Dropped Fields
|
|
33
|
+
|
|
34
|
+
Fields present in the source but NOT exposed to the frontend:
|
|
35
|
+
|
|
36
|
+
| JSON Key | Reason |
|
|
37
|
+
|----------|--------|
|
|
38
|
+
{{DROPPED_ROWS}}
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Changelog
|
|
43
|
+
|
|
44
|
+
| Date | Change | Author |
|
|
45
|
+
|------|--------|--------|
|
|
46
|
+
| {{DATE}} | Initial mapping generated | {{AGENT}} |
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{SOURCE_NAME}} Normalizer
|
|
3
|
+
|
|
4
|
+
Transforms the raw ingestion model into the display DTO.
|
|
5
|
+
Business logic (renaming, computing, filtering) lives here — not in the adapter or DTO.
|
|
6
|
+
|
|
7
|
+
Part of the data contract pipeline:
|
|
8
|
+
Source → Adapter → Ingestion Model → **Normalizer** → Display DTO → API Router
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from loguru import logger
|
|
12
|
+
|
|
13
|
+
from src.models.ingestion.{{source_slug}} import {{ClassName}}Payload
|
|
14
|
+
from src.models.responses.{{source_slug}} import {{ClassName}}Response
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def normalize(raw: {{ClassName}}Payload) -> {{ClassName}}Response:
|
|
18
|
+
"""
|
|
19
|
+
Transform raw {{source_name}} payload into display DTO.
|
|
20
|
+
|
|
21
|
+
Every field mapping is explicit. If a field is intentionally dropped
|
|
22
|
+
from the display DTO, document WHY with a comment.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
raw: Validated ingestion model instance.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Frozen display DTO ready for API serialization.
|
|
29
|
+
"""
|
|
30
|
+
return {{ClassName}}Response(
|
|
31
|
+
# --- Direct mappings ---
|
|
32
|
+
# record_id=raw.record_id,
|
|
33
|
+
# score=raw.score,
|
|
34
|
+
|
|
35
|
+
# --- Computed fields ---
|
|
36
|
+
# total_responses=_compute_total(raw),
|
|
37
|
+
|
|
38
|
+
# --- Intentionally dropped fields ---
|
|
39
|
+
# raw.internal_id — not exposed to frontend (internal tracking only)
|
|
40
|
+
{{FIELD_MAPPINGS}}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# --- Helper functions for computed fields ---
|
|
45
|
+
|
|
46
|
+
# def _compute_total(raw: {{ClassName}}Payload) -> int:
|
|
47
|
+
# """Compute total from raw components."""
|
|
48
|
+
# return sum(raw.component_counts)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Sub-prompt: Extract Requirements
|
|
2
|
+
|
|
3
|
+
Use this prompt to extract field-level requirements from PRDs, tickets, or docs.
|
|
4
|
+
Paste the document content after the divider, then run this prompt.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are extracting structured data requirements from product documentation.
|
|
9
|
+
|
|
10
|
+
For each UI field, dashboard metric, filter, export column, or data-driven action mentioned in the document, produce a JSON entry with this shape:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"field_label": "Human-readable label as shown in UI",
|
|
15
|
+
"field_key": "camelCase identifier",
|
|
16
|
+
"entity": "Which entity this belongs to (e.g. CustomerSummary)",
|
|
17
|
+
"data_type": "string | number | boolean | date | list | dict",
|
|
18
|
+
"null_semantics": "What NULL means (e.g. 'no revenue on record', 'not applicable')",
|
|
19
|
+
"formula": "Derivation rule if computed (else null)",
|
|
20
|
+
"grain": "Row level, aggregate, daily, monthly (if metric)",
|
|
21
|
+
"source_hint": "Any mention of where data comes from (table name, API, file)",
|
|
22
|
+
"priority": "must-have | nice-to-have | deferred",
|
|
23
|
+
"notes": "Any ambiguity, open questions, or conflicting statements"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Rules:
|
|
28
|
+
- Extract ONLY what is explicitly stated or clearly implied by the document.
|
|
29
|
+
- Do not invent field requirements not mentioned.
|
|
30
|
+
- If a formula is ambiguous, set `formula: null` and add a note.
|
|
31
|
+
- If a field has multiple conflicting definitions, list all variants in `notes`.
|
|
32
|
+
- Output a JSON array of requirement objects, followed by a short `## Gaps and Ambiguities` section listing unresolved questions.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
DOCUMENT:
|