Lila Modeler v1.0.0-beta.26 EN GitHub
Docs›Referencia›Extensión BPMN

Extensión BPMN lila: y política de ids

Fuente de verdad: LILA_MODELER_ESTRUCTURA.md, sección 4 (ADR-012 "Identidad de elemento y de proceso", ADR-013 "Dónde vive cada dato", ADR-014 "Un solo namespace de extensión, definido una sola vez") y sección 5 (ruta de lila.moddle.json en el repositorio). Este documento es la referencia operativa para implementar el parser/serializador (packages/engine/src/bpmn/parse.ts) y el descriptor moddle (packages/engine/src/bpmn/lila.moddle.json); no repite el razonamiento de las ADR, lo aplica.

Este documento se apega a ADR-012 y ADR-014; cualquier discrepancia entre este archivo y esas ADR la resuelve LILA_MODELER_ESTRUCTURA.md y se corrige aquí.


1. Namespace

xmlns:lila="https://lila-modeler.org/schema/bpmn/1"

Decidido en ADR-014. El IRI no necesita resolver (no hay nada publicado en esa URL) — solo debe ser estable y no cambiar antes de M4. El sufijo /1 es la versión del namespace, no la versión del proceso: crece de forma aditiva (se agregan elementos nuevos; nunca se quita ni se resignifica uno existente) mientras el namespace siga siendo /1. Un cambio incompatible requeriría /2 — no se anticipa antes de M4.

El namespace se declara una sola vez, en bpmn:definitions, y se usa igual en:

  • el editor (apps/web, vía BpmnModeler({ moddleExtensions: { lila } })),
  • la CLI y el motor (packages/engine),
  • cualquier servidor futuro (packages/server, M6).

Los tres leen el mismo descriptor: packages/engine/src/bpmn/lila.moddle.json (sección 5 del documento de estructura). No existe una segunda copia del descriptor en ninguna otra parte del repositorio.

Round-trip

bpmn-moddle preserva namespaces desconocidos al hacer saveXML tras importXML (verificado con archivos reales de BPSim, qbp y Bizagi — ver investigacion-2026-09-03/02-bizagi-simulacion.md y 03-editor-bpmn.md). Esto es lo que permite que lila:* sobreviva a una herramienta que no lo conoce (round-trip por Camunda Modeler, Signavio, ADORIS, etc.) sin que esa herramienta necesite el descriptor. Ese comportamiento está verificado para bpmn-moddle y bpmn-js —el núcleo de Camunda Desktop Modeler— en la sección 8, con y sin el descriptor. Si alguna herramienta que importe resultara no preservar namespaces ajenos, el plan B es un sidecar annotations.json keyed por id de elemento con el mismo vocabulario (mismos nombres de campo que los elementos lila:*), fuera del .bpmn; nada de lo verificado hasta ahora lo necesita.


2. Elementos v1

Todos los elementos de la extensión son elementos XML hijos, nunca atributos del elemento BPMN que documentan — la única excepción es que cada elemento lila:* sí puede tener sus propios atributos (p. ej. lila:responsibility tiene los atributos type y roleRef). La razón de usar elementos y no atributos en el punto de inserción es permitir listas: un bpmn:task puede tener varios lila:systemRef (usa más de un sistema), varios lila:input/lila:output, etc. Un atributo XML no puede repetirse en el mismo nodo.

Todos (salvo versionTag, ver más abajo) van dentro de bpmn:extensionElements del elemento BPMN que documentan (tarea, evento, gateway, lane, o el propio bpmn:process):

<bpmn:task id="Task_7f3k2q1" name="Revisar solicitud">
  <bpmn:extensionElements>
    <lila:responsibility type="R" roleRef="rol-1"/>
    <lila:responsibility type="A" roleRef="rol-2"/>
    <lila:systemRef ref="sys-crm"/>
    <lila:documentRef ref="doc-solicitud"/>
    <lila:input ref="doc-solicitud"/>
    <lila:output ref="doc-solicitud-revisada"/>
  </bpmn:extensionElements>
  <bpmn:incoming>Flow_1</bpmn:incoming>
  <bpmn:outgoing>Flow_2</bpmn:outgoing>
