Skip to content

@miragon/rules/element-id-naming ​

Reports an element whose ID does not follow the project's naming convention.

Examples ​

element-id-naming/invalid.bpmn

Invalid model: IDs off the naming convention

Valid model: IDs on the naming convention

5 findings
  • OrderReceived Element id must match the naming convention <startEvent_camelCase>
  • ReviewOrder Element id must match the naming convention <serviceTask_camelCase>
  • OrderReviewed Element id must match the naming convention <endEvent_camelCase>
  • ReceivedToReview Element id must match the naming convention <flow_camelCase>
  • ReviewToReviewed Element id must match the naming convention <flow_camelCase>

Invalid: readable labels, but IDs with no type prefix (ReviewOrder, not serviceTask_reviewOrder)

Valid: the same model with <typePrefix>_<Name> IDs

The same, as XML. 👎 wrong:

xml
<bpmn:serviceTask id="Activity_0049ryx" name="Claim membership" />
<bpmn:serviceTask id="serviceTask_ClaimMembership" name="Claim membership" />
<bpmn:serviceTask id="task_claimMembership" name="Claim membership" />

👍 right:

xml
<bpmn:serviceTask id="serviceTask_claimMembership" name="Claim membership" />

Why ​

Wherever an element ID surfaces (a test assertion, an incident in monitoring, a generated constant, a diff) someone has to read it and know what it points at. A naming convention makes that instant: the prefix says what type the element is, the name says what it does, so a developer or a business stakeholder recognises the step without opening the model. Without one the same ID is just a string.

serviceTask_claimMembership     what it is, and what it does
Activity_0049ryx                neither

Why this matters for agentic BPMN ​

An agent navigates a model by its IDs: to identify an element, describe a change, or apply a targeted edit, it needs IDs that are structured and categorisable. Without a prefix convention the IDs are arbitrary strings that carry no type information a machine can key off.

Typical AI artifact without this rule: a mix of Activity_1, task_claim and ServiceTask_Claim for elements of the same kind: no shared shape an agent can rely on to tell a gateway from a task from a flow.

What this rule guarantees: every ID follows one <typePrefix>_<Name> convention, so both a reviewer and an agent can read an element's type and purpose straight off its ID and address it unambiguously.

Default convention ​

A camelCase element-type prefix plus a camelCase name.

Element typePrefix
bpmn:StartEventstartEvent_
bpmn:EndEventendEvent_
bpmn:BoundaryEvent, bpmn:IntermediateCatchEvent, bpmn:IntermediateThrowEventevent_
bpmn:ServiceTaskserviceTask_
bpmn:UserTaskuserTask_
bpmn:SendTask / ReceiveTask / ManualTask / ScriptTasksendTask_, …
bpmn:BusinessRuleTaskbusinessRuleTask_
bpmn:Tasktask_
bpmn:CallActivitycallActivity_
bpmn:SubProcesssubProcess_
bpmn:Gateway (every kind)gateway_
bpmn:SequenceFlowflow_

Types are resolved by exact $type first, then by inheritance, so bpmn:Transaction and bpmn:AdHocSubProcess pick up the sub-process convention, and every gateway kind picks up gateway_, without being listed. Because the exact $type wins over the inheritance fallback, switching a concrete type off with false (or overriding it) also stops it from inheriting a base convention — and the concrete task types each need their own entry, since the default map lists them individually rather than only bpmn:Task.

Two things are intentionally left out. bpmn:Process is not covered: a process ID is a public contract (the deployment key, and what a call activity references), not a diagram-internal identifier. Any type not in the table is skipped too, so an exotic element nobody configured never produces a report.

Event-definition qualifiers ​

An event id may optionally name its event definition — messageStartEvent_membershipRequested instead of startEvent_membershipRequested. The accepted set is derived from the element's own<bpmn:*EventDefinition> children, so a qualifier that matches passes and a qualifier that lies is reported. The qualifier is the definition name lowercased (bpmn:TimerEventDefinition → timer): message, timer, signal, conditional, escalation, error, link, terminate, compensate. It is prepended to whatever prefix the type resolves to (startEvent_ → timerStartEvent_, a custom boundaryEvent_ → messageBoundaryEvent_).

eventDefinitionQualifier controls this:

modeaccepted for a message start event with prefix startEvent_
optional (default)startEvent_… or messageStartEvent_…
requiredmessageStartEvent_… only (plain prefix when no definition)
offstartEvent_… only
👍 startEvent_membershipRequested         always valid
👍 messageStartEvent_membershipRequested  it IS a message start event
👎 timerStartEvent_membershipRequested    no timer event definition

The default optional only widens what passes, so enabling the rule never rejects an id that was valid before. A lying qualifier gets its own message — Element id claims a timer event, but this element has no timer event definition — separate from the plain wrong-prefix / wrong-case one.

Configuration ​

json
{
  "rules": {
    "@miragon/rules/element-id-naming": [
      "error",
      {
        "prefixes": {
          "bpmn:SequenceFlow": "Flow_",
          "bpmn:ScriptTask": false
        },
        "case": "snake_case",
        "eventDefinitionQualifier": "optional"
      }
    ]
  }
}
  • prefixes: merged over the defaults, so you only state what differs. false switches a type off entirely.
  • case: the shape of the part after the prefix: camelCase (default), PascalCase, snake_case or any.
  • eventDefinitionQualifier: whether an event id may name its event definition — optional (default), required or off. See Event-definition qualifiers.

Further reading ​