4 min read

Process Model

The Primmel process model provides BPMN-style primitives for describing flows of work. Processes are bound to roles, may read and write data registries, and may carry compliance requirements.

Primitives

role

role Roaster {
  name "Roaster"
}

A role has an ID and a human-readable name. Roles are bound to processes via the actor clause.

start_event, end_event

start_event Start1 { }
end_event   End1   { }

The boundaries of a flow. A model with multiple canvas diagrams typically has multiple start_events — one per diagram. There can be more end_events than start_events — a flow can terminate in multiple outcomes.

timer_event

timer_event PaymentWindow {
  type WAIT
  para "72 hours after order placed"
}

timer_event RestockTimer {
  type REPEAT
  para "First of every month"
}
type Meaning
WAIT Pause the flow until the specified time elapses.
REPEAT Fire on a recurring schedule, regardless of flow position.

REPEAT timers are typically wired in a loop to model recurring processes (see timer loop pattern).

exclusive_gateway

exclusive_gateway StockCheck {
  label "Is the item in stock?"
}

A decision point. Its outgoing edges are labelled with mutually exclusive descriptions; exactly one is taken. One edge should be marked description "default" as the fallback.

process

process Greet {
  name "Greet the world"
  actor Greeter
}

A unit of work. The actor clause binds it to a role. A real-world process also carries:

  • modality SHALL / SHOULD / MAY — the compliance weight.
  • validate_provision { ... } — the provisions it must satisfy. See compliance.
  • validate_measurement { ... } — the measurement expressions that must hold.
  • reference_data_registry { ... } — registries it reads from.
  • output { ... } — registries it writes to.
  • canvas <Name> — binds the process to a named diagram.

Nested processes

A process can contain child processes, forming a hierarchy. This is useful for decomposing a high-level activity into its sub-steps:

process Manufacturing {
  name "Manufacture product"
  actor Factory

  process Assembly {
    name "Assemble components"
    actor AssemblyLine
  }

  process QualityControl {
    name "Inspect quality"
    actor QA
  }
}

Child processes are defined inside the parent’s body. The parser sets each child’s parent field automatically to the parent’s ID.

On the parsed model:

  • process.parent — the ID of the parent process (empty string for top-level processes)
  • process.children — an array of child process IDs

Nesting is additive — existing flat models still work. A model with no nesting simply has empty children arrays and empty parent strings.

The dumper preserves nesting: dump(load(content)) emits child processes inside their parent, indented for readability.

approval

approval ApproveNewRoast {
  name "Approve a new roast profile"
  actor HeadRoaster
  modality SHALL
  approve_by OperationsManager
  approval_record { RoastProfileApprovalRegistry }
}

A specialised process that requires sign-off. Distinct from process because it carries an approve_by clause and an approval_record registry. See approval workflow.

canvas

canvas OrderFlow {
  elements {
    OrderStart  { x 0 y 0 }
    StockCheck  { x 0 y 100 }
    /* ... */
  }
  process_flow {
    Edge1 { from OrderStart to StockCheck description "Item is in stock" }
    Edge2 { from OrderStart to CancelOrder description "default" }
  }
  data { }
}

A container that lays out a flow on one diagram. Has three blocks:

Block Purpose
elements { ... } Nodes on this diagram, each with x/y layout coordinates.
process_flow { ... } Directed edges between nodes. Each edge has from, to, optional description and condition.
data { ... } Data registries visible on this diagram, with coordinates.

The header’s root <Name> declaration names the top-level canvas. A model with multiple canvases names them like pages.

Where to see it in action