# promotion-manifest.yaml # Manifesto de promoção lab -> homolog (ClubPetro). # A PR de promoção deve ser marcada com [lab] e conter este arquivo. # O pipeline lê os campos abaixo pra rodar os gates 0 + A-F + G + admissao. # # OBS: o schema exato é definido pelo parser do pipeline (referências vivas: # backend=external_apis, seed=action-plan). As chaves aqui são auto-descritivas; # reconcilie com o parser se ele exigir nomes específicos. apiVersion: v1 kind: PromotionManifest service: name: vanna-clubpetro kind: backend # serviço HTTP (não é MFE) description: > Deploy do Vanna 2.0 (text-to-SQL sobre LLM) que responde perguntas em pt-BR consultando o ClickHouse Cloud (database gold) com RLS por tenant (program_id + store_id). owner: dados-plataforma # TODO: confirmar squad/owner runtimePort: 8765 promotion: from: lab to: homolog # Onde o pipeline re-materializa o código (branch limpa, sem histórico do lab). kubernetesOverlay: k8s/hml # --------------------------------------------------------------------------- # Gate 0 — Stack # --------------------------------------------------------------------------- stack: language: python runtime: python-3.11 framework: vanna-2.0 + fastapi + uvicorn packageManager: pip # Stack padrão do core é NestJS>=10/TypeORM0.3 (yarn) OU MFE React17/MUI5. # Python é NO-GO por padrão -> exceção formal abaixo (Gate 0). stackException: requested: true reason: > O serviço é um deploy da biblioteca Vanna 2.0, que é Python e não tem equivalente em NestJS. O núcleo (agente LLM, memória vetorial ChromaDB, runner RLS do ClickHouse e o web component ) vem do upstream vanna-ai/vanna. Reescrever em Node significaria reimplementar toda essa cadeia — inviável e sem ganho. Mantém-se Python; todos os demais gates aplicáveis são cumpridos (ver abaixo). approvedBy: "" # TODO: preencher com o aprovador humano do stackException # --------------------------------------------------------------------------- # Gate B — Migrations # --------------------------------------------------------------------------- migrations: applicable: false reason: > O serviço não possui schema relacional próprio. O ClickHouse Cloud (database gold) é gerido fora do serviço; o "treino" (train.py) apenas popula um vector store ChromaDB local, idempotente e reconstruível (rm -rf chroma_db/ && python train.py). Não há migrations TypeORM. Toda query ao ClickHouse é parametrizada via settings do clickhouse_connect (RLS), nunca por interpolação de string — ver rls_runner.py. # --------------------------------------------------------------------------- # Gate D — Atalhos / segurança # --------------------------------------------------------------------------- security: envVersioned: false # apenas .env.example versionado; .env no .gitignore hardcodedSecrets: false # segredos vêm de k8s Secret (envFrom), nunca do git hardcodedLabHost: false # overlay homolog usa homologation.clubpetro.com npmrcVersioned: false # sem .npmrc no repo # --------------------------------------------------------------------------- # Gate G — Isolamento de credenciais de banco # --------------------------------------------------------------------------- database: # Credenciais do ClickHouse em secret DEDICADO ao serviço (não genérico). credentialsSecret: vanna-clubpetro-db genericSecret: false # NÃO usa sqlhomologation / sqluserhomolgeneric etc. engine: clickhouse-cloud database: gold # --------------------------------------------------------------------------- # Admissão de capacidade # --------------------------------------------------------------------------- capacity: replicas: 1 # ChromaDB é SQLite-based; réplicas > 1 corrompem o store strategy: Recreate # PVC ReadWriteOnce — não dá 2 pods montando ao mesmo tempo resources: requests: cpu: "200m" memory: "1Gi" limits: cpu: "1" memory: "2Gi" persistentVolume: size: 5Gi accessMode: ReadWriteOnce # store vetorial + cache ONNX + CSVs # --------------------------------------------------------------------------- # Deltas (avisos — não bloqueiam) # --------------------------------------------------------------------------- deltas: A_architecture: > Tenant (program_id/store_id) resolvido via RequestContext e validado por regex ^[A-Za-z0-9_-]+$ (agent.py). O embed confiável passa os IDs por query param do — não há token JWT nesta arquitetura de embed. C_contract: > OpenAPI auto-servido pelo FastAPI (/openapi.json, /docs). Rotas de chat já versionadas: /api/vanna/v2/chat_sse|chat_websocket|chat_poll (upstream). E_ops: > Probes de liveness/readiness em GET /health; replicas: 1; Dockerfile multi-stage rodando como usuário non-root (uid 10001). F_ci: > CI/CD via .gitea/workflows/cd.yml (build remoto no Cloud Build + rollout no GKE). bitbucket-pipelines.yml N/A: o repo vive no Gitea self-hosted.