From a8e55a4a8df308b8926cde05cb957d59ccb92643 Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Thu, 16 Apr 2026 13:04:44 -0300 Subject: [PATCH] update architecture docs for split MCP files, new clusters, Langfuse, and EC2 deploy --- docs/graphs/deployment.dot | 104 ++++---- docs/graphs/deployment.svg | 394 ++++++++++++++++++------------- docs/graphs/repo_structure.dot | 45 ++-- docs/graphs/repo_structure.svg | 417 ++++++++++++++++----------------- docs/index.html | 39 +-- 5 files changed, 530 insertions(+), 469 deletions(-) diff --git a/docs/graphs/deployment.dot b/docs/graphs/deployment.dot index 9051c94..1f9d006 100644 --- a/docs/graphs/deployment.dot +++ b/docs/graphs/deployment.dot @@ -5,50 +5,46 @@ digraph deployment { node [fontname="Helvetica" fontsize=10 style=filled color="#1e2a4a" fontcolor="#e8eaf0"] edge [fontname="Helvetica" fontsize=9 fontcolor="#8892a8" color="#4a5568"] - label="Deployment — Kind Cluster (dev) / EC2 (prod)" + label="Deployment — Kind Clusters (dev) / EC2 (prod)" labelloc=t fontsize=14 fontcolor="#0066ff" - user [label="Browser\nlocalhost:8040" fillcolor="#243056" shape=box] + user [label="Browser" fillcolor="#243056" shape=box] - subgraph cluster_kind { - label="Kind Cluster: unt (namespace: unt)" + subgraph cluster_ec2 { + label="EC2 (mcrn.ar)" + color="#ff9800" + fontcolor="#ff9800" + style=rounded + + nginx_edge [label="nginx (gateway container)\n443 · SSL\nstellarair.mcrn.ar\nlangfuse.mcrn.ar\nnova.mcrn.ar (Kong backend)" fillcolor="#121829" shape=box] + nova_ui [label="nova-ui\nnginx + Vue build" fillcolor="#0d1a33" shape=box] + nova_api [label="nova-api\nuvicorn · FastAPI\nMCP clients (stdio)" fillcolor="#0d1a33" shape=box] + } + + subgraph cluster_kind_unt { + label="Kind Cluster: unt (local dev)" color="#0066ff" fontcolor="#0066ff" style=rounded - subgraph cluster_frontend_pod { - label="Pod: ui" - color="#1e2a4a" - fontcolor="#4a5568" - ui [label="nginx\n:80\n\nVue SPA\nProxy → api:8000" fillcolor="#121829" shape=box] - ui_svc [label="Service: ui\nNodePort 30040" fillcolor="#0d1a33" shape=diamond fontsize=9] - } + unt_ui [label="ui pod\nnginx + Vue\nNodePort 30040" fillcolor="#121829" shape=box] + unt_api [label="api pod\nuvicorn · FastAPI" fillcolor="#121829" shape=box] + } - subgraph cluster_api_pod { - label="Pod: api" - color="#1e2a4a" - fontcolor="#4a5568" - api [label="uvicorn\n:8000\n\nFastAPI\nMCP clients (stdio)\nLangGraph agents" fillcolor="#121829" shape=box] - api_svc [label="Service: api\nClusterIP" fillcolor="#0d1a33" shape=diamond fontsize=9] - } + subgraph cluster_kind_lng { + label="Kind Cluster: lng (shared observability)" + color="#00c853" + fontcolor="#00c853" + style=rounded - subgraph cluster_langfuse_pod { - label="Pod: langfuse" - color="#1e2a4a" - fontcolor="#4a5568" - langfuse [label="Langfuse\n:3000\n\nTrace viewer" fillcolor="#121829" shape=box] - langfuse_svc [label="Service: langfuse\nNodePort 30030" fillcolor="#0d1a33" shape=diamond fontsize=9] - } - - subgraph cluster_pg_pod { - label="Pod: postgres" - color="#1e2a4a" - fontcolor="#4a5568" - pg [label="PostgreSQL\n:5432\n\nLangfuse data" fillcolor="#121829" shape=cylinder] - pg_svc [label="Service: postgres\nClusterIP" fillcolor="#0d1a33" shape=diamond fontsize=9] - } + lf_web [label="langfuse-web\nNext.js\nNodePort 30030" fillcolor="#121829" shape=box] + lf_worker [label="langfuse-worker\nClickHouse writer" fillcolor="#121829" shape=box] + lf_ch [label="ClickHouse\ntraces · spans" fillcolor="#0d1a33" shape=cylinder] + lf_pg [label="Postgres\nmetadata" fillcolor="#0d1a33" shape=cylinder] + lf_redis [label="Redis\nqueue · cache" fillcolor="#0d1a33" shape=cylinder] + lf_minio [label="MinIO\nS3 events" fillcolor="#0d1a33" shape=cylinder] } subgraph cluster_external { @@ -58,25 +54,33 @@ digraph deployment { style=dashed ext_weather [label="OpenMeteo" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"] ext_faa [label="FAA" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"] - ext_bedrock [label="AWS Bedrock" fillcolor="#243056" shape=octagon] - ext_kong [label="Kong Konnect\n(optional)" fillcolor="#243056" shape=octagon style="filled,dashed"] + ext_bedrock [label="Bedrock / Groq\nAnthropic / OpenAI" fillcolor="#243056" shape=octagon] + ext_kong [label="Kong Konnect\n(optional gateway)" fillcolor="#243056" shape=octagon style="filled,dashed"] } - // Port mappings - user -> ui_svc [label="host:8040 → 30040" color="#0066ff"] - ui_svc -> ui - ui -> api_svc [label="proxy"] - api_svc -> api + // Prod path + user -> nginx_edge [label="stellarair.mcrn.ar" color="#ff9800"] + user -> ext_kong [label="(API governance)" style=dashed color="#4a5568"] + ext_kong -> nginx_edge [label="X-Gateway-Secret" style=dashed color="#4a5568"] + nginx_edge -> nova_ui + nova_ui -> nova_api [label="/agents /scenarios"] + nova_api -> ext_weather [label="HTTP" color="#00c853"] + nova_api -> ext_faa [label="HTTP" color="#00c853"] + nova_api -> ext_bedrock [label="LLM calls" style=dashed] - api -> ext_weather [label="HTTP" color="#00c853"] - api -> ext_faa [label="HTTP" color="#00c853"] - api -> ext_bedrock [label="Converse API" style=dashed] - api -> langfuse_svc [label="traces" style=dotted] + // Dev path + user -> unt_ui [label="localhost:8040" color="#0066ff"] + unt_ui -> unt_api + unt_api -> ext_weather [style=dashed color="#00c853"] + unt_api -> ext_faa [style=dashed color="#00c853"] + unt_api -> ext_bedrock [style=dashed] - langfuse_svc -> langfuse - langfuse -> pg_svc - pg_svc -> pg - - user -> ext_kong [style=dashed label="(optional)" color="#4a5568"] - ext_kong -> ui_svc [style=dashed color="#4a5568"] + // Observability (shared by both dev and prod API) + unt_api -> lf_web [label="OTLP traces" style=dotted color="#00c853"] + nova_api -> lf_web [label="OTLP traces" style=dotted color="#00c853"] + lf_web -> lf_redis + lf_worker -> lf_redis + lf_worker -> lf_ch + lf_web -> lf_pg + lf_web -> lf_minio } diff --git a/docs/graphs/deployment.svg b/docs/graphs/deployment.svg index 79f8c87..47dad84 100644 --- a/docs/graphs/deployment.svg +++ b/docs/graphs/deployment.svg @@ -4,222 +4,280 @@ - - + + deployment - -Deployment — Kind Cluster (dev) / EC2 (prod) + +Deployment — Kind Clusters (dev) / EC2 (prod) -cluster_kind - -Kind Cluster: unt  (namespace: unt) +cluster_ec2 + +EC2 (mcrn.ar) -cluster_frontend_pod - -Pod: ui +cluster_kind_unt + +Kind Cluster: unt (local dev) -cluster_api_pod - -Pod: api +cluster_kind_lng + +Kind Cluster: lng (shared observability) -cluster_langfuse_pod - -Pod: langfuse - - -cluster_pg_pod - -Pod: postgres - - cluster_external - -External APIs + +External APIs user - -Browser -localhost:8040 + +Browser - - -ui_svc - -Service: ui -NodePort 30040 + + +nginx_edge + +nginx (gateway container) +443 · SSL +stellarair.mcrn.ar +langfuse.mcrn.ar +nova.mcrn.ar (Kong backend) - + -user->ui_svc - - -host:8040 → 30040 +user->nginx_edge + + +stellarair.mcrn.ar + + + +unt_ui + +ui pod +nginx + Vue +NodePort 30040 + + + +user->unt_ui + + +localhost:8040 - + ext_kong - -Kong Konnect -(optional) + +Kong Konnect +(optional gateway) - -user->ext_kong - - -(optional) - - - -ui - -nginx -:80 -Vue SPA -Proxy → api:8000 - - - -api_svc - -Service: api -ClusterIP - - - -ui->api_svc - - -proxy - - -ui_svc->ui - - +user->ext_kong + + +(API governance) - + + +nova_ui + +nova-ui +nginx + Vue build + + + +nginx_edge->nova_ui + + + + -api - -uvicorn -:8000 -FastAPI -MCP clients (stdio) -LangGraph agents +nova_api + +nova-api +uvicorn · FastAPI +MCP clients (stdio) - + + +nova_ui->nova_api + + +/agents /scenarios + + -langfuse_svc - -Service: langfuse -NodePort 30030 +lf_web + +langfuse-web +Next.js +NodePort 30030 - - -api->langfuse_svc - - -traces + + +nova_api->lf_web + + +OTLP traces - + ext_weather - -OpenMeteo + +OpenMeteo - - -api->ext_weather - - -HTTP + + +nova_api->ext_weather + + +HTTP - + ext_faa - -FAA + +FAA - - -api->ext_faa - - -HTTP + + +nova_api->ext_faa + + +HTTP - + ext_bedrock - -AWS Bedrock + +Bedrock / Groq +Anthropic / OpenAI - - -api->ext_bedrock - - -Converse API + + +nova_api->ext_bedrock + + +LLM calls - - -api_svc->api - - - - + -langfuse - -Langfuse -:3000 -Trace viewer +unt_api + +api pod +uvicorn · FastAPI - - -pg_svc - -Service: postgres -ClusterIP - - + -langfuse->pg_svc - - +unt_ui->unt_api + + - - -langfuse_svc->langfuse - - + + +unt_api->lf_web + + +OTLP traces - - -pg - - -PostgreSQL -:5432 -Langfuse data - - + -pg_svc->pg - - +unt_api->ext_weather + + - + + +unt_api->ext_faa + + + + -ext_kong->ui_svc - - +unt_api->ext_bedrock + + + + + +lf_pg + + +Postgres +metadata + + + +lf_web->lf_pg + + + + + +lf_redis + + +Redis +queue · cache + + + +lf_web->lf_redis + + + + + +lf_minio + + +MinIO +S3 events + + + +lf_web->lf_minio + + + + + +lf_worker + +langfuse-worker +ClickHouse writer + + + +lf_ch + + +ClickHouse +traces · spans + + + +lf_worker->lf_ch + + + + + +lf_worker->lf_redis + + + + + +ext_kong->nginx_edge + + +X-Gateway-Secret diff --git a/docs/graphs/repo_structure.dot b/docs/graphs/repo_structure.dot index 486b67a..f981bae 100644 --- a/docs/graphs/repo_structure.dot +++ b/docs/graphs/repo_structure.dot @@ -14,70 +14,67 @@ digraph repo_structure { mcp [label="mcp_servers/" fillcolor="#121829"] agents [label="agents/" fillcolor="#121829"] - irrop [label="irrop/" fillcolor="#121829"] api [label="api/" fillcolor="#121829"] ui_root [label="ui/" fillcolor="#121829"] ctrl [label="ctrl/" fillcolor="#121829"] + tests [label="tests/" fillcolor="#121829"] + woodpecker [label=".woodpecker/" fillcolor="#121829"] docs [label="docs/" fillcolor="#121829"] // MCP subtree - mcp_shared [label="shared/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] - mcp_ops [label="ops/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] - mcp_pax [label="passenger/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] - mcp_data [label="data/\nmodels.py\nreal/ (openmeteo, faa)\nmock/\nscenarios/ (4 scenarios)" fillcolor="#0d1a33" shape=box] + mcp_shared [label="shared/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box] + mcp_ops [label="ops/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box] + mcp_pax [label="passenger/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box] + mcp_llm [label="shared_llm.py\nGroq · Anthropic\nBedrock · OpenAI" fillcolor="#0d1a33" shape=box] + mcp_data [label="data/\nmodels.py\nreal/ (openmeteo, faa)\nscenarios/ (4 scenarios)" fillcolor="#0d1a33" shape=box] // Agents subtree - ag_efhas [label="fce.py\nFCE agent" fillcolor="#1a1a3a" shape=box] + ag_fce [label="fce.py\nFCE agent" fillcolor="#1a1a3a" shape=box] ag_handover [label="handover.py\nHandover agent" fillcolor="#1a1a3a" shape=box] - ag_shared [label="shared/\nmcp_client.py\nllm.py" fillcolor="#1a1a3a" shape=box] - - // IRROP subtree - ir_models [label="models/\nflight, passenger\ncrew, recovery" fillcolor="#1a2a1a" shape=box] - ir_rules [label="rules/\nfaa_part117\nrebooking\ncompensation" fillcolor="#1a2a1a" shape=box] - ir_pipeline [label="pipeline/\ningest → triage →\nrebook → compensate" fillcolor="#1a2a1a" shape=box] + ag_shared [label="shared/\nmcp_client.py\nparser.py · tool_runner.py" fillcolor="#1a1a3a" shape=box] // API subtree - api_main [label="main.py\nFastAPI + WebSocket" fillcolor="#2a1a1a" shape=box] - api_routes [label="routes/\nagents, scenarios, ws" fillcolor="#2a1a1a" shape=box] + api_main [label="main.py\nFastAPI + WebSocket\nhealth · runs · Langfuse" fillcolor="#2a1a1a" shape=box] + api_config [label="config.py\nPydantic Settings" fillcolor="#2a1a1a" shape=box] // UI subtree ui_fw [label="framework/\nsoleprint-ui\n(shared component lib)" fillcolor="#2a2a0d" shape=box] - ui_app [label="app/\nVue 3 SPA\npages/ components/\nmars-tokens.css" fillcolor="#2a2a0d" shape=box] + ui_app [label="app/\nVue 3 SPA\nconfig.ts (Kong proxy)" fillcolor="#2a2a0d" shape=box] // Ctrl subtree - ctrl_docker [label="Dockerfile.api\nDockerfile.ui\nnginx.conf\ndocker-compose.yml" fillcolor="#1a1a2a" shape=box] + ctrl_docker [label="Dockerfile.api\nDockerfile.ui\nnginx.conf" fillcolor="#1a1a2a" shape=box] ctrl_k8s [label="k8s/\nbase/ overlays/dev/\nkind-config.yaml" fillcolor="#1a1a2a" shape=box] - ctrl_tilt [label="Tiltfile\ntilt_config.json" fillcolor="#1a1a2a" shape=box] + ctrl_edge [label="edge/\ndocker-compose.yml\n(production)" fillcolor="#1a1a2a" shape=box] + ctrl_tilt [label="Tiltfile\ndeploy.sh" fillcolor="#1a1a2a" shape=box] // Edges root -> mcp root -> agents - root -> irrop root -> api root -> ui_root root -> ctrl + root -> tests + root -> woodpecker root -> docs mcp -> mcp_shared mcp -> mcp_ops mcp -> mcp_pax + mcp -> mcp_llm mcp -> mcp_data - agents -> ag_efhas + agents -> ag_fce agents -> ag_handover agents -> ag_shared - irrop -> ir_models - irrop -> ir_rules - irrop -> ir_pipeline - api -> api_main - api -> api_routes + api -> api_config ui_root -> ui_fw ui_root -> ui_app ctrl -> ctrl_docker ctrl -> ctrl_k8s + ctrl -> ctrl_edge ctrl -> ctrl_tilt } diff --git a/docs/graphs/repo_structure.svg b/docs/graphs/repo_structure.svg index fad761a..028c3bb 100644 --- a/docs/graphs/repo_structure.svg +++ b/docs/graphs/repo_structure.svg @@ -4,339 +4,334 @@ - - + + repo_structure - -Repository Structure + +Repository Structure root - -stellar-ops/ + +stellar-ops/ mcp - -mcp_servers/ + +mcp_servers/ root->mcp - - + + agents - -agents/ + +agents/ root->agents - - - - - -irrop - -irrop/ - - - -root->irrop - - + + - + api - -api/ + +api/ - + root->api - - + + - + ui_root - -ui/ + +ui/ - + root->ui_root - - + + - + ctrl - -ctrl/ + +ctrl/ - + root->ctrl - - + + + + + +tests + +tests/ + + + +root->tests + + + + + +woodpecker + +.woodpecker/ + + + +root->woodpecker + + - + docs - -docs/ + +docs/ - + root->docs - - + + - + mcp_shared - -shared/ -server.py -tools/ resources/ prompts/ + +shared/ +server.py +tools.py · resources.py · prompts.py - + mcp->mcp_shared - - + + - + mcp_ops - -ops/ -server.py -tools/ resources/ prompts/ + +ops/ +server.py +tools.py · resources.py · prompts.py - + mcp->mcp_ops - - + + - + mcp_pax - -passenger/ -server.py -tools/ resources/ prompts/ + +passenger/ +server.py +tools.py · resources.py · prompts.py - + mcp->mcp_pax - - + + + + + +mcp_llm + +shared_llm.py +Groq · Anthropic +Bedrock · OpenAI + + + +mcp->mcp_llm + + - + mcp_data - -data/ -models.py -real/ (openmeteo, faa) -mock/ -scenarios/ (4 scenarios) + +data/ +models.py +real/ (openmeteo, faa) +scenarios/ (4 scenarios) - + mcp->mcp_data - - + + - - -ag_efhas - -fce.py -FCE agent + + +ag_fce + +fce.py +FCE agent - - -agents->ag_efhas - - + + +agents->ag_fce + + - + ag_handover - -handover.py -Handover agent + +handover.py +Handover agent - + agents->ag_handover - - + + - + ag_shared - -shared/ -mcp_client.py -llm.py + +shared/ +mcp_client.py +parser.py · tool_runner.py - -agents->ag_shared - - - - - -ir_models - -models/ -flight, passenger -crew, recovery - - - -irrop->ir_models - - - - - -ir_rules - -rules/ -faa_part117 -rebooking -compensation - - -irrop->ir_rules - - - - - -ir_pipeline - -pipeline/ -ingest → triage → -rebook → compensate - - - -irrop->ir_pipeline - - +agents->ag_shared + + - + api_main - -main.py -FastAPI + WebSocket + +main.py +FastAPI + WebSocket +health · runs · Langfuse - + api->api_main - - + + - - -api_routes - -routes/ -agents, scenarios, ws + + +api_config + +config.py +Pydantic Settings - - -api->api_routes - - + + +api->api_config + + - + ui_fw - -framework/ -soleprint-ui -(shared component lib) + +framework/ +soleprint-ui +(shared component lib) - + ui_root->ui_fw - - + + - + ui_app - -app/ -Vue 3 SPA -pages/ components/ -mars-tokens.css + +app/ +Vue 3 SPA +config.ts (Kong proxy) - + ui_root->ui_app - - + + - + ctrl_docker - -Dockerfile.api -Dockerfile.ui -nginx.conf -docker-compose.yml + +Dockerfile.api +Dockerfile.ui +nginx.conf - + ctrl->ctrl_docker - - + + - + ctrl_k8s - -k8s/ -base/ overlays/dev/ -kind-config.yaml + +k8s/ +base/ overlays/dev/ +kind-config.yaml - + ctrl->ctrl_k8s - - + + + + + +ctrl_edge + +edge/ +docker-compose.yml +(production) + + + +ctrl->ctrl_edge + + ctrl_tilt - -Tiltfile -tilt_config.json + +Tiltfile +deploy.sh ctrl->ctrl_tilt - - + + diff --git a/docs/index.html b/docs/index.html index c2b27b6..eb53b56 100644 --- a/docs/index.html +++ b/docs/index.html @@ -193,7 +193,7 @@

SYSTEM ARCHITECTURE

-

End-to-end view: Vue UI → Kong gateway → FastAPI → MCP servers → live and scenario data sources. Langfuse traces every agent run.

+

End-to-end view: Vue UI → Kong gateway (optional) → FastAPI → MCP servers → live and scenario data sources. Langfuse (separate shared cluster) traces every agent run and tool call.

System Architecture
@@ -252,7 +252,7 @@

DEPLOYMENT

-

Kind cluster for dev (Tilt), docker-compose for quick start, EC2 for production demo. Entry point: localhost:8040.

+

Kind cluster for dev (Tilt), docker-compose for EC2 production (nova-api + nova-ui on shared gateway network). Woodpecker CI builds images on push to main. EC2 nginx proxies stellarair.mcrn.ar → container; Kong Konnect available as optional governance layer.

Deployment
@@ -264,38 +264,45 @@
stellar-ops/
 ├── mcp_servers/
-│   ├── shared/           server.py — tools/ resources/ prompts/
+│   ├── shared/              server.py · tools.py · resources.py · prompts.py
 │   │   └── tools: get_route_weather · get_hub_forecasts · get_airport_status
 │   │             get_flight_status · get_flight_details · get_irregular_ops
 │   │             get_airport_congestion · get_maintenance_flags
-│   ├── ops/              server.py — tools/ resources/ prompts/
+│   ├── ops/                 server.py · tools.py · resources.py · prompts.py
 │   │   └── tools: get_crew_notes · get_crew_duty_status · get_pending_rebookings
 │   │             generate_narrative
-│   ├── passenger/        server.py — tools/ resources/ prompts/
+│   ├── passenger/           server.py · tools.py · resources.py · prompts.py
 │   │   └── tools: generate_notification
+│   ├── shared_llm.py         multi-provider: Groq · Anthropic · Bedrock · OpenAI
 │   └── data/
-│       ├── models.py        FlightData, CrewMember, Passenger, MELItem, HubInfo
+│       ├── models.py         FlightData · CrewMember · Passenger · MELItem · HubInfo
 │       ├── real/             openmeteo.py · faa.py
 │       └── scenarios/        normal_ops · weather_disruption_ordmaintenance_delay_sfo · crew_swap_ewr
 ├── agents/
-│   ├── fce.py              FCE — "Behind Every Departure"
-│   ├── handover.py           Shift Handover agent
-│   └── shared/               mcp_client.py · llm.py (Bedrock/Anthropic)
-├── irrop/                  Disruption Recovery Engine (Project 3)
-│   ├── models/               flight · passenger · crew · recovery
-│   ├── rules/                faa_part117 · rebooking · compensation
-│   └── pipeline/             ingest → triage → rebook → compensate
+│   ├── fce.py                FCE — "Behind Every Departure" (passenger notifications)
+│   ├── handover.py           Shift Handover (ops brief: IMMEDIATE / MONITOR / FYI)
+│   └── shared/
+│       ├── mcp_client.py     MCPMultiClient + connect_servers context manager
+│       ├── parser.py         parse_tool_result · parse_resource_result · parse_prompt_result
+│       └── tool_runner.py    build_tool_caller — timeout · Langfuse span · error collection
 ├── api/
-│   └── main.py               FastAPI + WebSocket + scenario data API
+│   ├── main.py               FastAPI: agents, scenarios, WebSocket, /health, Langfuse traces
+│   └── config.py             Pydantic Settings — centralized env var reads
 ├── ui/
 │   ├── framework/            soleprint-ui (shared component library)
-│   └── app/                  Vue 3 SPA — Operations · Internals · Data
+│   └── app/                  Vue 3 SPA — Operations · Internals · Data · Settings
+│       └── src/config.ts     Kong proxy URL + API/WS base
 ├── ctrl/
 │   ├── Dockerfile.api/ui     Container builds
+│   ├── nginx.conf            UI nginx (proxies /agents /scenarios /config /health /ws)
 │   ├── k8s/                  base/ + overlays/dev/ (Kustomize)
 │   ├── Tiltfile              Dev environment (Kind cluster: unt)
-│   └── docker-compose.yml    Simple alternative
+│   ├── edge/                 Production docker-compose (nova-api + nova-ui on gateway net)
+│   └── deploy.sh             rsync (bypass CI) · edge (pull registry images)
+├── tests/                  69 tests: models · clients · MCP · scenarios · agents
+│   └── base.py               dual-mode: inprocess (default) · live (CONTRACT_TEST_MODE=live)
+├── .woodpecker/            CI pipeline — build API + UI, push to registry.mcrn.ar
 ├── docs/                   Architecture graphs (this page)
 └── .mcp.json                 Claude Code integration — 3 servers