</bpmn:task>
Elemento Atributos Qué documenta Apunta a
lila:responsibility type (R|A|C|I), roleRef Matriz RACI del elemento: una fila por combinación responsable/rol. Un elemento puede tener varios lila:responsibility (varios roles, o el mismo rol con distinto type no tiene sentido pero no se valida como error — es warning). roleRef → id en el catálogo de roles (catalog.json, ver ADR-013).
lila:systemRef ref Sistema/aplicación que interviene en el elemento. id en el catálogo de sistemas.
lila:documentRef ref Documento asociado al elemento (entrada, salida o solo referencia general). id en el catálogo de documentos.
lila:riskRef ref Riesgo asociado al elemento. id en el catálogo de riesgos.
lila:controlRef ref Control asociado al elemento (mitiga un riesgo). id en el catálogo de controles.
lila:kpiRef ref Indicador que mide el elemento. id en el catálogo de KPIs.
lila:input ref Documento/dato que el elemento consume. Subconjunto semántico de documentRef con dirección explícita; puede coexistir con documentRef para el mismo id. id en el catálogo de documentos.
lila:output ref Documento/dato que el elemento produce. id en el catálogo de documentos.
lila:versionTag value Ver sección 4 (clave de proceso). Único elemento que no cuelga de un elemento de flujo — cuelga de bpmn:process. — (valor libre, no referencia catálogo).

Todas las referencias (roleRef, y los ref de systemRef/documentRef/riskRef/controlRef/kpiRef/input/output) apuntan a ids del catálogo (catalog.json, fuera del .bpmn — ADR-013). Una referencia colgante (id que no existe en el catálogo) es un warning de lint, no un error de validación — el catálogo puede completarse después del diagrama.

versionTag va como hijo de bpmn:extensionElements de bpmn:process (no de bpmn:definitions ni de una tarea):

<bpmn:process id="credito-solicitud" isExecutable="false">
  <bpmn:extensionElements>
    <lila:versionTag value="1.3.0"/>
  </bpmn:extensionElements>
  ...
</bpmn:process>

Patrón calcado de zeebe:versionTag (Camunda 8), citado como precedente en ADR-012.

