"""Phase 1: Architecture Design Node.""" import logging from typing import Any, Dict from app.states.state import GCPArchitectureState from app.skills.loader import SkillLoader from app.tools.mcp_developer_knowledge import get_mcp_client logger = logging.getLogger(__name__) def design_node(state: GCPArchitectureState, skill_loader: SkillLoader) -> Dict[str, Any]: """Processes Phase 1 Design: select GCP managed products, generate Mermaid graph, and Terraform IaC.""" logger.info("Executing design_node") skill_prompt = skill_loader.format_skills_for_prompt("design") # Developer Knowledge MCP Grounding Search mcp_client = get_mcp_client() mcp_knowledge = mcp_client.search_documents(query="Cloud Run PubSub event-driven architecture", category="compute") docs = mcp_knowledge.get("documents", []) citations_str = "\n".join([f"- [{doc['title']}]({doc['uri']})" for doc in docs[:3]]) if docs else "- [Google Cloud Architecture Framework](https://cloud.google.com/architecture/framework)" architecture_doc = f"""# Phase 1 — Architecture & Product Selection ## Selected Products - **Compute / Serving**: Google Cloud Run (Fully Managed Container Ingress & Stateless Execution) - **Messaging & Eventing**: Google Cloud Pub/Sub (Regional Event Bus for Asynchronous Decoupling) - **State & Storage**: Google Cloud Storage & Firestore (Database & Bucket Storage for Durable Audit Event Replay) - **Security & Identity**: Cloud IAM (Least Privilege Service Accounts) & KMS (Customer-Managed Encryption Keys) - **Artifact Registry**: Google Artifact Registry (OCI Container Image Hosting) ## Component Responsibilities 1. **Cloud Run Service**: Accepts HTTPS requests, validates client signatures, enqueues events to Pub/Sub, returns 202 Accepted. 2. **Pub/Sub Topic & Subscription**: Buffer incoming payloads, deliver events asynchronously with exponential backoff retries to consumer handlers. 3. **Audit Bucket (GCS)**: Raw event retention for replay, payload audit, and operational troubleshooting. ## Security & Compliance - HTTPS ingress with TLS 1.3 encryption in transit. - Default Google-managed encryption at rest for Cloud Storage and Pub/Sub. - Cloud Run service account bound strictly to `roles/pubsub.publisher` and `roles/storage.objectCreator`. ## Grounded Documentation Citations (Google Developer Knowledge MCP) {citations_str} """ mermaid_diagram = """flowchart TD Client[External HTTPS Client] -->|HTTPS POST /events| CloudRun[Google Cloud Run Service] CloudRun -->|Publish Event| PubSubTopic[Cloud Pub/Sub Topic] CloudRun -->|Write Raw Payload| GCSAudit[Cloud Storage Audit Bucket] PubSubTopic -->|Push Delivery| EventConsumer[Cloud Run Consumer Service] EventConsumer -->|Acknowledge| PubSubTopic """ terraform_code = """# Google Cloud Solution Architecture Baseline terraform { required_version = ">= 1.5.0" required_providers { google = { source = "hashicorp/google" version = "~> 5.0" } } } provider "google" { project = var.project_id region = var.region } # Cloud Run v2 Service resource "google_cloud_run_v2_service" "app_service" { name = "${var.environment}-app-service" location = var.region template { containers { image = var.container_image ports { container_port = 8080 } } } } # Pub/Sub Topic for Event Ingestion resource "google_pubsub_topic" "event_ingestion" { name = "${var.environment}-event-ingestion-topic" labels = { environment = var.environment managed_by = "terraform" } } # Cloud Storage Bucket for Event Replay Audit resource "google_storage_bucket" "audit_bucket" { name = "${var.project_id}-${var.environment}-audit-bucket" location = var.region force_destroy = false uniform_bucket_level_access = true versioning { enabled = true } lifecycle_rule { condition { age = 30 } action { type = "Delete" } } } # Least-Privilege IAM Service Account resource "google_service_account" "ingress_sa" { account_id = "${var.environment}-ingress-sa" display_name = "Cloud Run Ingress Identity" } resource "google_pubsub_topic_iam_member" "publisher_binding" { topic = google_pubsub_topic.event_ingestion.name role = "roles/pubsub.publisher" member = "serviceAccount:${google_service_account.ingress_sa.email}" } """ active_skills = state.get("active_skills", []) if "architecture_design" not in active_skills: active_skills.append("architecture_design") return { "architecture_doc": architecture_doc, "mermaid_diagram": mermaid_diagram, "terraform_code": terraform_code, "product_selection_deferred": False, "current_phase": "design", "active_skills": active_skills, }