Skip to Content
PortuguêsDocumentação JumentixReferênciaColaboração e Empacotamento do Service Management

Colaboração e empacotamento do Service Management

Este é o documento E8 da cadeia de documentação E1–E8 do Service Management (JUM-494 ) — o último elo da cadeia e o portão terminal de documentação do épico sob o Requisito 094: o Project do Linear não pode ser marcado como Completed enquanto esta Issue estiver em qualquer estado diferente de concluída.

Ele documenta a frente de colaboração e empacotamento exatamente como foi entregue:

  • Colaboração — o catálogo compartilhado multiusuário (JUM-491 ): um módulo de backend Catalogs contract-first, concorrência otimista por registro de catálogo, exclusão em tombstone com restauração e um cliente de sincronização no designer que converge por read-back do documento.
  • Versionamento de pacotes de domínio (JUM-492 ): os pacotes de domínio exportados são dados versionados, com grafo de dependências e resolução de conflitos determinística e explicável na importação.
  • Empacotamento (JUM-493 ): o núcleo do designer livre de framework é o pacote versionado @jumentix/designer-core sob packages/designer-core/ — manifesto, build determinístico, declarações de tipo, prova de ausência de DOM e um teste de fumaça de consumidor contra o artefato construído. A publicação é somente dry-run (Requisito 070): o caminho de publicação é verificado, nunca disparado.

Por ser o portão terminal, seu conteúdo reflete o que foi entregue, incluindo tudo o que foi postergado — cada desescopo abaixo é nomeado, não silenciosamente omitido. A seção de fechamento da cadeia ao final é a evidência do Req 094: ela nomeia a cadeia E1–E8 por completo e registra o estado do portão.

Este documento é a referência em português. The English reference is in SERVICE-MANAGEMENT-COLLABORATION-PACKAGING.md.

Colaboração: o catálogo compartilhado (JUM-491)

Tudo antes do JUM-491 trata o designer como uma ferramenta de navegador único: o Cana persiste o documento do designer no IndexedDB do navegador, e o JUM-485 o sincroniza entre as abas desse navegador. O JUM-491 transforma o designer em um sistema multiusuário: uma equipe compartilha um único catálogo de desenhos de domínio através do backend — que é também a única segunda cópia contínua do trabalho do usuário em hardware diferente (o Cana não tem fallback; a exportação é manual).

O mecanismo completo pertence ao documento dedicado Sincronização de catálogo compartilhado (arquitetura, contrato OAS, modelo de autorização, prova de convergência, comandos de verificação). Este documento afirma o que a entrega significa para o épico — a unidade de concorrência, o caminho de rejeição, a semântica de exclusão e a regra de convergência — uma única vez, e remete a ele em vez de manter uma segunda explicação divergente.