Atributos extendidos (#509)

Propiedades definidas por el usuario para cada tipo de elemento, como las de Bizagi Modeler («SLA», «Sistema dueño», «Nivel de riesgo»…): el modelador define un atributo una vez para un tipo de elemento y lo rellena en cada elemento de ese tipo. Las dos mitades viven en el .bpmn, así que viajan con el proceso y el contenedor del proyecto no cambia.

<bpmn:process id="credito-solicitud" isExecutable="false">
  <bpmn:extensionElements>
    <lila:attributeDefinition id="Attr_k2m8p1q" name="SLA (h)" type="number" appliesTo="task" default="24"/>
    <lila:attributeDefinition id="Attr_a91nc0x" name="Nivel de riesgo" type="list" appliesTo="task">
      <lila:option value="Bajo"/>
      <lila:option value="Alto"/>
    </lila:attributeDefinition>
  </bpmn:extensionElements>
  <bpmn:task id="Task_7f3k2q1" name="Revisar solicitud">
    <bpmn:extensionElements>
      <lila:attributeValue ref="Attr_a91nc0x" value="Alto"/>
    </bpmn:extensionElements>
  </bpmn:task>
</bpmn:process>
Elemento Atributos Dónde
lila:attributeDefinition id (NCName, Attr_<sufijo>), name, type (text|number|list|date), appliesTo (task|gateway|event|subProcess|lane|process), default opcional; una list lleva sus opciones como hijos lila:option value="…" extensionElements de la bpmn:collaboration si el diagrama la tiene; si no, de su único bpmn:process (el ejemplo de arriba). Los lectores las aceptan también en cualquier elemento raíz (el proceso de un pool, como las escribían los primeros archivos) y toman cada id una vez, el sitio canónico primero; el editor las lleva al sitio canónico en el siguiente guardado de su diálogo.
lila:attributeValue ref (el id de la definición), value extensionElements del elemento que describe.
  • appliesTo agrupa tipos BPMN: task es todo *Task; gateway, todo *Gateway; event, todo *Event, eventos de borde incluidos; subProcess es subProcess, transaction, adHocSubProcess y callActivity; lane es lane y participant (el pool); process es bpmn:process. Flujos, datos y anotaciones de texto no llevan.
  • Los valores son texto en el XML y se validan, nunca se coaccionan (validateAttributeValue en packages/engine/src/bpmn/attributes.ts): un number es un decimal simple con punto (4.5 o -2; no 4,5, 4., +5, .5 ni 1e3); una date es un AAAA-MM-DD que existe (años por debajo de 100 incluidos); el valor de una list es una de sus opciones. La cadena vacía significa «sin rellenar» y no se escribe. El editor escribe números y fechas al salir del campo o con Intro, y solo si encajan, así que un 24,5 a medio teclear nunca deja 24 escrito.
  • Un elemento sin valor muestra el default de la definición, que no se copia al elemento.
  • Los valores apuntan a la definición por id, nunca por nombre, así que renombrar una definición los conserva. Aun así el editor pregunta antes de renombrar, cambiar de tipo, quitar opciones o borrar una definición que ya tiene valores en algunos elementos, nombrando las opciones quitadas y ofreciendo vaciar solo los valores que ya no encajan. Un valor cuya definición ya no está (editado a mano, otra herramienta) se conserva: se enseña con su ref en el panel y en el documento del proceso, nunca se pierde; varios valores del mismo atributo en un elemento se enseñan todos.
  • Las definiciones no se mueven cuando entran o salen pools: añadir el primer pool o borrar el último las lleva a la nueva raíz, borrar un pool cuyo proceso todavía las tenía las pasa antes a la colaboración, y pegar un pool nunca las copia (repetiría cada id). Una tarea dentro de un subproceso colapsado ve las mismas definiciones que el resto del diagrama.
  • El documento del proceso (Word/HTML) pone cada atributo rellenado (el valor propio o, si no, el de por defecto) como una línea con etiqueta bajo su elemento: el proceso y su pool bajo la descripción, cada carril bajo su encabezado, cada tarea, compuerta, evento y subproceso (las actividades de llamada incluidas, por su tipo BPMN) en su sección.
  • Un modelo sin atributos extendidos se escribe byte a byte como antes; uno con ellos se abre sin avisos en una herramienta que no tiene el descriptor lila, y vuelve intacto (sección 8).

Qué NO es v1

Los parámetros de simulación (processingTime, resources, interTriggerTimer, etc.) no son parte de esta extensión — viven en *.scenario.json, separados del .bpmn (ADR-007, ADR-013; formato completo en SCENARIO_FORMAT.md). lila: documenta el proceso (RACI, sistemas, documentos, riesgos, controles, KPIs, versión); no lo parametriza para simular.


3. Política de ids (ADR-012)

Id de elemento

  • El id de cada elemento BPMN (bpmn:task@id, bpmn:sequenceFlow@id, etc.) es la única clave para colgar datos de negocio o de simulación. Nunca el nombre visible (name).
  • Formato: NCName válido (XML Name sin :), <PrefijoPorTipo>_<sufijo>, p. ej. Task_7f3k2q1, Gateway_a91nc0x, Flow_k2m8p1q. Los ids del editor traen el sufijo del propio modelador; cuando es el motor quien tiene que acuñar uno (la rama de colisión del importador), el sufijo es un contador determinista comprobado contra los ids ya ocupados (Task_2, Task_3, …), así que importar dos veces el mismo archivo nunca da ids distintos (#326).
  • Prefijo por tipo (no exhaustivo, crece con el IR): Start_, End_, Task_, Gateway_ (XOR/OR/AND comparten prefijo; el tipo exacto vive en el IR, no en el id), Timer_, Flow_, SubProcess_.
  • Nunca se regenera un id existente al importar, exportar o renombrar el elemento (renombrar cambia name, no id). Regenerar el id rompería cualquier referencia externa: entradas de *.scenario.json keyed por id, filas de event log, referencias de catálogo.
  • Se genera un id nuevo al copiar/pegar un elemento — un elemento copiado es una entidad distinta y no debe arrastrar los datos (lila:*, escenario) del original bajo el mismo id.
  • Sanitización reversible para ids ajenos no-NCName: algunas herramientas (Bizagi entre ellas) pueden emitir ids que no son NCName válidos. Al importar, Lila sanitiza esos ids a NCName y guarda el mapa idSanitizado → idOriginal en ir.source.originalIds (ver la forma de ProcessIR en la sección 6 del documento de estructura), de modo que un re-export pueda restaurar el id original si la herramienta de destino lo necesita. La sanitización es determinista (mismo id ajeno → mismo id sanitizado) para que reimportar el mismo archivo no genere ids distintos cada vez.

El editor aplica el mismo sanitizeXmlIds del motor antes de entregar el documento a bpmn-js y conserva el mapa por instancia de modelador. Al exportar, restaura en una sola pasada tanto las declaraciones como las referencias (sourceRef, targetRef, default, bpmnElement y referencias textuales), incluidas las de BPMNDI. La apertura se prepara en una instancia candidata y solo reemplaza el lienzo activo cuando la importación completa termina; un XML mal formado o sin diagrama renderizable no sustituye el XML, selección, servicios ni historial anteriores. Si el lector reportó una pérdida semántica —por ejemplo, una referencia topológica rota— la exportación exige una decisión visible con el detalle del aviso, porque el árbol serializado ya no puede reconstruir ese contenido.

Id de proceso

  • Clave lógica de un proceso: bpmn:process@id (slug ASCII) + lila:versionTag. Ejemplo: credito-solicitud + 1.3.0. Es la clave que identifica "el mismo proceso, versión X" a través de reimportaciones y ediciones — no es solo process@id, porque dos versiones del mismo proceso de negocio pueden (y en general deben) coexistir como archivos o commits distintos con el mismo process@id.
  • bpmn:process@id se genera igual que cualquier otro id de elemento (NCName; ver arriba) pero se recomienda un slug ASCII legible (credito-solicitud, no Process_7f3k2q1) porque además de clave técnica funciona como nombre de carpeta/archivo en el layout de proyecto.
  • lila:versionTag es una cadena libre (semver recomendado, 1.3.0, pero no forzado por el esquema — un usuario puede versionar como v2, 2026-Q3, etc.). Ausente por defecto; su ausencia no es error.

exporter / exporterVersion

Todo .bpmn que Lila escribe declara, en bpmn:definitions:

<bpmn:definitions
    xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:lila="https://lila-modeler.org/schema/bpmn/1"
    exporter="Lila Modeler"
    exporterVersion="1.0.0-beta.1"
    ...>

exporterVersion es la versión de paquete de @lila-modeler/engine que generó el archivo (siempre la del motor, también cuando quien guarda es la app web: los dos atributos los escribe un único helper, marcarExportador en packages/engine/src/bpmn/ids.ts) — sirve para diagnosticar diferencias de comportamiento entre versiones del motor, igual que hacen bpmn-js y Camunda Modeler con sus propios exporter/exporterVersion. Bizagi, por comparación, no declara estos atributos en su export (verificado en 5 archivos reales — ver investigacion-2026-09-03/02-bizagi-simulacion.md), lo que hace imposible saber qué versión de Bizagi Modeler generó un archivo dado; Lila evita ese problema desde el día uno.


4. Descriptor moddle (ejemplo)

Vive en packages/engine/src/bpmn/lila.moddle.json (única definición — sección 5 del documento de estructura). Forma que tendrá (ejemplo v1, sujeto a los ajustes menores que exija bpmn-moddle en implementación):

{
  "name": "Lila",
  "uri": "https://lila-modeler.org/schema/bpmn/1",
  "prefix": "lila",
  "xml": {
    "tagAlias": "lowerCase"
  },
  "types": [
    {
      "name": "Responsibility",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "type", "isAttr": true, "type": "String" },
        { "name": "roleRef", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "SystemRef",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "DocumentRef",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "RiskRef",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "ControlRef",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "KpiRef",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "Input",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "Output",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "ref", "isAttr": true, "type": "String" }
      ]
    },
    {
      "name": "VersionTag",
      "superClass": ["Element"],
      "meta": { "allowedIn": ["bpmn:ExtensionElements"] },
      "properties": [
        { "name": "value", "isAttr": true, "type": "String" }
      ]
    }
  ]
}

