@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,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
// React escapes every token; previews never inject HTML or execute source.
|
|
3
|
+
export function CodePreview({content,name='',className=''}){
|
|
4
|
+
const source=typeof content==='string'?content:String(content);
|
|
5
|
+
const pattern=/("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/\/[^\n]*|#[^\n]*|\b(?:true|false|null|resource|variable|output|provider|terraform|module|locals|data)\b|\b\d+(?:\.\d+)?\b)/g;
|
|
6
|
+
const parts=source.split(pattern);
|
|
7
|
+
return <pre className={`code-preview ${className}`} aria-label={name||'Code preview'}><code>{parts.map((text,i)=>i%2?<span key={i} className={/^['"]/.test(text)?(/^\s*:/.test(parts[i+1]||'')?'code-key':'code-string'):/^(\/\/|#)/.test(text)?'code-comment':/^\d/.test(text)?'code-number':'code-keyword'}>{text}</span>:text)}</code></pre>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React,{useState} from 'react';
|
|
2
|
+
import {CheckCircle2, CircleHelp, Circle, RefreshCw, ChevronLeft, ChevronRight, MessageCircle, Plus, AlertCircle} from 'lucide-react';
|
|
3
|
+
import {reviewIssueLabel} from './component-review.mjs';
|
|
4
|
+
function QuestionAnswer({question,busy,onResolve}){
|
|
5
|
+
const [answer,setAnswer]=useState('');
|
|
6
|
+
return <details><summary>Record answer</summary><form onSubmit={e=>{e.preventDefault();if(answer.trim())onResolve(question.id,answer.trim());}}><label>Answer<textarea aria-label={`Answer: ${question.text}`} maxLength={2000} value={answer} onChange={e=>setAnswer(e.target.value)}/></label><button className="btn ghost" disabled={busy||!answer.trim()}>Save answer</button></form></details>;
|
|
7
|
+
}
|
|
8
|
+
function DetailAnswer({question,busy,onSave}){
|
|
9
|
+
const [answer,setAnswer]=useState('');const ownership=question.id==='detail-Ownership recorded',region=question.id==='detail-Cloud region selected';
|
|
10
|
+
return <form className="block-question-form" onSubmit={e=>{e.preventDefault();if(answer.trim())onSave(ownership?{origin:answer}:region?{config:{region:answer.trim()}}:{description:answer.trim()});}}><label>{ownership?'System ownership':region?'Cloud region':'Block responsibility'}{ownership?<select aria-label="System ownership" value={answer} onChange={e=>setAnswer(e.target.value)}><option value="">Choose…</option><option value="existing">Existing system</option><option value="proposed">Proposed component</option></select>:<textarea aria-label={region?'Cloud region answer':'Block responsibility answer'} maxLength={region?100:16000} value={answer} onChange={e=>setAnswer(e.target.value)}/>}</label><button className="btn ghost" disabled={busy||!answer.trim()}>Save detail</button></form>;
|
|
11
|
+
}
|
|
12
|
+
export function ReviewBadge({item,onClick}){
|
|
13
|
+
const Icon={reviewed:CheckCircle2,questions:CircleHelp,issues:AlertCircle,ready:Circle,changed:RefreshCw}[item.status];
|
|
14
|
+
const label=item.issues.length?reviewIssueLabel(item.issues):item.statusLabel,tone=item.status==='issues'?'questions':item.status;
|
|
15
|
+
return onClick?<button className={`block-review-badge ${tone} nodrag`} aria-label={`Review ${item.label}: ${label}`} title={`${label} · design review, not runtime validation`} onClick={e=>{e.stopPropagation();onClick();}}><Icon size={13}/>{label}</button>:<span className={`block-review-badge ${tone}`}><Icon size={14}/>{label}</span>;
|
|
16
|
+
}
|
|
17
|
+
export function ComponentReview({item,items,busy,onOpen,onConfirm,onDiscuss,onQuestion,onResolve,onDetailAnswer}){
|
|
18
|
+
const [adding,setAdding]=useState(false),[question,setQuestion]=useState('');const index=items.findIndex(r=>r.id===item.id);
|
|
19
|
+
return <section className="component-review" aria-label="Block design review">
|
|
20
|
+
<div className="block-review-heading"><strong>Block {index+1} of {items.length}</strong><ReviewBadge item={item}/></div>
|
|
21
|
+
<div className="block-review-progress"><progress aria-label="Blocks reviewed" max={items.length} value={items.filter(r=>r.status==='reviewed').length}/><small>{items.filter(r=>r.status==='reviewed').length} reviewed</small></div>
|
|
22
|
+
{item.status==='changed'&&<p>The block or its context changed. Check it again.</p>}
|
|
23
|
+
{item.architectNote&&<div className="block-architect-note"><strong>{item.architectNoteCurrent?'Architect’s review':'Previous review · context changed'}</strong><p>{item.architectNote}</p></div>}
|
|
24
|
+
<ul className="block-review-checks">{item.checks.map(c=><li key={c.label} className={c.passed?'complete':'incomplete'}>{c.passed?<CheckCircle2 size={14}/>:<CircleHelp size={14}/>} {c.label}</li>)}</ul>
|
|
25
|
+
{!!item.issues.length&&<ul className="block-review-questions">{item.issues.map(q=><li key={q.id}><strong>{q.kind==='finding'?'Design issue':'Question'}</strong><span>{q.text}</span>{q.action&&<small>Suggested next step: {q.action}</small>}{q.kind==='question'&&<QuestionAnswer question={q} busy={busy} onResolve={onResolve}/>} {q.kind==='detail'&&<DetailAnswer question={q} busy={busy} onSave={onDetailAnswer}/>} {q.kind==='finding'&&<button className="link" disabled={busy} onClick={()=>onDiscuss(q)}>Ask Architect to plan this fix</button>}</li>)}</ul>}
|
|
26
|
+
<div className="block-review-actions"><button className="btn primary" disabled={busy||item.issues.length>0||item.status==='reviewed'} onClick={onConfirm}><CheckCircle2 size={14}/>{item.status==='reviewed'?'Design reviewed':'Looks good'}</button><button className="btn ghost" disabled={busy} onClick={onDiscuss}><MessageCircle size={14}/>Ask Architect</button><button className="btn ghost" disabled={busy} onClick={()=>setAdding(!adding)}><Plus size={14}/>Question</button></div>
|
|
27
|
+
{adding&&<form className="block-question-form" onSubmit={e=>{e.preventDefault();if(question.trim()){onQuestion(question.trim());setQuestion('');setAdding(false);}}}><label>Question about this block<textarea aria-label="Question about this block" maxLength={1000} value={question} onChange={e=>setQuestion(e.target.value)} autoFocus/></label><button className="btn" disabled={busy||!question.trim()}>Save question</button></form>}
|
|
28
|
+
<small className="block-review-boundary">Design review only. Implementation and runtime checks are separate.</small>
|
|
29
|
+
<nav className="block-review-navigation" aria-label="Review blocks one by one"><button className="btn ghost" disabled={index===0} onClick={()=>onOpen(items[index-1].id)}><ChevronLeft size={14}/>Previous</button><button className="btn ghost" disabled={index===items.length-1} onClick={()=>onOpen(items[index+1].id)}>Next block<ChevronRight size={14}/></button></nav>
|
|
30
|
+
</section>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { consultingEstimate, WORK_PHASES, setDetail } from "./estimate.mjs";
|
|
3
|
+
import { resourcePlan } from "./resources.mjs";
|
|
4
|
+
const amount = (n, c) => n == null ? "Not estimated" : `${c} ${n.toLocaleString("en", { maximumFractionDigits: 2 })}`;
|
|
5
|
+
const number = (s) => s === "" || !Number.isFinite(Number(s)) || Number(s) < 0 ? undefined : Number(s);
|
|
6
|
+
|
|
7
|
+
export function DetailedCharges({ project, onChange }) {
|
|
8
|
+
const lines = project.estimate?.details || [];
|
|
9
|
+
const components = project.nodes.filter((n) => n.data.kind !== "note");
|
|
10
|
+
const change = (details) => onChange({ ...project, estimate: { ...(project.estimate || {}), details } });
|
|
11
|
+
return <details className="extra" aria-label="Detailed cloud charges">
|
|
12
|
+
<summary>Model detailed cloud costs</summary>
|
|
13
|
+
<p className="muted small">Add each billable dimension: for example input tokens, output tokens, requests, compute, storage and transfer. Detailed lines replace the coarse ROM line for that component. Enter a zero-cost line and explain why for an existing or free component.</p>
|
|
14
|
+
{lines.map((l, index) => {
|
|
15
|
+
const set = (patch) => onChange(setDetail(project, l.id, patch));
|
|
16
|
+
return <fieldset className="charge-editor" key={l.id}>
|
|
17
|
+
<legend>Charge {index + 1}</legend>
|
|
18
|
+
<div className="delivery-fields">
|
|
19
|
+
<label className="field">Component<select aria-label={`Charge ${index + 1} component`} value={l.componentId} onChange={(e) => set({ componentId: e.target.value })}>
|
|
20
|
+
{!components.some((n) => n.id === l.componentId) && <option value={l.componentId}>Removed component — reassign</option>}
|
|
21
|
+
{components.map((n) => <option key={n.id} value={n.id}>{n.data.label}</option>)}
|
|
22
|
+
</select></label>
|
|
23
|
+
{[['description','Charge description'],['quantity','Monthly quantity'],['unit','Unit'],['price','Unit price'],['currency','Currency'],['service','Service or SKU'],['region','Price region'],['source','Price source or quote'],['date','Price date']].map(([key,label]) => <label className="field" key={key}>{label}<input aria-label={`Charge ${index + 1} ${label.toLowerCase()}`} type={key === 'date' ? 'date' : 'text'} inputMode={['quantity','price'].includes(key) ? 'decimal' : undefined} value={l[key] ?? ''} onChange={(e) => set({ [key]: ['quantity','price'].includes(key) ? number(e.target.value) : key === 'currency' ? e.target.value.toUpperCase().slice(0,3) : e.target.value })} /></label>)}
|
|
24
|
+
</div>
|
|
25
|
+
<button className="btn ghost danger" onClick={() => change(lines.filter((x) => x.id !== l.id))}>Remove charge {index + 1}</button>
|
|
26
|
+
</fieldset>;
|
|
27
|
+
})}
|
|
28
|
+
<button className="btn" disabled={!components.length} onClick={() => change([...lines, { id: `charge-${crypto.randomUUID()}`, componentId: components[0].id, description: "", unit: "", currency: "USD", source: "", date: new Date().toISOString().slice(0,10), region: "", service: "" }])}>Add cloud charge</button>
|
|
29
|
+
</details>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function ConsultingView({ project, onChange }) {
|
|
33
|
+
const c = project.estimate?.consulting || {}, result = consultingEstimate(project);
|
|
34
|
+
const set = (patch) => onChange({ ...project, estimate: { ...(project.estimate || {}), consulting: { ...c, ...patch } } });
|
|
35
|
+
return <details className="extra" aria-label="Consulting and build estimate">
|
|
36
|
+
<summary>Consulting and build estimate · one-off</summary>
|
|
37
|
+
<p className="muted small">Enter person-days and your day rate; no rates or effort are assumed. Give the basis, scope and exclusions. Use zero days with a reason if a phase is out of scope. Person-days are effort, not calendar duration.</p>
|
|
38
|
+
<label className="field">Consulting currency<input aria-label="Consulting currency" value={c.currency || "USD"} onChange={(e) => set({ currency: e.target.value.toUpperCase().slice(0,3) })} /></label>
|
|
39
|
+
<div className="totals"><div><small>Expected one-off subtotal</small><strong>{amount(result.expected, result.currency)}</strong></div><div><small>Range</small><span>{result.low == null ? '—' : `${amount(result.low, result.currency)} – ${amount(result.high, result.currency)}`}</span></div><div><small>Status</small><span>{result.complete ? "All work packages estimated" : "Incomplete — work packages need estimates"}</span></div></div>
|
|
40
|
+
{WORK_PHASES.map(([id,label]) => {
|
|
41
|
+
const phase = c.phases?.[id] || {};
|
|
42
|
+
const edit = (patch) => set({ phases: { ...(c.phases || {}), [id]: { ...phase, ...patch } } });
|
|
43
|
+
return <fieldset className="charge-editor" key={id}><legend>{label}</legend><div className="delivery-fields">
|
|
44
|
+
{[['lowDays','Low days'],['days','Expected days'],['highDays','High days'],['rate','Day rate']].map(([key,name]) => <label className="field" key={key}>{name}<input aria-label={`${label} ${name.toLowerCase()}`} inputMode="decimal" value={phase[key] ?? ''} onChange={(e) => edit({ [key]: number(e.target.value) })} /></label>)}
|
|
45
|
+
<label className="field delivery-basis">Basis, scope and exclusions<textarea rows={2} aria-label={`${label} basis`} value={phase.basis || ''} onChange={(e) => edit({ basis: e.target.value })} /></label>
|
|
46
|
+
</div></fieldset>;
|
|
47
|
+
})}
|
|
48
|
+
<p className="muted small">Separate from cloud running costs. Taxes, licences, ongoing support and migration are excluded unless explicitly included.</p>
|
|
49
|
+
</details>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ResourcesView({ project }) {
|
|
53
|
+
const plan = resourcePlan(project);
|
|
54
|
+
return <details className="extra" aria-label="Cloud resource schedule"><summary>Resources and infrastructure coverage · {plan.rows.length} components</summary>
|
|
55
|
+
<p className="muted small">Derived from the current diagram. Generic services need confirmation. S3 foundation code covers buckets and their controls; drawn wires still need application integration and IAM.</p>
|
|
56
|
+
<div className="resource-schedule">{plan.rows.map((r) => <article key={r.id}><strong>{r.label}</strong><span>{r.provider.toUpperCase()} · {r.service} · {r.region}</span><small>{r.existing ? "Existing system" : r.selection} · {r.terraform}</small><small>{r.resources.join(', ') || 'Resource types to confirm'}</small><small>Cost dimensions: {r.costDrivers}</small></article>)}</div>
|
|
57
|
+
{plan.gaps.length > 0 && <p className="warn-text small">{plan.gaps.length} resource or integration tasks remain. The full list is included in the delivery pack.</p>}
|
|
58
|
+
</details>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {emulationGuidance,emulationRepairTask} from './emulation-guidance.mjs';
|
|
2
|
+
import {toProjectFile} from './project-file.mjs';
|
|
3
|
+
import {BrainActivity} from "./BrainActivity.jsx";
|
|
4
|
+
import React,{useState} from 'react';
|
|
5
|
+
import {Play,X,CheckCircle2,AlertCircle} from 'lucide-react';
|
|
6
|
+
import {emulationFeedback,emulationEvidenceSummary} from './emulation-feedback.mjs';
|
|
7
|
+
export function EmulationPanel({project,session,emulation,onDiscuss,discussionBusy=false,onOpenComponent,onClose,onBuild}){
|
|
8
|
+
const {result,previous,running}=emulation;
|
|
9
|
+
const [expanded,setExpanded]=useState(false),[taskError,setTaskError]=useState('');
|
|
10
|
+
const readiness=emulationGuidance(project,result)||emulation.preflight;
|
|
11
|
+
const guidance=readiness?.action==='build-and-test'&&!onBuild?{...readiness,next:'Application implementation is unavailable in this session. Download the repair task for the missing code and tests; testing can resume once that implementation is available.'}:readiness;
|
|
12
|
+
const feedback=emulationFeedback(project,result),thinking=discussionBusy&&!!result;
|
|
13
|
+
const title=running?'Testing this design…':thinking?'Architect is reviewing…':guidance?guidance.title:result?.status==='passed'?'Local checks passed':result?'Let’s resolve the next step':previous?'Design changed — rerun needed.':'Local implementation checks';
|
|
14
|
+
function saveReport(){const url=URL.createObjectURL(new Blob([JSON.stringify(result,null,2)],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download=`atmem-local-checks-r${project.revision}.json`;a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}
|
|
15
|
+
function saveTask(){try{toProjectFile(project);const url=URL.createObjectURL(new Blob([emulationRepairTask(project,result)],{type:'text/markdown'}));const a=document.createElement('a');a.href=url;a.download='architect-repair-task.md';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);setTaskError('');}catch(error){setTaskError(error.message);}}
|
|
16
|
+
const icon=(label,Icon,action,options={})=><button type="button" className={`emulation-icon ${options.active?'active':''}`} aria-label={label} title={label} onClick={action} disabled={options.disabled} aria-pressed={options.toggle?options.active:undefined}><Icon size={19}/></button>;
|
|
17
|
+
return <aside className={`emulation-panel ${running||thinking?'is-working':''}`} aria-label="Local checks">
|
|
18
|
+
<div className="emulation-heading"><span className={`emulation-orb ${running||thinking?'working':''}`}><BrainActivity size={34} active={running||thinking}/></span><div><span className="emulation-eyebrow">LOCAL CHECKS</span><h3 aria-live="polite">{title}</h3></div>{onClose&&icon('Close local checks',X,onClose)}</div>
|
|
19
|
+
<div className="emulation-controls row">
|
|
20
|
+
<button type="button" className="btn primary" aria-label="Run local checks" aria-busy={running||thinking} title={running?'Local checks running':thinking?'Architect is reviewing':emulation.preflight?'Complete the next step below before testing':'Run local checks'} disabled={running||thinking||!session?.token||!!emulation.preflight} onClick={()=>emulation.run()}>{running||thinking?<BrainActivity size={24}/>:<Play size={19}/>} Run local checks</button>
|
|
21
|
+
{running&&<button type="button" className="btn" onClick={emulation.stop}>Stop waiting</button>}
|
|
22
|
+
{result&&onDiscuss&&<button type="button" className="btn" disabled={discussionBusy||running} onClick={()=>onDiscuss(result,'question')}>Ask Architect about these results</button>}
|
|
23
|
+
<button type="button" className="btn" aria-expanded={expanded} onClick={()=>setExpanded(!expanded)}>{expanded?'Hide details':'Check details and settings'}</button>
|
|
24
|
+
</div>
|
|
25
|
+
<div className="emulation-track" aria-label="Local check progress"><span className={running?'current':result?'complete':''}>Test</span><i/><span className={thinking?'current':''}>Review</span><i/><span>Improve</span><i/><span>Retest</span></div>
|
|
26
|
+
{running&&<div className="emulation-progress" role="progressbar" aria-label="Running local checks"><span/></div>}
|
|
27
|
+
<p className="emulation-caption">{running?'Checking the generated code. Keep exploring the diagram.':thinking?'Your architect is working through the evidence in chat.':guidance?guidance.reason:result?emulationEvidenceSummary(result):'Runs local checks on the generated implementation and shows the results here. Monitoring needs its own implementation and configuration.'}</p>
|
|
28
|
+
{guidance&&!running&&!thinking&&<div className="emulation-next-step"><p><strong>Next:</strong> {guidance.next}</p>{guidance.action==='build-and-test'&&onBuild?<button className="btn primary" disabled={discussionBusy} onClick={onBuild}>Implement and check</button>:guidance.owner==='engineering'?<button className="btn primary" onClick={saveTask}>Download repair task</button>:guidance.needsModel?<button className="btn primary" disabled={discussionBusy} onClick={()=>onDiscuss?.(result,'correction')}>Work through this with Architect</button>:<button className="btn ghost" onClick={()=>setExpanded(true)}>See test details</button>}{guidance.componentId&&onOpenComponent&&<button className="link" onClick={()=>onOpenComponent(guidance.componentId)}>Review affected block</button>}{taskError&&<p role="alert">{taskError}</p>}</div>}
|
|
29
|
+
{emulation.retestLimitReached&&<p role="status">Two automatic retests are complete. Review the latest correction, then choose Run local checks to continue.</p>}
|
|
30
|
+
{expanded&&<div className="emulation-evidence">
|
|
31
|
+
<label><input type="checkbox" checked={emulation.coach} onChange={event=>emulation.setCoach(event.target.checked)}/> Ask Architect to explain new check results automatically</label>
|
|
32
|
+
<label><input type="checkbox" checked={emulation.autoRetest} onChange={event=>emulation.setAutoRetest(event.target.checked)}/> Rerun checks after an accepted correction (up to two attempts)</label>
|
|
33
|
+
<p>Corrections follow Draw as we talk and remain undoable. Stop waiting ends your wait; server work can continue until its time limit.</p>
|
|
34
|
+
{result?.artifactDigest&&<button type="button" className="btn" onClick={saveReport}>Download check report (JSON)</button>}
|
|
35
|
+
{feedback&&<p>{feedback.question}</p>}{result?.message&&<p>{result.message}</p>}
|
|
36
|
+
{result?.backend&&<p>{result.backend} {result.backendVersion} · r{result.designRevision} · {result.environment}</p>}
|
|
37
|
+
{result?.checks?.map((c,i)=><div className="emulation-check" key={i}>{c.passed?<CheckCircle2 size={16}/>:<AlertCircle size={16}/>}<span>{c.name}{c.message&&<small>{c.message}</small>}</span></div>)}
|
|
38
|
+
{result?.blockers?.map((b,i)=><p key={i}>{b}</p>)}
|
|
39
|
+
{!!result?.capabilities?.length&&<details><summary>What was checked</summary><ul>{result.capabilities.map((item,i)=><li key={i}>{item.service}: {item.mode}</li>)}</ul></details>}
|
|
40
|
+
{!!result?.emulatorPatches?.length&&<details><summary>Emulator compatibility fixes</summary><ul>{result.emulatorPatches.map(item=><li key={item.id}>{item.id}: {item.reason}</li>)}</ul></details>}
|
|
41
|
+
{!!result?.limitations?.length&&<details open><summary>Still requires cloud verification</summary><ul>{result.limitations.map((v,i)=><li key={i}>{v}</li>)}</ul></details>}
|
|
42
|
+
</div>}
|
|
43
|
+
</aside>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import {InlineNote} from './HelpNote.jsx';
|
|
2
|
+
import {BrainActivity} from "./BrainActivity.jsx";
|
|
3
|
+
import {systemDescription,requirementExplanation} from './system-description.mjs';
|
|
4
|
+
import { resolveCommentRevision } from "./consultation.mjs";
|
|
5
|
+
// Consultant views: Discover (qualify), Options and Decisions (design), Build card.
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { requirementNumber } from "./derive.mjs";
|
|
8
|
+
import { Plus, Trash2 } from "lucide-react";
|
|
9
|
+
import {
|
|
10
|
+
BRIEF_FIELDS,
|
|
11
|
+
CRITERIA,
|
|
12
|
+
GATE,
|
|
13
|
+
LEVELS,
|
|
14
|
+
OPTIONS,
|
|
15
|
+
STATUSES,
|
|
16
|
+
VERDICTS,
|
|
17
|
+
defaultAssessment,
|
|
18
|
+
newDecision,
|
|
19
|
+
optionScore,
|
|
20
|
+
} from "./engagement.mjs";
|
|
21
|
+
import { OPTION_NAMES } from "./pack.mjs";
|
|
22
|
+
import {
|
|
23
|
+
decisionHistory,
|
|
24
|
+
measureProblem,
|
|
25
|
+
reviseDecision,
|
|
26
|
+
} from "./records.mjs";
|
|
27
|
+
|
|
28
|
+
const area = (props) => <textarea rows={2} {...props} />;
|
|
29
|
+
|
|
30
|
+
// One understood topic: read as text; edit only when asked.
|
|
31
|
+
function Known({ topic, value, onSave }) {
|
|
32
|
+
const [editing, setEditing] = React.useState(false);
|
|
33
|
+
return (
|
|
34
|
+
<div className={`known-item ${topic.state}`}>
|
|
35
|
+
<dt>
|
|
36
|
+
{topic.label}
|
|
37
|
+
{topic.state === "assumed" && <em> · assumed</em>}
|
|
38
|
+
{!editing && (
|
|
39
|
+
<button className="link small" aria-label={`Edit ${topic.label}`} onClick={() => setEditing(true)}>
|
|
40
|
+
Edit
|
|
41
|
+
</button>
|
|
42
|
+
)}
|
|
43
|
+
</dt>
|
|
44
|
+
{editing ? (
|
|
45
|
+
<dd>
|
|
46
|
+
<textarea autoFocus rows={2} aria-label={topic.label} defaultValue={value} onBlur={(e) => (onSave(e.target.value), setEditing(false))} />
|
|
47
|
+
</dd>
|
|
48
|
+
) : (
|
|
49
|
+
<dd>{value}</dd>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// What we've understood from the chat, as a small card on the board.
|
|
56
|
+
export function DiscoverView({ project, onChange, topics, open = true, onToggle }) {
|
|
57
|
+
const brief = project.brief || {};
|
|
58
|
+
const useCases = project.useCases || [];
|
|
59
|
+
const setCase = (i, patch) =>
|
|
60
|
+
onChange({ ...project, useCases: useCases.map((u, j) => (j === i ? { ...u, ...patch } : u)) });
|
|
61
|
+
const facts = (project.facts || []).filter((f) => f.name !== "cloud.preference" && f.name !== "data.sensitive");
|
|
62
|
+
const heard = topics.filter((t) => t.state !== "open");
|
|
63
|
+
if (!open) return null;
|
|
64
|
+
return (
|
|
65
|
+
<section className="notes-card" aria-label="Discover">
|
|
66
|
+
<div className="doc-sub">
|
|
67
|
+
<h3>What we know so far</h3>
|
|
68
|
+
</div>
|
|
69
|
+
{heard.length > 0 && (
|
|
70
|
+
<>
|
|
71
|
+
<p className="muted small">Your words — edit anything.</p>
|
|
72
|
+
{facts.length > 0 && (
|
|
73
|
+
<div className="facts">
|
|
74
|
+
{facts.map((f) => (
|
|
75
|
+
<span key={f.id} className={`chip ${f.conflictsWith ? "missing" : ""}`} title={`${f.text} (${f.madeAt})`}>
|
|
76
|
+
{typeof f.value === "number" ? f.value.toLocaleString("en") : String(f.value)} {f.unit}
|
|
77
|
+
<button aria-label={`Forget ${f.unit}`} onClick={() => onChange({ ...project, facts: project.facts.filter((x) => x.id !== f.id) })}>
|
|
78
|
+
×
|
|
79
|
+
</button>
|
|
80
|
+
</span>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
<dl className="known">
|
|
85
|
+
{heard.map((t) => (
|
|
86
|
+
<Known
|
|
87
|
+
key={t.id}
|
|
88
|
+
topic={t}
|
|
89
|
+
value={brief[t.id] || ""}
|
|
90
|
+
onSave={(value) => onChange({ ...project, brief: { ...brief, [t.id]: value } })}
|
|
91
|
+
/>
|
|
92
|
+
))}
|
|
93
|
+
</dl>
|
|
94
|
+
<p className="muted small">
|
|
95
|
+
Still to cover, whenever you're ready:{" "}
|
|
96
|
+
{topics.filter((t) => t.state === "open").map((t) => t.label.toLowerCase()).join(", ") || "nothing — that's everything for now."}
|
|
97
|
+
</p>
|
|
98
|
+
</>
|
|
99
|
+
)}
|
|
100
|
+
<details className="extra">
|
|
101
|
+
<summary>Comparing ideas? Qualify use cases</summary>
|
|
102
|
+
<div className="cases compact">
|
|
103
|
+
{useCases.map((u, i) => (
|
|
104
|
+
<div key={u.id} className={`case verdict-${u.verdict.replace(" ", "-").toLowerCase()}`}>
|
|
105
|
+
<input aria-label="Use case" placeholder="Idea" value={u.title} onChange={(e) => setCase(i, { title: e.target.value })} />
|
|
106
|
+
<div className="seg verdicts">
|
|
107
|
+
{VERDICTS.map((v) => (
|
|
108
|
+
<button key={v} className={u.verdict === v ? "on" : ""} onClick={() => setCase(i, { verdict: v })}>
|
|
109
|
+
{v}
|
|
110
|
+
</button>
|
|
111
|
+
))}
|
|
112
|
+
</div>
|
|
113
|
+
<button className="icon" aria-label="Remove use case" onClick={() => onChange({ ...project, useCases: useCases.filter((_, j) => j !== i) })}>
|
|
114
|
+
<Trash2 size={13} />
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
))}
|
|
118
|
+
<button
|
|
119
|
+
className="link small"
|
|
120
|
+
onClick={() =>
|
|
121
|
+
onChange({
|
|
122
|
+
...project,
|
|
123
|
+
useCases: [...useCases, { id: `uc-${Date.now()}`, title: "", value: "Medium", risk: "Medium", fit: "Medium", verdict: "Park", reason: "" }],
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
>
|
|
127
|
+
+ Add use case
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
</details>
|
|
131
|
+
</section>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function OptionsView({ project, onDiscuss, onPreview, onChoose, onContinue, busy }) {
|
|
136
|
+
const notes = project.assessment?.notes || {};
|
|
137
|
+
const ready = Object.values(notes).some(Boolean);
|
|
138
|
+
const selected = [...(project.decisions||[])].reverse().find(d=>d.subject==='platform.cloud'&&d.status==='Accepted')?.decision;
|
|
139
|
+
return <section className="doc" aria-label="Choices">
|
|
140
|
+
<header className="doc-head"><h2>Choices</h2></header>
|
|
141
|
+
<button className="btn" onClick={() => onDiscuss('Help me compare AWS, Azure, Google Cloud and IBM for this design. Ask one useful question about my priorities, then refine the comparison from my answer. Populate optionAssessment when enough is known. Keep the diagram unchanged.')}>Discuss cloud options</button>
|
|
142
|
+
{selected && <p className="cloud-selection" role="status"><strong>Selected: {OPTION_NAMES[selected]}</strong> <button className="btn primary" disabled={busy} onClick={onContinue}>Review →</button></p>}
|
|
143
|
+
<DecisionsView project={project} onDiscuss={onDiscuss} embedded/>
|
|
144
|
+
<h3>Cloud alternatives</h3>
|
|
145
|
+
|
|
146
|
+
{['current','aws','azure','gcp','ibm'].filter(id => notes[id]).map(id => <article className="consultation-summary" key={id}>
|
|
147
|
+
<h3>{OPTION_NAMES[id]}{selected===id ? " · Selected" : ""}</h3><InlineNote label={`About ${OPTION_NAMES[id]}`}>{notes[id].replace(/^(?:AtMem AI Architect|At[Ff]orma) proposal — confirm: /,'')}</InlineNote>
|
|
148
|
+
<button className="link" onClick={() => onDiscuss(`Explain how ${OPTION_NAMES[id]} fits our design and the main tradeoff. Ask one question to help me decide; keep this as a discussion until I explicitly choose.`)}>Discuss this option</button>
|
|
149
|
+
{id!=="current" && <><button className="link" onClick={()=>onPreview(id)}>Preview service architecture</button><button className="btn primary" disabled={busy||selected===id} onClick={()=>onChoose(id)}>{selected===id ? `${OPTION_NAMES[id]} selected` : `${OPTION_NAMES[id]} →`}</button></>}
|
|
150
|
+
</article>)}
|
|
151
|
+
</section>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function DecisionsView({ project, onDiscuss, embedded=false }) {
|
|
155
|
+
const all = project.decisions || [];
|
|
156
|
+
const choices = all.filter(d => !d.subject?.startsWith('design.'));
|
|
157
|
+
const current = choices.filter(d => d.status !== 'Superseded');
|
|
158
|
+
const sketches = all.filter(d => d.subject?.startsWith('design.'));
|
|
159
|
+
const labels = {'platform.cloud':'Cloud provider','cloud.region':'Region','data.country':'Country / data residency','infrastructure.tool':'Infrastructure tool'};
|
|
160
|
+
return <section className={embedded?undefined:"doc"} aria-label="Decisions">
|
|
161
|
+
<header className="doc-head"><h2>Selected choices</h2></header>
|
|
162
|
+
<button className="btn" onClick={() => onDiscuss('Review our current design and explicit choices. Explain the next choice we need to make and ask me one useful question. Do not treat a sketch or a suggestion as an accepted decision.')}>Discuss the next choice</button>
|
|
163
|
+
{!current.length && <p className="muted">No choices selected.</p>}
|
|
164
|
+
{current.map(d => <article className="consultation-summary" key={d.id}>
|
|
165
|
+
<h3>{labels[d.subject] || d.title}</h3><p>{OPTION_NAMES[d.decision] || d.decision || 'Still being discussed'}</p>
|
|
166
|
+
<small className="muted">{d.status === 'Accepted' ? 'Confirmed in our conversation' : 'Proposed — still to discuss'}</small>
|
|
167
|
+
{(d.context||d.consequences)&&<InlineNote label="Decision details"><p>{d.context}</p><p>{d.consequences}</p></InlineNote>}
|
|
168
|
+
<button className="link" onClick={() => onDiscuss(`Let’s revisit our choice about ${labels[d.subject] || d.title}: ${d.decision}. Explain the tradeoff and ask what I would like to change before updating it.`)}>Revisit in chat</button>
|
|
169
|
+
{choices.some(h => h.subject===d.subject && h.status==='Superseded') && <details className="extra"><summary>Earlier choices</summary>{choices.filter(h => h.subject===d.subject && h.status==='Superseded').map(h=><p key={h.id}>{h.decision} {h.reason && `— ${h.reason}`}</p>)}</details>}
|
|
170
|
+
</article>)}
|
|
171
|
+
{!!sketches.length && <details className="extra"><summary>Diagram change history</summary>{sketches.map(d=><p key={d.id}>{d.title} · {d.decision}</p>)}</details>}
|
|
172
|
+
</section>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function BuildCard({ project, run, onChange, onOpen, environment, onEnvironment, onPlan, onPulumi, onPack }) {
|
|
176
|
+
const scope = project.nodes.filter((n) => n.data.poc);
|
|
177
|
+
const tested = run && run.revision === project.revision && run.status !== "running";
|
|
178
|
+
const results = Object.fromEntries(
|
|
179
|
+
(tested ? run.steps : []).flatMap((s) => (s.output?.measures || []).map((m) => [m.label, m])),
|
|
180
|
+
);
|
|
181
|
+
const outputs = project.nodes.filter((n) => n.data.measures?.length);
|
|
182
|
+
const gate = project.gate || {};
|
|
183
|
+
const unowned = (project.decisions || []).filter((d) => !d.owner?.trim()).length;
|
|
184
|
+
return (
|
|
185
|
+
<aside className="deploy build" aria-label="Build">
|
|
186
|
+
<h3>Prototype</h3>
|
|
187
|
+
<p className="muted small">
|
|
188
|
+
{scope.length}/{project.nodes.length} blocks in scope · faded blocks are out
|
|
189
|
+
</p>
|
|
190
|
+
<h4>Success criteria</h4>
|
|
191
|
+
{outputs.length ? (
|
|
192
|
+
<ul className="checks">
|
|
193
|
+
{outputs.flatMap((n) =>
|
|
194
|
+
n.data.measures.map((m) => {
|
|
195
|
+
const r = results[m.label];
|
|
196
|
+
return (
|
|
197
|
+
<li key={n.id + m.label} className={r ? (r.pass ? "ok" : "bad") : ""}>
|
|
198
|
+
<button className="link-plain" onClick={() => onOpen(n.id)}>
|
|
199
|
+
{m.label} {m.op} {String(m.target)}
|
|
200
|
+
{r && <span className="muted"> · {String(r.value)}</span>}
|
|
201
|
+
</button>
|
|
202
|
+
</li>
|
|
203
|
+
);
|
|
204
|
+
}),
|
|
205
|
+
)}
|
|
206
|
+
</ul>
|
|
207
|
+
) : (
|
|
208
|
+
<p className="muted small">Add measures to the final check block.</p>
|
|
209
|
+
)}
|
|
210
|
+
<h4>Scale gate</h4>
|
|
211
|
+
{GATE.map((g) => (
|
|
212
|
+
<label key={g.id} className="field">
|
|
213
|
+
{g.label}
|
|
214
|
+
{area({
|
|
215
|
+
"aria-label": g.label,
|
|
216
|
+
placeholder: g.hint,
|
|
217
|
+
value: gate[g.id] || "",
|
|
218
|
+
onChange: (e) => onChange({ ...project, gate: { ...gate, [g.id]: e.target.value } }),
|
|
219
|
+
})}
|
|
220
|
+
</label>
|
|
221
|
+
))}
|
|
222
|
+
{unowned > 0 && <p className="warn-text small">{unowned} decision{unowned > 1 ? "s" : ""} without an owner</p>}
|
|
223
|
+
<div className="seg">
|
|
224
|
+
{["staging", "production"].map((env) => (
|
|
225
|
+
<button key={env} className={environment === env ? "on" : ""} onClick={() => onEnvironment(env)}>
|
|
226
|
+
{env[0].toUpperCase() + env.slice(1)}
|
|
227
|
+
</button>
|
|
228
|
+
))}
|
|
229
|
+
</div>
|
|
230
|
+
<div className="row">
|
|
231
|
+
<button className="btn" onClick={onPlan}>
|
|
232
|
+
Export {environment} plan
|
|
233
|
+
</button>
|
|
234
|
+
<button className="btn ghost" onClick={onPulumi}>
|
|
235
|
+
Pulumi
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
<button className="btn primary" onClick={onPack}>
|
|
239
|
+
Client pack
|
|
240
|
+
</button>
|
|
241
|
+
</aside>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function MeasuresEditor({ measures = [], disabled, onChange }) {
|
|
246
|
+
const set = (i, patch) => onChange(measures.map((m, j) => (j === i ? { ...m, ...patch } : m)));
|
|
247
|
+
return (
|
|
248
|
+
<div className="measures">
|
|
249
|
+
<h4>Success measures</h4>
|
|
250
|
+
{measures.map((m, i) => (
|
|
251
|
+
<div key={i} className="measure">
|
|
252
|
+
<input aria-label="Measure label" disabled={disabled} value={m.label} onChange={(e) => set(i, { label: e.target.value })} />
|
|
253
|
+
<input aria-label="Measure field" disabled={disabled} className="mono" value={m.path} onChange={(e) => set(i, { path: e.target.value })} />
|
|
254
|
+
<select aria-label="Measure operator" disabled={disabled} value={m.op} onChange={(e) => set(i, { op: e.target.value })}>
|
|
255
|
+
<option>{">="}</option>
|
|
256
|
+
<option>{"<="}</option>
|
|
257
|
+
<option>{"=="}</option>
|
|
258
|
+
</select>
|
|
259
|
+
<input
|
|
260
|
+
aria-label="Measure target"
|
|
261
|
+
disabled={disabled}
|
|
262
|
+
value={String(m.target)}
|
|
263
|
+
onChange={(e) => {
|
|
264
|
+
const v = e.target.value;
|
|
265
|
+
set(i, { target: v === "true" ? true : v === "false" ? false : v !== "" && !isNaN(Number(v)) ? Number(v) : v });
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
<button className="icon" aria-label="Remove measure" disabled={disabled} onClick={() => onChange(measures.filter((_, j) => j !== i))}>
|
|
269
|
+
<Trash2 size={13} />
|
|
270
|
+
</button>
|
|
271
|
+
</div>
|
|
272
|
+
))}
|
|
273
|
+
<button
|
|
274
|
+
className="btn ghost"
|
|
275
|
+
disabled={disabled}
|
|
276
|
+
onClick={() => onChange([...measures, { label: "New measure", path: "status", op: "==", target: "passed" }])}
|
|
277
|
+
>
|
|
278
|
+
<Plus size={13} /> Add measure
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function RequirementComments({ requirement, comments, onSave, onRemove, target, number, onReview, onResolve, reviewing, onDiscuss }) {
|
|
285
|
+
const [open,setOpen]=React.useState(false), [text,setText]=React.useState(''), [quote,setQuote]=React.useState('');
|
|
286
|
+
const editor=React.useRef(null);
|
|
287
|
+
React.useEffect(()=>{if(target?.id===requirement.id){setQuote(target.quote);setOpen(true);}},[target,requirement.id]);
|
|
288
|
+
React.useEffect(()=>{if(open){editor.current?.focus();editor.current?.scrollIntoView({block:'nearest'});}},[open,target]);
|
|
289
|
+
const save=review=>{onSave(text.trim(),quote,review);setText('');setOpen(false);};
|
|
290
|
+
return <aside className="requirement-comments" aria-label={`Comments on ${requirement.id}`}>
|
|
291
|
+
{comments.filter(c=>!['reviewed','dismissed'].includes(c.status)).map(c=><div className="requirement-comment" key={c.id}>
|
|
292
|
+
<strong>Your feedback on {number}</strong>{c.selectedText && <blockquote>{c.selectedText}</blockquote>}<p>{c.text}</p>
|
|
293
|
+
{c.status==='proposed' ? <div className="requirement-revision">
|
|
294
|
+
<h5>Suggested revision — not applied yet</h5><p className="revision-label">Current wording</p><p>{c.before}</p>
|
|
295
|
+
<p className="revision-label">Proposed wording</p><p className="revision-after">{c.after}</p><p>{c.explanation}</p>
|
|
296
|
+
<button className="requirement-add-comment" onClick={()=>onResolve(c.id,true)}>Apply this revision</button>
|
|
297
|
+
<button className="link" onClick={()=>onResolve(c.id,false)}>Keep current wording</button>
|
|
298
|
+
</div> : <><p className="comment-status">{c.reviewMessage || 'Saved feedback. Ask AtMem AI Architect to suggest wording, then review the change here.'}</p><button className="requirement-add-comment" disabled={reviewing} onClick={()=>onReview([c])}>{reviewing?<><BrainActivity/> Preparing suggestions…</>:'Suggest a revision'}</button><button className="link" onClick={()=>onDiscuss(`Help me clarify my feedback on ${number} (${requirement.id}): ${c.text}. Ask one question before proposing a change.`)}>Discuss feedback</button></>}
|
|
299
|
+
<button className="link" onClick={()=>onRemove(c.id)} aria-label={`Remove comment ${c.id}`}>Remove</button>
|
|
300
|
+
</div>)}
|
|
301
|
+
{comments.some(c=>['reviewed','dismissed'].includes(c.status)) && <section className="comment-history"><h4>Feedback history</h4>{comments.filter(c=>['reviewed','dismissed'].includes(c.status)).map(c=><div key={c.id}><p>{c.text}</p><small>{c.status==='reviewed'?'Revision applied':'Original wording kept'}</small>{c.after&&<p>{c.after}</p>}</div>)}</section>}
|
|
302
|
+
{!open ? <button className="requirement-add-comment" onClick={()=>{setQuote('');setOpen(true);}}>Comment or request a change</button> : <div className="comment-composer">
|
|
303
|
+
<strong>Feedback on {number}</strong>{quote && <blockquote>{quote}</blockquote>}
|
|
304
|
+
<textarea ref={editor} aria-label={`Comment on ${requirement.id}`} value={text} maxLength={500} onChange={e=>setText(e.target.value)} placeholder="What doesn’t fit, what’s missing, or what would you like explained?" />
|
|
305
|
+
<p className="comment-status">AtMem AI Architect will suggest new wording below. You decide whether to apply it.</p>
|
|
306
|
+
<button className="btn" disabled={!text.trim()||reviewing} onClick={()=>save(true)}>Suggest a revision</button>
|
|
307
|
+
<button className="link" disabled={!text.trim()} onClick={()=>save(false)}>Save for later</button>
|
|
308
|
+
<button className="link" onClick={()=>{setOpen(false);setText('');}}>Cancel</button>
|
|
309
|
+
</div>}
|
|
310
|
+
</aside>;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function RequirementsView({checks,onContinue, project, onDiscuss, onChange, onReview, reviewing, onUpload, onOpenProject }) {
|
|
314
|
+
const [commentMenu,setCommentMenu]=React.useState(null), [commentTarget,setCommentTarget]=React.useState(null);
|
|
315
|
+
React.useEffect(()=>{
|
|
316
|
+
if(!commentMenu)return;
|
|
317
|
+
const dismiss=()=>setCommentMenu(null);
|
|
318
|
+
const key=e=>{if(e.key==='Escape')dismiss();};
|
|
319
|
+
document.addEventListener('pointerdown',dismiss);document.addEventListener('keydown',key);window.addEventListener('resize',dismiss);
|
|
320
|
+
return ()=>{document.removeEventListener('pointerdown',dismiss);document.removeEventListener('keydown',key);window.removeEventListener('resize',dismiss);};
|
|
321
|
+
},[commentMenu]);
|
|
322
|
+
function textMenu(event,r) {
|
|
323
|
+
const selection=window.getSelection();
|
|
324
|
+
if(!selection?.rangeCount || selection.isCollapsed)return;
|
|
325
|
+
const range=selection.getRangeAt(0), element=event.currentTarget;
|
|
326
|
+
if(!element.contains(range.startContainer) || !element.contains(range.endContainer))return;
|
|
327
|
+
const quote=selection.toString().trim();if(!quote)return;
|
|
328
|
+
event.preventDefault();event.stopPropagation();
|
|
329
|
+
setCommentMenu({id:r.id,quote:quote.slice(0,1500),x:Math.max(8,Math.min(event.clientX,window.innerWidth-250)),y:Math.max(8,Math.min(event.clientY,window.innerHeight-70))});
|
|
330
|
+
}
|
|
331
|
+
const needs = project.needs || [];
|
|
332
|
+
const comments = project.extensions?.requirementComments || [];
|
|
333
|
+
const pending = comments.filter(c=>c.status==='open' && needs.some(r=>r.id===c.requirementId));
|
|
334
|
+
const saveComments = value => onChange({...project,extensions:{...project.extensions,requirementComments:value}});
|
|
335
|
+
|
|
336
|
+
const names = Object.fromEntries(project.nodes.map(n => [n.id, n.data.label]));
|
|
337
|
+
const titles = {suggested:'Suggestions still to discuss',functional:'Functional requirements',security:'Security and access',compliance:'Data governance and compliance',availability:'Availability and recovery',reliability:'Reliability',performance:'Performance',scalability:'Capacity and growth',maintainability:'Operations and maintainability',cost:'Cost constraints',other:'Quality requirements'};
|
|
338
|
+
const groups = new Map();
|
|
339
|
+
for (const r of needs) {
|
|
340
|
+
const key = r.source==='agent' ? 'suggested' : r.kind === 'functional' ? 'functional' : r.category || 'other';
|
|
341
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
342
|
+
groups.get(key).push(r);
|
|
343
|
+
}
|
|
344
|
+
const questions = (project.openQuestions || []).filter(q=>q.status==='open');
|
|
345
|
+
const purpose = project.brief?.application || project.requirements;
|
|
346
|
+
const system=systemDescription(project);
|
|
347
|
+
return <section className="doc requirements-document" aria-label="Requirements">
|
|
348
|
+
<div className="requirements-actions">
|
|
349
|
+
{!!pending.length && <button className="btn" disabled={reviewing} onClick={()=>onReview(pending.slice(0,6))}>{reviewing?'Reviewing comments…':'Suggest revisions for saved feedback'} ({Math.min(6,pending.length)})</button>}
|
|
350
|
+
</div>
|
|
351
|
+
{commentMenu && <div className="ctx-menu requirement-text-menu" role="menu" aria-label="Selected text actions" style={{left:commentMenu.x,top:commentMenu.y}} onPointerDown={e=>e.stopPropagation()}>
|
|
352
|
+
<button role="menuitem" ref={node=>node?.focus({preventScroll:true})} onClick={()=>{setCommentTarget({...commentMenu,nonce:Date.now()});setCommentMenu(null);}}>Comment on selected text</button>
|
|
353
|
+
</div>}
|
|
354
|
+
<article className="requirements-paper" aria-label="Requirements document">
|
|
355
|
+
<header className="requirements-cover">
|
|
356
|
+
<p className="document-kicker">AtMem AI Architect · Working requirements</p>
|
|
357
|
+
<h2>{project.name || 'Solution requirements'}</h2>
|
|
358
|
+
<p className="document-subtitle">Where we’ve reached</p>
|
|
359
|
+
<p>This document brings together the requirements developed from our board and discussion. Proposed requirements remain open for review as the design evolves.</p>
|
|
360
|
+
<p className="document-meta">Design revision {project.revision || 1} · Working draft</p>
|
|
361
|
+
</header>
|
|
362
|
+
<section className="requirements-chapter"><h3>Purpose and scope</h3>
|
|
363
|
+
<p>{purpose || 'We are developing the scope together. Describe the outcome you want in chat, and this document will grow with the design.'}</p>
|
|
364
|
+
</section>
|
|
365
|
+
<section className="requirements-chapter"><h3>How the system works</h3><p>{system.overview}</p>{system.flows.map((f,i)=><div className="system-interaction" key={f.id}><h4>{i+1}. {f.title}</h4><p>{f.description}</p><p>{f.contract}</p><p className="document-meta">Proposed verification: {f.validation}</p></div>)}</section>
|
|
366
|
+
<section className="requirements-chapter"><h3>Component responsibilities and design rationale</h3>{system.components.map(c=><section key={c.id} id={`component-spec-${c.id}`} className="system-component"><h4>{c.name}</h4><p className="document-meta">{c.service} · {c.placement}</p><p>{c.purpose}</p><p>{c.boundary}</p><h5>Inputs and outputs</h5><ul>{[...c.inputs,...c.outputs].map((v,i)=><li key={i}>{v}</li>)}</ul>{!c.inputs.length&&!c.outputs.length&&<p>No connections are drawn for this component.</p>}<h5>Contribution to requirements</h5>{c.requirements.length?<ul>{c.requirements.map(r=><li key={r}>{r}</li>)}</ul>:<p>No requirement is explicitly linked to this component yet.</p>}<h5>Implementation and verification</h5><p>{c.implementation}</p><p className="document-meta">Engineering considerations, to be confirmed during implementation:</p><ul>{c.engineering.map(v=><li key={v}>{v}</li>)}</ul></section>)}</section>
|
|
367
|
+
{[...groups].sort(([a],[b])=>(a==='suggested')-(b==='suggested')).map(([key,rows])=>{const Tag='section';return <Tag className="requirements-chapter" key={key}>
|
|
368
|
+
{key==='suggested'?<h4>Suggestions still to discuss ({rows.length})</h4>:<h3>{titles[key] || 'Additional requirements'}</h3>}
|
|
369
|
+
{key==='suggested'&&<p className="document-meta">These are early considerations, not agreed requirements. We’ll discuss the most relevant ones as the design develops.</p>}
|
|
370
|
+
{rows.map(r=><div className="requirement-paragraph" key={r.id}>
|
|
371
|
+
<h4 className="requirement-number">{requirementNumber(project,r.id)}</h4>
|
|
372
|
+
<p className="requirement-text" onContextMenu={event=>textMenu(event,r)}>{r.statement}</p>
|
|
373
|
+
{r.measure && <p className="acceptance-target">Acceptance target: {r.measure.metric} {r.measure.op} {r.measure.target} {r.measure.unit || ''}.</p>}
|
|
374
|
+
<p className="document-meta">{r.source === 'agent' ? 'Proposed for discussion' : 'Recorded from our discussion'}{r.links?.length ? ` · Related design: ${r.links.map(id=>names[id] || 'earlier component').join(', ')}` : ''}</p>
|
|
375
|
+
<div className="requirement-reason"><p><strong>{r.rationale?"Why this matters.":"Design explanation (guidance)."}</strong> {requirementExplanation(r)}</p>{r.basis&&<p className="document-meta">Basis: {r.basis}</p>}<button className="link document-discuss" onClick={()=>onDiscuss(`Explain why ${requirementNumber(project,r.id)} (${r.id}) is needed: ${r.statement}. Use the actual board and discussion as evidence, distinguish assumptions, explain the consequence of leaving it out, and ask whether it fits my needs.`)}>Explain why we need this</button></div>
|
|
376
|
+
<RequirementComments onReview={onReview} reviewing={reviewing} onDiscuss={onDiscuss} onResolve={(id,accept)=>onChange(resolveCommentRevision(project,id,accept))} target={commentTarget} number={requirementNumber(project,r.id)} requirement={r} comments={comments.filter(c=>c.requirementId===r.id)} onSave={(text,selectedText,review)=>{const comment={id:crypto.randomUUID(),requirementId:r.id,text,selectedText,statement:r.statement,status:'open',createdAt:new Date().toISOString()};saveComments([...comments,comment]);if(review)onReview([comment]);}} onRemove={id=>saveComments(comments.filter(c=>c.id!==id))} />
|
|
377
|
+
</div>)}
|
|
378
|
+
<button className="link document-discuss" onClick={()=>onDiscuss(`Let's discuss the ${titles[key] || key} section of our requirements document further. Current requirements: ${rows.map(r=>`${r.id}: ${r.statement}`).join('; ')}. Ask one useful question and refine the document from my answer.`)}>Discuss this section further in chat</button>
|
|
379
|
+
</Tag>;})}
|
|
380
|
+
{!needs.length && <section className="requirements-chapter"><h3>Requirements to develop</h3><p>As we discuss the idea and shape the board, AtMem AI Architect will summarise the functional requirements, constraints and acceptance targets here.</p></section>}
|
|
381
|
+
{!!project.assumptions?.length && <section className="requirements-chapter"><h3>Assumptions to validate</h3>{project.assumptions.map(a=><p key={a.id}>{a.statement}</p>)}</section>}
|
|
382
|
+
{!!questions.length && <section className="requirements-chapter"><h3>Open questions</h3>{questions.map(q=><p key={q.id}>{q.question}</p>)}<button className="link document-discuss" onClick={()=>onDiscuss(`Help resolve the open questions in our requirements document, one at a time. Start with the question that most affects the design.`)}>Let’s work through these in chat</button></section>}
|
|
383
|
+
{checks&&<section className="requirements-chapter"><h3>Design checks and decisions</h3>{checks}</section>}
|
|
384
|
+
<footer className="document-footer">{onContinue&&<button className="btn primary" disabled={reviewing} onClick={onContinue}>Continue to Choices →</button>}</footer>
|
|
385
|
+
</article>
|
|
386
|
+
</section>;
|
|
387
|
+
}
|