O que foi entregue

  • Um módulo de backend Catalogs contract-first (apps/service-management-api/src/modules/Catalogs/domain/Model/Catalog.ts), hexagonal como o módulo de referência Users: agregado de domínio (incremento de versão, tombstone, restauração), CatalogAuthorizationPolicy puro, casos de uso (CatalogUseCases.ts), o ponto de aplicação da concorrência otimista (CatalogDataRepository.ts), o CatalogController validado pela OAS e a composição (composeCatalogsServices.ts). Seis operações em /catalogs na spec/1.0.0.yml canônica — listar, criar, obter, atualizar, excluir, restaurar — garantidas por bun run oas:check-routes.
  • Concorrência otimista por registro de catálogo. O registro (um desenho de domínio compartilhado) é a unidade de concorrência declarada: grosso o bastante para que um relacionamento entre duas entidades sempre tenha uma versão consistente a verificar, fino o bastante para que colegas nunca se bloqueiem entre domínios. O token version gerenciado pelo servidor começa em 1 na criação e incrementa a cada escrita; uma escrita obsoleta é rejeitada com um 409 revisável cujos metadados carregam catalogId, expectedVersion, currentVersion e o registro atual — a edição perdedora nunca é descartada.
  • Exclusão é tombstone, recuperável. A exclusão define deletedAt e incrementa a versão, de modo que a exclusão se propaga no read-back; uma cópia localmente modificada gera um conflito deleted-remotely em vez de desaparecer; POST /catalogs/{id}/restore recupera o registro.
  • Autorização no servidor, alinhada ao TENANT-RBAC. A matriz de papéis ganha escopos de catálogo (admin: read/create/update/delete; user: read/create/update — membros da equipe editam, apenas admins excluem), e a política de tenant vincula o catálogo a exatamente uma organização. Um cliente não pode conceder acesso a si mesmo — provado por testes de integração positivos/negativos. Veja Contrato de autorização de tenant e RBAC.
  • Eventos no mediador. Toda escrita bem-sucedida publica catalogs.catalog.created | updated | deleted | restored com { id, organization, version, actor } — o mesmo token de versão que a API aplica — no message mediator (CatalogService.ts); a publicação nunca quebra a escrita primária.
  • Um cliente de sincronização do designer livre de DOM (apps/service-management/src/state/catalogSyncClient.js): um consumidor irmão do mesmo fluxo de eventos confirmados do Cana que a sincronização de abas do JUM-485 assina. Commits locais agendam um push com debounce dos domínios compartilhados modificados (modificação decidida pelo marcador durável domain.context.catalog = { id, version, contentHash }, carregado aditivamente pelo padrão do JUM-492); a convergência é um read-back do documento por polling, diferenciado por (id, version) — a regra de ressincronização do Cana aplicada através da rede: uma lacuna é um sinal de recarga, nunca um replay de eventos, porque os cursores do Cana são por instância de cliente e não significam nada entre máquinas. Mudanças remotas atravessam o mesmo caminho único applyRemoteDocument da sincronização de abas, de modo que o isolamento de undo, a reconciliação de seleção e o truncamento de redo são idênticos.
  • Uma prova real de convergência. catalogSync.integration.test.ts sobe o backend Express real (autenticação JWT real, mediador real) e executa dois clientes reais do designer sobre fetch real; um é particionado atrás de um ECONNREFUSED real, ambos continuam editando e, ao restabelecer, o read-back os converge — a edição particionada sobrevive como um conflito revisável, resolvido explicitamente (take-server / take-local, onde take-local é uma nova escrita deliberada contra a versão atual do servidor, nunca uma sobrescrita cega).

O que o JUM-491 deliberadamente desescopou (candidatos ao carry-over de 12-01)

Nomeados em Sincronização de catálogo compartilhado e reafirmados aqui porque o portão precisa vê-los:

  • Fan-out por WebSocket para os navegadores (push em vez de poll): os adaptadores de broker existem em packages/message-mediator, mas nenhum fan-out para clientes do designer está ligado; o read-back por polling é correto sob qualquer escolha de broker.
  • Chrome de UI de compartilhamento/conflito no designer: o módulo cliente é livre de DOM e combinável; as superfícies de compartilhar/descompartilhar/conflito (e a UX do provedor de token) são um trabalho futuro.
  • Escritas condicionais nativas no nível do driver: o read-check-write do repositório é o comportamento de referência; os drivers de produção devem depois mapear a mesma verificação para condicionais nativas de IStoreMutationOptions.expectedVersion.
  • Fila de intenção de exclusão offline: uma exclusão local offline de um domínio compartilhado não pode ser enviada; o registro sobrevivente no servidor é readmitido no read-back — a fronteira “o documento confirmado vence”, com escopo de sessão por desenho nesta fatia.

O que isto muda para o usuário — e o que não muda

Antes do JUM-491, o designer era por navegador e a exportação era a única forma de o trabalho sair da máquina — o documento E6, Adoção do Cana, migração e comportamento offline do Service Management, é o dono dessa história de dados e este documento não a repete. O catálogo compartilhado adiciona a via que faltava: o trabalho que o usuário compartilha agora vive no servidor e converge entre máquinas. Duas fronteiras honestas permanecem, afirmadas de antemão:

  • Um alvo de sincronização não é um backup. Ele propaga exclusões; não substitui a política de quota/expulsão de armazenamento nem o contrato de durabilidade offline.
  • O Cana ainda não tem fallback. Quando o catálogo está inalcançável, o cliente o declara (degraded na superfície de status) e continua salvando localmente no Cana — nunca degrada silenciosamente para um modo oculto de usuário único.

Versionamento de pacotes de domínio (JUM-492)

Um pacote de domínio (a exportação <domain>-package.json) é dado versionado, não código, e a importação é uma política determinística em vez de um acréscimo incondicional. O contrato público está fixado no Requisito 126, Contrato 3; a implementação vive em src/packages/packageVersioning.js (livre de DOM), ligada através do exportador (buildDomainPackageDocument) e do importador (designerImporters.js).

O documento de pacote versionado