Notas de implementación (no normativas, se resuelven en el ticket que crea el archivo real):

  • meta.allowedIn documenta la intención (todos menos VersionTag van en el extensionElements de un elemento de flujo; VersionTag en el de bpmn:process); bpmn-moddle no valida allowedIn en tiempo de parseo — la validación real de "quién puede contener qué" la hace packages/engine/src/bpmn/validate.ts.
  • No se usan associations porque ninguno de estos tipos necesita sustituir o extender un tipo BPMN existente; todos son elementos nuevos que cuelgan de bpmn:extensionElements, que ya acepta cualquier values[] de un namespace declarado.
  • Todas las propiedades son isAttr: true (atributos del elemento lila:*, no hijos propios) — mantiene cada elemento en una sola línea XML y es suficiente para v1 porque ninguno necesita texto libre ni anidamiento.

5. Fragmento de ejemplo completo

bpmn:extensionElements de una tarea con responsabilidad RACI, referencias de catálogo y versión de proceso:

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions
    xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:lila="https://lila-modeler.org/schema/bpmn/1"
    id="Definitions_1"
    exporter="Lila Modeler"
    exporterVersion="1.0.0-beta.1"
    targetNamespace="http://lila-modeler.org/schema/bpmn">

  <bpmn:process id="credito-solicitud" name="Solicitud de crédito" isExecutable="false">
    <bpmn:extensionElements>
      <lila:versionTag value="1.3.0"/>
    </bpmn:extensionElements>

    <bpmn:startEvent id="Start_9k2m1qa" name="Solicitud recibida">
      <bpmn:outgoing>Flow_a1</bpmn:outgoing>
    </bpmn:startEvent>

    <bpmn:task id="Task_7f3k2q1" name="Revisar solicitud">
      <bpmn:extensionElements>
        <lila:responsibility type="R" roleRef="rol-1"/>
        <lila:responsibility type="A" roleRef="rol-2"/>
        <lila:systemRef ref="sys-crm"/>
        <lila:documentRef ref="doc-solicitud"/>
        <lila:input ref="doc-solicitud"/>
        <lila:output ref="doc-solicitud-revisada"/>
        <lila:kpiRef ref="kpi-tiempo-revision"/>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_a1</bpmn:incoming>
      <bpmn:outgoing>Flow_a2</bpmn:outgoing>
    </bpmn:task>

    <bpmn:endEvent id="End_k9x3m2z" name="Fin">
      <bpmn:incoming>Flow_a2</bpmn:incoming>
    </bpmn:endEvent>

    <bpmn:sequenceFlow id="Flow_a1" sourceRef="Start_9k2m1qa" targetRef="Task_7f3k2q1"/>
    <bpmn:sequenceFlow id="Flow_a2" sourceRef="Task_7f3k2q1" targetRef="End_k9x3m2z"/>
  </bpmn:process>
