@atmem/architect 0.0.0-stage → 0.2.0-alpha.4.1
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/CONSTITUTION.md +144 -0
- package/README.md +33 -2
- package/agent-skills/discovery/SKILL.md +48 -0
- package/agent-skills/discovery/topics.json +139 -0
- package/agent-skills/generation-capabilities.json +760 -0
- package/agent-skills/service-lifecycle.json +21 -0
- package/agent-skills/specialists/architecture/SKILL.md +15 -0
- package/agent-skills/specialists/aws/SKILL.md +17 -0
- package/agent-skills/specialists/azure/SKILL.md +7 -0
- package/agent-skills/specialists/composition/SKILL.md +17 -0
- package/agent-skills/specialists/devops/SKILL.md +10 -0
- package/agent-skills/specialists/finops/SKILL.md +7 -0
- package/agent-skills/specialists/gcp/SKILL.md +7 -0
- package/agent-skills/specialists/ibm/SKILL.md +7 -0
- package/agent-skills/specialists/manifest.json +13 -0
- package/agent-skills/specialists/pulumi/SKILL.md +7 -0
- package/agent-skills/specialists/security/SKILL.md +7 -0
- package/agent-skills/specialists/terraform/SKILL.md +14 -0
- package/agent-skills/upstream/aws/LICENSE +175 -0
- package/agent-skills/upstream/aws/SKILL.md +125 -0
- package/agent-skills/upstream/azure/LICENSE +21 -0
- package/agent-skills/upstream/azure/SKILL.md +387 -0
- package/agent-skills/upstream/gcp/LICENSE +202 -0
- package/agent-skills/upstream/gcp/SKILL.md +159 -0
- package/agent-skills/upstream/manifest.json +57 -0
- package/agent-skills/upstream/pulumi/LICENSE +201 -0
- package/agent-skills/upstream/pulumi/SKILL.md +501 -0
- package/agent-skills/upstream/terraform/LICENSE +373 -0
- package/agent-skills/upstream/terraform/SKILL.md +316 -0
- package/agent-skills/workflows/workflows.json +601 -0
- package/cli/architect.mjs +3 -0
- package/cli/runtime.mjs +115 -0
- package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
- package/dist/assets/index-B97MkVMs.css +1 -0
- package/dist/assets/index-qtCM2D7O.js +21271 -0
- package/dist/assets/index.es-C6C0sneB.js +18 -0
- package/dist/assets/purify.es-CAi2OJFm.js +3 -0
- package/dist/assets/visual-export-DjbjgQbA.js +173 -0
- package/dist/favicon.svg +2 -0
- package/dist/fonts/DM-Sans-OFL.txt +93 -0
- package/dist/fonts/Manrope-OFL.txt +93 -0
- package/dist/fonts/NotoSans-Regular.ttf +0 -0
- package/dist/fonts/OFL.txt +94 -0
- package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
- package/dist/index.html +4 -0
- package/dist/licenses/aws-architect-LICENSE.txt +17 -0
- package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
- package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
- package/dist/licenses/cloud-emulation-notices.txt +36619 -0
- package/dist/licenses/dependency-notices.txt +6379 -0
- package/dist/licenses/emulation-notices.txt +717 -0
- package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/dist/licenses/memory-dependency-notices.txt +3319 -0
- package/dist/licenses/openarchflow-LICENSE.txt +21 -0
- package/dist/licenses/python-dependency-notices.txt +11951 -0
- package/docs/upstream-lock.json +26 -0
- package/knowledge/engineering-practices.json +536 -0
- package/package.json +30 -4
- package/packaging/uv-release-lock.json +54 -0
- package/public/licenses/aws-architect-LICENSE.txt +17 -0
- package/public/licenses/aws-mcp-LICENSE.txt +175 -0
- package/public/licenses/aws-mcp-NOTICE.txt +1 -0
- package/public/licenses/cloud-emulation-notices.txt +36619 -0
- package/public/licenses/dependency-notices.txt +6379 -0
- package/public/licenses/emulation-notices.txt +717 -0
- package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/public/licenses/memory-dependency-notices.txt +3319 -0
- package/public/licenses/openarchflow-LICENSE.txt +21 -0
- package/public/licenses/python-dependency-notices.txt +11951 -0
- package/scripts/terraform-render.mjs +8 -0
- package/server/__init__.py +0 -0
- package/server/abstraction.py +28 -0
- package/server/agent_runtime.py +153 -0
- package/server/app.py +257 -0
- package/server/architect.py +112 -0
- package/server/architecture_activity.py +67 -0
- package/server/attachments.py +101 -0
- package/server/cloud-emulation-requirements.txt +29 -0
- package/server/codex_app_server.py +219 -0
- package/server/codex_engineering_backend.py +266 -0
- package/server/codex_worker_policy.py +170 -0
- package/server/contracts.py +315 -0
- package/server/copilot_worker.py +16 -0
- package/server/dependency_mirror.py +54 -0
- package/server/design_patch.py +43 -0
- package/server/emulation-requirements.txt +8 -0
- package/server/emulation_adapters.py +20 -0
- package/server/emulation_routes.py +98 -0
- package/server/emulation_worker.py +351 -0
- package/server/engineering_artifacts.py +215 -0
- package/server/engineering_assets/application-runtime.json +4372 -0
- package/server/engineering_assets/architecture_work.schema.json +79 -0
- package/server/engineering_assets/materialize_runtime.py +62 -0
- package/server/engineering_assets/prepare_tools.py +96 -0
- package/server/engineering_assets/sql_validation.py +59 -0
- package/server/engineering_contexts.py +120 -0
- package/server/engineering_delivery.py +210 -0
- package/server/engineering_finalization.py +210 -0
- package/server/engineering_handoff.py +163 -0
- package/server/engineering_jobs.py +206 -0
- package/server/engineering_knowledge.py +75 -0
- package/server/engineering_reconciliation.py +159 -0
- package/server/engineering_routes.py +469 -0
- package/server/engineering_sessions.py +273 -0
- package/server/engineering_tools.py +166 -0
- package/server/generation_lint.py +18 -0
- package/server/local_providers.py +181 -0
- package/server/local_validation.py +236 -0
- package/server/memory-requirements.lock +181 -0
- package/server/memory-requirements.txt +6 -0
- package/server/memory_routes.py +82 -0
- package/server/memory_service.py +273 -0
- package/server/memory_worker.py +66 -0
- package/server/openai_voice.py +80 -0
- package/server/packaged_app.py +9 -0
- package/server/pricing.py +84 -0
- package/server/process_control_observation.py +31 -0
- package/server/prompts.py +123 -0
- package/server/public_faq_emulation_worker.py +44 -0
- package/server/requirements.lock +1618 -0
- package/server/requirements.txt +68 -0
- package/server/runtime_dependency_locks.py +27 -0
- package/server/runtime_evidence.py +93 -0
- package/server/settings.py +14 -0
- package/server/skills.py +103 -0
- package/server/storage_emulation_worker.py +160 -0
- package/server/structured_emulation_worker.py +48 -0
- package/server/terraform_routes.py +118 -0
- package/server/upstream/__init__.py +0 -0
- package/server/upstream/agent/__init__.py +1 -0
- package/server/upstream/agent/aws_architect.py +600 -0
- package/server/upstream/agent/config/__init__.py +1 -0
- package/server/upstream/agent/config/agent_config.py +82 -0
- package/server/upstream/agent/config/prompts.py +138 -0
- package/server/upstream/agent/rate_limiter.py +62 -0
- package/server/upstream/utils/__init__.py +1 -0
- package/server/upstream/utils/file_writer.py +43 -0
- package/server/validation_applicability.py +85 -0
- package/server/validation_jobs.py +252 -0
- package/server/validation_workers.py +48 -0
- package/server/voice_routes.py +91 -0
- package/server/voice_worker.py +26 -0
- package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
- package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
- package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
- package/src/ArchitectureActivity.jsx +54 -0
- package/src/ArchitectureWorkPanel.jsx +46 -0
- package/src/ArtifactChecks.jsx +49 -0
- package/src/BlockRequirements.jsx +13 -0
- package/src/BrainActivity.jsx +11 -0
- package/src/ChatMessage.jsx +19 -0
- package/src/ChatResizeHandle.jsx +6 -0
- package/src/CodePreview.jsx +8 -0
- package/src/ComponentReview.jsx +31 -0
- package/src/DeliveryViews.jsx +59 -0
- package/src/EmulationPanel.jsx +44 -0
- package/src/EngagementViews.jsx +385 -0
- package/src/EngineeringPanel.jsx +151 -0
- package/src/ImplementationDelivery.jsx +55 -0
- package/src/JourneyViews.jsx +400 -0
- package/src/MemoryPanel.jsx +46 -0
- package/src/OpenAIVoicePanel.jsx +65 -0
- package/src/OutputsView.jsx +65 -0
- package/src/TerraformView.jsx +57 -0
- package/src/WorkbenchDialogs.jsx +532 -0
- package/src/activity-presentation.mjs +28 -0
- package/src/ai-qualification-scenarios.mjs +31 -0
- package/src/approval-application-files.mjs +32 -0
- package/src/approval-applications.mjs +82 -0
- package/src/approval-spec-kit.mjs +30 -0
- package/src/architecture-activity.css +28 -0
- package/src/architecture-activity.mjs +63 -0
- package/src/architecture-work.mjs +176 -0
- package/src/artifact-work.mjs +103 -0
- package/src/async-documents.mjs +191 -0
- package/src/atflow.css +1140 -0
- package/src/atforma.css +748 -0
- package/src/atmem-tokens.css +79 -0
- package/src/aws-preflight.mjs +60 -0
- package/src/aws-public-faq.mjs +102 -0
- package/src/aws-validation-scenarios.mjs +10 -0
- package/src/aws-validation.mjs +48 -0
- package/src/azure-plan-mocks.mjs +25 -0
- package/src/azure-structured.mjs +133 -0
- package/src/bedrock-summary.mjs +152 -0
- package/src/bounded-rag.mjs +60 -0
- package/src/brand.css +9 -0
- package/src/canvas-view.mjs +28 -0
- package/src/capability-composition.mjs +44 -0
- package/src/chat-attachments.mjs +13 -0
- package/src/cloud-provider-locks.mjs +9 -0
- package/src/cloud-storage-scenarios.mjs +2 -0
- package/src/component-review.mjs +55 -0
- package/src/composition-spec-kit.mjs +30 -0
- package/src/consultation-context.mjs +15 -0
- package/src/consultation.mjs +52 -0
- package/src/container-contracts.mjs +5 -0
- package/src/container-inspection.mjs +75 -0
- package/src/container-runtime.mjs +30 -0
- package/src/container-security-inspection.mjs +49 -0
- package/src/container-terraform.mjs +109 -0
- package/src/conversation.mjs +17 -0
- package/src/core.mjs +863 -0
- package/src/cross-cloud-storage.mjs +100 -0
- package/src/delivery-capabilities.mjs +25 -0
- package/src/delivery.mjs +58 -0
- package/src/deployment-ownership.mjs +10 -0
- package/src/deployment-region.mjs +11 -0
- package/src/derive.mjs +31 -0
- package/src/design-build-flow.mjs +10 -0
- package/src/diagram-attention.mjs +24 -0
- package/src/discovery.mjs +359 -0
- package/src/document.mjs +170 -0
- package/src/emulation-feedback.mjs +61 -0
- package/src/emulation-guidance.mjs +37 -0
- package/src/engagement.mjs +274 -0
- package/src/engineering-client.mjs +73 -0
- package/src/engineering-defaults.mjs +17 -0
- package/src/engineering.css +31 -0
- package/src/enterprise-application.mjs +76 -0
- package/src/enterprise-aws-host-infrastructure.mjs +62 -0
- package/src/enterprise-aws-host-tests.mjs +15 -0
- package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
- package/src/enterprise-aws-infrastructure.mjs +138 -0
- package/src/enterprise-google-infrastructure.mjs +84 -0
- package/src/enterprise-google-tests.mjs +25 -0
- package/src/estimate.mjs +188 -0
- package/src/existing-references.mjs +18 -0
- package/src/external-identities.mjs +17 -0
- package/src/faq-lambda-build.mjs +28 -0
- package/src/gcp-structured.mjs +104 -0
- package/src/generation-provider.mjs +7 -0
- package/src/generation-quality.mjs +69 -0
- package/src/handoff.mjs +141 -0
- package/src/ibm-registry.mjs +12 -0
- package/src/ibm-structured.mjs +103 -0
- package/src/identifiers.mjs +15 -0
- package/src/implementation-map.mjs +14 -0
- package/src/implementation-outcome.mjs +8 -0
- package/src/incomplete-terraform.mjs +9 -0
- package/src/integrations.ts +137 -0
- package/src/interface-contracts.mjs +30 -0
- package/src/invoice-application.mjs +66 -0
- package/src/invoice-aws-operations.mjs +136 -0
- package/src/invoice-aws.mjs +143 -0
- package/src/invoice-azure-operations.mjs +147 -0
- package/src/invoice-azure.mjs +134 -0
- package/src/invoice-builder.mjs +35 -0
- package/src/invoice-dependencies.mjs +43 -0
- package/src/invoice-gcp-operations.mjs +178 -0
- package/src/invoice-gcp.mjs +104 -0
- package/src/invoice-ibm-operations.mjs +278 -0
- package/src/invoice-ibm.mjs +142 -0
- package/src/invoice-operations.mjs +143 -0
- package/src/invoice-plan.mjs +45 -0
- package/src/invoice-region.mjs +6 -0
- package/src/invoice-spec-kit.mjs +61 -0
- package/src/invoice-state.mjs +7 -0
- package/src/invoice-terraform.mjs +29 -0
- package/src/invoice-tests.mjs +122 -0
- package/src/journey-workspace.css +72 -0
- package/src/journey.mjs +87 -0
- package/src/levels.mjs +145 -0
- package/src/local-agent.mjs +184 -0
- package/src/main.jsx +2458 -0
- package/src/model-policy-preflight.mjs +34 -0
- package/src/openai-voice.mjs +102 -0
- package/src/output-bundle.mjs +11 -0
- package/src/ownership-inspection.mjs +28 -0
- package/src/pack.mjs +201 -0
- package/src/pricing.mjs +31 -0
- package/src/production-state.mjs +8 -0
- package/src/project-copy.mjs +10 -0
- package/src/project-file.mjs +160 -0
- package/src/project-links.mjs +3 -0
- package/src/protected-state.mjs +43 -0
- package/src/public-faq-builder.mjs +19 -0
- package/src/public-faq-clouds.mjs +251 -0
- package/src/public-faq.mjs +122 -0
- package/src/pulumi.mjs +123 -0
- package/src/python-container.mjs +25 -0
- package/src/python-runtime.mjs +50 -0
- package/src/rag-handler.mjs +43 -0
- package/src/rag-web-assets.mjs +52 -0
- package/src/rag-web.mjs +68 -0
- package/src/records.mjs +177 -0
- package/src/resources.mjs +163 -0
- package/src/review.mjs +252 -0
- package/src/runtime/EnterpriseDocument.java +89 -0
- package/src/runtime/approved_http_destination.py +72 -0
- package/src/runtime/aws-lambda-qualification-lock.json +7 -0
- package/src/runtime/aws-python-lock.json +82 -0
- package/src/runtime/aws-web-python-lock.json +887 -0
- package/src/runtime/aws_json_model.py +42 -0
- package/src/runtime/azure-python-lock.json +863 -0
- package/src/runtime/azure-sources.json +8 -0
- package/src/runtime/azure-test-lock.json +23 -0
- package/src/runtime/azure_acceptance.py +192 -0
- package/src/runtime/azure_structured.py +153 -0
- package/src/runtime/bounded_rag.py +126 -0
- package/src/runtime/catalog_admin.py +70 -0
- package/src/runtime/cloud-sql-proxy-lock.json +8 -0
- package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
- package/src/runtime/connector_cli.py +50 -0
- package/src/runtime/connector_sync.py +70 -0
- package/src/runtime/durable-python-lock.json +247 -0
- package/src/runtime/durable_workflow.py +325 -0
- package/src/runtime/enterprise-application-manifest.json +110 -0
- package/src/runtime/enterprise-application-sources.json +266 -0
- package/src/runtime/enterprise-auth-python-lock.json +592 -0
- package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
- package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
- package/src/runtime/enterprise-aws-task-template.json +235 -0
- package/src/runtime/enterprise-document-lock.json +18 -0
- package/src/runtime/enterprise-document.Dockerfile +15 -0
- package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
- package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
- package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
- package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
- package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
- package/src/runtime/enterprise-runtime.Dockerfile +15 -0
- package/src/runtime/enterprise-web-app.js +1 -0
- package/src/runtime/enterprise-web-entry.js +105 -0
- package/src/runtime/enterprise-web-lock.json +29 -0
- package/src/runtime/enterprise-web-notices.txt +229 -0
- package/src/runtime/enterprise-web.css +4 -0
- package/src/runtime/enterprise-web.html +8 -0
- package/src/runtime/enterprise_audit.py +91 -0
- package/src/runtime/enterprise_audit_schema.sql +25 -0
- package/src/runtime/enterprise_auth_schema.sql +67 -0
- package/src/runtime/enterprise_auth_store.py +241 -0
- package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
- package/src/runtime/enterprise_aws_host_probe.py +105 -0
- package/src/runtime/enterprise_aws_hosts.py +136 -0
- package/src/runtime/enterprise_aws_jobs.py +300 -0
- package/src/runtime/enterprise_aws_objects.py +93 -0
- package/src/runtime/enterprise_aws_transfer.py +83 -0
- package/src/runtime/enterprise_azure_batch_api.py +128 -0
- package/src/runtime/enterprise_azure_host.py +599 -0
- package/src/runtime/enterprise_azure_jobs.py +294 -0
- package/src/runtime/enterprise_azure_objects.py +234 -0
- package/src/runtime/enterprise_azure_pool.py +161 -0
- package/src/runtime/enterprise_azure_transfer.py +122 -0
- package/src/runtime/enterprise_configuration.py +136 -0
- package/src/runtime/enterprise_document.py +116 -0
- package/src/runtime/enterprise_document_cli.py +32 -0
- package/src/runtime/enterprise_document_contract.py +54 -0
- package/src/runtime/enterprise_document_job.py +109 -0
- package/src/runtime/enterprise_document_jobs.py +122 -0
- package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
- package/src/runtime/enterprise_extraction_cache.py +186 -0
- package/src/runtime/enterprise_extraction_schema.sql +17 -0
- package/src/runtime/enterprise_google_jobs.py +275 -0
- package/src/runtime/enterprise_google_transfer.py +85 -0
- package/src/runtime/enterprise_http.py +212 -0
- package/src/runtime/enterprise_identity.py +52 -0
- package/src/runtime/enterprise_knowledge.py +152 -0
- package/src/runtime/enterprise_models.py +94 -0
- package/src/runtime/enterprise_native.py +181 -0
- package/src/runtime/enterprise_oauth.py +132 -0
- package/src/runtime/enterprise_operator.py +160 -0
- package/src/runtime/enterprise_parser_service.py +136 -0
- package/src/runtime/enterprise_parser_transport.py +68 -0
- package/src/runtime/enterprise_queries.py +255 -0
- package/src/runtime/enterprise_queries_schema.sql +27 -0
- package/src/runtime/enterprise_query_service.py +111 -0
- package/src/runtime/enterprise_secrets.py +110 -0
- package/src/runtime/enterprise_sessions.py +95 -0
- package/src/runtime/enterprise_sources.py +260 -0
- package/src/runtime/enterprise_web.py +39 -0
- package/src/runtime/enterprise_worker.py +68 -0
- package/src/runtime/export_contract_acceptance.py +18 -0
- package/src/runtime/faq-output-contract.json +40 -0
- package/src/runtime/faq-web-app.js +41 -0
- package/src/runtime/faq-web.css +1 -0
- package/src/runtime/faq-web.html +6 -0
- package/src/runtime/gcp-sources.json +9 -0
- package/src/runtime/gcp_acceptance.py +110 -0
- package/src/runtime/gcp_structured.py +150 -0
- package/src/runtime/http_acceptance.py +39 -0
- package/src/runtime/iap-web-app.js +36 -0
- package/src/runtime/iap-web-source.json +1 -0
- package/src/runtime/ibm-python-lock.json +856 -0
- package/src/runtime/ibm-sources.json +9 -0
- package/src/runtime/ibm_acceptance.py +133 -0
- package/src/runtime/ibm_structured.py +113 -0
- package/src/runtime/invoice-azapi-provider-lock.json +7 -0
- package/src/runtime/invoice-ibm-provider-lock.json +7 -0
- package/src/runtime/invoice-parser-lock.json +1199 -0
- package/src/runtime/invoice-parser-python-lock.json +56 -0
- package/src/runtime/invoice-parser.Dockerfile +190 -0
- package/src/runtime/invoice-sources.json +58 -0
- package/src/runtime/invoice-test-sources.json +24 -0
- package/src/runtime/invoice-web.css +2 -0
- package/src/runtime/invoice-web.html +12 -0
- package/src/runtime/invoice-web.js +112 -0
- package/src/runtime/invoice_configuration.py +17 -0
- package/src/runtime/invoice_document.py +95 -0
- package/src/runtime/invoice_extraction.py +96 -0
- package/src/runtime/invoice_http.py +129 -0
- package/src/runtime/invoice_identity.py +5 -0
- package/src/runtime/invoice_maintenance.py +40 -0
- package/src/runtime/invoice_native.py +77 -0
- package/src/runtime/invoice_object_store.py +153 -0
- package/src/runtime/invoice_operations.py +108 -0
- package/src/runtime/invoice_operator.py +79 -0
- package/src/runtime/invoice_operator_core.py +39 -0
- package/src/runtime/invoice_operator_job.py +147 -0
- package/src/runtime/invoice_parser_cli.py +17 -0
- package/src/runtime/invoice_parser_identity.py +39 -0
- package/src/runtime/invoice_parser_service.py +89 -0
- package/src/runtime/invoice_parser_transport.py +89 -0
- package/src/runtime/invoice_processing.py +24 -0
- package/src/runtime/invoice_retention.py +16 -0
- package/src/runtime/invoice_runtime_secrets.py +43 -0
- package/src/runtime/invoice_stage.py +80 -0
- package/src/runtime/invoice_web.py +39 -0
- package/src/runtime/invoice_worker.py +50 -0
- package/src/runtime/lambda_faq.py +5 -0
- package/src/runtime/parser-requirements.lock +4 -0
- package/src/runtime/policy_review.py +18 -0
- package/src/runtime/postgres-qualification-lock.json +21 -0
- package/src/runtime/public-faq-sources.json +22 -0
- package/src/runtime/public_catalog_admin.py +122 -0
- package/src/runtime/public_catalog_store.py +125 -0
- package/src/runtime/public_faq.py +144 -0
- package/src/runtime/public_faq_app.py +49 -0
- package/src/runtime/python-container-lock.json +18 -0
- package/src/runtime/rag-sources.json +10 -0
- package/src/runtime/rag_acceptance.py +60 -0
- package/src/runtime/rag_aws_adapter.py +95 -0
- package/src/runtime/rds-ca-source.json +9 -0
- package/src/runtime/rds-global-ca.pem +2888 -0
- package/src/runtime/state-contract-check-source.json +4 -0
- package/src/runtime/state_contract_check.py +338 -0
- package/src/runtime/structured-source.json +1 -0
- package/src/runtime/structured-web-app.js +71 -0
- package/src/runtime/structured-web-source.json +1 -0
- package/src/runtime/structured_http.py +105 -0
- package/src/runtime/structured_output.py +48 -0
- package/src/runtime/web-python-lock.json +802 -0
- package/src/runtime/web_user_admin.py +44 -0
- package/src/runtime/workflow_application.py +62 -0
- package/src/runtime/workflow_bindings.py +128 -0
- package/src/runtime/workflow_configuration.py +40 -0
- package/src/runtime/workflow_connection.py +105 -0
- package/src/runtime/workflow_contract.py +54 -0
- package/src/runtime/workflow_database.py +94 -0
- package/src/runtime/workflow_errors.py +6 -0
- package/src/runtime/workflow_http.py +178 -0
- package/src/runtime/workflow_identity.py +69 -0
- package/src/runtime/workflow_payload.py +118 -0
- package/src/runtime/workflow_process_stage.py +88 -0
- package/src/runtime/workflow_profile.py +91 -0
- package/src/runtime/workflow_review.py +106 -0
- package/src/runtime/workflow_role_bootstrap.py +104 -0
- package/src/runtime/workflow_schema.sql +72 -0
- package/src/runtime/workflow_worker.py +142 -0
- package/src/runtime-settings.mjs +15 -0
- package/src/s3-access.mjs +14 -0
- package/src/source-contracts.mjs +17 -0
- package/src/spec-kit.mjs +186 -0
- package/src/state-custody.mjs +32 -0
- package/src/state-inspection.mjs +45 -0
- package/src/state-spec-kit.mjs +13 -0
- package/src/storage-tests.mjs +25 -0
- package/src/structured-api.mjs +133 -0
- package/src/structured-contract.mjs +19 -0
- package/src/structured-operation-policy.mjs +2 -0
- package/src/structured-web.mjs +17 -0
- package/src/style.css +1 -0
- package/src/system-description.mjs +26 -0
- package/src/terraform-admission.mjs +66 -0
- package/src/terraform-composition.mjs +83 -0
- package/src/terraform-expressions.mjs +46 -0
- package/src/terraform-validation-coverage.mjs +11 -0
- package/src/terraform.mjs +128 -0
- package/src/text-statistics.mjs +51 -0
- package/src/upstream/openarchflow/format-payload.ts +26 -0
- package/src/upstream/openarchflow/import/mermaid.ts +205 -0
- package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
- package/src/upstream/openarchflow/layout.ts +54 -0
- package/src/upstream/openarchflow/providers/aws.ts +196 -0
- package/src/upstream/openarchflow/providers/azure.ts +126 -0
- package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
- package/src/upstream/openarchflow/providers/generic.ts +227 -0
- package/src/upstream/openarchflow/providers/integration.ts +22 -0
- package/src/upstream/openarchflow/providers/types.ts +2 -0
- package/src/upstream/openarchflow/templates.ts +937 -0
- package/src/upstream-catalog.mjs +10 -0
- package/src/useChatScroll.jsx +36 -0
- package/src/useEmulation.jsx +36 -0
- package/src/validation-receipt.mjs +10 -0
- package/src/visual-export.mjs +92 -0
- package/src/voice-conversation.mjs +117 -0
- package/src/work-intake.mjs +24 -0
- package/src/workflow-runtime-inventory.mjs +3 -0
package/src/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{font-family:'DM Sans',sans-serif;color:#263244;background:#f6f7f9;font-synthesis:none;font-size:13px;--green:#34846c;--border:#e5e9ef;--muted:#8993a2}*{box-sizing:border-box}body{margin:0}button,input,textarea,select{font:inherit}button,a,input,textarea,select{-webkit-tap-highlight-color:transparent}button{cursor:pointer}button{display:inline-flex;align-items:center;justify-content:center;gap:7px;border:0;background:none;color:inherit}button:disabled{opacity:.45;cursor:not-allowed}button:focus-visible,a:focus-visible{outline:2px solid #34846c;outline-offset:3px}input:focus,textarea:focus,select:focus{outline:1px solid #71ad99}a{color:#34846c}h1,h2,h3,p{margin:0}h1,h2,h3{font-family:'Manrope',sans-serif}strong{font-weight:600}.app-shell{display:flex;min-height:100dvh;height:100dvh;overflow:hidden}.rail{width:66px;background:#fff;border-right:1px solid var(--border);display:flex;align-items:center;flex-direction:column;padding:18px 0;flex-shrink:0;gap:13px;z-index:5}.brand{font-family:Georgia,serif;font-weight:bold;font-style:italic;font-size:40px;width:40px;height:37px;letter-spacing:-7px;padding:0 6px 0 0;color:#263e35}.brand span{color:#50a48a;font-style:normal;font-size:38px}.rail-divider{height:1px;background:var(--border);width:27px;margin:9px 0 4px}.rail-icon{width:40px;height:40px;color:#8a94a4;border-radius:9px}.rail-icon:hover{background:#f2f6f4;color:var(--green)}.rail-icon.active{color:var(--green);background:#eaf3ef}.rail-bottom{margin-top:auto;display:flex;flex-direction:column;align-items:center;gap:14px}.avatar{display:flex;align-items:center;justify-content:center;border-radius:50%;height:32px;width:32px;background:#ecece3;color:#717958;font-size:11px;font-weight:600;flex-shrink:0}.small-avatar{width:29px;height:29px}.main-shell{flex:1;min-width:0;display:flex;flex-direction:column}.topbar{height:61px;border-bottom:1px solid var(--border);background:#fff;display:flex;align-items:center;justify-content:space-between;padding:0 27px;flex-shrink:0}.breadcrumbs{display:flex;align-items:center;gap:12px;font-size:12px}.breadcrumbs>span:first-child{color:#8a94a3}.breadcrumbs>svg{color:#acb3be}.tag{font-size:9px;border:1px solid #e1e6eb;padding:3px 6px;border-radius:4px;color:#8b95a4;letter-spacing:.4px;font-weight:600;white-space:nowrap}.header-actions{display:flex;align-items:center;gap:18px}.save-state{display:flex;align-items:center;gap:6px;font-size:10px;color:#8e98a6}.button{border:1px solid #dce2e8;border-radius:6px;background:#fff;padding:8px 12px;font-size:11px;font-weight:550;min-height:33px;white-space:nowrap}.button:hover{background:#f7f9fa;border-color:#bdc7d0}.button.subtle{background:transparent}.projectbar{padding:24px 28px 23px;display:flex;justify-content:space-between;align-items:center;background:#fff;flex-shrink:0}.eyebrow{font-size:9px;font-weight:650;letter-spacing:1.6px;color:#81918d;display:flex;align-items:center;gap:7px}.tiny-grid{color:#438b73;font-size:14px}h1{font-size:25px;letter-spacing:-1.1px;font-weight:750;margin:5px 0 7px}.projectbar p{font-size:11px;color:#8a94a2}.connections{color:#647083}.count-badge{border-radius:4px;background:#f1f4f6;padding:1px 5px;color:#8d97a6;font-size:10px}.workspace-toolbar{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:#fff;padding:9px 21px;height:56px;flex-shrink:0}.phase-tabs{display:flex;align-items:center;gap:9px}.phase{font-size:11px;color:#939daa;padding:8px 10px;border-radius:6px;gap:6px}.phase.active{color:#2e7b62;background:#edf5f1;font-weight:600}.phase-number{font-size:9px;width:16px;height:16px;border-radius:4px;background:#f0f2f5;display:flex;align-items:center;justify-content:center;color:#98a1ac}.phase.active .phase-number{background:#d9ece3;color:#3f876c}.phase-arrow{color:#c2c9d2}.toolbar-actions{display:flex;gap:11px;align-items:center}.design-only{font-size:10px;color:#8a94a0;display:flex;align-items:center;gap:6px}.icon-button{height:28px;width:28px;border-radius:5px;color:#909aaa;padding:4px;flex-shrink:0}.icon-button:hover{background:#edf1f4;color:#3f7061}.run-button{background:#34846c;color:#fff;border-color:#34846c;box-shadow:0 1px 2px #1b58441a}.run-button:hover{background:#2a735b;color:#fff;border-color:#2a735b}.workspace{display:grid;grid-template-columns:237px minmax(300px,1fr) 325px;flex:1;min-height:0}.workspace.library-hidden{grid-template-columns:minmax(300px,1fr) 325px}.library{background:#fff;border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.panel-title{display:flex;align-items:center;justify-content:space-between;padding:15px 15px 12px}.panel-title h2{font-size:12px;font-weight:700;letter-spacing:-.2px}.searchbox{display:flex;align-items:center;gap:7px;margin:0 14px 13px;border:1px solid #e5e9ee;border-radius:6px;background:#fbfcfd;padding:9px;color:#9ca6b2}.searchbox input{background:none;border:0;outline:0;min-width:0;width:100%;font-size:10px;color:#5a6779}.searchbox input::placeholder{color:#9aa5b2}.searchbox kbd{font-size:10px;border:1px solid #e3e7ed;border-radius:3px;padding:0 3px}.provider-filters{display:flex;flex-wrap:wrap;gap:5px;padding:0 14px 14px;border-bottom:1px solid #f0f2f5}.provider-filters button{border:1px solid #e6eaf0;border-radius:5px;font-size:9px;color:#8792a2;padding:5px 7px;gap:5px}.provider-filters button.chosen{background:#edf5f1;color:#387d63;border-color:#cbe0d6}.provider-filters button span{width:5px;height:5px;border-radius:50%}.library-caption{font-size:8px;letter-spacing:1.25px;color:#a0a8b3;display:flex;justify-content:space-between;padding:17px 16px 8px}.library-caption span:last-child{letter-spacing:0;font-size:9px;background:#f1f3f6;border-radius:3px;padding:0 4px}.component-list{padding:0 8px 12px;overflow:auto;flex:1;scrollbar-width:thin}.library-item{display:flex;text-align:left;padding:11px 7px;width:100%;border-radius:6px;gap:9px;justify-content:flex-start}.library-item:hover{background:#f3f7f5}.library-item-icon{width:30px;height:31px;background:#f5f7fa;border:1px solid #eef0f4;border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.library-item>span:nth-child(2){flex:1;min-width:0}.library-item strong{font-size:10px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.library-item small{display:block;color:#9aa4b1;font-size:9px;margin-top:4px;text-transform:capitalize}.library-item>svg{color:#b3bdc8}.library-footer{display:flex;align-items:center;gap:10px;border-top:1px solid var(--border);padding:15px;color:#90a094}.library-footer strong{font-size:10px;color:#6e7f75}.library-footer p{font-size:9px;color:#a0aab6;margin-top:3px}.canvas-area{position:relative;overflow:hidden;background:#f8f9fb;min-height:350px}.react-flow{height:100%;width:100%}.canvas-heading{position:absolute;top:19px;left:23px;right:20px;display:flex;align-items:center;gap:8px;z-index:3;pointer-events:none}.canvas-heading>*{pointer-events:auto}.canvas-heading>div{flex:1}.canvas-breadcrumb{font-size:8px;letter-spacing:1.2px;color:#a0aab6}.canvas-heading h2{font-size:12px;letter-spacing:-.2px;margin-top:6px;font-weight:650;color:#788391}.resource-node{width:232px;background:#fff;border:1px solid #dde3eb;border-radius:9px;box-shadow:0 3px 8px #2f435709;transition:border-color .18s,box-shadow .18s;overflow:visible}.resource-node.selected{border-color:#5b9c85;box-shadow:0 0 0 3px #34846c16,0 3px 8px #2f435709}.node-top{padding:10px 12px;display:flex;align-items:center;gap:7px;color:#909aa8;font-size:9px;border-bottom:1px solid #f0f2f6}.node-menu{margin-left:auto;color:#b0b8c4}.provider-badge{width:30px;height:30px;border-radius:7px;display:inline-flex;justify-content:center;align-items:center;font-size:14px;font-weight:700;color:var(--provider);background:color-mix(in srgb,var(--provider),transparent 92%);border:1px solid color-mix(in srgb,var(--provider),transparent 87%);flex-shrink:0}.provider-badge.small{width:18px;height:18px;border-radius:4px;font-size:10px}.node-main{padding:17px 12px;display:flex;align-items:flex-start;gap:10px;min-height:82px}.node-icon{color:var(--provider);display:flex;padding-top:2px}.node-main strong{font-size:11px;color:#3b485a}.node-main p{font-size:9px;line-height:1.6;color:#9ba5b2;margin-top:4px;max-width:170px}.node-foot{display:flex;align-items:center;padding:8px 11px;border-top:1px solid #f0f2f6;font-size:8px;color:#9ba5b1;gap:5px}.node-foot>span:last-child{margin-left:auto}.status-dot{width:5px;height:5px;border-radius:50%;background:#b7c0cc;display:inline-block;flex-shrink:0}.status-dot.passed{background:#51a284}.status-dot.running{background:#d5ad48;animation:pulse .7s infinite}.status-dot.failed{background:#cf6666}.status-dot.skipped{background:#b6bdc7}.resource-node.state-passed{border-color:#81b49e}.resource-node.state-failed{border-color:#d98484;background:#fffafa}.resource-node.state-running{border-color:#cfb264;box-shadow:0 0 0 4px #dfc07520}.resource-node.state-skipped{opacity:.55}.react-flow__handle{background:#fff!important;border:1.5px solid #91a0b2!important;width:9px!important;height:9px!important}.react-flow__handle:hover{background:#4c9e80!important}.react-flow__controls{box-shadow:0 2px 8px #2e3b4910!important;border:1px solid #e4e8ef;border-radius:6px;overflow:hidden;bottom:45px!important;left:14px!important}.react-flow__controls-button{width:26px!important;height:27px!important;border-bottom-color:#edf0f3!important;color:#7a8796!important}.react-flow__minimap{bottom:38px!important;width:120px!important;height:75px!important;border:1px solid #e5e9ee;border-radius:6px;overflow:hidden;background:#fff!important}.canvas-note{position:absolute;left:50%;bottom:67px;transform:translateX(-50%);display:flex;align-items:center;gap:11px;z-index:3;background:#fff;border:1px solid #e0e7e1;border-radius:8px;padding:13px 14px;width:360px;box-shadow:0 5px 20px #233d2f06}.note-icon{width:30px;height:30px;background:#edf5f0;color:#6b9b7e;border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.canvas-note strong{font-size:10px;font-weight:550}.canvas-note p{font-size:9px;color:#9ba4b0;margin-top:4px}.canvas-note button{margin-left:auto;color:#a3b4aa;padding:0}.canvas-status{position:absolute;bottom:0;left:0;right:0;height:31px;display:flex;align-items:center;justify-content:space-between;background:#fbfcfd;border-top:1px solid var(--border);padding:0 15px;font-size:9px;color:#9ba5b2;z-index:3}.canvas-status>span{display:flex;align-items:center;gap:5px}.canvas-status button{font-size:9px;padding:0;color:#7a8f84}.status-separator{margin:0 7px;color:#c1c7cf}.agent-panel{background:#fff;border-left:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.agent-header{display:flex;align-items:center;padding:17px 15px;gap:10px}.agent-avatar{background:#e9f3ee;border:1px solid #dcebe3;color:#4f9578;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center}.agent-header>div:nth-child(2){flex:1}.agent-header h2{font-size:12px;font-weight:700;letter-spacing:-.25px}.agent-header span{font-size:8px;color:#97a4a0;display:flex;align-items:center;gap:5px;margin-top:5px}.agent-header .status-dot{width:4px;height:4px}.panel-tabs{display:flex;border-bottom:1px solid var(--border);padding:0 13px;gap:15px}.panel-tabs button{padding:10px 0 11px;font-size:10px;color:#a0a9b5;position:relative;gap:4px}.panel-tabs button.active{color:#43866b}.panel-tabs button.active:after{content:'';height:2px;background:#43866b;position:absolute;bottom:-1px;left:0;right:0}.panel-tabs button span{font-size:8px;color:#bd6b60;background:#fcf0ed;border-radius:3px;padding:1px 4px}.messages{flex:1;min-height:0;overflow:auto;padding:19px 18px;scrollbar-width:thin}.conversation-date{font-size:8px;letter-spacing:1.4px;color:#b0b7c1;text-align:center;margin:1px 0 24px}.message{margin-bottom:22px}.message-label{display:flex;align-items:center;gap:5px;font-size:9px;font-weight:600;color:#5a7e6b;margin-bottom:9px}.message p{font-size:11px;line-height:1.85;white-space:pre-wrap;color:#7f8a99}.message.user{background:#f5f7f9;border:1px solid #eef1f5;border-radius:8px;padding:12px}.message.user .message-label{color:#6c7b90}.message.user p{color:#5c6a7c}.workflow-chips{display:flex;flex-wrap:wrap;gap:5px;margin-top:15px}.workflow-chips span{font-size:8px;color:#87a191;background:#f4f8f5;border:1px solid #e6eee8;padding:5px 6px;border-radius:4px}.chat-bottom{padding:12px 13px 10px;border-top:1px solid #f3f5f7}.suggestions{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:11px}.suggestions button{font-size:8px;border:1px solid #e6ebef;border-radius:4px;padding:6px;color:#96a0ae;justify-content:space-between;gap:10px}.suggestions button:hover{border-color:#99b7a8;color:#4d816b}.chat-composer{border:1px solid #d9e2dd;border-radius:8px;background:#fff;box-shadow:0 1px 4px #27443106;padding:10px}.chat-composer textarea{resize:none;width:100%;height:54px;border:0;background:transparent;font-size:10px;line-height:1.6;outline:0;color:#526c60}.chat-composer textarea::placeholder{color:#a7b0bb}.chat-composer>div{display:flex;align-items:center;justify-content:space-between}.model-select{font-size:8px;color:#94a09b;padding:0;gap:5px}.send-button{width:26px;height:26px;border-radius:5px;color:#fff;background:#4a8f73}.composer-footnote{display:block;text-align:center;color:#afb7c0;font-size:8px;margin-top:8px}.proposal{background:#f3f8f5;border:1px solid #d8e7de;border-radius:7px;padding:12px}.proposal .eyebrow{font-size:8px;margin-bottom:9px;letter-spacing:1px}.proposal>strong{font-size:11px;color:#44725c}.proposal p{font-size:9px;line-height:1.7;color:#849c8d;margin:8px 0 11px}.proposal>div:last-child{display:flex;gap:6px}.proposal .button{font-size:9px;padding:6px 8px}.app-footer{height:35px;background:#fff;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 22px;font-size:8px;color:#a3acb8;flex-shrink:0}.app-footer>span:first-child{display:flex;align-items:center;gap:6px}.footer-brand{font-size:19px;font-family:Georgia,serif;font-weight:bold;font-style:italic;color:#718c7c;letter-spacing:-2px;margin-right:3px}.app-footer button{font-size:8px;color:#9ca7b4;padding:0;gap:5px}.test-controller{position:absolute;bottom:58px;left:50%;transform:translateX(-50%);z-index:4;display:flex;align-items:center;gap:12px;background:#fff;border:1px solid #dae5dd;border-radius:9px;box-shadow:0 5px 25px #284a3610;padding:14px;max-width:95%;width:540px}.test-label{display:flex;gap:8px;align-items:center;flex:1}.test-symbol{color:#4a9273;background:#edf5f0;border-radius:6px;padding:7px;display:flex}.test-label strong{font-size:10px}.test-label p{font-size:8px;color:#96a49b;margin-top:4px}.test-controller select{font-size:9px;border:1px solid var(--border);border-radius:5px;padding:8px;max-width:150px;color:#758272;background:#fff}.test-controller .button{font-size:9px}.execution-panel{background:#fff;border-top:1px solid var(--border);flex-shrink:0}.execution-header{display:flex;align-items:center;width:100%;height:40px;justify-content:flex-start;padding:0 20px;gap:8px;color:#95a1ad;font-size:10px}.execution-header strong{color:#687687;font-size:10px}.trace-badge{font-size:7px;letter-spacing:.7px;background:#f3f6f4;color:#94a398;border:1px solid #e5ece7;border-radius:3px;padding:3px 5px}.execution-hint{margin-left:auto;font-size:9px;color:#a5aeb9}.execution-steps{display:flex;align-items:center;gap:10px;padding:5px 22px 16px;overflow:auto}.execution-steps>svg{color:#c1cad3;flex-shrink:0}.execution-step{border:1px solid #e2e8e4;border-radius:6px;padding:10px;gap:9px;justify-content:flex-start;min-width:145px;text-align:left;color:#84a28f}.execution-step.chosen{background:#f1f7f3;border-color:#9dbba9}.execution-step>span:nth-child(2){flex:1}.execution-step strong{font-size:9px;display:block;color:#6b8073;max-width:150px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.execution-step small{font-size:8px;display:block;color:#9aa99e;margin-top:4px}.execution-step.failed{border-color:#e9c7c7;color:#c37b7b}.execution-step.skipped{opacity:.5}.run-summary{display:flex;align-items:center;gap:5px;color:#49916f;font-size:9px}.run-summary.failed{color:#c17772}.rotated{transform:rotate(90deg)}.inspector,.review-panel{padding:20px 17px;overflow:auto;min-height:0;flex:1}.inspector-title{display:flex;align-items:center;gap:10px;margin-bottom:24px}.inspector-title small{color:#9aa6b2;font-size:9px}.inspector-title h3{font-size:12px;margin-top:4px}.inspector label{display:flex;flex-direction:column;gap:7px;font-size:8px;letter-spacing:1.1px;color:#9aa6b2;margin-bottom:17px}.inspector input,.inspector select{width:100%;border:1px solid #e1e7ed;border-radius:5px;background:#fcfdfe;padding:9px;font-size:10px;color:#6f7f8b;letter-spacing:0}.inspector-info{background:#f4f7f5;border:1px solid #e5ece7;color:#93a59a;display:flex;gap:8px;padding:11px;border-radius:6px;font-size:9px;line-height:1.6;margin:14px 0}.inspector-info svg{flex-shrink:0;margin-top:2px}.section-label{font-size:8px;letter-spacing:1.2px;color:#9ca8b0;margin:23px 0 13px}.inspector-empty{text-align:center;padding:34px 15px;color:#a2b0aa;line-height:1.8}.inspector-empty h3{font-size:13px;color:#7d9085;margin-top:10px}.inspector-empty p{font-size:10px;margin-top:8px}.inspector details{border:1px solid #e4e9ef;border-radius:5px;margin-top:10px;overflow:hidden}.inspector summary{font-size:10px;padding:10px;color:#788b81;cursor:pointer}.inspector pre{margin:0;padding:12px;background:#f6f8fa;border-top:1px solid #e9edf2;font-size:9px;line-height:1.7;white-space:pre-wrap;overflow-wrap:anywhere;color:#728575}.trace-result{font-size:10px;text-transform:capitalize;display:flex;gap:7px;align-items:center;color:#7c9488}.error-box{color:#b57570;font-size:10px;background:#fff4f2;border:1px solid #f0dcd7;border-radius:5px;padding:11px;line-height:1.7;margin-top:10px}.button.danger{color:#bb7770;font-size:9px;margin-top:20px}.review-summary{padding:7px 0 22px;color:#69977c}.review-summary h3{font-size:14px;color:#657e6e;margin:10px 0 7px}.review-summary p{font-size:10px;color:#a0ada4}.review-issue{display:flex;align-items:flex-start;text-align:left;gap:8px;width:100%;padding:12px 9px;border:1px solid #e5ebe7;background:#f7faf8;border-radius:6px;font-size:10px;line-height:1.7;color:#8c9d92;margin-bottom:9px}.review-issue svg{flex-shrink:0;margin-top:2px}.review-issue.error{color:#bc8379;background:#fff8f5;border-color:#f0e1db}.review-todo{display:flex;align-items:center;gap:9px;color:#96a2af;font-size:10px;padding:9px 0}.full{width:100%;margin-top:15px}.deployment-overlay{position:absolute;inset:75px 0 32px;z-index:4;background:#f8f9fbdd;display:flex;align-items:center;justify-content:center;padding:15px;overflow:auto;backdrop-filter:blur(3px)}.deploy-card{background:white;border:1px solid #e0e7e2;border-radius:12px;box-shadow:0 15px 50px #2d4b3610;padding:25px;width:430px;max-width:100%;margin:auto}.deploy-icon{display:flex;align-items:center;justify-content:center;width:49px;height:49px;background:#edf5ef;color:#5a9172;border-radius:12px;margin-bottom:17px}.deploy-card .eyebrow{font-size:8px}.deploy-card h2{font-size:23px;letter-spacing:-.8px;margin:7px 0 10px}.deploy-card>p{font-size:11px;line-height:1.8;color:#92a198}.segmented{background:#f5f7f6;padding:4px;border-radius:6px;display:flex;margin:19px 0}.segmented button{flex:1;font-size:10px;padding:7px;color:#9caaa1;border-radius:4px}.segmented button.active{background:white;box-shadow:0 1px 4px #314a3b12;color:#5e856f}.deploy-check{display:flex;align-items:center;gap:9px;color:#629178;padding:11px 0;border-bottom:1px solid #f1f4f2;font-size:10px}.deploy-check span{flex:1}.deploy-check small{display:block;color:#a9b4ac;font-size:8px;margin-top:4px}.deploy-check.incomplete{color:#a4aea7}.deploy-footnote{display:block;font-size:8px;color:#a7b2aa;text-align:center;margin-top:13px}.modal-backdrop{position:fixed;inset:0;background:#1c2d3c50;backdrop-filter:blur(5px);z-index:30;display:flex;align-items:center;justify-content:center;padding:25px}.modal{position:relative;background:#fff;border:1px solid #e2e8e4;border-radius:14px;box-shadow:0 20px 90px #1e393233;padding:34px;width:560px;max-width:100%;max-height:90vh;overflow:auto}.modal-close{position:absolute;right:15px;top:15px}.modal h2{font-size:25px;letter-spacing:-1px;margin:10px 0 12px}.modal>p{font-size:12px;color:#8c9990;line-height:1.8;margin:12px 0}.connection-row{display:flex;align-items:center;gap:11px;border:1px solid #e6ebe7;border-radius:7px;padding:12px;margin-bottom:8px}.connection-row>div{flex:1}.connection-row strong{font-size:12px}.connection-row small{display:block;font-size:10px;color:#a3aea7;margin-top:5px}.connection-icon{display:flex;color:#85a58f;background:#f2f7f3;border-radius:6px;padding:9px}.cloud-connect-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.cloud-connect-grid>div{display:flex;flex-direction:column;align-items:center;gap:9px;border:1px solid #e8ede9;border-radius:7px;padding:13px 3px}.cloud-connect-grid strong{font-size:10px}.cloud-connect-grid small{font-size:8px;color:#9da8a1}.license-item{border-bottom:1px solid #e6ece8;padding:15px 0}.license-item strong{font-size:12px;display:block}.license-item span{display:block;font-size:10px;color:#91a096;margin-top:6px}.license-item p{font-size:11px;color:#9aa59d;line-height:1.7;margin:9px 0}.license-item a,.modal>a{font-size:10px;display:inline-block;margin:8px 0}.modal-fine{font-size:10px!important}.toast{position:fixed;bottom:47px;left:50%;transform:translateX(-50%);z-index:50;background:#2b5141;color:#fff;box-shadow:0 6px 30px #19362825;border:1px solid #47715b;border-radius:8px;padding:12px 18px;font-size:11px;max-width:min(600px,90vw);display:flex;align-items:center;gap:9px}.toast svg{flex-shrink:0}.empty{padding:16px;color:#a4afaa;font-size:10px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.3}}@media(min-width:1650px){.workspace{grid-template-columns:260px minmax(400px,1fr) 360px}.workspace.library-hidden{grid-template-columns:minmax(400px,1fr) 360px}.resource-node{width:242px}.node-main strong{font-size:12px}.library-item strong{font-size:11px}.message p{font-size:12px}.agent-header h2{font-size:13px}.projectbar{padding-top:28px;padding-bottom:27px}h1{font-size:29px}.projectbar p{font-size:12px}}@media(max-width:1200px){.workspace{grid-template-columns:205px minmax(250px,1fr) 285px}.workspace.library-hidden{grid-template-columns:minmax(250px,1fr) 285px}.rail{width:55px}.design-only,.save-state{display:none}.test-controller{flex-wrap:wrap;width:340px}.test-label{flex-basis:100%}.canvas-note{width:285px}.canvas-note p{max-width:185px}.header-actions{gap:10px}.projectbar{padding:20px}.panel-tabs{gap:12px}.phase-tabs{gap:2px}.phase{padding:8px}.phase-arrow{width:10px}.phase-number{display:none}}@media(max-width:900px){.app-shell{height:auto;min-height:100dvh;overflow:auto}.main-shell{min-height:100dvh}.workspace,.workspace.library-hidden{grid-template-columns:180px minmax(0,1fr);min-height:1050px;flex:initial}.library{grid-column:1;grid-row:1;height:600px}.canvas-area{grid-column:2;grid-row:1;height:600px}.agent-panel{grid-column:1/-1;grid-row:2;height:450px;border-top:1px solid var(--border)}.library-hidden .canvas-area{grid-column:1/-1}.messages{padding:15px 22px}.chat-bottom{padding-left:22px;padding-right:22px}.chat-composer textarea{height:36px}.agent-header{padding:12px 20px}.app-footer>span:nth-child(2),.execution-hint{display:none}.execution-steps{flex-wrap:wrap}.rail{position:sticky;top:0;height:100dvh}.canvas-heading{left:14px}.canvas-heading h2{font-size:10px}.topbar{padding:0 15px}.projectbar{gap:12px}.projectbar h1{font-size:23px}.breadcrumbs{font-size:10px;gap:7px}.workspace-toolbar{padding:9px 12px}.phase{font-size:10px}.phase>svg{display:none}.toolbar-actions{gap:5px}.canvas-note{width:260px}.app-footer{padding:0 13px}.connections{font-size:10px}.projectbar p{line-height:1.7}}@media(max-width:560px){.rail{width:44px}.rail-icon{width:32px;height:36px}.brand{font-size:34px}.topbar{height:50px;padding:0 10px}.breadcrumbs>span:first-child,.breadcrumbs>svg,.breadcrumbs .tag,.header-actions .button{display:none}.projectbar{padding:18px 12px;align-items:flex-start}.projectbar h1{font-size:19px}.projectbar p{font-size:10px}.projectbar .eyebrow{font-size:7px;letter-spacing:1px}.projectbar .connections{padding:6px;min-height:28px}.connections .count-badge{display:none}.connections svg{width:12px}.workspace{grid-template-columns:124px minmax(0,1fr)}.library .panel-title{padding:12px 8px}.panel-title h2{font-size:10px}.panel-title .icon-button{width:20px}.searchbox{margin:0 7px 10px;padding:7px;gap:4px}.searchbox input{font-size:9px}.searchbox kbd{display:none}.provider-filters{padding:0 7px 10px;gap:4px}.provider-filters button{font-size:8px;padding:4px}.library-caption{padding:13px 8px 6px;font-size:7px;letter-spacing:.5px}.library-item{padding:9px 2px;gap:5px}.library-item-icon{width:23px;height:26px}.library-item-icon svg{width:14px}.library-item strong{font-size:9px}.library-item small{font-size:7px}.library-item>svg{display:none}.component-list{padding:0 5px 8px}.library-footer{padding:10px 7px;gap:5px}.library-footer svg{display:none}.library-footer strong{font-size:8px}.library-footer p{font-size:7px}.phase{font-size:9px;padding:7px 5px}.phase-tabs{gap:0}.phase-arrow{width:8px}.workspace-toolbar{padding:8px 5px}.toolbar-actions .icon-button{display:none}.run-button{padding:7px 8px;font-size:9px}.canvas-note{width:175px;bottom:65px;padding:10px;gap:7px}.note-icon{display:none}.canvas-note strong{font-size:9px}.canvas-note p{font-size:8px;line-height:1.6}.canvas-note button{display:none}.canvas-status{padding:0 7px;font-size:7px}.canvas-status .status-separator{margin:0 2px}.canvas-status button{font-size:7px}.react-flow__minimap{display:none}.canvas-heading{top:13px;left:10px;right:10px}.canvas-heading h2{font-size:9px;line-height:1.6}.canvas-breadcrumb{font-size:7px;letter-spacing:.6px}.test-controller{width:180px;padding:10px;gap:8px}.test-label strong{font-size:9px}.test-label p{font-size:7px;line-height:1.5}.test-symbol{display:none}.test-controller select{max-width:100%;width:100%;font-size:8px}.test-controller .button{width:100%}.execution-header{padding:0 10px;gap:5px}.trace-badge{display:none}.execution-header strong{font-size:9px}.run-summary{font-size:8px}.execution-steps{padding:4px 12px 12px}.execution-step{min-width:130px}.app-footer{font-size:7px}.app-footer button{font-size:7px}.modal{padding:28px 20px}.modal h2{font-size:22px}.cloud-connect-grid{grid-template-columns:repeat(2,1fr)}.deployment-overlay{padding:10px;inset:65px 0 32px}.deploy-card{padding:15px}.deploy-card h2{font-size:18px}.deploy-card .eyebrow{font-size:7px}.deploy-card p{font-size:10px}.deploy-check{font-size:9px}.deploy-icon{height:36px;width:36px;margin-bottom:12px}}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {resourcePlan} from './resources.mjs';
|
|
2
|
+
import {requirementNumber} from './derive.mjs';
|
|
3
|
+
import {engineeringSteps} from './handoff.mjs';
|
|
4
|
+
export function requirementExplanation(r){
|
|
5
|
+
if(r.rationale)return r.rationale;
|
|
6
|
+
const text=r.statement.toLowerCase();
|
|
7
|
+
if(/cit|source|evidence/.test(text))return 'Source references make an answer inspectable: the reader can check the supporting material, its relevance and whether it is current. The answer path must retain the link between retrieved evidence and the final response; an unsupported answer should be distinguishable from one backed by documents.';
|
|
8
|
+
if(/permission|access|authoriz|identity/.test(text))return 'This requirement defines who can see or change information. Enforcement belongs on the request and data-access paths, including retrieval and generated responses; a visual sign-in component alone does not establish that boundary.';
|
|
9
|
+
if(/delet|revok|update/.test(text))return 'Changes must propagate to the copies and indexes used by the application. Otherwise a user can receive stale information or retain access after the source has changed. Validation needs to exercise the change while requests are in flight as well as after synchronization.';
|
|
10
|
+
return `This requirement makes “${r.statement}” an outcome the implementation must demonstrate. Its linked components identify where that behavior belongs; acceptance should exercise the complete user-visible path rather than checking that those resources merely exist.`;
|
|
11
|
+
}
|
|
12
|
+
export function systemDescription(project){
|
|
13
|
+
const nodes=project.nodes.filter(n=>n.data.kind!=='note'),byId=new Map(nodes.map(n=>[n.id,n])),plan=resourcePlan(project);
|
|
14
|
+
const components=nodes.map(n=>{
|
|
15
|
+
const incoming=project.edges.filter(e=>e.target===n.id&&byId.has(e.source)),outgoing=project.edges.filter(e=>e.source===n.id&&byId.has(e.target));
|
|
16
|
+
const linked=(project.needs||[]).filter(r=>r.links?.includes(n.id));
|
|
17
|
+
const row=plan.rows.find(r=>r.id===n.id);
|
|
18
|
+
return {id:n.id,name:n.data.label,service:n.data.service||row?.service||n.data.provider,purpose:n.data.description?.trim()||`The diagram assigns ${n.data.label} a ${n.data.kind||'component'} role. Its specific responsibility needs to be established from the connected flows below.`,boundary:n.data.origin==='existing'&&n.data.deploymentMode==='create'?'Existing application source with a proposed new container deployment; retain source ownership and verify the supplied image.':n.data.origin==='existing'?'Existing system: integrate with its owner and access contract; the design does not authorize replacing it.':'Proposed component: its responsibility is part of the current design, with implementation and deployment still subject to validation.',inputs:incoming.map(e=>`${byId.get(e.source).data.label} → ${n.data.label}: ${e.label||'connection drawn without a payload description'}${e.access?` (access: ${e.access})`:''}${e.integration?` (integration: ${e.integration})`:''}`),outputs:outgoing.map(e=>`${n.data.label} → ${byId.get(e.target).data.label}: ${e.label||'connection drawn without a payload description'}${e.integration?` (integration: ${e.integration})`:''}`),requirements:linked.map(r=>`${requirementNumber(project,r.id)}: ${r.statement}`),placement:`${n.data.provider||'Provider undecided'} · region: ${n.data.config?.region||'not selected'} · classification: ${n.data.config?.classification||'not recorded'}`,implementation:row?.terraform||'Implementation has not been assessed.',engineering:row?engineeringSteps(row):[]};
|
|
19
|
+
});
|
|
20
|
+
const flows=project.edges.filter(e=>byId.has(e.source)&&byId.has(e.target)).map(e=>({id:e.id,title:`${byId.get(e.source).data.label} → ${byId.get(e.target).data.label}`,description:e.label||'The diagram records this connection but does not yet describe its payload or interaction.',contract:[e.integration&&`Integration contract: ${e.integration}.`,e.access&&`Access contract: ${e.access}.`].filter(Boolean).join(' ')||'Payload schema, authorization and failure handling need an explicit interface contract.',validation:'Verify the expected exchange, reject malformed or unauthorized input, and exercise interruption and recovery. Retry and duplicate-handling behavior must match the operation; it cannot be inferred from an arrow.'}));
|
|
21
|
+
return {overview:`The current design contains ${nodes.length} components and ${flows.length} connections. ${nodes.filter(n=>n.data.origin==='existing').length} components are existing systems. The descriptions below follow the actual board; implementation guidance is identified separately from recorded behavior.`,components,flows};
|
|
22
|
+
}
|
|
23
|
+
export function systemDescriptionMarkdown(project){
|
|
24
|
+
const d=systemDescription(project);
|
|
25
|
+
return `## How the system works\n\n${d.overview}\n\n${d.flows.map((f,i)=>`### Interaction ${i+1}: ${f.title}\n\n${f.description}\n\n${f.contract}\n\nProposed verification: ${f.validation}`).join('\n\n')}\n\n## Component responsibilities and design rationale\n\n${d.components.map(c=>`### ${c.name}\n\n${c.service} · ${c.placement}\n\n${c.purpose}\n\n${c.boundary}\n\nInputs: ${c.inputs.join('; ')||'No incoming connection is drawn.'}\n\nOutputs: ${c.outputs.join('; ')||'No outgoing connection is drawn.'}\n\nRequirement traceability: ${c.requirements.join('; ')||'No requirement has been explicitly linked to this component.'}\n\nImplementation status: ${c.implementation}\n\nImplementation considerations (guidance):\n${c.engineering.map(s=>'- '+s).join('\n')}`).join('\n\n')}`;
|
|
26
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import catalogue from '../agent-skills/generation-capabilities.json' with {type:'json'};
|
|
2
|
+
import {existingDeployment} from './deployment-ownership.mjs';
|
|
3
|
+
import {deliveryUnitName} from './terraform-composition.mjs';
|
|
4
|
+
import {relocateTerraformValues} from './terraform-expressions.mjs';
|
|
5
|
+
const equal=(a,b)=>JSON.stringify(a)===JSON.stringify(b);
|
|
6
|
+
const reference=name=>'${var.'+name+'}';
|
|
7
|
+
const gated=config=>config.variable?.review_approved?.type==='bool'&&config.variable.review_approved.default===false&&Array.isArray(config.resource?.terraform_data?.review_gate?.lifecycle?.precondition)&&config.resource.terraform_data.review_gate.lifecycle.precondition.some(p=>typeof p?.condition==='string'&&p.condition.includes('var.review_approved'));
|
|
8
|
+
// Admission derives ownership/paths from the project and reviewed catalogue, never
|
|
9
|
+
// from a module's self-reported manifest. It inspects only the emitted file map.
|
|
10
|
+
export function inspectTerraformModules(project,plan,files,root,add){
|
|
11
|
+
const configs=[{path:'',prefix:'',config:root}],scopes=new Map();
|
|
12
|
+
const eligible=project.nodes.filter(n=>n.data.kind!=='note'&&!existingDeployment(n)&&catalogue.contracts.some(c=>c.deliveryUnit&&(!c.deliveryUnit.proposedOnly||n.data.origin==='proposed')&&c.provider===n.data.provider&&c.service===n.data.service&&c.implementation===n.data.implementation));
|
|
13
|
+
const expected=eligible.length>=2?eligible.map(n=>({id:n.id,implementation:n.data.implementation,module:deliveryUnitName(n.id),path:'units/'+deliveryUnitName(n.id)})).sort((a,b)=>a.id<b.id?-1:a.id>b.id?1:0):[];
|
|
14
|
+
const active=plan.composition?.version===1&&expected.length>=2;
|
|
15
|
+
const allowedFiles=new Set(['main.tf.json']),allowedAliases=new Set(),expectedOutputs=new Set();
|
|
16
|
+
const fail=(text,id)=>add('unreviewed-module',text,id);
|
|
17
|
+
if(!active){if(Object.keys(root.module||{}).length)fail('Module dependencies require a reviewed project-owned capability; arbitrary module sources are not admitted.');}
|
|
18
|
+
else{
|
|
19
|
+
if(!equal(plan.composition.units,expected))fail('The claimed composition units do not match the current project and reviewed capability catalogue.');
|
|
20
|
+
if(!gated(root)||!root.resource.terraform_data.review_gate.lifecycle.precondition.some(p=>p.condition?.replace(/\s/g,'')==='${var.review_approved&&length(local.unfinished_work)==0}'))add('missing-work-gate','The composition must retain its default-deny approval and unfinished-scope gate.');
|
|
21
|
+
if(Object.keys(root.resource||{}).some(type=>type!=='terraform_data')||Object.keys(root.resource?.terraform_data||{}).some(name=>name!=='review_gate')||Object.keys(root.data||{}).length)fail('Root resources/data outside the composition gate require a separately reviewed adapter.');
|
|
22
|
+
for(const name of Object.keys(root.module||{}))if(!expected.some(u=>u.module===name))fail('An undeclared module was added to the composition: '+name+'.');
|
|
23
|
+
let manifest;try{manifest=JSON.parse(files['composition.json']);}catch{fail('The composition manifest is missing or malformed.');}
|
|
24
|
+
if(manifest&&(manifest.version!==1||!Array.isArray(manifest.units)||manifest.units.length!==expected.length))fail('The composition manifest has unexpected unit identities.');
|
|
25
|
+
for(const unit of expected){
|
|
26
|
+
const args=root.module?.[unit.module],path=unit.path+'/main.tf.json';allowedFiles.add(path);
|
|
27
|
+
const node=project.nodes.find(n=>n.id===unit.id),contract=catalogue.contracts.find(c=>c.deliveryUnit&&c.provider===node.data.provider&&c.service===node.data.service&&c.implementation===node.data.implementation);
|
|
28
|
+
for(const required of contract.deliveryUnit.requiredFiles||[])if(typeof files[unit.path+'/'+required]!=='string'||!files[unit.path+'/'+required].trim())fail('A required unit artifact or native test is missing: '+required+'.',unit.id);
|
|
29
|
+
if(!args||args.source!=='./'+unit.path){fail('The delivery unit must use its exact owned local source: '+unit.path+'.',unit.id);continue;}
|
|
30
|
+
let config;try{config=JSON.parse(files[path]);if(!config||Array.isArray(config)||typeof config!=='object')throw Error();}catch{fail('Missing or invalid owned module configuration: '+path+'.',unit.id);continue;}
|
|
31
|
+
configs.push({path:unit.path,prefix:'module.'+unit.module+'.',config,provider:project.nodes.find(n=>n.id===unit.id).data.provider});scopes.set(unit.id,{path:unit.path,prefix:'module.'+unit.module+'.',config,module:unit.module});
|
|
32
|
+
if(Object.keys(config.module||{}).length||Object.keys(config.provider||{}).length||config.terraform?.backend||config.terraform?.cloud)fail('A child cannot import another module, configure providers or select whole-composition state.',unit.id);
|
|
33
|
+
if(!gated(config))add('missing-work-gate','The delivery unit lost its default-deny approval gate.',unit.id);
|
|
34
|
+
if(!equal(args.depends_on,['terraform_data.review_gate']))add('missing-work-gate','Every delivery unit must depend on the complete composition gate.',unit.id);
|
|
35
|
+
const variables=config.variable||{},names=Object.fromEntries(Object.keys(variables).map(key=>[key,key==='review_approved'?'review_approved':unit.module+'__'+key]));
|
|
36
|
+
const allowedArgs=new Set(['source','providers','depends_on',...Object.keys(variables)]);
|
|
37
|
+
if(Object.keys(args).some(key=>!allowedArgs.has(key)))fail('A module has unreviewed arguments or instance expansion.',unit.id);
|
|
38
|
+
for(const [key,body]of Object.entries(variables)){
|
|
39
|
+
if(args[key]!==reference(names[key])||!root.variable?.[names[key]]){fail('A module input is absent or bound outside its owned current-context namespace: '+key+'.',unit.id);continue;}
|
|
40
|
+
if(key!=='review_approved'){
|
|
41
|
+
const expectedVariable=structuredClone(body);try{if(body.validation)expectedVariable.validation=relocateTerraformValues(body.validation,names);}catch{fail('A child input has an invalid variable-validation closure: '+key+'.',unit.id);continue;}
|
|
42
|
+
if(!equal(root.variable[names[key]],expectedVariable))fail('A root input changed its child type/default/sensitivity/validation contract: '+key+'.',unit.id);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const required=config.terraform?.required_providers||{};
|
|
46
|
+
if(Object.keys(args.providers||{}).some(key=>!Object.hasOwn(required,key)))fail('A provider binding is not declared by the unit.',unit.id);
|
|
47
|
+
for(const [local,definition]of Object.entries(required)){
|
|
48
|
+
const alias=local+'.'+unit.module,providers=root.provider?.[local],matches=(Array.isArray(providers)?providers:[providers]).filter(p=>p?.alias===unit.module);allowedAliases.add(alias);
|
|
49
|
+
if(args.providers?.[local]!==alias||matches.length!==1)fail('A unit is not bound to its exact declared provider configuration: '+local+'.',unit.id);
|
|
50
|
+
const rootDefinition=root.terraform?.required_providers?.[local];
|
|
51
|
+
if(!rootDefinition||String(rootDefinition.source).toLowerCase()!==String(definition.source).toLowerCase()||!String(rootDefinition.version||'').split(', ').includes(definition.version))fail('Root provider source/version does not retain the child requirement: '+local+'.',unit.id);
|
|
52
|
+
}
|
|
53
|
+
for(const [name,body]of Object.entries(config.output||{})){
|
|
54
|
+
const key=unit.module+'__'+name;expectedOutputs.add(key);const actual=root.output?.[key];
|
|
55
|
+
if(actual?.value!=='${module.'+unit.module+'.'+name+'}'||!!actual?.sensitive!==!!body.sensitive)fail('A published module output changed its unit identity or sensitivity: '+name+'.',unit.id);
|
|
56
|
+
}
|
|
57
|
+
for(const gap of Array.isArray(config.locals?.unfinished_work)?config.locals.unfinished_work:[])if(!Array.isArray(root.locals?.unfinished_work)||!root.locals.unfinished_work.includes(unit.module+':'+gap))add('missing-work-gate','Root unfinished scope omits a child blocker: '+gap+'.',unit.id);
|
|
58
|
+
const record=(Array.isArray(manifest?.units)?manifest.units:[]).find(r=>r?.id===unit.id);
|
|
59
|
+
if(!record||record.module!==unit.module||record.path!==unit.path||record.implementation!==unit.implementation||!equal(record.inputs,names)||!equal(record.providerConfigurations,args.providers)||!equal(record.outputs,Object.keys(config.output||{})))fail('Module metadata does not match the actual project-owned input/provider/output closure.',unit.id);
|
|
60
|
+
}
|
|
61
|
+
for(const [local,values]of Object.entries(root.provider||{}))for(const body of Array.isArray(values)?values:[values])if(!allowedAliases.has(local+'.'+body?.alias))fail('A root provider configuration is not owned by a declared delivery unit.');
|
|
62
|
+
for(const output of Object.keys(root.output||{}))if(!expectedOutputs.has(output))fail('An output outside the declared module closure was added.');
|
|
63
|
+
}
|
|
64
|
+
for(const path of Object.keys(files))if(/\.tf(?:\.json)?$/.test(path)&&!allowedFiles.has(path))fail('Additional Terraform configuration is outside the inspected module closure: '+path+'.');
|
|
65
|
+
return {configs,scopes,composed:active};
|
|
66
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {identityToken} from './identifiers.mjs';
|
|
2
|
+
import {relocateTerraformValues} from './terraform-expressions.mjs';
|
|
3
|
+
// Stable, injective identity. Labels, business domains and array order never dispatch.
|
|
4
|
+
export const deliveryUnitName=id=>{
|
|
5
|
+
if(typeof id!=='string'||!id||new TextEncoder().encode(id).length>512)throw Error('Use a nonempty delivery-unit identity within 512 UTF-8 bytes; no identity was clipped.');
|
|
6
|
+
return 'unit_'+identityToken(id);
|
|
7
|
+
};
|
|
8
|
+
const encode=value=>JSON.stringify(value,null,2)+'\n';
|
|
9
|
+
const expr=value=>'${'+value+'}';
|
|
10
|
+
function safePath(path){return typeof path==='string'&&path.length>0&&!path.startsWith('/')&&!path.includes('\\')&&!path.split('/').some(s=>!s||s==='.'||s==='..'||s==='.terraform'||s.includes('\0'));}
|
|
11
|
+
function locks(text=''){
|
|
12
|
+
const result=new Map();
|
|
13
|
+
for(const match of text.matchAll(/provider\s+"([^"]+)"\s*\{([^}]+)\}/g)){
|
|
14
|
+
const version=/\bversion\s*=\s*"([^"]+)"/.exec(match[2])?.[1];
|
|
15
|
+
if(!version)throw Error('A composed provider lock is missing its reviewed version.');
|
|
16
|
+
result.set(match[1].toLowerCase(),{version,text:match[0]});
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
// Adapter output enters through an explicit package contract. It is generated source,
|
|
21
|
+
// never client-supplied Terraform. Linking emits no account plan and invokes no tools.
|
|
22
|
+
export function composeTerraformUnits(project,units,{environment='staging',gaps=[]}={}){
|
|
23
|
+
if(!Array.isArray(units)||units.length<2||units.length>64)throw Error('Composition requires 2–64 explicitly compiled delivery units.');
|
|
24
|
+
const names=units.map(u=>deliveryUnitName(u.id));if(new Set(names).size!==names.length)throw Error('Duplicate delivery-unit identity; no files were overwritten.');
|
|
25
|
+
const root={terraform:{required_version:'>= 1.17.1, < 2.0.0',required_providers:{}},provider:{},variable:{review_approved:{type:'bool',default:false}},locals:{unfinished_work:[...gaps,...(environment==='production'?['composition-state-custody-and-operations-review-required']:[])]},resource:{terraform_data:{review_gate:{input:{design_revision:project.revision,environment},lifecycle:{precondition:[{condition:'${var.review_approved && length(local.unfinished_work) == 0}',error_message:'Review the whole composition and implement all preserved scope before planning. Production state and operations require separate acceptance.'}]}}}},module:{},output:{}};
|
|
26
|
+
const files={},examples={review_approved:false},manifest=[],providerLocks=new Map();
|
|
27
|
+
for(const unit of [...units].sort((a,b)=>a.id<b.id?-1:a.id>b.id?1:0)){
|
|
28
|
+
if(!unit.files||typeof unit.files['main.tf.json']!=='string')throw Error('A delivery unit must contain its generated main.tf.json.');
|
|
29
|
+
const name=deliveryUnitName(unit.id),path='units/'+name,config=JSON.parse(unit.files['main.tf.json']);
|
|
30
|
+
if(config.terraform?.required_version)root.terraform.required_version=[...new Set([...root.terraform.required_version.split(', '),...config.terraform.required_version.split(', ')])].join(', ');
|
|
31
|
+
if(config.module&&Object.keys(config.module).length)throw Error('Nested modules need a separately reviewed composition contract.');
|
|
32
|
+
if(config.terraform?.backend||config.terraform?.cloud)throw Error('A child delivery unit cannot choose whole-composition state custody. Compile its staging package before linking.');
|
|
33
|
+
if(!config.variable?.review_approved||config.variable.review_approved.type!=='bool'||config.variable.review_approved.default!==false||!config.resource?.terraform_data?.review_gate)throw Error('A delivery unit must preserve its default-deny review gate.');
|
|
34
|
+
if(config.locals?.unfinished_work?.length)root.locals.unfinished_work.push(...config.locals.unfinished_work.map(gap=>name+':'+gap));
|
|
35
|
+
const variableNames=Object.fromEntries(Object.keys(config.variable||{}).map(key=>[key,key==='review_approved'?'review_approved':name+'__'+key]));
|
|
36
|
+
const args={source:'./'+path,providers:{},depends_on:['terraform_data.review_gate']};
|
|
37
|
+
const inputs=JSON.parse(unit.files['deployment.tfvars.json.example']||'{}');
|
|
38
|
+
for(const [key,value]of Object.entries(config.variable||{})){
|
|
39
|
+
const target=variableNames[key];
|
|
40
|
+
if(key!=='review_approved'){
|
|
41
|
+
root.variable[target]=structuredClone(value);
|
|
42
|
+
if(value.validation)root.variable[target].validation=relocateTerraformValues(value.validation,variableNames);
|
|
43
|
+
if(Object.hasOwn(inputs,key))examples[target]=structuredClone(inputs[key]);
|
|
44
|
+
}
|
|
45
|
+
args[key]=expr('var.'+target);
|
|
46
|
+
}
|
|
47
|
+
const unitLocks=locks(unit.files['.terraform.lock.hcl']);
|
|
48
|
+
for(const [local,required]of Object.entries(config.terraform?.required_providers||{})){
|
|
49
|
+
if(!required.source||!required.version)throw Error('Each child provider needs its explicit reviewed source and version.');
|
|
50
|
+
const old=root.terraform.required_providers[local];
|
|
51
|
+
if(old&&old.source.toLowerCase()!==required.source.toLowerCase())throw Error('Conflicting provider source for '+local+'.');
|
|
52
|
+
const source='registry.terraform.io/'+required.source.toLowerCase(),lock=unitLocks.get(source);
|
|
53
|
+
if(!lock)throw Error('Missing reviewed provider lock for '+source+'.');
|
|
54
|
+
if(providerLocks.has(source)&&providerLocks.get(source).version!==lock.version)throw Error('Conflicting reviewed provider versions for '+source+': '+providerLocks.get(source).version+' and '+lock.version+'. Qualify compatible adapters before composing.');
|
|
55
|
+
providerLocks.set(source,lock);
|
|
56
|
+
root.terraform.required_providers[local]={source:required.source,version:[...new Set([...(old?.version.split(', ')||[]),required.version])].join(', ')};
|
|
57
|
+
const configurations=config.provider?.[local];
|
|
58
|
+
if(!configurations||Array.isArray(configurations))throw Error('The child provider configuration requires a reviewed single-instance adapter.');
|
|
59
|
+
if(configurations.alias||required.configuration_aliases)throw Error('Child provider aliases require an explicit reviewed mapping.');
|
|
60
|
+
const alias=name;
|
|
61
|
+
(root.provider[local]||=[]).push({...relocateTerraformValues(configurations,variableNames),alias});
|
|
62
|
+
args.providers[local]=local+'.'+alias;
|
|
63
|
+
}
|
|
64
|
+
if(Object.keys(config.provider||{}).some(key=>!config.terraform?.required_providers?.[key]))throw Error('Undeclared child provider configuration.');
|
|
65
|
+
delete config.provider;
|
|
66
|
+
for(const [output,definition]of Object.entries(config.output||{}))root.output[name+'__'+output]={value:expr('module.'+name+'.'+output),...(definition.sensitive?{sensitive:true}:{})};
|
|
67
|
+
root.module[name]=args;
|
|
68
|
+
for(const [file,body]of Object.entries(unit.files)){
|
|
69
|
+
if(!safePath(file)||typeof body!=='string')throw Error('Delivery-unit output contains an unsupported path or nontext file.');
|
|
70
|
+
if(/(^|\/)backend\.(hcl|tf)$|\.tfstate(?:\.|$)|\.tfplan$/.test(file))throw Error('State, private backend inputs and plans do not belong in composed source.');
|
|
71
|
+
if(file.endsWith('.tf')||file.endsWith('.tf.json')&&file!=='main.tf.json')throw Error('Additional Terraform configuration needs a reviewed module contract.');
|
|
72
|
+
files[path+'/'+file]=file==='main.tf.json'?encode(config):body;
|
|
73
|
+
}
|
|
74
|
+
manifest.push({id:unit.id,module:name,path,implementation:unit.implementation,inputs:variableNames,outputs:Object.keys(config.output||{}),providerConfigurations:args.providers,files:Object.keys(unit.files).sort()});
|
|
75
|
+
}
|
|
76
|
+
root.locals.unfinished_work=[...new Set(root.locals.unfinished_work)].sort();
|
|
77
|
+
files['main.tf.json']=encode(root);files['composition.json']=encode({version:1,units:manifest,boundary:'Generated module/input/provider/file links. Independent schema, runtime and destination checks remain required.'});
|
|
78
|
+
files['deployment.tfvars.json.example']=encode(examples);
|
|
79
|
+
files['.terraform.lock.hcl']='# Reviewed provider locks shared by all delivery units.\n\n'+[...providerLocks].sort(([a],[b])=>a.localeCompare(b)).map(([,lock])=>lock.text+'\n').join('\n');
|
|
80
|
+
files['.gitignore']='.terraform/\n*.tfstate*\n*.tfplan\n*.tfvars\n*.tfvars.json\n!deployment.tfvars.json.example\nbackend.hcl\n';
|
|
81
|
+
files['README.md']='# Composed delivery units\n\nRun Terraform from this root. Review composition.json and every unit README. Each unit retains its own application files, input validations, local tests, resource protections and review gate. Provider configurations and reviewed inputs are explicit at the root; source labels never choose a provider. Tests under a unit run independently from that unit directory with mock providers. Those tests are configuration checks, not application or cloud acceptance.\n\nReview all destination inputs and immutable images, then run terraform init -backend=false and terraform validate. Keep real values and credentials outside source. Do not apply this package without separate authorization and destination acceptance. Production remains blocked until whole-composition state custody and operating responsibilities are implemented and verified; a child cannot choose state for other units. Unsupported required integrations remain unfinished work.\n';
|
|
82
|
+
return files;
|
|
83
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Relocate variable traversals inside Terraform templates. Quoted literal text,
|
|
2
|
+
// escaped interpolation and object attributes are never treated as references.
|
|
3
|
+
export function relocateTerraformTemplate(source,names){
|
|
4
|
+
if(typeof source!=='string')return source;
|
|
5
|
+
let i=0;
|
|
6
|
+
function template(endQuote=false){
|
|
7
|
+
let out='';
|
|
8
|
+
while(i<source.length){
|
|
9
|
+
if(endQuote&&source[i]==='"'){out+=source[i++];return out;}
|
|
10
|
+
if(endQuote&&source[i]==='\\'){out+=source.slice(i,i+2);i+=2;continue;}
|
|
11
|
+
if(source.startsWith('$${',i)||source.startsWith('%%{',i)){out+=source.slice(i,i+3);i+=3;continue;}
|
|
12
|
+
if(source.startsWith('${',i)||source.startsWith('%{',i)){out+=source.slice(i,i+2);i+=2;out+=expression();continue;}
|
|
13
|
+
out+=source[i++];
|
|
14
|
+
}
|
|
15
|
+
if(endQuote)throw Error('Unterminated Terraform expression string.');
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
function expression(){
|
|
19
|
+
let out='',depth=0;
|
|
20
|
+
while(i<source.length){
|
|
21
|
+
const c=source[i];
|
|
22
|
+
if(source.startsWith('<<',i))throw Error('Heredoc relocation requires a reviewed expression adapter.');
|
|
23
|
+
if(c==='"'){out+=source[i++]+template(true);continue;}
|
|
24
|
+
if(c==='{'){depth++;out+=source[i++];continue;}
|
|
25
|
+
if(c==='}'){out+=source[i++];if(depth--===0)return out;continue;}
|
|
26
|
+
if(/[A-Za-z_]/.test(c)){
|
|
27
|
+
const start=i;while(i<source.length&&/[A-Za-z0-9_-]/.test(source[i]))i++;
|
|
28
|
+
const word=source.slice(start,i);
|
|
29
|
+
if(word==='var'&&!/[A-Za-z0-9_.-]/.test(source[start-1]||'')){
|
|
30
|
+
const match=/^(\s*\.\s*)([A-Za-z_][A-Za-z0-9_-]*)/.exec(source.slice(i));
|
|
31
|
+
if(match){if(!Object.hasOwn(names,match[2]))throw Error('Undeclared Terraform variable traversal: '+match[2]);out+='var'+match[1]+names[match[2]];i+=match[0].length;continue;}
|
|
32
|
+
}
|
|
33
|
+
out+=word;continue;
|
|
34
|
+
}
|
|
35
|
+
out+=source[i++];
|
|
36
|
+
}
|
|
37
|
+
throw Error('Unterminated Terraform template expression.');
|
|
38
|
+
}
|
|
39
|
+
return template();
|
|
40
|
+
}
|
|
41
|
+
export function relocateTerraformValues(value,names){
|
|
42
|
+
if(typeof value==='string')return relocateTerraformTemplate(value,names);
|
|
43
|
+
if(Array.isArray(value))return value.map(v=>relocateTerraformValues(v,names));
|
|
44
|
+
if(value&&typeof value==='object')return Object.fromEntries(Object.entries(value).map(([k,v])=>[k,relocateTerraformValues(v,names)]));
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// A root-only response from an older helper cannot certify a composed package.
|
|
2
|
+
export function moduleValidationCoverage(result,modules=[]){
|
|
3
|
+
if(result?.status!=='passed'||!modules.length)return result;
|
|
4
|
+
const checks=Array.isArray(result.checks)?result.checks:[];
|
|
5
|
+
const missing=[];
|
|
6
|
+
for(const unit of modules)for(const stage of ['init','validate','test']){
|
|
7
|
+
const check=checks.find(c=>c.moduleId===unit.id&&c.modulePath===unit.path&&c.stage===stage&&c.passed===true);
|
|
8
|
+
if(!check||stage==='test'&&(!Number.isSafeInteger(check.testsReported)||check.testsReported<1))missing.push({id:unit.id,path:unit.path,stage});
|
|
9
|
+
}
|
|
10
|
+
return missing.length?{...result,status:'incomplete',reportedStatus:result.status,missingModuleChecks:missing,message:'Root checks do not validate the complete composition: required delivery-unit schema or nonempty mock-test checks are missing.'}:result;
|
|
11
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {approvalApplicationFiles} from './approval-application-files.mjs';
|
|
2
|
+
import {compileCapabilityComposition} from './delivery-capabilities.mjs';
|
|
3
|
+
import {applyStateCustody} from './state-custody.mjs';
|
|
4
|
+
import {ownershipInventory} from './ownership-inspection.mjs';
|
|
5
|
+
import {inspectTerraformModules} from './terraform-admission.mjs';
|
|
6
|
+
import{hasInvoice}from'./invoice-plan.mjs';
|
|
7
|
+
import{invoiceTerraform}from'./invoice-terraform.mjs';
|
|
8
|
+
import {awsPublicFaqTerraform,azurePublicFaqTerraform,gcpPublicFaqTerraform,ibmPublicFaqTerraform} from './public-faq-clouds.mjs';
|
|
9
|
+
import {ibmStructuredTerraform} from './ibm-structured.mjs';
|
|
10
|
+
import {CLOUD_PROVIDER_LOCKS} from './cloud-provider-locks.mjs';
|
|
11
|
+
import {azureStructuredTerraform} from './azure-structured.mjs';
|
|
12
|
+
import {gcpStructuredTerraform} from './gcp-structured.mjs';
|
|
13
|
+
import {generationProviders} from './generation-provider.mjs';
|
|
14
|
+
import {compileStructuredApi} from './structured-api.mjs';
|
|
15
|
+
import {generationQuality} from './generation-quality.mjs';
|
|
16
|
+
import {hasContainers} from './container-contracts.mjs';
|
|
17
|
+
import {containerTerraform} from './container-terraform.mjs';
|
|
18
|
+
import {isAwsGeneration} from './generation-provider.mjs';
|
|
19
|
+
import {incompleteTerraform} from './incomplete-terraform.mjs';
|
|
20
|
+
import {existingReferenceFiles} from './existing-references.mjs';
|
|
21
|
+
import {hasCrossCloud,crossCloudTerraform} from './cross-cloud-storage.mjs';
|
|
22
|
+
import {compileWeb} from './rag-web.mjs';
|
|
23
|
+
import {sourceContractFiles} from './source-contracts.mjs';
|
|
24
|
+
import {compileRag} from './bounded-rag.mjs';
|
|
25
|
+
import {awsPreflightFiles} from './aws-preflight.mjs';
|
|
26
|
+
import {compileSummaries} from './bedrock-summary.mjs';
|
|
27
|
+
import { compileAsyncDocuments } from './async-documents.mjs';
|
|
28
|
+
import { pythonRuntimeFiles } from "./python-runtime.mjs";
|
|
29
|
+
// Pure draft generator. The separate validation service can initialize providers and validate; no cloud execution.
|
|
30
|
+
import { productionStateFiles } from "./production-state.mjs";
|
|
31
|
+
import { compileTextStatistics } from "./text-statistics.mjs";
|
|
32
|
+
import { storageTestFiles } from "./storage-tests.mjs";
|
|
33
|
+
import { resourcePlan } from "./resources.mjs";
|
|
34
|
+
import { blockers, review } from "./review.mjs";
|
|
35
|
+
|
|
36
|
+
function compileTerraform(project, environment = "staging") {
|
|
37
|
+
const composed=resourcePlan(project);
|
|
38
|
+
if(composed.composition)return compileCapabilityComposition(project,composed,resourcePlan,environment);
|
|
39
|
+
if(hasInvoice(project))return invoiceTerraform(project,resourcePlan(project),environment);
|
|
40
|
+
if(hasContainers(project))return containerTerraform(project,resourcePlan(project),environment);
|
|
41
|
+
if(hasCrossCloud(project))return crossCloudTerraform(project,environment);
|
|
42
|
+
const plan = resourcePlan(project);
|
|
43
|
+
if(plan.publicFaqPlan.entries.length){const provider=generationProviders(project).join(',');const adapter={aws:awsPublicFaqTerraform,azure:azurePublicFaqTerraform,gcp:gcpPublicFaqTerraform,ibm:ibmPublicFaqTerraform}[provider];if(adapter)return adapter(project,plan,environment);}
|
|
44
|
+
if(generationProviders(project).join(',')==='azure'&&plan.azureStructuredPlan.entries.length)return azureStructuredTerraform(project,plan,environment);
|
|
45
|
+
if(generationProviders(project).join(',')==='gcp'&&plan.gcpStructuredPlan.entries.length)return gcpStructuredTerraform(project,plan,environment);
|
|
46
|
+
if(generationProviders(project).join(',')==='ibm'&&plan.ibmStructuredPlan.entries.length)return ibmStructuredTerraform(project,plan,environment);
|
|
47
|
+
if(!isAwsGeneration(project))return incompleteTerraform(project,plan,environment);
|
|
48
|
+
const env = ["production","qualification"].includes(environment) ? environment : "staging";
|
|
49
|
+
const resources = { terraform_data: { review_gate: {
|
|
50
|
+
input: { design_revision: project.revision, environment: env },
|
|
51
|
+
lifecycle: { precondition: [{ condition: "${var.review_approved && length(local.unfinished_work) == 0}", error_message: "Review this sketch and finish the work listed in resource-schedule.json before a deployment plan can succeed." }] },
|
|
52
|
+
} } };
|
|
53
|
+
const bindingVariables={};
|
|
54
|
+
for(const binding of plan.accessPlan.bindings){
|
|
55
|
+
const variable=binding.variable;
|
|
56
|
+
bindingVariables[variable]={type:'string',nullable:false,description:`Existing IAM role or user ARN for input ${variable}. The referenced identity is not modified; API access contracts create a separate scoped caller role.`,validation:{condition:`\${can(regex("^arn:aws:iam::[0-9]{12}:(role|user)/[A-Za-z0-9_+=,.@/-]+$", var.${variable}))}`,error_message:'Supply an IAM role/user ARN, not a root, session, wildcard or service principal.'}};
|
|
57
|
+
resources.terraform_data.review_gate.lifecycle.precondition.push({condition:`\${can(regex(format("^arn:aws:iam::%s:(role|user)/", var.aws_account_id), var.${variable}))}`,error_message:'The bound IAM principal must belong to the selected AWS account.'});
|
|
58
|
+
}
|
|
59
|
+
const add = (type, name, body) => { (resources[type] ||= {})[name] = { ...body, depends_on: ["terraform_data.review_gate"] }; };
|
|
60
|
+
for (const r of plan.rows.filter((r) => r.foundation)) {
|
|
61
|
+
const name = r.address;
|
|
62
|
+
const bucket = `\${aws_s3_bucket.${name}.id}`;
|
|
63
|
+
add("aws_s3_bucket", name, { bucket_prefix: `atforma-${name.slice(0, 24).replaceAll("_", "-")}-`, force_destroy: false, lifecycle: { prevent_destroy: env !== "qualification" }, tags: { ManagedBy: "AtMem AI Architect", Environment: env } });
|
|
64
|
+
add("aws_s3_bucket_public_access_block", name, { bucket, block_public_acls: true, block_public_policy: true, ignore_public_acls: true, restrict_public_buckets: true });
|
|
65
|
+
add("aws_s3_bucket_server_side_encryption_configuration", name, { bucket, rule: { apply_server_side_encryption_by_default: { sse_algorithm: "AES256" } } });
|
|
66
|
+
add("aws_s3_bucket_versioning", name, { bucket, versioning_configuration: { status: "Enabled" } });
|
|
67
|
+
add("aws_s3_bucket_ownership_controls", name, { bucket, rule: { object_ownership: "BucketOwnerEnforced" } });
|
|
68
|
+
const grants=plan.accessPlan.connections.filter(edge=>edge.target===r.id).flatMap((edge,index)=>{
|
|
69
|
+
const Principal={AWS:`\${var.${edge.principalVariable}}`};
|
|
70
|
+
return [
|
|
71
|
+
{Sid:`OperatorBucket${index}`,Effect:'Allow',Principal,Action:['s3:ListBucket','s3:GetBucketLocation'],Resource:`\${aws_s3_bucket.${name}.arn}`},
|
|
72
|
+
{Sid:`OperatorObjects${index}`,Effect:'Allow',Principal,Action:['s3:GetObject','s3:GetObjectVersion',...(edge.access==='s3-read-write'?['s3:PutObject']:[])],Resource:`\${aws_s3_bucket.${name}.arn}/*`},
|
|
73
|
+
];
|
|
74
|
+
});
|
|
75
|
+
add("aws_s3_bucket_policy", name, { bucket, policy: JSON.stringify({ Version: "2012-10-17", Statement: [{ Sid: "DenyInsecureTransport", Effect: "Deny", Principal: "*", Action: "s3:*", Resource: [`\${aws_s3_bucket.${name}.arn}`, `\${aws_s3_bucket.${name}.arn}/*`], Condition: { Bool: { "aws:SecureTransport": "false", "aws:PrincipalIsAWSService": "false" } } },...grants] }) });
|
|
76
|
+
}
|
|
77
|
+
const compute=compileTextStatistics(plan,env);
|
|
78
|
+
const async=compileAsyncDocuments(plan,env);
|
|
79
|
+
const summaries=compileSummaries(plan,env);
|
|
80
|
+
const rag=compileRag(plan,env);
|
|
81
|
+
const structuredApi=compileStructuredApi(plan,env);
|
|
82
|
+
for(const [type,items]of Object.entries(rag.resources))resources[type]={...(resources[type]||{}),...items};
|
|
83
|
+
for(const [type,items]of Object.entries(summaries.resources))resources[type]={...(resources[type]||{}),...items};
|
|
84
|
+
for(const [type,items]of Object.entries(async.resources))resources[type]={...(resources[type]||{}),...items};
|
|
85
|
+
resources.terraform_data.review_gate.lifecycle.precondition.push(...async.preconditions);
|
|
86
|
+
for(const [type,items]of Object.entries(compute.resources))resources[type]={...(resources[type]||{}),...items};
|
|
87
|
+
for(const [type,items]of Object.entries(structuredApi.resources))resources[type]={...(resources[type]||{}),...items};
|
|
88
|
+
const web=compileWeb(plan,env,resources);
|
|
89
|
+
// Untrusted diagram text is never interpolated into Terraform expressions or resource names.
|
|
90
|
+
const unfinished = [...(env==='production'?plan.sourceContractPlan.sources.map(s=>`native-vendor-integration-unverified-${s.address}`):[]),...plan.gapDetails.map(g => g.id), ...blockers(review(project)).map((_, i) => `architecture review blocker ${i + 1}`)];
|
|
91
|
+
const config = {
|
|
92
|
+
terraform: { required_version: env === "production" ? ">= 1.10.0, < 2.0.0" : ">= 1.5.0, < 2.0.0", ...(env === "production" ? {backend:{s3:{encrypt:true,use_lockfile:true}}}:{}), required_providers: { aws: { source: "hashicorp/aws", version: "~> 6.0" } } },
|
|
93
|
+
variable: {
|
|
94
|
+
...bindingVariables,
|
|
95
|
+
...compute.variables, ...async.variables, ...summaries.variables, ...rag.variables,
|
|
96
|
+
aws_region: { type: "string", description: "Explicitly choose the reviewed deployment region; no region is assumed." },
|
|
97
|
+
aws_account_id: { type: "string", description: "Expected AWS account ID; provider refuses a different account.", validation: { condition: '${can(regex("^[0-9]{12}$", var.aws_account_id))}', error_message: "Use a twelve digit AWS account ID." } },
|
|
98
|
+
review_approved: { type: "bool", default: false, description: "Set only after reviewing the design, cost, security and generated Terraform." },
|
|
99
|
+
},
|
|
100
|
+
provider: { aws: { region: "${var.aws_region}", allowed_account_ids: ["${var.aws_account_id}"] } },
|
|
101
|
+
locals: { unfinished_work: unfinished }, resource: resources,
|
|
102
|
+
...((Object.keys(compute.data).length||Object.keys(web.data).length)?{data:{...compute.data,...web.data}}:{}),
|
|
103
|
+
output: { ...compute.outputs, ...async.outputs, ...summaries.outputs, ...rag.outputs, ...web.outputs, design_revision: { value: project.revision }, environment: { value: env }, implemented_foundations: { value: plan.rows.filter((r) => r.foundation).length }, buckets: { value: Object.fromEntries(plan.rows.filter(r=>r.foundation).map(r=>[r.address,{name:`${"${"}aws_s3_bucket.${r.address}.id}`,arn:`${"${"}aws_s3_bucket.${r.address}.arn}`}])) } },
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
...awsPreflightFiles(),
|
|
107
|
+
'.terraform.lock.hcl':CLOUD_PROVIDER_LOCKS.aws,
|
|
108
|
+
...existingReferenceFiles(project),
|
|
109
|
+
'.gitignore':'.terraform/\nbuild/\n*.tfstate\n*.tfstate.*\n*.tfplan\nreviewed-plan.json\n.preflight/\n*.tfvars\n*.tfvars.json\n*.zip\n__pycache__/\n',
|
|
110
|
+
...(env === "production" ? productionStateFiles():{}),
|
|
111
|
+
...storageTestFiles(plan),
|
|
112
|
+
...sourceContractFiles(plan),
|
|
113
|
+
...compute.files, ...async.files, ...summaries.files, ...rag.files, ...structuredApi.files, ...web.files,
|
|
114
|
+
...pythonRuntimeFiles([...plan.rows.filter(r=>r.compute).map(r=>({address:r.address,sdk:false})),...async.builds,...summaries.builds,...rag.builds]),
|
|
115
|
+
"main.tf.json": JSON.stringify(config, null, 2),
|
|
116
|
+
"resource-schedule.json": JSON.stringify(plan, null, 2),
|
|
117
|
+
"README.md": `# AtMem AI Architect Terraform sketch\n\nRevision r${project.revision}; ${env}.\n\nExplicit proposed S3 blocks get native resources: a bucket, public-access block, encryption, versioning, bucket-owner-enforced ownership and a deny-insecure-transport policy. Buckets prevent destruction. Typed existing IAM-principal bindings and direct s3-read/s3-read-write edges generate required same-account role/user ARN inputs and bucket-scoped access policies. Labels alone never grant permissions. Read includes versioned reads; write adds PutObject, not deletion. Existing identities are not created or changed. Other IAM, model access, application code, network policy, services and integrations still require engineering work. Typed text-statistics Lambda components additionally generate a tested handler, execution role, scoped logging, published version, live alias and caller permission. Other workloads remain incomplete; this is not a complete deployable application unless all required implementations and integrations are covered and verified.\n\nDefault review gate prevents a successful plan; unresolved work also blocks it. Review resource-schedule.json and the architecture document, implement missing generator capabilities and regenerate from the project; do not hand-clear unfinished_work to make a plan pass.\n\nFor production, read STATE.md and supply the reviewed S3 backend configuration; local state is for staging validation only. Build packaged runtimes first using python3 build-runtime.py when present (see BUILD.md). Run the shipped Python application tests where present: python3 -m unittest discover -s app-tests -p '*_test.py'. Run terraform test for generated mock-provider checks.\n\nOn your own machine: install Terraform, review provider version constraints, then run terraform init -backend=false and terraform validate. Retain the generated provider lock file. Configure the standard AWS credential chain separately; never put credentials in project files. Supply aws_region, aws_account_id and every generated principal_*_arn input to terraform plan. Principal ARNs must identify existing roles/users in that account; root/session/wildcard and cross-account bindings are rejected. Review inherited IAM permissions, permission boundaries and organization policies separately: a scoped bucket grant does not revoke permissions granted elsewhere. Run the read-only account prerequisites in PREFLIGHT.md. Review the actual account, region, changes, costs and permissions before approving any apply. This export carries no validation evidence. Use AtMem AI Architect’s Terraform → Test Terraform view to run local schema checks. No cloud plan or deployment has been validated.\n\nReview AES256 versus customer-managed keys, retention, version-storage cost, access policies and state storage against your requirements. The diagram's region is documented but not silently used; aws_region must be deliberately supplied.\n\nReferences:\n- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket\n- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block\n- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration\n- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning\n- https://developer.hashicorp.com/terraform/language/resources/terraform-data\n`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function terraformSketch(project,environment="staging"){
|
|
122
|
+
const files=applyStateCustody(project,environment,{...compileTerraform(project,environment),...existingReferenceFiles(project)});
|
|
123
|
+
Object.assign(files,approvalApplicationFiles(project,environment));
|
|
124
|
+
const root=JSON.parse(files['main.tf.json']);
|
|
125
|
+
files['deployment-ownership.json']=JSON.stringify(ownershipInventory(project,inspectTerraformModules(project,resourcePlan(project),files,root,()=>{}).configs),null,2)+'\n';
|
|
126
|
+
files['generation-quality.json']=JSON.stringify(generationQuality(project,resourcePlan(project),files,{environment}),null,2);
|
|
127
|
+
return files;
|
|
128
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// A bounded implementation selected explicitly in the project, never inferred from labels.
|
|
2
|
+
export const TEXT_STATISTICS_HANDLER = `"""Authenticated direct invocation; no document text is logged."""
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
MAX_INPUT_BYTES = int(os.environ.get("MAX_INPUT_BYTES", "65536"))
|
|
6
|
+
|
|
7
|
+
def handler(event, context):
|
|
8
|
+
if not isinstance(event, dict) or set(event) != {"text"} or not isinstance(event["text"], str):
|
|
9
|
+
raise ValueError("Expected an object containing only a text string")
|
|
10
|
+
text = event["text"]
|
|
11
|
+
try:
|
|
12
|
+
size = len(text.encode("utf-8"))
|
|
13
|
+
except UnicodeEncodeError:
|
|
14
|
+
raise ValueError("Text must be valid Unicode") from None
|
|
15
|
+
if size > MAX_INPUT_BYTES:
|
|
16
|
+
raise ValueError("Text exceeds the configured byte limit")
|
|
17
|
+
return {"wordCount": len(text.split()), "characterCount": len(text)}
|
|
18
|
+
`;
|
|
19
|
+
export function textStatisticsArtifacts(rows) {
|
|
20
|
+
const files={};
|
|
21
|
+
for(const row of rows.filter(r=>r.compute)){
|
|
22
|
+
files[`handlers/${row.address}.py`]=TEXT_STATISTICS_HANDLER;
|
|
23
|
+
files[`app-tests/${row.address}_test.py`]=`import importlib.util\nimport os\nimport unittest\nfrom pathlib import Path\nfrom contextlib import redirect_stdout\nfrom io import StringIO\nos.environ["MAX_INPUT_BYTES"]="${row.settings?.maxInputBytes??65536}"\nspec=importlib.util.spec_from_file_location("handler",Path(__file__).resolve().parents[1]/"build/${row.address}/${row.address}.py")\nmodule=importlib.util.module_from_spec(spec)\nspec.loader.exec_module(module)\nclass HandlerTests(unittest.TestCase):\n def test_counts(self):\n self.assertEqual(module.handler({"text":"one two\\nthree"},None),{"wordCount":3,"characterCount":13})\n self.assertEqual(module.handler({"text":""},None),{"wordCount":0,"characterCount":0})\n self.assertEqual(module.handler({"text":"café 世界"},None),{"wordCount":2,"characterCount":7})\n def test_invalid_and_oversized(self):\n for value in [None,[],{}, {"text":1}, {"text":"secret","unexpected":True}, {"text":"é"*(module.MAX_INPUT_BYTES//2+1)}]:\n with self.assertRaises(ValueError):module.handler(value,None)\n def test_document_not_logged(self):\n output=StringIO()\n with redirect_stdout(output):module.handler({"text":"private synthetic document"},None)\n self.assertEqual(output.getvalue(),"")\n`;
|
|
24
|
+
}
|
|
25
|
+
return files;
|
|
26
|
+
}
|
|
27
|
+
export function compileTextStatistics(plan, environment="staging") {
|
|
28
|
+
const resources={},data={},variables={},outputs={};
|
|
29
|
+
const add=(type,name,body)=>(resources[type]||={})[name]={...body,depends_on:[...(body.depends_on||[]),'terraform_data.review_gate']};
|
|
30
|
+
for(const row of plan.rows.filter(r=>r.compute)){
|
|
31
|
+
const tags={ManagedBy:"AtMem AI Architect",Environment:environment,Component:row.address};
|
|
32
|
+
const n=row.address,ref=(type,field)=>`\u0024{${type}.${n}.${field}}`;
|
|
33
|
+
const number=(suffix,defaultValue,min,max)=>{const name=`${n}_${suffix}`;variables[name]={type:'number',default:defaultValue,description:`Reviewable ${suffix} for ${n}; confirm workload requirements.`,validation:{condition:`\u0024{var.${name} >= ${min} && var.${name} <= ${max} && floor(var.${name}) == var.${name}}`,error_message:`Use an integer from ${min} to ${max}.`}};return `\u0024{var.${name}}`;};
|
|
34
|
+
const memory=number('memory_mb',row.settings?.memoryMb??128,128,10240),timeout=number('timeout_seconds',row.settings?.functionTimeoutSeconds??10,1,900),concurrency=number('concurrency',row.settings?.concurrency??1,1,1000),maxBytes=number('max_input_bytes',row.settings?.maxInputBytes??65536,1,1000000);
|
|
35
|
+
const retentionName=`${n}_log_retention_days`;
|
|
36
|
+
variables[retentionName]={type:'number',default:row.settings?.logRetentionDays??30,description:'Review log retention against policy; default is not a compliance claim.',validation:{condition:`\u0024{contains([1,3,5,7,14,30,60,90,120,150,180,365,400,545,731,1096,1827,2192,2557,2922,3288,3653], var.${retentionName})}`,error_message:'Choose a supported finite CloudWatch log retention period.'}};
|
|
37
|
+
const versionName=`${n}_release_version`;variables[versionName]={type:'string',default:null,description:'Null deploys the newly published version. A prior numeric version explicitly rolls the live alias back.',validation:{condition:`\u0024{var.${versionName} == null ? true : can(regex("^[1-9][0-9]*$", var.${versionName}))}`,error_message:'Use a published numeric version, or null for the current release.'}};
|
|
38
|
+
add('aws_iam_role',n,{tags,name_prefix:`atmem-${n.slice(0,24)}-`,assume_role_policy:JSON.stringify({Version:'2012-10-17',Statement:[{Effect:'Allow',Principal:{Service:'lambda.amazonaws.com'},Action:'sts:AssumeRole'}]})});
|
|
39
|
+
add('aws_cloudwatch_log_group',n,{tags,name:`/aws/lambda/${ref('aws_iam_role','name')}`,retention_in_days:`\u0024{var.${retentionName}}`});
|
|
40
|
+
add('aws_iam_role_policy',n,{role:ref('aws_iam_role','id'),policy:JSON.stringify({Version:'2012-10-17',Statement:[{Effect:'Allow',Action:['logs:CreateLogStream','logs:PutLogEvents'],Resource:`${ref('aws_cloudwatch_log_group','arn')}:*`}]})});
|
|
41
|
+
|
|
42
|
+
add('aws_lambda_function',n,{tags,function_name:ref('aws_iam_role','name'),role:ref('aws_iam_role','arn'),runtime:'python3.13',architectures:['arm64'],handler:`${n}.handler`,filename:`\u0024{path.module}/build/${n}.zip`,source_code_hash:`\u0024{filebase64sha256(format("%s/build/${n}.zip", path.module))}`,memory_size:memory,timeout,reserved_concurrent_executions:concurrency,publish:true,environment:{variables:{MAX_INPUT_BYTES:`\u0024{tostring(var.${n}_max_input_bytes)}`}},depends_on:[`aws_iam_role_policy.${n}`,`aws_cloudwatch_log_group.${n}`]});
|
|
43
|
+
add('aws_lambda_alias',n,{name:'live',function_name:ref('aws_lambda_function','function_name'),function_version:`\u0024{var.${versionName} == null ? aws_lambda_function.${n}.version : var.${versionName}}`});
|
|
44
|
+
for(const edge of plan.accessPlan.connections.filter(e=>e.target===row.id&&e.access==='lambda-invoke')){
|
|
45
|
+
const id=`invoke_${Array.from(new TextEncoder().encode(edge.id),b=>b.toString(16).padStart(2,'0')).join('')}`;
|
|
46
|
+
add('aws_lambda_permission',id,{action:'lambda:InvokeFunction',function_name:ref('aws_lambda_function','function_name'),qualifier:ref('aws_lambda_alias','name'),principal:`\u0024{var.${edge.principalVariable}}`});
|
|
47
|
+
}
|
|
48
|
+
outputs[n]={value:{invoke_arn:ref('aws_lambda_alias','arn'),published_version:ref('aws_lambda_function','version'),log_group:ref('aws_cloudwatch_log_group','name')}};
|
|
49
|
+
}
|
|
50
|
+
return {resources,data,variables,outputs,files:textStatisticsArtifacts(plan.rows)};
|
|
51
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Derived from OpenArchFlow. Copyright (c) 2026 Rafael Sales. MIT.
|
|
2
|
+
// AtForma: type-only imports and rendering-independent integration; see THIRD_PARTY_NOTICES.md.
|
|
3
|
+
function deepParseJson(value: unknown): unknown {
|
|
4
|
+
if (typeof value === "string") {
|
|
5
|
+
const t = value.trim();
|
|
6
|
+
if ((t.startsWith("{") || t.startsWith("[")) && t.length > 1) {
|
|
7
|
+
try { return deepParseJson(JSON.parse(t)); } catch { /* not JSON */ }
|
|
8
|
+
}
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) return value.map(deepParseJson);
|
|
12
|
+
if (value !== null && typeof value === "object") {
|
|
13
|
+
return Object.fromEntries(
|
|
14
|
+
Object.entries(value as Record<string, unknown>).map(([k, v]) => [k, deepParseJson(v)]),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function prettyPayload(value: unknown): string {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.stringify(deepParseJson(value), null, 2);
|
|
23
|
+
} catch {
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
}
|