A exportação emite o formato v2 { kind: "domain-package", version: "2.0.0", exportedAt, package: { name, version, dependencies: [{ name, range }] }, domain }. O bloco package declara a identidade a partir do contexto do domínio: packageName (caindo para o nome do domínio), packageVersion (caindo para 1.0.0) e entradas de packageDependencies (name@range; um nome puro é uma dependência apenas de presença). A compatibilidade é explícita nas duas direções: documentos v1 legados continuam importando com uma identidade 1.0.0 sintetizada; um documento cujo major é mais novo que o do importador, ou um kind diferente de domain-package, é recusado com clareza.

Semântica de versão, redefinida para um modelo de dados

Os significados habituais de semver não se mapeiam a um modelo de dados, então o Requisito 126 os redefine:

  • patch — apenas documentação/metadados (descrições de campos, formatos, constraints, texto de contexto do domínio, dicas de composição OAS);
  • minor — estrutura aditiva (uma nova entidade, campo ou contrato de mensagem; uma flag required afrouxada);
  • major — remoção ou estreitamento (uma entidade/campo/contrato removido, mudança de tipo de campo ou de PK/FK/unique, uma flag required endurecida, uma mudança de RBAC ou de invariante, uma mudança de declaração de agregado).

Proveniência e o registro de pacotes instalados

O conteúdo importado é carimbado: o domínio carrega context.provenance = { package, version } mais context.packageName/context.packageVersion, e cada entidade importada carrega meta.provenance. Os normalizadores carregam esses campos aditivamente — apenas quando a origem os declara — de modo que payloads pré-JUM-492 permanecem inalterados e a proveniência atravessa armazenamento, cargas e a exportação full-suite intacta. O registro de pacotes instalados deriva da proveniência somente: um domínio construído à mão nunca é uma instalação, então importar um pacote com nome igual ao de um domínio local acrescenta (com a recomputação de ids do JUM-617) em vez de fundir com conteúdo não relacionado.

O grafo de dependências

As dependências resolvem transitivamente sobre o registro com o pacote entrante sobreposto. Os ranges seguem a convenção npm: */vazio (qualquer), exato 1.2.3, caret ^1.2.3 (mesmo major; para 0.x, mesmo minor), til ~1.2.3 (mesmo major.minor); qualquer outra coisa é inválida e não satisfaz nada, então é reportada em vez de silenciosamente aceita. Uma dependência ausente ou incompatível com o range é reportada pela região de status e a importação prossegue — o designer reporta, não é o resolvedor. Um ciclo que o pacote entrante fecharia é reportado pela cadeia de nomes e a importação é recusada — ciclos são detectados, nunca percorridos.

Resolução semântica de conflitos

Reimportar um pacote instalado resolve de forma determinística e explicável:

  • mesma versão + conteúdo igual → no-op — reimportação idempotente, provada por teste;
  • mesma versão + conteúdo diferente → recusado (same-version-conflict, com as divergências listadas — imutabilidade de versão);
  • versão mais antiga → recusado (downgrade-rejected);
  • versão mais nova → merge. Mudanças aditivas e de metadados (semântica patch/minor — AUTO_MERGE_CLASSES) aplicam-se automaticamente. Remoções, estreitamentos e sempre RBAC e invariantes (REQUIRES_DECISION_CLASSES) mantêm o conteúdo existente do designer e são listados na prévia de merge, renderizada na superfície de schema-diff antes que qualquer coisa mude; o merge só se aplica após o usuário aceitar explicitamente (um window.confirm com portão — um dos portões de ação destrutiva que o JUM-543 deliberadamente mantém). RBAC e invariantes estão sempre na classe de decisão: resolver automaticamente uma política de segurança ou uma invariante de domínio é uma decisão que um algoritmo de merge não deve tomar. Todos os resultados emergem via showStatus, nunca alert().

A correspondência de entidades dentro de um merge é por nome, nunca por id: ids colidentes são recomputados na importação pela regra do JUM-617 , de modo que ids jamais podem ser a chave de correspondência; novas entidades entrantes recebem ids livres de colisão através do callback uniqueId do importador.

Provado por: designerPackageVersioning.test.ts (parsing/ordenação de versões, satisfação de ranges, parsing de dependências e resolução transitiva, detecção de ciclos, toda classe de conflito), designerRoundTrip.test.ts (exportação→importação versionada deep-equal com proveniência, ponto fixo de reexportação, reimportação idempotente, recusa de reimportação conflitante, merge determinístico com RBAC preservado, pares de dependências compatíveis/incompatíveis, JUM-617 preservado no caminho de acréscimo) e designerExporters.test.ts (o formato do documento de pacote v2, fixado).