</bpmn:definitions>

Correspondencia con el catálogo referenciado (catalog.json, fuera de este documento — ver SCENARIO_FORMAT.md/LILA_MODELER_ESTRUCTURA.md sección 5 para su ubicación): rol-1, rol-2, sys-crm, doc-solicitud, doc-solicitud-revisada y kpi-tiempo-revision son ids que deben existir ahí; si no existen, validate(ir) emite un warning de referencia colgante por cada uno, no un error.


6. Consistencia con las ADR

  • ADR-012 (identidad de elemento y de proceso): implementado en la sección 3 de este documento — NCName con prefijo por tipo, nunca regenerado, nuevo al copiar, sanitización reversible, clave de proceso = process@id + versionTag, exporter/exporterVersion en definitions.
  • ADR-014 (namespace único, definido una sola vez): implementado en las secciones 1, 2 y 4 — un solo IRI, un solo descriptor compartido por editor/CLI/servidor, elementos (no atributos en el punto de inserción) para permitir listas, crecimiento aditivo, round-trip verificado en headless en la sección 8 (no hizo falta el fallback a annotations.json).

Si en el futuro alguna decisión de este documento entra en conflicto con una ADR nueva o revisada, gana la ADR y este documento se actualiza para reflejarla (nunca al revés).


7. Round-trip en la app web

