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
Catalogscontract-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-coresobpackages/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
Catalogscontract-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),CatalogAuthorizationPolicypuro, casos de uso (CatalogUseCases.ts), o ponto de aplicação da concorrência otimista (CatalogDataRepository.ts), oCatalogControllervalidado pela OAS e a composição (composeCatalogsServices.ts). Seis operações em/catalogsnaspec/1.0.0.ymlcanônica — listar, criar, obter, atualizar, excluir, restaurar — garantidas porbun 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
versiongerenciado 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 carregamcatalogId,expectedVersion,currentVersione o registro atual — a edição perdedora nunca é descartada. - Exclusão é tombstone, recuperável. A exclusão define
deletedAte incrementa a versão, de modo que a exclusão se propaga no read-back; uma cópia localmente modificada gera um conflitodeleted-remotelyem vez de desaparecer;POST /catalogs/{id}/restorerecupera 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 | restoredcom{ 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áveldomain.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 únicoapplyRemoteDocumentda 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.tssobe o backend Express real (autenticação JWT real, mediador real) e executa dois clientes reais do designer sobrefetchreal; um é particionado atrás de umECONNREFUSEDreal, 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, ondetake-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 (
degradedna 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 (umwindow.confirmcom 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 viashowStatus, nuncaalert().
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/parapackages/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 porci-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 (tsconfigpaths,moduleNameMapperdo 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 oIDesignerStoreapenas como tipo/contrato — é exatamente a árvoresrc/do pacote; o build (scripts/build.js) a copia verbatim paradist/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 — nemLocalStorageDesignerStorenemCanaDesignerStoresã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.tsvalida 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 vianpm pack --dry-run --json;dom-free.test.tsvarre a AST do artefato construído em busca de qualquer referência awindow,document,localStorage,indexedDB,alert()ou FileReader/DOMParser e de qualquer import que cruze a fronteira do pacote;consumer-smoke.test.tsexecuta o teste de aceite da issue — importa o artefato construído em um processo separado sem DOM (semdocument, semwindow, semlocalStorage) 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, comnpm:org:check:xpertmindspara o lado da organização) reconhece o pacote como qualquer outro pacote de workspace não privado e executabun publish --dry-run --access public; oprepublishOnlyforç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:
| Elo | Issue | Documento | Estado |
|---|---|---|---|
| E1 | JUM-464 | Contratos de ambiente de runtime | Done |
| E3 | JUM-473 | Arquitetura de módulos do Service Management | Done |
| E4 | JUM-479 | Garantias de paridade de contratos do Service Management | Done |
| E5 | JUM-482 | Console de operações do Service Management | Done |
| E6 | JUM-487 | Adoção do Cana, migração e comportamento offline do Service Management | Done |
| E7 | JUM-490 | Design system e shell PWA do Service Management | Done |
| E8 | JUM-494 | este documento | este 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
- O mecanismo do catálogo compartilhado por completo — pertence a Sincronização de catálogo compartilhado: a tabela do contrato OAS, a matriz de decisão de autorização, a API do cliente de sincronização e os comandos de verificação.
- A história de dados por navegador — pertence ao documento E6, Adoção do Cana, migração e comportamento offline do Service Management; este documento a referencia para a linha de base que o catálogo compartilhado estende.
- A porta de armazenamento e a fronteira de módulos — pertence ao documento E3, Arquitetura de módulos do Service Management.
- As garantias de paridade de exportação e o texto completo do contrato — pertencem ao documento E4, Garantias de paridade de contratos do Service Management, e estão fixados no Requisito 126, Contrato 3.
- O console de operações (Service Configuration, o editor de ambiente de runtime, a prévia PM2, o Deploy Management) — pertence ao documento E5, Console de operações do Service Management.
Referências
- Colaboração (JUM-491):
apps/service-management-api/src/modules/Catalogs/domain/Model/Catalog.ts,CatalogAuthorizationPolicy,CatalogUseCases.ts,CatalogService.ts,CatalogDataRepository.ts,CatalogController,composeCatalogsServices.ts,spec/1.0.0.yml,apps/service-management/src/state/catalogSyncClient.js, Sincronização de catálogo compartilhado - Versionamento de pacotes de domínio (JUM-492):
src/packages/packageVersioning.js,buildDomainPackageDocument,designerImporters.js - Empacotamento (JUM-493):
packages/designer-core/(manifesto, barrel,scripts/build.js, README), com as suítespackaging.test.ts,dom-free.test.tseconsumer-smoke.test.ts; a fonte da verdade permanece a fronteira livre de DOM sobapps/service-management/src/ - Suítes:
catalogSyncClient.test.ts,catalogSync.integration.test.ts,designerPackageVersioning.test.ts,designerRoundTrip.test.ts,designerExporters.test.ts - Requisitos: Requisito 094 (portão de conclusão de documentação do épico), Requisito 076 (paridade EN/PT), Requisito 070 (publicação somente dry-run), Requisito 102 (atualizações de projeto), Requisito 126, Contrato 3 (propriedade e contratos públicos, Contrato 3)
- 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), Console de operações do Service Management (E5), Adoção do Cana, migração e comportamento offline do Service Management (E6), Design system e shell PWA do Service Management (E7), Aplicativo Service Management, Contrato de autorização de tenant e RBAC
- Linear: JUM-491 , JUM-492 , JUM-493 , JUM-494 , JUM-547 , JUM-617