Empacotamento: o núcleo do designer @jumentix (JUM-493)

Esta frente foi entregue como um pacote com dry run verificado — nunca uma publicação automática. Esta seção antes registrava o JUM-493 como desescopado (a decisão de 12-01); o pacote desde então foi entregue, e este documento registra o estado entregue. O núcleo do designer livre de framework é agora o pacote versionado packages/designer-core/ sob a organização xpertminds — ESM seguro para navegador, zero dependências de runtime, licenciado sob MIT, com metadados de procedência apontando para sua localização no monorepo.

  • O pacote é a casa canônica, não uma cópia. Os módulos do núcleo migraram de apps/service-management/src/ para packages/designer-core/src/ — o portão de fronteira de workspaces proíbe um pacote importar de uma app, então a direção da dependência foi invertida: a SPA zero-build agora consome o pacote por especificadores bare @jumentix/designer-core/…, resolvidos pelo import map para uma árvore vendored (apps/service-management/vendor/designer-core/, segura sob o containment, sincronizada por ci-cd/sync-service-management-designer-core.js — o mesmo modelo de vendorização do bundle do Cana) no navegador, e pelos mapeamentos de path do repositório (tsconfig paths, moduleNameMapper do Jest) diretamente aos fontes canônicos nos testes. A superfície distribuída — o modelo de domínio e seus normalizadores, o motor de validação/checagem de modelo, os exportadores (JSON, Markdown, JSON Schema, AsyncAPI, boilerplate bundle, package, OAS), os importadores, o motor de schema-diff/prévia de merge, o codegen hexagonal e o IDesignerStore apenas como tipo/contrato — é exatamente a árvore src/ do pacote; o build (scripts/build.js) a copia verbatim para dist/ e gera declarações de tipo a partir dos fontes anotados com JSDoc usando o compilador TypeScript fixado do repositório. Fora, garantido por teste: todo módulo DOM (script.js, ui/, pwa/), os clientes de sincronização (state/designerSync.js, state/catalogSyncClient.js) e os adaptadores de armazenamento — nem LocalStorageDesignerStore nem CanaDesignerStore são distribuídos, e o pacote não depende do Cana.
  • A barra de aceite é atendida por prova, não por construção. Três suítes sob packages/designer-core/test/ fixam o pacote: packaging.test.ts valida o manifesto (pontos de entrada no output construído, mapa de exports com types primeiro, files, licença, sideEffects, scripts somente dry-run, no estilo da suíte de packaging do cana), afirma que o conjunto de arquivos construído é exatamente o fechamento declarado e afirma o conteúdo do tarball empacotado via npm pack --dry-run --json; dom-free.test.ts varre a AST do artefato construído em busca de qualquer referência a window, document, localStorage, indexedDB, alert() ou FileReader/DOMParser e de qualquer import que cruze a fronteira do pacote; consumer-smoke.test.ts executa o teste de aceite da issue — importa o artefato construído em um processo separado sem DOM (sem document, sem window, sem localStorage) e executa um round trip de validação → exportação → reimportação sobre o modelo de exemplo, deep-equal com ponto fixo de reexportação.
  • A política de publicação permanece: somente dry-run. Conforme o Requisito 070, não existe publicação automática. A superfície de dry-run do repositório (bun run npm:publish:dry-run:packages, com npm:org:check:xpertminds para o lado da organização) reconhece o pacote como qualquer outro pacote de workspace não privado e executa bun publish --dry-run --access public; o prepublishOnly força um rebuild limpo antes, de modo que o dry run verifica um artefato determinístico cujo conteúdo a suíte de packaging validou.
  • Política de versionamento. O pacote segue semver sobre seu barrel público: patch para correções internas, minor para exportações aditivas, major para superfície removida ou estreitada. Os contratos de dados que ele lê e escreve (exportação full-suite, documento de pacote de domínio) permanecem versionados no payload sob a política do JUM-492 (Requisito 126, Contrato 3) — a versão do pacote não os repete. O versionamento de pacotes de domínio do JUM-492 se apoia exatamente nessa separação.

A consequência prática para o usuário permanece a do documento E6: a exportação é como o trabalho sai da máquina — como documento full-suite ou como pacote de domínio versionado — e o catálogo compartilhado (acima) é a única segunda cópia contínua. O pacote muda quem pode depender do núcleo, não como o trabalho do usuário do designer é armazenado.

