@atmem/architect 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONSTITUTION.md +144 -0
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +31 -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-BTLHaa3J.css +1 -0
- package/dist/assets/index-CQ2C1gKF.js +21338 -0
- package/dist/assets/index.es-DTyqIdaO.js +18 -0
- package/dist/assets/purify.es-CAi2OJFm.js +3 -0
- package/dist/assets/visual-export-Ox0j5xj5.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/atmem-APACHE-2.0.txt +202 -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/atmem-APACHE-2.0.txt +202 -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 +193 -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 +221 -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 +172 -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 +483 -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 +185 -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_context.py +58 -0
- package/server/memory_routes.py +141 -0
- package/server/memory_service.py +306 -0
- package/server/memory_worker.py +129 -0
- package/server/openai_voice.py +80 -0
- package/server/orca_handoff.py +90 -0
- package/server/packaged_app.py +9 -0
- package/server/pricing.py +190 -0
- package/server/process_control_observation.py +31 -0
- package/server/prompts.py +136 -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/server/windows_snapshot.py +119 -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 +39 -0
- package/src/ArtifactChecks.jsx +49 -0
- package/src/BlockRequirements.jsx +13 -0
- package/src/BrainActivity.jsx +11 -0
- package/src/ChatMessage.jsx +29 -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 +387 -0
- package/src/EngineeringPanel.jsx +151 -0
- package/src/FileTree.jsx +7 -0
- package/src/FinalChecklist.jsx +15 -0
- package/src/HelpNote.jsx +3 -0
- package/src/ImplementationDelivery.jsx +91 -0
- package/src/JourneyViews.jsx +403 -0
- package/src/LocationChoices.jsx +12 -0
- package/src/MemoryPanel.jsx +58 -0
- package/src/OpenAIVoicePanel.jsx +66 -0
- package/src/OutputsView.jsx +71 -0
- package/src/ProjectChecks.jsx +44 -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 +18 -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 +190 -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/final-package.mjs +34 -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 +106 -0
- package/src/journey.mjs +97 -0
- package/src/levels.mjs +145 -0
- package/src/local-agent.mjs +184 -0
- package/src/main.jsx +2481 -0
- package/src/model-policy-preflight.mjs +34 -0
- package/src/openai-voice.mjs +102 -0
- package/src/output-bundle.mjs +13 -0
- package/src/ownership-inspection.mjs +28 -0
- package/src/pack.mjs +201 -0
- package/src/pricing.mjs +85 -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 +37 -0
- package/src/useEmulation.jsx +36 -0
- package/src/useEngineeringStatus.mjs +17 -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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {resourcePlan} from './resources.mjs';
|
|
2
|
+
import {containerContract} from './container-contracts.mjs';
|
|
3
|
+
|
|
4
|
+
// These are next-step explanations, not evidence of implemented or tested behavior.
|
|
5
|
+
export function emulationGuidance(project,report,{beforeRun=false}={}){
|
|
6
|
+
if(!report||(report.checks||[]).length)return null;
|
|
7
|
+
let plan;try{if(project)plan=resourcePlan(project);}catch{}
|
|
8
|
+
const base={title:'Testing couldn’t start',reason:'The local test setup is incomplete.',next:'Prepare a repair task so the missing implementation can be fixed.',owner:'engineering',needsModel:false,componentId:null};
|
|
9
|
+
if(report.status==='unavailable')return {...base,reason:'The local test runner is not available.',next:'Enable the test runner, then try again.'};
|
|
10
|
+
if(report.status==='failed')return {...base,reason:'The test runner stopped before it could check your application.',next:'Use the report to fix the test runner, then try again.'};
|
|
11
|
+
if(plan&&!plan.rows.length)return {...base,reason:'There is no system drawn yet.',next:'Describe what you want to build so Architect can draw a first design.',owner:'user'};
|
|
12
|
+
const browserIds=new Set((project?.nodes||[]).filter(n=>n.data.origin==='existing'&&n.data.provider==='tools'&&!n.data.binding&&/\bbrowser\b/i.test(n.data.label)).map(n=>n.id));
|
|
13
|
+
const missing=plan?.rows.find(r=>!r.implemented&&!browserIds.has(r.id));
|
|
14
|
+
if(missing)return {...base,title:'Application code is needed before testing',reason:`${missing.label} is in your design, but runnable application code is not available for this test.`,next:'Choose “Implement and check”. Architect will generate code and connections, attempt local tests, and show results in Implementation. Nothing is deployed to the cloud.',action:'build-and-test',componentId:missing.id};
|
|
15
|
+
if(browserIds.size)return {...base,reason:'The browser needs to be marked as an external client.',next:'Architect can propose that small correction, then try the test again.',owner:'architect',needsModel:true,componentId:[...browserIds][0]};
|
|
16
|
+
const access=plan?.gapDetails.find(g=>g.id.startsWith('access-'));
|
|
17
|
+
if(access)return {...base,reason:'An access rule is missing.',next:'Confirm who should be allowed to use this block; Architect can add the rule.',owner:'user',needsModel:true,componentId:access.componentIds?.[0]};
|
|
18
|
+
const connection=plan?.connections.find(e=>!e.generated);
|
|
19
|
+
if(connection)return {...base,reason:'The connections between the application’s parts are not implemented yet.',next:'Choose “Implement and check” to generate its connections and run local checks.',action:'build-and-test',componentId:connection.source};
|
|
20
|
+
const container=project?.nodes.find(containerContract);
|
|
21
|
+
if(container&&!beforeRun)return {...base,reason:`${container.data.label} has infrastructure defined, but no supported local application test yet.`,next:'Choose “Implement and check” to use its supplied image and run local checks.',action:'build-and-test',componentId:container.id};
|
|
22
|
+
return beforeRun?null:base;
|
|
23
|
+
}
|
|
24
|
+
export function guidanceMessage(guidance){
|
|
25
|
+
return `**${guidance.title}.**\n\n${guidance.reason}\n\n**Next:** ${guidance.next}`;
|
|
26
|
+
}
|
|
27
|
+
export function emulationRepairTask(project,report){
|
|
28
|
+
const guidance=report?emulationGuidance(project,report):emulationPreflight(project);
|
|
29
|
+
report=report||{};
|
|
30
|
+
return `# Architect repair task\n\n${guidance?.reason||'Investigate the test failure.'}\n\n## Next engineering step\n\n${guidance?.next||'Reproduce the failure and repair its cause.'}\n\n## Reproduce\n\n- Design: ${project.name}\n- Saved design version (internal): ${report.designRevision??project.revision}\n- Artifact: ${report.artifactDigest||'not available'}\n- Checks executed: ${(report.checks||[]).length}\n- Affected component: ${guidance?.componentId||'test runner / implementation pipeline'}\n\n## Preserve\n\nKeep all requested scope, existing systems, component IDs, connections and access boundaries. Fix the generator or test runner, not an exported copy. Do not clear gates or report success without executing the relevant checks.\n\n## Reported blockers\n\n${(report.blockers||[]).map(b=>'- '+String(b)).join('\n')||'- No structured blocker was returned; inspect the full report.'}\n\n## Acceptance\n\nRegenerate from this project, execute the affected tests and regression checks, and retain a fresh report tied to the generated files. Separate local checks from cloud plans and deployment. This task is a work request, not evidence of a completed fix.\n`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Readiness guidance is not a test report and must never be stored as evidence.
|
|
34
|
+
export function emulationPreflight(project){
|
|
35
|
+
const guidance=emulationGuidance(project,{status:'blocked',checks:[]},{beforeRun:true});
|
|
36
|
+
return guidance?.action==='build-and-test'?guidance:null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import topicsFile from "../agent-skills/discovery/topics.json" with { type: "json" };
|
|
2
|
+
import { cleanAssumption, cleanQuestion, cleanRequirement } from "./records.mjs";
|
|
3
|
+
// The consultant's engagement record: brief, cloud options, decisions.
|
|
4
|
+
// Kept beside the design so every deliverable traces back to the board.
|
|
5
|
+
|
|
6
|
+
export const CRITERIA = [
|
|
7
|
+
{ id: "cost", label: "Cost", hint: "Build and run cost, licence fit, cost controls" },
|
|
8
|
+
{ id: "risk", label: "Risk", hint: "Delivery risk, skills, lock-in, vendor maturity" },
|
|
9
|
+
{ id: "scalability", label: "Scalability", hint: "Fleet size, latency, throughput, regions" },
|
|
10
|
+
{ id: "security", label: "Security", hint: "Identity, data residency, guardrails, compliance" },
|
|
11
|
+
];
|
|
12
|
+
export const OPTIONS = ["current", "aws", "azure", "gcp", "ibm"];
|
|
13
|
+
export const STATUSES = ["Proposed", "Accepted", "Superseded"];
|
|
14
|
+
// Discovery topics come from the agent skill, so the planner, UI and outputs agree.
|
|
15
|
+
export const BRIEF_FIELDS = [...topicsFile.topics]
|
|
16
|
+
.sort((x, y) => x.order - y.order)
|
|
17
|
+
.map((t) => ({ id: t.id, label: t.label, question: t.question, beginner: t.beginner, essential: t.essential }));
|
|
18
|
+
export const VERDICTS = ["Pursue", "Park", "Rule out"];
|
|
19
|
+
export const LEVELS = ["High", "Medium", "Low"];
|
|
20
|
+
export const GATE = [
|
|
21
|
+
{ id: "adoption", label: "Adoption evidence", hint: "Who used the prototype, and what changed?" },
|
|
22
|
+
{ id: "economics", label: "Value vs cost", hint: "Measured value against run and build cost" },
|
|
23
|
+
{ id: "risks", label: "Residual risk", hint: "Open risks and who accepted them" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const text = (value, max = 2000) => {const result=String(value??"");if(result.length>max)throw Error(`A project field exceeds ${max.toLocaleString('en')} characters. Split it into linked requirements; no content was truncated.`);return result;};
|
|
27
|
+
const score = (value) => (Number.isInteger(value) && value >= 1 && value <= 5 ? value : null);
|
|
28
|
+
|
|
29
|
+
export function defaultAssessment() {
|
|
30
|
+
return {
|
|
31
|
+
weights: Object.fromEntries(CRITERIA.map((c) => [c.id, 1])),
|
|
32
|
+
scores: Object.fromEntries(OPTIONS.map((o) => [o, Object.fromEntries(CRITERIA.map((c) => [c.id, null]))])),
|
|
33
|
+
notes: Object.fromEntries(OPTIONS.map((o) => [o, ""])),
|
|
34
|
+
current: "",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function cleanMeasures(list) {
|
|
39
|
+
return list.slice(0, 10).flatMap((m) => {
|
|
40
|
+
if (!m || !/^[a-z_][a-z0-9_.]{0,79}$/i.test(m.path || "") || ![">=", "<=", "=="].includes(m.op)) return [];
|
|
41
|
+
const target =
|
|
42
|
+
typeof m.target === "number" && Number.isFinite(m.target)
|
|
43
|
+
? m.target
|
|
44
|
+
: typeof m.target === "boolean"
|
|
45
|
+
? m.target
|
|
46
|
+
: text(m.target, 100);
|
|
47
|
+
return [{ label: text(m.label || m.path, 150), path: m.path, op: m.op, target }];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function cleanEngagement(p) {
|
|
52
|
+
for(const name of ['needs','assumptions','openQuestions','facts'])if(Array.isArray(p[name])&&p[name].length>300)throw Error(`The project has more than 300 ${name}; no records were truncated.`);
|
|
53
|
+
const out = {};
|
|
54
|
+
if (["maintenance", "churn"].includes(p.domain)) out.domain = p.domain;
|
|
55
|
+
if (typeof p.id === "string" && p.id) out.id = text(p.id, 100);
|
|
56
|
+
if (typeof p.createdAt === "string" && !isNaN(Date.parse(p.createdAt))) out.createdAt = p.createdAt;
|
|
57
|
+
if (Array.isArray(p.needs)) out.needs = p.needs.slice(0, 300).map(cleanRequirement);
|
|
58
|
+
if (Array.isArray(p.assumptions)) out.assumptions = p.assumptions.slice(0, 300).map(cleanAssumption);
|
|
59
|
+
if (Array.isArray(p.openQuestions)) out.openQuestions = p.openQuestions.slice(0, 300).map(cleanQuestion);
|
|
60
|
+
if (p.extensions && typeof p.extensions === "object" && !Array.isArray(p.extensions)) out.extensions = p.extensions;
|
|
61
|
+
if (p.brief && typeof p.brief === "object") {
|
|
62
|
+
// Older briefs used consultant headings; fold them into the discovery topics.
|
|
63
|
+
const brief = Object.fromEntries(BRIEF_FIELDS.map(({ id }) => [id, text(p.brief[id])]));
|
|
64
|
+
for (const [old, id] of Object.entries(topicsFile.legacy)) {
|
|
65
|
+
const value = text(p.brief[old]).trim();
|
|
66
|
+
if (value && !brief[id].includes(value)) brief[id] = brief[id] ? `${brief[id]}\n${value}` : value;
|
|
67
|
+
}
|
|
68
|
+
out.brief = brief;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(p.facts))
|
|
71
|
+
out.facts = p.facts.slice(0, 300).map((f) => ({
|
|
72
|
+
id: text(f?.id, 60),
|
|
73
|
+
name: text(f?.name, 60),
|
|
74
|
+
value: typeof f?.value === "number" || typeof f?.value === "boolean" ? f.value : text(f?.value, 100),
|
|
75
|
+
unit: text(f?.unit, 40),
|
|
76
|
+
text: text(f?.text, 300),
|
|
77
|
+
madeAt: /^\d{4}-\d{2}-\d{2}$/.test(f?.madeAt) ? f.madeAt : new Date().toLocaleDateString("en-CA"),
|
|
78
|
+
...(f?.item ? { item: text(f.item, 40) } : {}),
|
|
79
|
+
...(f?.period ? { period: text(f.period, 40) } : {}),
|
|
80
|
+
...(f?.conflictsWith ? { conflictsWith: text(f.conflictsWith, 60) } : {}),
|
|
81
|
+
}));
|
|
82
|
+
if (typeof p.workflow === "string") out.workflow = text(p.workflow, 60);
|
|
83
|
+
if (Array.isArray(p.asked)) out.asked = p.asked.slice(0, 100).map((a) => text(a, 80));
|
|
84
|
+
if (p.lastAsked && typeof p.lastAsked === "object")
|
|
85
|
+
out.lastAsked = Object.fromEntries(["id", "topic", "question", "beginner", "why"].map((k) => [k, text(p.lastAsked[k], 400)]));
|
|
86
|
+
if (p.sketchOffered) out.sketchOffered = true;
|
|
87
|
+
if (typeof p.conceptSig === "string") out.conceptSig = p.conceptSig.slice(0, 20000);
|
|
88
|
+
if (p.choices && typeof p.choices === "object")
|
|
89
|
+
out.choices = Object.fromEntries(Object.entries(p.choices).slice(0, 100).filter(([, v]) => typeof v === "boolean").map(([k, v]) => [text(k, 80), v]));
|
|
90
|
+
if (p.reviewAccepted && typeof p.reviewAccepted === "object")
|
|
91
|
+
out.reviewAccepted = Object.fromEntries(
|
|
92
|
+
Object.entries(p.reviewAccepted)
|
|
93
|
+
.slice(0, 200)
|
|
94
|
+
.map(([k, v]) => [text(k, 300), { reason: text(v?.reason, 1000), owner: text(v?.owner, 120), date: text(v?.date, 10) }]),
|
|
95
|
+
);
|
|
96
|
+
if (p.estimate && typeof p.estimate === "object") {
|
|
97
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : undefined);
|
|
98
|
+
out.estimate = {
|
|
99
|
+
...(["small","medium","large","extra-large"].includes(p.estimate.projectSize) ? {projectSize:p.estimate.projectSize} : {}),
|
|
100
|
+
details: (Array.isArray(p.estimate.details) ? p.estimate.details : []).slice(0, 500).map((l, i) => ({
|
|
101
|
+
id: text(l?.id || `charge-${i + 1}`, 100), componentId: text(l?.componentId, 100),
|
|
102
|
+
description: text(l?.description, 200), quantity: num(l?.quantity), price: num(l?.price),
|
|
103
|
+
unit: text(l?.unit, 80), currency: text(l?.currency || "USD", 3).toUpperCase(),
|
|
104
|
+
source: text(l?.source, 500), date: text(l?.date, 10), region: text(l?.region, 80), service: text(l?.service, 200),
|
|
105
|
+
})),
|
|
106
|
+
...(p.estimate.consulting ? { consulting: {
|
|
107
|
+
currency: text(p.estimate.consulting.currency || "USD", 3).toUpperCase(),
|
|
108
|
+
phases: Object.fromEntries(["discovery", "design", "build", "test", "deploy"].map((id) => {
|
|
109
|
+
const l = p.estimate.consulting.phases?.[id] || {};
|
|
110
|
+
return [id, { lowDays: num(l.lowDays), days: num(l.days), highDays: num(l.highDays), rate: num(l.rate), basis: text(l.basis, 1000) }];
|
|
111
|
+
})),
|
|
112
|
+
} } : {}),
|
|
113
|
+
usage: Object.fromEntries(Object.entries(p.estimate.usage || {}).filter(([, v]) => num(v) !== undefined).slice(0, 20).map(([k, v]) => [text(k, 40), v])),
|
|
114
|
+
prices: Object.fromEntries(
|
|
115
|
+
Object.entries(p.estimate.prices || {})
|
|
116
|
+
.slice(0, 200)
|
|
117
|
+
.map(([k, v]) => [
|
|
118
|
+
text(k, 100),
|
|
119
|
+
{ price: num(v?.price), currency: text(v?.currency || "USD", 3).toUpperCase(), source: text(v?.source, 300), url: text(v?.url, 500), date: text(v?.date, 10), note: text(v?.note, 300), region: text(v?.region, 80), service: text(v?.service, 200), ...(num(v?.tierLimit) !== undefined ? { tierLimit:v.tierLimit } : {}) },
|
|
120
|
+
]),
|
|
121
|
+
),
|
|
122
|
+
...(num(p.estimate.low) !== undefined ? { low: p.estimate.low } : {}),
|
|
123
|
+
...(num(p.estimate.high) !== undefined ? { high: p.estimate.high } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(p.useCases))
|
|
127
|
+
out.useCases = p.useCases.slice(0, 20).map((u, i) => ({
|
|
128
|
+
id: text(u?.id || `uc-${i + 1}`, 60),
|
|
129
|
+
title: text(u?.title, 200),
|
|
130
|
+
value: LEVELS.includes(u?.value) ? u.value : "Medium",
|
|
131
|
+
risk: LEVELS.includes(u?.risk) ? u.risk : "Medium",
|
|
132
|
+
fit: LEVELS.includes(u?.fit) ? u.fit : "Medium",
|
|
133
|
+
verdict: VERDICTS.includes(u?.verdict) ? u.verdict : "Park",
|
|
134
|
+
reason: text(u?.reason, 1000),
|
|
135
|
+
}));
|
|
136
|
+
if (p.gate && typeof p.gate === "object")
|
|
137
|
+
out.gate = Object.fromEntries(GATE.map(({ id }) => [id, text(p.gate[id])]));
|
|
138
|
+
if (p.assessment && typeof p.assessment === "object") {
|
|
139
|
+
const base = defaultAssessment();
|
|
140
|
+
const a = p.assessment;
|
|
141
|
+
out.assessment = {
|
|
142
|
+
weights: Object.fromEntries(
|
|
143
|
+
CRITERIA.map((c) => {
|
|
144
|
+
const w = a.weights?.[c.id];
|
|
145
|
+
return [c.id, Number.isInteger(w) && w >= 0 && w <= 5 ? w : 1];
|
|
146
|
+
}),
|
|
147
|
+
),
|
|
148
|
+
scores: Object.fromEntries(
|
|
149
|
+
OPTIONS.map((o) => [o, Object.fromEntries(CRITERIA.map((c) => [c.id, score(a.scores?.[o]?.[c.id])]))]),
|
|
150
|
+
),
|
|
151
|
+
notes: Object.fromEntries(OPTIONS.map((o) => [o, text(a.notes?.[o], 1000)])),
|
|
152
|
+
current: text(a.current ?? base.current, 1000),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (Array.isArray(p.decisions))
|
|
156
|
+
out.decisions = p.decisions.slice(0, 200).map((d, i) => ({
|
|
157
|
+
id: text(d?.id || `adr-${i + 1}`, 60),
|
|
158
|
+
subject: text(d?.subject || `general.${d?.id || i + 1}`, 120),
|
|
159
|
+
title: text(d?.title, 200),
|
|
160
|
+
owner: text(d?.owner, 120),
|
|
161
|
+
status: STATUSES.includes(d?.status) ? d.status : "Proposed",
|
|
162
|
+
date: /^\d{4}-\d{2}-\d{2}$/.test(d?.date) ? d.date : new Date().toLocaleDateString("en-CA"),
|
|
163
|
+
context: text(d?.context),
|
|
164
|
+
options: text(d?.options),
|
|
165
|
+
decision: text(d?.decision),
|
|
166
|
+
consequences: text(d?.consequences),
|
|
167
|
+
revision: Number.isInteger(d?.revision) ? d.revision : null,
|
|
168
|
+
supersedes: d?.supersedes ? text(d.supersedes, 60) : null,
|
|
169
|
+
supersededBy: d?.supersededBy ? text(d.supersededBy, 60) : null,
|
|
170
|
+
reason: text(d?.reason, 1000),
|
|
171
|
+
}));
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Weighted mean on the 1–5 scale; null until every weighted criterion is scored.
|
|
176
|
+
export function optionScore(assessment, option) {
|
|
177
|
+
const scores = assessment.scores[option];
|
|
178
|
+
let total = 0,
|
|
179
|
+
weight = 0;
|
|
180
|
+
for (const c of CRITERIA) {
|
|
181
|
+
const w = assessment.weights[c.id];
|
|
182
|
+
if (!w) continue;
|
|
183
|
+
if (scores[c.id] == null) return null;
|
|
184
|
+
total += w * scores[c.id];
|
|
185
|
+
weight += w;
|
|
186
|
+
}
|
|
187
|
+
return weight ? Math.round((total / weight) * 10) / 10 : null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function newDecision(project, fields = {}) {
|
|
191
|
+
const decisions = project.decisions || [];
|
|
192
|
+
const n = Math.max(0, ...decisions.map((d) => Number(/^adr-(\d+)$/.exec(d.id)?.[1] || 0))) + 1;
|
|
193
|
+
const id = `adr-${String(n).padStart(3, "0")}`;
|
|
194
|
+
return {
|
|
195
|
+
id,
|
|
196
|
+
subject: fields.subject || `general.${id}`,
|
|
197
|
+
title: "",
|
|
198
|
+
owner: "",
|
|
199
|
+
status: "Proposed",
|
|
200
|
+
date: new Date().toLocaleDateString("en-CA"),
|
|
201
|
+
context: "",
|
|
202
|
+
options: "",
|
|
203
|
+
decision: "",
|
|
204
|
+
consequences: "",
|
|
205
|
+
revision: project.revision,
|
|
206
|
+
supersedes: null,
|
|
207
|
+
supersededBy: null,
|
|
208
|
+
reason: "",
|
|
209
|
+
...fields,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function recommendationDecision(project, option, names) {
|
|
214
|
+
const a = project.assessment || defaultAssessment();
|
|
215
|
+
const line = (o) => {
|
|
216
|
+
const s = optionScore(a, o);
|
|
217
|
+
return `- ${names[o]}: ${s ?? "not fully scored"}${a.notes[o] ? ` — ${a.notes[o]}` : ""}`;
|
|
218
|
+
};
|
|
219
|
+
return newDecision(project, {
|
|
220
|
+
subject: "platform.cloud",
|
|
221
|
+
title: `Platform for ${project.domain === "maintenance" ? "predictive maintenance" : project.name}: ${names[option]}`,
|
|
222
|
+
context: project.requirements || "",
|
|
223
|
+
options: [
|
|
224
|
+
`Weights: ${CRITERIA.map((c) => `${c.label} ×${a.weights[c.id]}`).join(", ")} (scores 1–5, higher is better).`,
|
|
225
|
+
...OPTIONS.map(line),
|
|
226
|
+
].join("\n"),
|
|
227
|
+
decision: `Recommend ${names[option]}.`,
|
|
228
|
+
consequences: "",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Proposals and templates replace the design, never the engagement record.
|
|
233
|
+
export function keepEngagement(next, current) {
|
|
234
|
+
const kept = {};
|
|
235
|
+
for (const key of ["id", "createdAt", "requirements", "brief", "useCases", "assessment", "decisions", "gate", "needs", "assumptions", "openQuestions", "extensions", "facts", "workflow", "asked", "lastAsked", "sketchOffered", "estimate", "reviewAccepted", "conceptSig", "choices"]) if (current[key] !== undefined) kept[key] = current[key];
|
|
236
|
+
// The problem statement is the client's, not the design's.
|
|
237
|
+
if (current.requirements?.trim()) kept.requirements = current.requirements;
|
|
238
|
+
return { ...next, ...kept };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Offline consultant answers for questions about the engagement itself.
|
|
242
|
+
export function consult(message, project) {
|
|
243
|
+
const t = message.toLowerCase();
|
|
244
|
+
const brief = project.brief || {};
|
|
245
|
+
const cases = project.useCases || [];
|
|
246
|
+
const open = BRIEF_FIELDS.filter((f) => !brief[f.id]?.trim());
|
|
247
|
+
if (/ask next|next question|what should i ask/.test(t))
|
|
248
|
+
return open.length
|
|
249
|
+
? `Ask about ${open[0].label.toLowerCase()}: “${open[0].question}”${open[1] ? `\n\nThen ${open[1].label.toLowerCase()}.` : ""}`
|
|
250
|
+
: "The brief is complete. Play it back to the client to confirm, then qualify the use cases.";
|
|
251
|
+
if (/summari[sz]e|what we know|play back/.test(t)) {
|
|
252
|
+
const known = BRIEF_FIELDS.filter((f) => brief[f.id]?.trim());
|
|
253
|
+
return [
|
|
254
|
+
project.requirements ? `Problem: ${project.requirements}` : "Problem: not captured yet.",
|
|
255
|
+
...known.map((f) => `${f.label}: ${brief[f.id].replace(/\n/g, "; ")}`),
|
|
256
|
+
open.length ? `Still unknown: ${open.map((f) => f.label.toLowerCase()).join(", ")}.` : "Nothing major unknown.",
|
|
257
|
+
].join("\n");
|
|
258
|
+
}
|
|
259
|
+
if (/viable|qualif|rule out|use cases?/.test(t)) {
|
|
260
|
+
if (!cases.length)
|
|
261
|
+
return "No use cases listed yet. Add each one you heard in Discover. Pursue only what is high value with acceptable risk and a clear strategic fit; rule out the rest explicitly so nobody invests in them by accident.";
|
|
262
|
+
const by = (v) => cases.filter((c) => c.verdict === v).map((c) => c.title || "untitled");
|
|
263
|
+
const risky = cases.filter((c) => c.verdict === "Pursue" && (c.risk === "High" || c.value === "Low"));
|
|
264
|
+
return [
|
|
265
|
+
`Pursue: ${by("Pursue").join(", ") || "none"}.`,
|
|
266
|
+
`Park: ${by("Park").join(", ") || "none"}.`,
|
|
267
|
+
`Ruled out: ${by("Rule out").join(", ") || "none"}.`,
|
|
268
|
+
...risky.map((c) => `Check “${c.title}”: marked Pursue with ${c.risk === "High" ? "high risk" : "low value"}. Is that defensible?`),
|
|
269
|
+
].join("\n");
|
|
270
|
+
}
|
|
271
|
+
if (/evidence|scale|go live|production/.test(t))
|
|
272
|
+
return "Scale only when value demonstrably exceeds cost and risk. Before scaling, record in Build: who used the prototype and what changed (adoption), measured value against run and build cost (economics), and the open risks with who accepted them. Every success measure should pass on the current design.";
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Backend-owned jobs: no native thread IDs, host paths or model credentials.
|
|
2
|
+
import {unzipSync} from 'fflate';
|
|
3
|
+
export async function engineeringCall(session, action, payload, {signal,fetcher=fetch}={}) {
|
|
4
|
+
if(!session?.token)throw new Error('Connect to the session service first.');
|
|
5
|
+
const response=await fetcher(`/api/engineering/${action}`,{method:'POST',signal,
|
|
6
|
+
headers:{'Content-Type':'application/json',Authorization:`Bearer ${session.token}`},body:JSON.stringify(payload)});
|
|
7
|
+
if(!response.ok){
|
|
8
|
+
let detail;try{detail=(await response.json()).detail;}catch{}
|
|
9
|
+
const error=new Error(typeof detail==='string'?detail:'The engineering request could not be accepted. Your draft is retained.');
|
|
10
|
+
error.status=response.status;throw error;
|
|
11
|
+
}
|
|
12
|
+
if(action==='download'||action==='handoff')return response;
|
|
13
|
+
return response.json();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function mergeEngineeringEvents(current,incoming){
|
|
17
|
+
const bySequence=new Map(current.map(event=>[event.sequence,event]));
|
|
18
|
+
for(const event of incoming)bySequence.set(event.sequence,event);
|
|
19
|
+
return [...bySequence.values()].sort((a,b)=>a.sequence-b.sequence);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function verifiedEngineeringDownload(session,payload,manifest,options){
|
|
23
|
+
const response=await engineeringCall(session,'download',payload,options);
|
|
24
|
+
const bytes=await response.arrayBuffer();
|
|
25
|
+
const digest=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(x=>x.toString(16).padStart(2,'0')).join('');
|
|
26
|
+
if(digest!==manifest.sha256 || response.headers.get('X-Artifact-Sha256')!==digest)
|
|
27
|
+
throw new Error('The package digest did not match its capture receipt. Download was stopped.');
|
|
28
|
+
return new Blob([bytes],{type:'application/zip'});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function verifiedHandoffDownload(session,payload,manifest,options){
|
|
32
|
+
const response=await engineeringCall(session,'handoff',payload,options);
|
|
33
|
+
const bytes=await response.arrayBuffer();
|
|
34
|
+
const digest=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(x=>x.toString(16).padStart(2,'0')).join('');
|
|
35
|
+
if(response.headers.get('X-Artifact-Sha256')!==digest||response.headers.get('X-Source-Artifact-Sha256')!==manifest.sha256)
|
|
36
|
+
throw new Error('The handoff digest or source package did not match its receipt. Download was stopped.');
|
|
37
|
+
let expanded=0;
|
|
38
|
+
const files=unzipSync(new Uint8Array(bytes),{filter:entry=>{
|
|
39
|
+
expanded+=entry.originalSize;
|
|
40
|
+
if(expanded>150_000_000)throw new Error('The handoff exceeds its receiving size bound. Download was stopped.');
|
|
41
|
+
return true;
|
|
42
|
+
}});
|
|
43
|
+
const sha=async value=>[...new Uint8Array(await crypto.subtle.digest('SHA-256',value))].map(x=>x.toString(16).padStart(2,'0')).join('');
|
|
44
|
+
const original=files['evidence/original-implementation.zip'];
|
|
45
|
+
if(!original||await sha(original)!==manifest.sha256)
|
|
46
|
+
throw new Error('The original implementation inside the handoff did not match its receipt. Download was stopped.');
|
|
47
|
+
const expected=manifest.files.filter(file=>file.kind==='file');
|
|
48
|
+
const receiving=Object.keys(files).filter(name=>name.startsWith('implementation/')&&!name.endsWith('/'));
|
|
49
|
+
if(receiving.length!==expected.length)throw new Error('The handoff file inventory changed. Download was stopped.');
|
|
50
|
+
for(const file of expected){
|
|
51
|
+
const content=files['implementation/'+file.path];
|
|
52
|
+
if(!content||content.byteLength!==file.bytes||await sha(content)!==file.sha256)
|
|
53
|
+
throw new Error('An implementation file inside the handoff did not match its receipt. Download was stopped.');
|
|
54
|
+
}
|
|
55
|
+
return new Blob([bytes],{type:'application/zip'});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// User requests and model deltas remain separate from tool-progress/acceptance claims.
|
|
60
|
+
export function engineeringConversation(events){
|
|
61
|
+
const ordered=mergeEngineeringEvents([],events),accepted=new Set(ordered.filter(e=>e.kind==='steering_accepted').map(e=>e.requestId)),uncertain=new Set(ordered.filter(e=>e.kind==='steering_delivery_uncertain').map(e=>e.requestId));
|
|
62
|
+
const entries=[];
|
|
63
|
+
for(const event of ordered){
|
|
64
|
+
if(event.kind==='accepted'&&event.origin!=='automatic_repair'&&typeof event.text==='string')entries.push({id:event.sequence,role:'user',text:event.text,status:'accepted'});
|
|
65
|
+
else if(event.kind==='steering_submitted'&&typeof event.text==='string')entries.push({id:event.sequence,role:'user',text:event.text,status:accepted.has(event.requestId)?'accepted':uncertain.has(event.requestId)?'uncertain':'pending'});
|
|
66
|
+
else if(event.kind==='message'&&typeof event.text==='string'){
|
|
67
|
+
const previous=entries.at(-1);
|
|
68
|
+
if(previous?.role==='assistant'&&previous.runId===event.runId)previous.text+=event.text;
|
|
69
|
+
else entries.push({id:event.sequence,role:'assistant',runId:event.runId,text:event.text});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Reuse user-accepted placement without asking for the same fact on every block.
|
|
2
|
+
// This does not verify regional service availability or invent a residency decision.
|
|
3
|
+
export function prefillAcceptedSettings(project){
|
|
4
|
+
const accepted=subject=>(project.decisions||[]).filter(d=>d.subject===subject&&d.status==='Accepted').at(-1)?.decision;
|
|
5
|
+
const region=accepted('cloud.region'),provider=accepted('platform.cloud')||project.provider;
|
|
6
|
+
if(typeof region!=='string'||!region.trim()||!['aws','azure','gcp','ibm'].includes(provider))return project;
|
|
7
|
+
const applied=[];
|
|
8
|
+
const nodes=project.nodes.map(n=>{
|
|
9
|
+
const d=n.data,current=d.config?.region;
|
|
10
|
+
if(d.kind==='note'||d.provider!==provider||(d.origin==='existing'&&d.deploymentMode!=='create')||(current&&!/^(to be selected|unknown|undecided|tbd)$/i.test(current.trim())))return n;
|
|
11
|
+
applied.push({componentId:n.id,field:'config.region',value:region,basis:'User-accepted project cloud.region decision'});
|
|
12
|
+
return {...n,data:{...d,config:{...d.config,region}}};
|
|
13
|
+
});
|
|
14
|
+
if(!applied.length)return project;
|
|
15
|
+
const previous=(project.extensions?.engineeringDefaults||[]).filter(x=>!applied.some(a=>a.componentId===x.componentId&&a.field===x.field));
|
|
16
|
+
return {...project,nodes,extensions:{...project.extensions,engineeringDefaults:[...previous,...applied]}};
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.engineering-panel { min-width: 0; }
|
|
2
|
+
.engineering-panel form { display: grid; gap: .6rem; margin: 1rem 0; }
|
|
3
|
+
.engineering-panel textarea { width: 100%; box-sizing: border-box; resize: vertical; font: inherit; min-height: 7rem; }
|
|
4
|
+
.engineering-panel button { max-width: 100%; white-space: normal; }
|
|
5
|
+
.engineering-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; }
|
|
6
|
+
.engineering-message { white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; max-height: 22rem; overflow: auto; }
|
|
7
|
+
.engineering-panel details { max-height: 16rem; overflow: auto; overflow-wrap: anywhere; }
|
|
8
|
+
.engineering-panel ol { padding-left: 1.5rem; }
|
|
9
|
+
.engineering-panel li { margin: .4rem 0; }
|
|
10
|
+
.engineering-artifact { margin-top: 1rem; padding: .8rem; border: 1px solid currentColor; border-radius: .5rem; }
|
|
11
|
+
.artifact-checks { margin-top: 1rem; border-top: 1px solid currentColor; padding-top: .8rem; }
|
|
12
|
+
.artifact-checks h3 { margin: 0; }
|
|
13
|
+
.artifact-checks button { margin: .3rem .3rem .3rem 0; }
|
|
14
|
+
.artifact-checks pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: .8rem; }
|
|
15
|
+
|
|
16
|
+
.app.build-workspace { grid-template-columns:minmax(0,1fr); grid-template-rows:auto minmax(0,1fr); height:100dvh; min-height:100vh; }
|
|
17
|
+
.build-workspace .canvas { overflow:auto; background:var(--paper); }
|
|
18
|
+
.build-workspace .component-navigator,.build-workspace .current-view { display:none; }
|
|
19
|
+
.native-work-area { max-width:960px; margin:0 auto; padding:clamp(16px,3vw,40px); }
|
|
20
|
+
.engineering-conversation { display:grid; gap:1rem; margin:1.5rem 0; }
|
|
21
|
+
.engineering-entry { min-width:0; border-left:3px solid var(--line); padding:.5rem 1rem; }
|
|
22
|
+
.engineering-entry.user { background:var(--board); border-radius:.5rem; }
|
|
23
|
+
.engineering-entry pre { white-space:pre-wrap; overflow-wrap:anywhere; font:inherit; max-height:24rem; overflow:auto; }
|
|
24
|
+
.engineering-panel textarea { border:1px solid var(--color-line-strong); border-radius:10px; padding:12px; background:var(--board); }
|
|
25
|
+
.engineering-panel button { border:1px solid var(--line); border-radius:8px; background:var(--paper); padding:8px 12px; min-height:38px; }
|
|
26
|
+
.engineering-panel form > button { justify-self:start; background:var(--action); color:var(--color-brand-ink); border-color:transparent; font-weight:600; min-width:128px; }
|
|
27
|
+
.architecture-work { margin: .8rem 0; }
|
|
28
|
+
.engineering-panel details.architecture-work { max-height: none; overflow: visible; }
|
|
29
|
+
.architecture-work > ul { max-height: 24rem; overflow: auto; padding-left: 1.3rem; }
|
|
30
|
+
.architecture-work pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: .8rem; }
|
|
31
|
+
.architecture-work small { display: block; }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import sources from'./runtime/enterprise-application-sources.json'with{type:'json'};
|
|
2
|
+
|
|
3
|
+
// A source package is not a supported complete-project capability. Native hosting,
|
|
4
|
+
// account inputs, model/source/SQL configuration and whole-export gates must bind it.
|
|
5
|
+
export function enterpriseApplicationFiles(provider,address){
|
|
6
|
+
if(!['aws','azure','gcp','ibm'].includes(provider)||typeof address!=='string'||!/^block_[0-9a-f]+$/.test(address))throw Error('Use the explicit private application provider and canonical address');
|
|
7
|
+
const root='applications/'+address+'/',files={},manifest=sources.manifest;
|
|
8
|
+
const names=[...manifest.files,...manifest.providerFiles[provider]];
|
|
9
|
+
for(const name of names){if(typeof sources.files[name]!=='string')throw Error('Missing private application asset '+name);files[root+'runtime/'+name]=sources.files[name];}
|
|
10
|
+
const lockName='enterprise-runtime-'+provider+'-python-lock.json',lock=JSON.parse(sources.files[lockName]);
|
|
11
|
+
if(lock.provider!==provider||typeof lock.requirements!=='string'||!lock.requirements.includes('--hash=sha256:'))throw Error('Use the exact selected-provider dependency closure');
|
|
12
|
+
files[root+'requirements.lock']=lock.requirements;files[root+'dependency-lock.json']=sources.files[lockName];
|
|
13
|
+
files[root+'Dockerfile']=sources.files['enterprise-runtime.Dockerfile'];
|
|
14
|
+
files[root+'container-base.json']=sources.files['python-container-lock.json'];
|
|
15
|
+
files[root+'application-manifest.json']=JSON.stringify({format:manifest.format,provider,files:names,entrypoints:manifest.entrypoints,
|
|
16
|
+
sourceHashes:Object.fromEntries([...names,'enterprise-runtime.Dockerfile',lockName,'python-container-lock.json'].map(name=>[name,sources.hashes[name]])),
|
|
17
|
+
documentWorkerImplemented:manifest.nativeDocumentWorkerProviders.includes(provider),wholeProjectQualified:false},null,2)+'\n';
|
|
18
|
+
files[root+'.dockerignore']='*\n!Dockerfile\n!requirements.lock\n!runtime/\n'+names.map(name=>'!runtime/'+name+'\n').join('');
|
|
19
|
+
files[root+'configuration-contract.json']=JSON.stringify({format:'enterprise-process-configuration-v1',provider,
|
|
20
|
+
deployment:{direct:'ENTERPRISE_DEPLOYMENT_JSON',mountedFile:'ENTERPRISE_DEPLOYMENT_FILE',defaultFile:'/app/enterprise-deployment.json',schema:'enterprise-knowledge-v1',maximumBytes:32768},
|
|
21
|
+
documentExecutor:{direct:'ENTERPRISE_DOCUMENT_EXECUTOR_JSON',mountedFile:'ENTERPRISE_DOCUMENT_EXECUTOR_FILE',defaultFile:'/app/enterprise-document-executor.json',schema:'enterprise-document-executor-v1',maximumBytes:32768,provider,parserRevision:'Must equal the approved deployment/build receipt; not an invented digest.',nativeConfiguration:'Use the corresponding Terraform document_executor_configuration output.'},
|
|
22
|
+
suppliedSecrets:'Native platform injection or owned mounted files only; no secret values belong in this source package.',wholeProjectQualified:false},null,2)+'\n';
|
|
23
|
+
files[root+'BUILD.md']=BUILD;
|
|
24
|
+
for(const [directory,names,recipe]of [['parser/',manifest.parserFiles,'enterprise-document.Dockerfile'],['parser/pdf/',manifest.pdfParserFiles,'invoice-parser.Dockerfile']]){
|
|
25
|
+
for(const name of names)files[root+directory+(name===recipe?'Dockerfile':name)]=sources.files[name];
|
|
26
|
+
files[root+directory+'.dockerignore']='*\n'+names.filter(name=>name!==recipe).map(name=>'!'+name+'\n').join('');
|
|
27
|
+
files[root+directory+'source-manifest.json']=JSON.stringify({sourceHashes:Object.fromEntries(names.map(name=>[name,sources.hashes[name]])),scope:'Exact parser build sources; not a native isolation or security acceptance receipt.'},null,2)+'\n';
|
|
28
|
+
}
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const BUILD=`# Private application delivery
|
|
33
|
+
|
|
34
|
+
Build this exact directory as Linux/amd64 with the emitted Dockerfile and hash lock.
|
|
35
|
+
The API is the default command. The manifest also records the dedicated worker and
|
|
36
|
+
operator commands. Keep parser/OCR tools in their separate reviewed artifact: no
|
|
37
|
+
document parser, Java runtime or Docker daemon runs in this credentialed worker.
|
|
38
|
+
|
|
39
|
+
Build the supplied parser recipes separately, in this order, and record their
|
|
40
|
+
actual inspected source/image identities before using them in native configuration:
|
|
41
|
+
|
|
42
|
+
docker build --platform linux/amd64 -t atmem-private-pdf-base:local parser/pdf
|
|
43
|
+
docker build --platform linux/amd64 --build-arg PDF_PARSER_IMAGE=atmem-private-pdf-base:local -t atmem-private-document:local parser
|
|
44
|
+
docker build --platform linux/amd64 -t atmem-private-application:local .
|
|
45
|
+
|
|
46
|
+
Local tags are build handles, not immutable publication evidence. Keep the exact
|
|
47
|
+
image IDs/build receipts and obtain authorization before any registry publication.
|
|
48
|
+
|
|
49
|
+
Supply the approved deployment as ENTERPRISE_DEPLOYMENT_JSON or an absolute
|
|
50
|
+
ENTERPRISE_DEPLOYMENT_FILE mount. Supply exactly one form, never conflicting sources.
|
|
51
|
+
The API reads only its deployment metadata; it does not construct a parser or model.
|
|
52
|
+
For an implemented native worker, supply ENTERPRISE_DOCUMENT_EXECUTOR_JSON or its
|
|
53
|
+
absolute FILE mount from the same Terraform output and parser build receipt. The
|
|
54
|
+
worker checks provider, parser revision, native destination and worker identity before
|
|
55
|
+
reading source/encryption secrets or SQL. No arbitrary plug-in, executable or URL
|
|
56
|
+
can be selected through this configuration. Default worker concurrency is two;
|
|
57
|
+
ENTERPRISE_WORKER_CONCURRENCY accepts one to four after capacity/quotas are reviewed.
|
|
58
|
+
|
|
59
|
+
AWS and Google native document loading are implemented locally. Azure/IBM loading
|
|
60
|
+
remains an implementation requirement; their application source/dependency emission
|
|
61
|
+
does not advertise a complete worker or substitute another cloud. No complete
|
|
62
|
+
private capability is promoted by these files.
|
|
63
|
+
|
|
64
|
+
The configured worker attaches the durable cleanup ledger and runs recovery alongside
|
|
65
|
+
question processing. Runtime startup never initializes schemas, adopts existing SQL
|
|
66
|
+
state, creates company membership or generates replacement encryption keys. Use the
|
|
67
|
+
operator lifecycle with separate existing native SQL identities before enabling it.
|
|
68
|
+
Keep corporate OIDC/source registrations and approved directory mappings intact.
|
|
69
|
+
|
|
70
|
+
Preserve image/source digests, reviewed dependency/security evidence and actual
|
|
71
|
+
destination artifact publication records. Parser revision, local image configuration
|
|
72
|
+
digest and registry manifest digest are distinct identities; never invent or equate
|
|
73
|
+
them. Full application/worker/database/model/source networking, secrets, enrollment,
|
|
74
|
+
artifact publication, operations and Spec Kit composition remain mandatory before
|
|
75
|
+
whole-project acceptance. No account plan, publication or deployment ran here.
|
|
76
|
+
`;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {resourceAddress} from './identifiers.mjs';
|
|
2
|
+
import probeSource from './runtime/enterprise-aws-host-probe-source.json' with{type:'json'};
|
|
3
|
+
import {CLOUD_PROVIDER_LOCKS}from'./cloud-provider-locks.mjs';
|
|
4
|
+
import {awsHostVerificationTests}from'./enterprise-aws-host-tests.mjs';
|
|
5
|
+
|
|
6
|
+
// Owned host-verification building block. This does not provision the EC2 pool,
|
|
7
|
+
// document store, native execution permissions, application or complete export.
|
|
8
|
+
export function awsHostVerificationTerraform(project,row,environment='staging'){
|
|
9
|
+
if(!project||typeof project.id!=='string'||!project.id||project.id.length>255||!Number.isSafeInteger(project.revision)||project.revision<1)throw Error('Use the owned project/revision');
|
|
10
|
+
if(!row||row.provider!=='aws'||typeof row.id!=='string'||!row.id||typeof row.region!=='string'||!(/^(af|ap|ca|eu|il|me|mx|sa|us)-(east|west|south|north|central|southeast|southwest|northeast|northwest)-[1-9]$/).test(row.region))throw Error('Preserve the explicit AWS provider and selected region');
|
|
11
|
+
encodeURIComponent(row.id);
|
|
12
|
+
const address=resourceAddress(row.id);
|
|
13
|
+
if(row.address!==address||!['staging','production','qualification'].includes(environment))throw Error('Use canonical component ownership and environment');
|
|
14
|
+
const n=address,v=name=>'${var.'+name+'}',r=(type,name,attribute)=>'${'+type+'.'+name+'.'+attribute+'}',l=name=>'${local.'+name+'}';
|
|
15
|
+
const literal=value=>value.replace(/\$\{/g,()=>'$${').replace(/%\{/g,()=>'%%{');
|
|
16
|
+
const variable={review_approved:{type:'bool',default:false,nullable:false,description:'Review the exact SSM command, existing worker IAM and host ownership before apply.'}},examples={review_approved:false};
|
|
17
|
+
function input(name,description,example,pattern,defaultValue){
|
|
18
|
+
variable[name]={type:'string',nullable:false,description,validation:{condition:'${can(regex('+JSON.stringify(pattern)+',var.'+name+'))}',error_message:'Supply the reviewed '+name+' input.'}};
|
|
19
|
+
if(defaultValue!==undefined)variable[name].default=defaultValue;examples[name]=example;return v(name);
|
|
20
|
+
}
|
|
21
|
+
const account=input('account_id','Existing destination account, never inferred from a source node.','123456789012','^[0-9]{12}$');
|
|
22
|
+
const region=input('region','Keep the selected architecture region; regenerate to change.',row.region,'^'+row.region+'$',row.region);
|
|
23
|
+
const cluster=input('cluster_arn','Existing owned parser ECS cluster. This module does not create/adopt a compute pool.','arn:aws:ecs:'+row.region+':123456789012:cluster/document-parser','^arn:aws:ecs:[a-z]{2}-[a-z]+-[1-9]:[0-9]{12}:cluster/[A-Za-z0-9_-]{1,255}$');
|
|
24
|
+
const worker=input('worker_role_arn','Existing question-worker IAM role; append one named policy without replacing its other policies.','arn:aws:iam::123456789012:role/question-worker','^arn:aws:iam::[0-9]{12}:role/[A-Za-z0-9_+=,.@/-]{1,128}$');
|
|
25
|
+
const seed='https://atmem.ai/workspaces/'+encodeURIComponent(project.id)+'/'+n+'/'+environment;
|
|
26
|
+
const locals={name:'${format("doc-host-%s",substr(sha256('+JSON.stringify(seed)+'),0,12))}',
|
|
27
|
+
policy_sha256:'${sha256(jsonencode({format="enterprise-aws-host-policy-v1",clusterArn=var.cluster_arn,probeSha256=sha256('+JSON.stringify(literal(probeSource))+')}))}',
|
|
28
|
+
expected:{format:'enterprise-aws-host-policy-v1',clusterArn:cluster,policySha256:l('policy_sha256')}};
|
|
29
|
+
const b64=btoa(Array.from(new TextEncoder().encode(probeSource),value=>String.fromCharCode(value)).join(''));
|
|
30
|
+
const launcher='import base64;exec(compile(base64.b64decode(\''+b64+'\'),"owned-host-probe","exec"))';
|
|
31
|
+
const quoted="'"+launcher.replace(/'/g,"'\"'\"'")+"'";
|
|
32
|
+
const prefix='exec /usr/bin/python3 -I -c '+quoted+' ';
|
|
33
|
+
locals.document={schemaVersion:'2.2',description:'Fixed read-only parser host inspection; no caller command or secret parameters.',parameters:{},
|
|
34
|
+
mainSteps:[{action:'aws:runShellScript',name:'verifyHost',precondition:{StringEquals:['platformType','Linux']},
|
|
35
|
+
inputs:{timeoutSeconds:'30',runCommand:['${format("%s%s",'+JSON.stringify(prefix)+',base64encode(jsonencode(local.expected)))}']}}]};
|
|
36
|
+
const resource={terraform_data:{[n+'_review']:{input:{design_revision:project.revision,source_component:literal(row.id),environment,
|
|
37
|
+
requested_worker_arn:worker,resolved_worker_arn:'${data.aws_iam_role.'+n+'_worker.arn}'},lifecycle:{precondition:[
|
|
38
|
+
{condition:v('review_approved'),error_message:'Review the selected host policy and existing worker before apply.'},
|
|
39
|
+
{condition:'${startswith(var.cluster_arn,format("arn:aws:ecs:%s:%s:cluster/",var.region,var.account_id))}',error_message:'Keep the parser cluster in the selected account/region.'},
|
|
40
|
+
{condition:'${startswith(var.worker_role_arn,format("arn:aws:iam::%s:role/",var.account_id))}',error_message:'Keep the referenced worker in the selected account.'},
|
|
41
|
+
{condition:'${data.aws_iam_role.'+n+'_worker.arn == var.worker_role_arn}',error_message:'The existing worker role must match the full accepted ARN, including its path.'}]}}}};
|
|
42
|
+
const data={aws_iam_role:{[n+'_worker']:{name:'${basename(var.worker_role_arn)}'}}};
|
|
43
|
+
const add=(type,name,body)=>((resource[type]??={})[name]={...body,depends_on:['terraform_data.'+n+'_review',...(body.depends_on||[])]});
|
|
44
|
+
add('aws_ssm_document',n,{name:l('name'),document_type:'Command',document_format:'JSON',target_type:'/AWS::EC2::Instance',content:'${jsonencode(local.document)}',
|
|
45
|
+
tags:{'atmem-document-policy':l('policy_sha256')},lifecycle:{prevent_destroy:true,postcondition:[{condition:'${self.hash_type == "Sha256" && can(regex("^[0-9a-f]{64}$",self.hash))}',error_message:'Require the native SHA256 document hash before exposing verification configuration.'}]}});
|
|
46
|
+
const doc=r('aws_ssm_document',n,'arn'),container='${format("arn:aws:ecs:%s:%s:container-instance/%s/*",var.region,var.account_id,basename(var.cluster_arn))}';
|
|
47
|
+
locals.worker_policy={Version:'2012-10-17',Statement:[
|
|
48
|
+
{Sid:'InspectOwnedCluster',Effect:'Allow',Action:['ecs:ListContainerInstances'],Resource:cluster},
|
|
49
|
+
{Sid:'InspectOwnedInstances',Effect:'Allow',Action:['ecs:DescribeContainerInstances'],Resource:container,Condition:{ArnEquals:{'ecs:cluster':cluster}}},
|
|
50
|
+
{Sid:'FixedProbeDocumentOnly',Effect:'Allow',Action:['ssm:GetDocument','ssm:SendCommand'],Resource:doc},
|
|
51
|
+
{Sid:'TaggedParserHostsOnly',Effect:'Allow',Action:['ssm:SendCommand'],Resource:'${format("arn:aws:ec2:%s:%s:instance/*",var.region,var.account_id)}',Condition:{StringEquals:{'ssm:resourceTag/atmem-document-policy':l('policy_sha256')}}},
|
|
52
|
+
{Sid:'ReadProbeInvocation',Effect:'Allow',Action:['ssm:GetCommandInvocation'],Resource:'*'}]};
|
|
53
|
+
add('aws_iam_role_policy',n,{name:l('name'),role:'${data.aws_iam_role.'+n+'_worker.name}',policy:'${jsonencode(local.worker_policy)}'});
|
|
54
|
+
const output={host_verification:{value:{clusterArn:cluster,hostPolicySha256:l('policy_sha256'),hostDocumentArn:doc,hostDocumentHash:r('aws_ssm_document',n,'hash'),hostDocumentVersion:'${tostring(aws_ssm_document.'+n+'.latest_version)}'},
|
|
55
|
+
depends_on:['aws_ssm_document.'+n,'aws_iam_role_policy.'+n],description:'Fixed command/worker policy only. Compute pool and complete private application are not provided by this module.'}};
|
|
56
|
+
const main={terraform:{required_version:'>= 1.17.1, < 2.0.0',required_providers:{aws:{source:'hashicorp/aws',version:'6.72.0'}}},provider:{aws:{region,allowed_account_ids:[account]}},variable,locals,data,resource,output};
|
|
57
|
+
return {'main.tf.json':JSON.stringify(main,null,2)+'\n','.terraform.lock.hcl':CLOUD_PROVIDER_LOCKS.aws,
|
|
58
|
+
'deployment.tfvars.json.example':JSON.stringify(examples,null,2)+'\n',
|
|
59
|
+
'tests/host-verification.tftest.hcl':awsHostVerificationTests(n,seed,row.region),
|
|
60
|
+
'host-policy-boundary.json':JSON.stringify({component:row.id,address:n,provider:'aws',region:row.region,scope:'Fixed SSM verifier and additive existing-worker policy; not a compute pool or complete private export.',wholeProjectQualified:false,cloudOperations:false},null,2)+'\n',
|
|
61
|
+
'README.md':'# Owned AWS parser-host verification\n\nThis module creates one fixed, parameterless SSM command and appends one named policy to the existing question worker. It references the selected existing parser cluster. It does not create a compute pool, replace a worker role, deploy an application or register a complete private capability.\n\nOnly this SSM document may be submitted, and only instances tagged with the exact policy fingerprint may be targeted. The worker pins native document version/hash, independently checks command content and restricts task placement to the instances it just verified. GetCommandInvocation requires resource wildcard in the native IAM contract; no command discovery, document mutation or Parameter Store read is granted. Account inherited permissions still require inspection.\n\nProvision the dedicated private host pool with matching ECS/daemon/IMDS settings and tags before use. This module deliberately emits no ready parser configuration or invented image/AMI. Runtime/identity, host drift, IAM and eventual native behavior remain subject to independent destination qualification. No account plan or deployment has run.\n'};
|
|
62
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function awsHostVerificationTests(n,seed,region){
|
|
2
|
+
const name=`format("doc-host-%s",substr(sha256(${JSON.stringify(seed)}),0,12))`;
|
|
3
|
+
const run=(name,body)=>`run "${name}" {\n command = plan\n${body}\n}\n`;
|
|
4
|
+
const assertion=(condition,message)=>` assert {\n condition = ${condition}\n error_message = ${JSON.stringify(message)}\n }`;
|
|
5
|
+
let text=`mock_provider "aws" {}\noverride_resource {\n override_during = plan\n target = aws_ssm_document.${n}\n values = {\n arn = format("arn:aws:ssm:${region}:123456789012:document/%s",${name})\n hash = "${'f'.repeat(64)}"\n hash_type = "Sha256"\n latest_version = 1\n }\n}\nvariables {\n review_approved = true\n account_id = "123456789012"\n cluster_arn = "arn:aws:ecs:${region}:123456789012:cluster/document-parser"\n worker_role_arn = "arn:aws:iam::123456789012:role/question-worker"\n}\n`;
|
|
6
|
+
text+=`override_data {\n target = data.aws_iam_role.${n}_worker\n values = {\n arn = "arn:aws:iam::123456789012:role/question-worker"\n name = "question-worker"\n }\n}\n`;
|
|
7
|
+
text+=run('fixed_host_verification',assertion(`length(keys(jsondecode(aws_ssm_document.${n}.content).parameters)) == 0 && length(jsondecode(aws_ssm_document.${n}.content).mainSteps) == 1 && jsondecode(aws_ssm_document.${n}.content).mainSteps[0].action == "aws:runShellScript"`,'Only the fixed parameterless probe is allowed.')+'\n'+assertion(`output.host_verification.hostDocumentVersion == "1" && output.host_verification.hostDocumentHash == "${'f'.repeat(64)}" && output.host_verification.clusterArn == var.cluster_arn && length(output.host_verification.hostPolicySha256) == 64`,'Runtime must receive exact native document identity and selected host policy.'));
|
|
8
|
+
text+=run('worker_permission_boundary',assertion(`aws_iam_role_policy.${n}.role == "question-worker" && length(jsondecode(aws_iam_role_policy.${n}.policy).Statement) == 5 && jsondecode(aws_iam_role_policy.${n}.policy).Statement[2].Resource == aws_ssm_document.${n}.arn && toset(jsondecode(aws_iam_role_policy.${n}.policy).Statement[2].Action) == toset(["ssm:GetDocument","ssm:SendCommand"])`,'Preserve the existing worker and grant only this probe document.')+'\n'+assertion(`jsondecode(aws_iam_role_policy.${n}.policy).Statement[3].Condition.StringEquals["ssm:resourceTag/atmem-document-policy"] == output.host_verification.hostPolicySha256 && toset(jsondecode(aws_iam_role_policy.${n}.policy).Statement[4].Action) == toset(["ssm:GetCommandInvocation"])`,'Only tagged hosts and invocation reads; no caller-selected commands or Parameter Store authority.'));
|
|
9
|
+
text+=run('review_required',` variables { review_approved = false }\n expect_failures = [terraform_data.${n}_review]`);
|
|
10
|
+
text+=run('foreign_cluster_rejected',` variables { cluster_arn = "arn:aws:ecs:${region}:000000000000:cluster/foreign" }\n expect_failures = [terraform_data.${n}_review]`);
|
|
11
|
+
text+=run('foreign_worker_rejected',` variables { worker_role_arn = "arn:aws:iam::000000000000:role/foreign" }\n expect_failures = [terraform_data.${n}_review]`);
|
|
12
|
+
text+=run('wrong_worker_path_rejected',` variables { worker_role_arn = "arn:aws:iam::123456789012:role/incorrect-path/question-worker" }\n expect_failures = [terraform_data.${n}_review]`);
|
|
13
|
+
text+=run('region_drift_rejected',` variables { region = "us-east-1" }\n expect_failures = [var.region]`);
|
|
14
|
+
return text;
|
|
15
|
+
}
|