La app web abre un .bpmn con bpmn-js y lo vuelve a escribir con saveXML, es decir con bpmn-moddle serializando el árbol que bpmn-moddle pudo leer. Eso fija exactamente qué sobrevive a abrir-y-exportar (LILA-192, apps/web/src/modelerXml.ts):

Se conserva

  • Los ids originales del archivo, incluidos los que no son NCName. Al importar se sanean de forma reversible (sección 3) y al exportar se restauran uno a uno, en atributos, en texto y en las referencias del BPMNDI, con las comillas y las entidades del original.
  • Los elementos lila: de este documento y las extensiones ajenas (bizagi: y compañía): el descriptor lila va en moddleExtensions y el resto viaja como contenido genérico.
  • El diagrama (bpmndi), la documentación, los nombres y la topología.
  • exporter/exporterVersion en definitions, que los reescribe siempre marcarExportador.

No se conserva

  • Las referencias por id que apuntan a un elemento que el archivo nunca declara —messageRef, dataStoreRef, categoryValueRef, dataObjectRef—: bpmn-moddle no las resuelve, no llegan al árbol y el archivo exportado ya no las lleva. Los fixtures de examples/bizagi-exports son un caso real.
  • Lo que el import descarta con un aviso de contenido no parseable o de referencia de topología sin resolver: si no entró en el modelo, no puede salir en el XML.

Cómo se avisa (nunca en silencio, que era la queja de LILA-192)

  • La barra de estado enseña la lista completa como error —no como aviso—, con los ids: «N elementos o referencias se perderán al exportar: …» (LILA-193).
  • Todo lo que escribe un .bpmn con pérdida pasa antes por el mismo diálogo, con esa misma lista y dos salidas: «Exportar .bpmn» ofrece «Exportar igualmente»/«Cancelar» y guardar el proyecto ofrece «Guardar igualmente»/«Cancelar». Cancelar no descarga ni escribe nada en disco.
  • autorizarExportacion corta con un error cualquier exportación con pérdida que no traiga aceptarPerdida, es decir el sí explícito de ese diálogo: el XML que alimenta a la simulación —que el usuario no ve— nunca lo trae, y el snapshot de guardar solo lo trae después del sí.
  • En Electron, cancelar el diálogo al guardar devuelve «no se guardó» al cierre de la ventana, así que el cierre se cancela y no se pierde nada.

Nada de esto reescribe el serializador: la fidelidad byte a byte con el archivo de origen no es una promesa de la app, y conservar atributos rotos exigiría un serializador propio.


8. Round-trip en otras herramientas (LILA-068)

