Console de operações do Service Management
Este é o documento E5 da cadeia de documentação E1–E8 do Service Management (JUM-482 ). Ele documenta o console de operações — as superfícies que transformam o designer de uma ferramenta de modelagem em algo que descreve e controla um serviço em execução — exatamente como o código se comporta hoje, após a entrega da frente do console de operações (JUM-480 , JUM-481 , JUM-543 , JUM-544 , JUM-546 ).
O console abrange a aba Service Configuration (perfil de runtime, a prévia do ecossistema PM2 e o editor de ambiente de runtime), a aba Deploy Management e — por suas regras de ciclo de vida — o Communication Interface Designer. Essas abas carregam regras que um usuário não consegue descobrir clicando: quais combinações de run-mode × cloud-provider e de service-type × deploy-target são válidas e por que uma inválida é rejeitada, qual arquivo de ambiente uma gravação escreve e de onde a prévia do PM2 obtém sua lista de processos. Este documento torna essas regras legíveis, nomeia a verificação que comprova cada uma e estabelece a regra da fonte compartilhada que as impede de divergir.
Dois limites deliberados:
- Os contratos de comunicação são referenciados, não duplicados. A API de ambiente de runtime e o endpoint do ecossistema PM2 estão fixados no Requisito 126, Contratos 1 e 1b, e a semântica de arquivos env/enums no documento E1, Contratos de ambiente de runtime (JUM-464 ). Este documento os referencia; não os repete.
- Persistência e comportamento de boot estão fora do escopo. A porta de armazenamento e a migração de armazenamento pertencem ao documento E3, Arquitetura de módulos do Service Management; suas promessas ao usuário — onde os dados vivem, os modos de perda e o recurso da exportação — pertencem ao documento E6, Adoção do Cana no Service Management, migração e comportamento offline.
A matriz de capacidades compartilhada (JUM-544, JUM-481)
As duas superfícies de validação do console — Service Configuration e Deploy
Management — leem as matrizes do Requisito 059 de uma única fonte legível por
máquina:
packages/designer-core/src/model/deployCapabilityMatrix.js,
o leitor dos dois documentos de matriz
(JUMENTIX-DEPLOY-TARGET-AND-PACKAGING-MATRIX
e
JUMENTIX-SERVICE-FACTORY-CAPABILITIES-MATRIX).
A regra da fonte compartilhada: a matriz é transcrita exatamente uma vez, neste módulo. Um futuro contribuidor que estender ou corrigir a matriz edita o módulo fonte — e os documentos de matriz no mesmo PR, como o próprio cabeçalho do módulo exige — nunca uma segunda cópia dentro da validação de uma aba. Os dois validadores do console já demonstram o porquê:
collectServiceConfigurationIssues(serviceConfigurationValidation.js, JUM-544) consome o mapa de suporte run-mode × cloud-provider e o mapa de portas ativas.collectDeployTargetIssues(deployTargetValidation.js, JUM-481) consome o mapa de suporte service-type × deploy-target, o mapa de protocolos por service type, o conjunto de targets gerenciados por PM2 e os vocabulários de drivers.
O que a matriz codifica, e por que uma combinação inválida é rejeitada:
- Run mode × cloud provider.
dedicated-serverroda emself-hosted;virtual-machineemaws/google/azure;containeremdockerouself-hosted;functionsemaws/vercel/cloudflare. Qualquer coisa fora dessas linhas (por exemplofunctions+self-hosted, ou um run mode baseado em PM2 contravercel) não tem deploy target na matriz do Requisito 059 — o design não poderia ser construído por nenhuma linha de empacotamento que a factory entrega, portanto é rejeitado em vez de registrado. - Service type × deploy target. As linhas gerenciadas por PM2
(
dedicated-server,vm,ec2) aceitam serviçosrestapi,websocket+restapiegrpc+restapi; as linhas de funções (lambda,vercel-functions,cloudflare-workers) aceitam apenasfunctions. Um serviçofunctionsem um target PM2 — ou um serviço REST/realtime em um target de funções — não tem linha na matriz. - Exposição de protocolos. Um service type expõe apenas os protocolos que
ele realmente vincula:
restapiserve HTTP,websocket+restapiadiciona WebSocket,grpc+restapiadiciona gRPC, e APIs de funções são entrypoints HTTP. Pedir que um serviçorestapivincule WebSocket é rejeitado porque o runtime nunca iniciaria tal listener. - Aplicabilidade do perfil PM2. Apenas targets gerenciados por PM2 carregam
um
pm2Profile(dev/staging/production); um perfil em um target gerenciado pelo provedor (serverless) é um design que a matriz não consegue construir, e um perfil ausente em um target gerenciado por PM2 deixa o perfil de ecossistema sem escolha. - Portas ativas por service kind. Um serviço
rest-apinão vincula nenhum listener realtime, portanto suas portas WebSocket/gRPC não utilizadas não são validadas — apenas as portas que o service kind selecionado realmente vincula precisam ser inteiros em 1–65535 e mutuamente distintos.
Dois fatos de vocabulário que vale conhecer antes de estender a matriz:
- Duas grafias coexistem por design. O Service Configuration usa o
vocabulário de armazenamento do Requisito 126 (
serviceKind:rest-api,websocket-rest-api,grpc-rest-api); o Deploy Management usa as grafias da própria matriz (serviceType:restapi,websocket+restapi,grpc+restapi,functions), que são anteriores a ele. O módulo documenta a divergência em vez de disfarçá-la. - Os vocabulários de drivers espelham o contrato de ambiente de runtime por
referência.
databaseDriver/keyValueDriversão definidos pela matriz do Requisito 059 como “oJUMENTIX_DATABASE_DRIVERselecionado”, portanto o módulo espelha os conjuntos de enum do Contrato 1 — e a paridade é verificada por teste, não presumida (abaixo).
Comprovado por:
serviceConfigurationValidation.test.ts
e
deployTargetValidation.test.ts
— este último também lê o script.js e os enums da allowlist do server.js
para garantir que os vocabulários de drivers não possam divergir do contrato
de ambiente de runtime.
Service Configuration: validar antes do estado (JUM-544)
O portão de gravação da aba valida o perfil candidato antes de ele tocar o
estado (script.js): collectServiceConfigurationIssues roda sobre os
valores do formulário, e cada issue tem severidade error, portanto um perfil
inválido é recusado e relatado na superfície de status da aba — o comportamento
anterior silenciosamente coagia portas ruins de volta aos padrões. O mesmo
coletor revalida o perfil persistido sempre que a aba é renderizada
(renderServiceConfigStatus em
inspectors.js), de modo
que um estado inválido vindo do armazenamento é sinalizado em vez de exibido
como válido. As regras:
- Vocabulário —
serviceKind,runModeecloudProviderprecisam ser valores que o esquema de armazenamento (Requisito 126, Contrato 2) e os selects da UI conhecem. - Portas — cada porta que o service kind selecionado realmente vincula precisa ser um inteiro em 1–65535, e nenhum par de portas ativas pode colidir; portas inativas são ignoradas.
- Run mode × cloud provider — a combinação precisa existir na matriz compartilhada (acima), e a rejeição nomeia os provedores que a matriz realmente suporta para o run mode escolhido.
Edição multi-ambiente (JUM-480) — por referência cruzada
O contrato do editor de ambiente de runtime — ambientes aceitos e seu mapeamento de arquivos, a classificação de chaves em três níveis (editável / somente leitura / nunca exposta), conjuntos de enum por chave, semântica de gravação e o envelope de erro — está fixado no Requisito 126, Contratos 1 e 1b e no documento E1, Contratos de ambiente de runtime. O que este documento adiciona é apenas o comportamento desse contrato no lado do console:
- A edição é sequencial e por arquivo. O seletor de Environment (
dev,staging,ci) carrega exatamente um ambiente por vez através deGET /api/runtime/env; o painel sempre nomeia o arquivo exato que a próxima gravação escreve (Editing target: .env.dev (environment "dev"), direto do payload da API); e uma gravação escreve somente aquele arquivo — não existe edição em lote entre arquivos. A linha de direcionamento por arquivo é fixada porpm2EcosystemUi.contract.test.ts. - Um ambiente não aceito é rejeitado, nunca coagido. O servidor resolve o
parâmetro
environmentcontra um conjunto explícito de valores aceitos (dev/development→.env.dev,staging→.env.staging,ci/test→.env.ci) e responde a um valor desconhecido com400nomeando a lista de aceitos — ele nunca cai silenciosamente paradev. - A gravação é validada e confirmada. Apenas chaves da allowlist de
escrita são aceitas, os valores são verificados contra os conjuntos de enum
do Contrato 1 (valores fora do enum são rejeitados com a lista de aceitos e
nada é escrito), a escrita é atômica (arquivo temporário,
fsync, rename) e a resposta retorna o estado pós-gravação que o painel confirma (Environment "staging" saved to .env.staging).
A prévia do ecossistema PM2 (JUM-480) — pela fonte, não pela string de comando
O painel de perfil de runtime prévia os processos PM2 sob os quais o service kind desenhado rodaria. A propriedade mais importante dessa prévia — e a mais provável de ser desfeita por um atalho futuro — é de onde ela lê:
- A prévia lê os arquivos reais
pm2/ecosystem.*.cjsatravés deGET /api/runtime/pm2-ecosystem(Requisito 126, Contrato 1b), nunca uma lista de processos embutida no código. Adicionar um app a um arquivo de ecossistema muda a prévia sem mudança de código e sem reinício do servidor — o endpoint carrega o módulo de ecossistema com cache invalidado a cada leitura. A justificativa pertence ao registro escrito: no dia em que um contribuidor embutir uma lista literal de processos ou uma invocação de gerenciador de pacotes no designer, a prévia começa a mentir sobre a realidade, e a transição para o Bun (JUM-33 , JUM-40 ) mudará o formato de invocação por baixo dela. É por isso que o Contrato 1b proíbe qualquer string de gerenciador de pacotes (pnpm run,bun run,npm run) ou nome de scriptpm2:start:*no servidor e no designer: o comando relatado é derivado da definição do ecossistema (seu caminho e o nome do app), portanto permanece verdadeiro qualquer que seja o gerenciador de pacotes que invoque o PM2. Este documento, portanto, descreve a prévia pela sua fonte, não pelas strings literais de comando que ela produz hoje. - Estados de borda honestos, nunca um painel silenciosamente vazio. Um
ambiente sem arquivo de ecossistema (
ci/testmapeiam paraecosystem.ci.cjs, que o repositório não define) é um estado explícitoexists: falserenderizado como “No PM2 ecosystem file for environment …”, não um erro e não uma lista vazia apresentada como real. Um arquivo de ecossistema ilegível ou sintaticamente quebrado emerge como o envelope da classe 500 comcodeepath— paralelo à classe de sistema de arquivos dos env (JUM-543) — de modo que umpm2/ecosystem.*.cjsquebrado é identificável como um problema de instalação, nunca confundido com uma requisição malformada. - O ambiente da prévia é independente do ambiente de edição. O seletor da
prévia oferece os ambientes para os quais o repositório define ecossistemas —
dev,staging,production(fixado pela suíte de contrato da UI) — enquanto o editor de env mira os arquivos env editáveis. Produção tem um ecossistema, mas nenhum arquivo env editável; o console mantém esses eixos separados em vez de confundi-los. - A filtragem é por service kind; os nomes vêm do arquivo. O painel
seleciona os apps do ecossistema cujos nomes terminam com os sufixos que o
service kind desenhado implica (
restapipara REST-only, maiswebsocketapiougrpcapipara os kinds realtime) e sugere um único comando derivado cobrindo exatamente esses apps. A correspondência por sufixo funciona para todo prefixo de ambiente porque nenhum nome de app é enumerado no designer. - A prévia é transitória. Ela vive em estado de UI no nível do módulo,
nunca no payload persistido
service-management.v1(Requisito 126, Contrato 2) — um retrato derivado do servidor não é estado de design.
O dashboard de monitoramento PM2 — stream WebSocket (Contrato 1e) + HTTP one-shot (1c)
A aba Monitoramento é telemetria runtime, não outra prévia estática.
- Caminho primário da UI:
WS /api/runtime/pm2-ws(Requisito 126, Contrato 1e). A aba abre um WebSocket quando ativa, assina ambiente + intervalo de refresh (500–2000 ms, padrão 1000) e renderiza gráficos de host (CPU/memória/disco) com D3 vendored (sem CDN), stacks de processo, filtros e ações start/stop/restart. O histórico agregado + sparks por processo persiste no Cana emmonitoringHistorydentro deservice-management.v1(Contrato 2). Sair da aba fecha o socket. - HTTP one-shot:
GET /api/runtime/pm2-metrics(Contrato 1c) permanece para testes e ferramentas. Ambos os transportes coletam via API Node do PM2, comparam nomes live com o ecosystem, incluem métricas de host, disk I/O por processo (Linux; Darwin via Bun FFI /proc_pid_rusage; Windows) e scrapes opcionais deasync-context-metrics(counters +recentStoresdo Map ALS com redact). O scrape usaGET http://127.0.0.1:<JUMENTIX_HTTP_PORT>/async-context-metricsem cada processo com essa porta — reinicie o RestAPI a partir de um checkout que inclua a rota (JUM-767+) se o Monitoring reportarASYNC_CONTEXT_ROUTE_MISSING. Cada linha de processo expõe um controle de ajuda descrevendo o papel do app.
Se o PM2 não puder ser carregado, conectado ou listado, o endpoint HTTP falha
com o envelope explícito de métricas PM2 e o WebSocket emite error /
action-result falho em vez de inventar processos saudáveis.
Comprovado por:
pm2Ecosystem.integration.test.ts
(leituras do ecossistema, shape de métricas + host, subscribe/ações WebSocket,
edição refletida sem reinício, arquivo ausente, envelope 500, rejeição de
ambientes desconhecidos) e
pm2EcosystemUi.contract.test.ts
(garantia estrutural de nenhum comando embutido e UI de Monitoring via WebSocket).
Deploy Management: o contrato de metadados do Requisito 059 (JUM-481)
Todo deploy target carrega o contrato de metadados do Service Management do
Requisito 059 — { name, region, runtime, serviceType, deployTarget, runtimeProtocol, databaseDriver, keyValueDriver, pm2Profile } — fixado como
uma extensão retrocompatível do esquema de armazenamento (Requisito 126,
Contrato 2: a chave versionada permanece inalterada). As regras da aba:
- O que um target pode conter é de responsabilidade de
collectDeployTargetIssues(deployTargetValidation.js): os seis vocabulários, a linha da matriz service-type × deploy-target, a exposição de protocolos e a aplicabilidade do perfil PM2 — cada rejeição nomeia a restrição violada (a seção da matriz compartilhada acima dá as razões). Toda issue tem severidadeerror. - A lista revalida cada entrada persistida.
renderDeployments(inspectors.js) roda o coletor sobre cada target armazenado e sinaliza uma entrada rejeitada inline com suas issues, em vez de renderizá-la como um design construível. A validação, portanto, é aplicada na superfície onde os targets são consumidos, independentemente de como a entrada chegou lá. - Entradas legadas migram para frente na carga, sem perdas.
normalizeDeploymentInput(designerState.js) migra o formato pré-JUM-481{ name, type, region, runtime }:typetorna-sedeployTargetatravés de um mapa de aliases (dedicated→dedicated-server), e os metadados ausentes recebem padrões derivados da matriz — o primeiro service type que o target suporta, o primeiro protocolo desse type, os drivers padrão do contrato de ambiente de runtime e o perfil PM2devapenas em targets gerenciados por PM2. Valores legados sem contraparte na matriz (por exemploazure-functions) são mantidos literalmente — a migração nunca descarta informação silenciosamente; a regra de vocabulário sinaliza a entrada, e o operador decide. O fato denormalizeStatePayloadrestaurar a seçãodeploymentsé a exceção do JUM-481 ao recorte de carga fixado.
Comprovado por:
deployTargetValidation.test.ts
e a cobertura de migração na carga em
designerState.test.ts.
Ciclo de vida dos deploy targets: edição, duplicação e validação de campos (JUM-546)
A JUM-481 é dona do que um target pode conter; a JUM-546 é dona de como os targets são gerenciados. O ciclo de vida completo da aba é adicionar, editar in-place, duplicar e excluir:
- Edição in-place.
Editcarrega a entrada no formulário; o botão de adição torna-seSave Target(com a opçãoCancel Edit) e o mesmo portão de validação se aplica à substituição. Uma edição pode manter o próprio nome — a verificação de unicidade exclui a entrada que está sendo substituída — e excluir uma entrada no meio de uma edição cancela a edição (ou reposiciona o índice) em vez de sobrescrever outro target. - Duplicação.
Duplicatearmazena uma cópia profunda independente — nunca uma referência compartilhada — renomeada pela regra(copy)(nome (copy), depoisnome (copy 2), … até ser único, comparado sem distinção de maiúsculas). Deploy targets são a única coisa que operadores criam em conjuntos quase idênticos (o mesmo serviço em staging e produção, a mesma configuração em várias regiões); a duplicação é a defesa primária contra as inconsistências de redigitação que a validação de campos teria de capturar. - Validação de campos —
collectDeployTargetFieldIssuesemdeployTargetLifecycleValidation.js, executada no portão de adição/edição junto às regras de matriz da JUM-481: o nome é obrigatório e único; o runtime/version é obrigatório e deve seguir um padrão de nome-mais-versão (nodejs22.x,python3.12— texto livre comolatesté rejeitado); a região é obrigatória em todo target de nuvem e opcional na linha self-hosted Dedicated Server (SSH), onde o campo pode carregar informação de host. O conjunto self-hosted é lido do leitor compartilhado da matriz (SELF_HOSTED_DEPLOY_TARGETSempackages/designer-core/src/model/deployCapabilityMatrix.js), nunca transcrito. Toda rejeição nomeia a razão na superfície de status da JUM-543, e o candidato nunca toca o estado. - Dicas de campo por tipo de target. A linha de dica sob o formulário
(
deployTargetFieldHint) acompanha a linha da matriz selecionada: targets gerenciados por PM2 (linhas VM/dedicado) são orientados à informação de host e ao perfil PM2; provedores de funções, ao runtime/version, com o select de perfil PM2 desabilitado e limpo — perfil PM2 não se aplica.
Comprovado por:
deployTargetLifecycle.test.ts
(as regras como funções puras) e
deployTargetLifecycle.browser.integration.test.ts
(a UI real em WebKit: adição validada, razões de rejeição na região de
status, a regra de renomeação (copy), independência da edição in-place, a
regra de região por tipo e o comportamento da dica e do select de PM2).
Regras de ciclo de vida — o que existe hoje e o que está aberto
A cadeia de issues reserva o ciclo de vida completo (edição in-place, duplicação, validação em nível de campo, unicidade) das duas listas do console para JUM-545 (interface adapters) e JUM-546 (deploy targets). A JUM-545 e a JUM-546 foram entregues — esta seção registra o ciclo de vida que o código realmente implementa hoje.
Interface adapters (Communication Interface Designer). Um adapter é
adicionado, editado in-place e excluído (JUM-545). Os portões de
adição e edição compartilham um único caminho de validação
(upsertInterfaceAdapter em src/validation/interfaceAdapterValidation.js):
o framework deve pertencer ao subconjunto por tipo de interface da matriz de
tempo de execução canônica (src/model/interfaceFrameworkMatrix.js — as
grafias canônicas da JUM-461, sem duplicatas de alias derby/sails), o
entrypoint deve ser um caminho TypeScript/JavaScript sob src/interface/, o
mapeamento de controller deve seguir o formato XController.action, e
duplicatas — mesmo tipo + entrypoint, ou mesmo mapeamento de controller — são
rejeitadas com o motivo na superfície de status. Entradas persistidas
anteriores ao portão são sinalizadas inline em vez de passarem como designs
válidos.
Deploy targets (Deploy Management). O ciclo de vida completo foi entregue
com a JUM-546 — adicionar, editar in-place, duplicar e excluir, com
validação em nível de campo (nome único, padrão de runtime/version, região
por tipo de target) no portão de adição/edição e a regra de renomeação
(copy) nas duplicações. Ver a seção da JUM-546 acima.
A razão de esta seção de lacuna honesta existir: as listas do console são as superfícies onde um design se torna uma intenção operacional, e uma entrada que só se torna válida após um reload — ou uma entrada duplicada que nada rejeita — é uma regra que o usuário não consegue ver. Nomear as issues responsáveis mantém a regra visível até o código alcançá-la.
O contrato de superfície de status que toda superfície do console segue (JUM-543)
O console nunca bloqueia para dar feedback. A JUM-543 substituiu todos os
window.alert por superfícies de status não bloqueantes, e cada painel do
console segue o mesmo modelo:
- Uma única região de toast aria-live (
#status-region,role="status",aria-live="polite") anuncia mensagens de validação e falhas de API; avisos de severidade info se ocultam automaticamente, erros persistem. Os portões de ações destrutivas mantêm deliberadamente seuwindow.confirm— um toast não substitui um portão. - Linhas de status inline por painel — o status da prévia do PM2, o status do Service Configuration, o status do ambiente de runtime e a linha de direcionamento por arquivo — carregam falhas com ambiente, arquivo e causa onde o usuário está olhando, em vez de um erro silencioso no console.
- O cliente renderiza o envelope de erro da API literalmente.
error/details, maiscodeepathnas classes 500 de sistema de arquivos, são exibidos exatamente como retornados — não há remapeamento de erro no lado do cliente, de modo que a separação parse/validação/sistema de arquivos documentada no Requisito 126 chega intacta ao usuário.
O que este documento deliberadamente não cobre
- Persistência e comportamento de boot — a porta
IDesignerStoree a migração para o Cana já entregue (JUM-484 ) pertencem ao documento E3, e suas promessas ao usuário ao documento E6. - Exportação/importação das abas do console — desde o JUM-547 as seções
interfaces,serviceConfigurationedeploymentsatravessam a exportação JSON de suíte completa, eruntimeEnvironmentatravessa apenas como a seleção de ambiente; o documento E4 é o dono desse escopo e de sua prova em Garantias de paridade de contratos do Service Management. - O formato literal de invocação do PM2 — fixado pelo Requisito 126, Contrato 1b, e com mudança prevista na transição para o Bun (JUM-33 , JUM-40 ); a prévia é documentada pela sua fonte precisamente para que este documento sobreviva a essa transição.
Referências
- Leitor compartilhado da matriz:
packages/designer-core/src/model/deployCapabilityMatrix.js; documentos de matriz: JUMENTIX-DEPLOY-TARGET-AND-PACKAGING-MATRIX, JUMENTIX-SERVICE-FACTORY-CAPABILITIES-MATRIX - Validadores:
serviceConfigurationValidation.js,deployTargetValidation.js,deployTargetLifecycleValidation.js - Endpoints do servidor:
server.js; cola da UI:script.js,inspectors.js, estado/migração:designerState.js - Fontes de ecossistema:
pm2/ecosystem.dev.config.cjs,pm2/ecosystem.staging.config.cjs,pm2/ecosystem.production.config.cjs - Suítes:
serviceConfigurationValidation.test.ts,deployTargetValidation.test.ts,deployTargetLifecycle.test.ts,designerState.test.ts,pm2EcosystemUi.contract.test.ts,runtimeEnvUi.contract.test.ts,pm2Ecosystem.integration.test.ts,runtimeEnv.integration.test.ts,runtimeEnvContract.integration.test.ts,deployTargetLifecycle.browser.integration.test.ts - Requisitos: Requisito 126, Contratos 1 e 1b (Contratos 1, 1b e 2), 059 (as matrizes de deploy e da factory), 076 (paridade EN/PT)
- Documentos irmãos da cadeia E: Contratos de ambiente de runtime (E1), Arquitetura de módulos do Service Management (E3), Garantias de paridade de contratos do Service Management (E4), Aplicativo Service Management, Funcionalidades e uso do Domain Designer
- Linear: JUM-480 , JUM-481 , JUM-543 , JUM-544 , JUM-545 , JUM-546 , JUM-547 , JUM-464 , JUM-33 , JUM-40