A exportação full-suite (JUM-547), o pacote portátil

A exportação JSON é o documento full-suite versionado ({ kind: "service-management-suite", version: "2.0.0", domains, relationships, interfaces, serviceConfiguration, runtimeEnvironment, codeWorkspace, deployments, view }) carregando as cinco abas em um formato reimportável (JUM-547 ). Uma decisão de segurança registrada importa para o empacotamento: o pacote carrega somente a seleção do ambiente de runtime ({ environment, fileName }) — nunca valores, porque os valores espelham o conteúdo real de .env da máquina onde o designer roda. Nenhum segredo pode sair em um pacote; na importação a seleção é restaurada e os valores da máquina local são preservados. O contrato está fixado no Requisito 126, Contrato 3; o documento E5, Console de operações do Service Management, é o dono do lado do console de operações da história do ambiente de runtime.

Fechamento da cadeia: a evidência do portão E1–E8 (Req 094)

Esta seção é a evidência do portão de documentação do épico: a cadeia nomeada por completo, o estado de cada elo e as fronteiras de propriedade que impedem que dois documentos divirjam sobre o mesmo comportamento.

A cadeia publicada. A cadeia se chama E1–E8; o projeto publicou sete issues de documentação dedicadas — E1 e E3 até E8. Nenhuma issue de documentação E2 existe no projeto (uma auditoria da lista de issues do projeto confirma que nenhuma foi criada), então o portão fecha sobre os sete documentos publicados:

EloIssueDocumentoEstado
E1JUM-464 Contratos de ambiente de runtimeDone
E3JUM-473 Arquitetura de módulos do Service ManagementDone
E4JUM-479 Garantias de paridade de contratos do Service ManagementDone
E5JUM-482 Console de operações do Service ManagementDone
E6JUM-487 Adoção do Cana, migração e comportamento offline do Service ManagementDone
E7JUM-490 Design system e shell PWA do Service ManagementDone
E8JUM-494 este documentoeste PR

Cada elo é publicado em EN e PT-BR, sincronizados conforme o Requisito 076 — nenhum dos artefatos é um stub.

Consistência: um dono por comportamento, os demais remetem. A cadeia foi auditada contra descrições duplicadas e divergentes; onde dois documentos tocam o mesmo comportamento, a propriedade é:

  • A história dos dados (onde o trabalho vive, a migração unidirecional, o comportamento offline dos dados, a exportação como único caminho de recuperação) — pertence ao E6; o E7 afirma a fronteira shell↔dados uma única vez e remete, e este documento remete para a linha de base por-navegador que o catálogo compartilhado estende.
  • O contrato da porta de armazenamento e a fronteira de módulos livre de DOM — pertence ao E3; este documento remete para a fronteira de empacotamento em vez de derivá-la novamente.
  • O contrato de exportação e o contrato de pacotes de domínio — fixados pelo Requisito 126 (Contrato 3), com as garantias de paridade pertencendo ao E4; este documento ensina o comportamento do JUM-492 e cita o contrato em vez de reafirmá-lo.
  • A semântica de arquivos env/enums e a API de runtime-env — pertence ao E1; o E5 é o dono das superfícies do console de operações que as consomem, incluindo o contrato de superfície de status que todos os documentos acima referenciam.
  • O mecanismo do catálogo compartilhado — pertence a Sincronização de catálogo compartilhado (o documento dedicado do JUM-491); este documento afirma o significado da entrega para o épico e remete.

Estado do portão, exatamente. Sob o Req 094, o Project não pode ser marcado como Completed até que esta Issue esteja concluída. Neste PR: E1, E3–E7 estão Done; o E8 é entregue por este PR e transiciona somente após o merge. Nenhuma verificação pendente é descrita como passando aqui: os artefatos do JUM-491 que este documento referencia (SHARED-CATALOG-SYNC.md, catalogSyncClient.js, o módulo Catalogs, o teste de convergência) chegam com o PR próprio deles, e a frente de empacotamento do JUM-493 desde então foi entregue como @jumentix/designer-core — somente dry-run, conforme a seção acima — ambos afirmados, não suavizados. A evidência de conclusão do Project conforme o Req 094 (ligando esta Issue, seu PR e a evidência de commits, e os resultados de validação de integridade da documentação) é registrada no feed de Project Updates do épico conforme o Requisito 102 quando o portão fecha.

O que este documento deliberadamente não cobre

Referências