Toda la apuesta de ADR-014 —un namespace propio dentro del .bpmn, en vez de un sidecar— solo vale si otra herramienta puede abrir un archivo de Lila, guardarlo y devolver los lila: intactos. Verificado headless contra el par sobre el que está construido Camunda Desktop Modeler (es Electron

  • bpmn-js; ver investigacion-2026-09-03/03-editor-bpmn.md): bpmn-moddle 10.2.0 y bpmn-js 18.28.0, las versiones que instala este repositorio.

Lo que viaja es examples/pedido/model.bpmn con un elemento de cada tipo v1 (sección 2) escrito por el propio escritor del motor —dieciocho elementos lila: (los atributos extendidos incluidos) repartidos en varios elementos dueños, con repeticiones— y el round-trip corre dos veces: con lila.moddle.json, que es lo que hacen el editor y la CLI de Lila, y sin él, que es lo que tiene una herramienta ajena (un namespace sin declarar que bpmn-moddle conserva como contenido genérico). En los dos casos, y por las dos librerías, el guardado devuelve:

  • todos los elementos lila:, colgando del mismo elemento dueño y con los mismos valores de atributo;
  • el namespace, declarado una sola vez en bpmn:definitions;
  • la bpmn:documentation de los elementos anotados, sus ids y el diagrama (bpmndi).

También aguanta si la otra herramienta escribe un prefijo propio (solo el IRI es normativo, sección 1), y el segundo guardado es idéntico al primero: de ahí en adelante el archivo es un punto fijo.

Pruebas: packages/engine/test/bpmn-roundtrip.test.ts (bpmn-moddle) y apps/web/src/bpmnRoundtrip.test.ts (bpmn-js), con el archivo anotado y la comparación en packages/engine/test/bpmn-roundtrip.fixture.ts. Lo que se compara es un inventario de los elementos lila: leído del texto del XML, normalizado en prefijo y en orden de atributos, no el archivo entero byte a byte: bpmn-moddle reformatea todo lo que reserializa, y la sección 7 ya dice que la fidelidad byte a byte no es una promesa.

Herramienta Qué lee y escribe su XML lila: tras abrir y guardar Cómo se comprobó
Camunda Desktop Modeler (y cualquier editor construido sobre las librerías de bpmn.io) bpmn-js 18.28.0 sobre bpmn-moddle 10.2.0 se conserva round-trip headless sobre ese mismo núcleo, con y sin el descriptor (apps/web/src/bpmnRoundtrip.test.ts)
Cualquier herramienta sobre bpmn-moddle a secas (conversores, servidores, la CLI de Lila) bpmn-moddle 10.2.0 se conserva packages/engine/test/bpmn-roundtrip.test.ts
Lila Modeler mismo (web y escritorio) bpmn-js con moddleExtensions: { lila } se conserva sección 7, más apps/web/src/exportar.test.ts
Signavio propietaria, SaaS sin verificar no hay cuenta disponible
ADONIS propietaria sin verificar no hay licencia disponible
Bizagi Modeler propietaria sin verificar no hay licencia disponible; la dirección opuesta —que Lila conserve un bizagi: ajeno al abrir y exportar— la cubren los fixtures de examples/bizagi-exports en apps/web/src/exportar.test.ts

Qué no cubre esto. No se manejó la interfaz de Camunda Desktop Modeler: la prueba headless pasa por la mismísima instancia de moddle con la que bpmn-js lee y escribe el XML (importXML es moddle.fromXML, saveXML es moddle.toXML, y nada entre ambas toca el árbol que se serializa), pero no por su lienzo, que jsdom no sabe dibujar. Signavio y ADONIS no se probaron en absoluto, por falta de acceso; si alguna descarta namespaces ajenos, los descarta para todos los elementos lila: a la vez, que es justo lo que mediría el inventario de arriba.

El plan B sigue sin escribirse. El sidecar annotations.json que la sección 1 guarda como respaldo no hace falta: ninguna herramienta verificada aquí descarta el namespace. Su ticket se abre el día que se demuestre que una herramienta que importa lo tira, y el formato del archivo no cambia antes de eso.

✎ Editar esta páginaPara v1.0.0-beta.26