Skip to content

Aspects and formalisms ​

A system has one graph. Its assets and connections are its structure: what the system is made of. An aspect is one more model of the same system, held in the same graph beside the structure — the order process of a shop, for instance. A system can hold any number of aspects, and needs no assets to hold one.

A formalism is the kind of model an aspect is. It is declared as data, and fixes:

  • the kinds of member an aspect of it has (a process: start, end, task, branch, split, join),
  • the kinds of line between members (a process: then),
  • the rules an aspect is checked against (one start; every step reachable from it),
  • what a member may name in the rest of the system (a task is performed by an asset),
  • how it is laid out, and how it will be run.
WordMeansExample
structurethe assets and connections of a systemthe tills, the bar, who hands over to whom
aspectone further model of the same system, in the same graphthe shop's order process
formalismthe kind of model an aspect is, declared as dataprocess
membera node of an aspecta step
linean edge between two members of one aspecta flow: check then pack
linka member naming something of the structureperformedBy: 'warehouse'
member typea kind of member a system declares for itself, in one formalismapproval, a branch of a process

system-core is the mechanism and names no formalism. The formalisms are the package system-formalisms (@wity-ai/system-formalisms), one folder each, and are handed to a system when it is made — as the graph library is:

js
import { GraphAbstract } from '@wity/graph-headless';
import { SystemGraph } from 'wity-system';
import { formalisms } from 'wity-system/formalisms';

const sys = new SystemGraph({ GraphAbstract, formalisms });

The formalisms this version has: process (Process), state-machine (State machine), petri-net (Petri net), behaviour-tree (Behaviour tree), syntax-tree (Syntax tree).

Writing and reading an aspect ​

An aspect is one value:

js
sys.addAsset('warehouse', 'team', { label: 'Warehouse' });

sys.setAspect({
    id: 'order', formalism: 'process', label: 'Handling an order',
    members: [
        { id: 'placed',  kind: 'start',  label: 'Order placed' },
        { id: 'check',   kind: 'task',   label: 'Check stock', performedBy: 'warehouse', minutes: 5 },
        { id: 'instock', kind: 'branch', label: 'In stock?' },
        { id: 'pack',    kind: 'task',   label: 'Pack', minutes: 10 },
        { id: 'done',    kind: 'end' },
        { id: 'refused', kind: 'end' },
    ],
    lines: [
        { from: 'placed', to: 'check' }, { from: 'check', to: 'instock' },
        { from: 'instock', to: 'pack', label: 'yes' }, { from: 'instock', to: 'refused', label: 'no' },
        { from: 'pack', to: 'done' },
    ],
});

sys.getAspect('order');      // the same value; each line with its kind ({ kind: 'then', from, to })
sys.getAspects();            // every aspect, in order — getAspects('process') for one formalism's
sys.checkAspect('order');    // [] — what breaks a rule: [{ rule, member?, line?, message }]
  • A member has an id, a kind (or a type — a member type of the system), and optionally a label, a place (x, y), what it names (a link field such as performedBy) and any fields of its own (minutes).
  • A line has from, to, optionally a label and fields of its own, and a kind — which may be left out where the formalism has only one.
  • The order of members and lines is kept. Where the order of a member's lines means something, it is their order in lines.
  • Shape and soundness are different things. A value that is not an aspect of its formalism in shape (an unknown kind, a line to a member that is not there) is refused: setAspect throws, naming where. An aspect that is one in shape but breaks a rule is kept — a draft is a draft — and checkAspect says what breaks which rule.

One member or one line at a time — every other member stays the node it was:

js
sys.setAspectMember('order', { id: 'weigh', kind: 'task', label: 'Weigh' }, { before: 'done' });
sys.setAspectLine('order', { from: 'pack', to: 'weigh' });
sys.removeAspectLine('order', { from: 'pack', to: 'done' });
sys.removeAspectMember('order', 'weigh');     // its lines go with it
sys.removeAspect('order');

Analyses ​

There is one set of analyses. Asked plainly, each answers for the structure. Asked with in: <aspect id>, the same call answers for that aspect: its members are the nodes, named by their ids, and its lines are followed.

js
sys.impact('billing');                          // an asset: what is downstream and upstream of it
sys.impact('instock', { in: 'order' });         // a step of the process: what follows it, and what leads to it
// → { downstream: [{ uid, depth, via }], upstream: [...] }

sys.shortestPath('placed', 'done', { in: 'order', costField: 'minutes' });
// → { path: ['placed', 'check', 'instock', 'pack', 'done'], cost: 15 }   the way on which a field adds up to the least

sys.cascade(['pack'], { in: 'order' });
// → { reachable, cutOff, disabled }       with a step taken out: what can still be reached from the start
sys.cascade(['pack'], { in: 'order', direction: 'upstream' }).cutOff;
// → the steps that can no longer get to an end

sys.membersLinkedTo('warehouse');
// → [{ aspect: 'order', member: 'check', link: 'performed-by' }]      who names this asset

propagate, reachable and allPaths take in the same way; line keeps an analysis to one kind of line. Where an aspect begins and ends is its formalism's roles (entry, exit).

A link is an edge of the graph (from the member to what it names), kept in step with the member's field: it is there while both ends are. Remove the asset and the edge goes; the member still says who it named, and checkAspect reports it. A formalism's own analyses are functions in its folder that take the aspect as a value — lanes(sys.getAspect('order')).

sys.aspectGraph(id) is an aspect as a graph (an L1 view, with uidOf(memberId) and idOf(uid)), for a formalism's own analyses and engines.

What the structure sees ​

Nothing changes for the structure. getAssets, getConnections, cascade, impact, shortestPath, the regions, the scenarios and the simulator see the assets and connections only — with or without aspects, they give the same answers. A scenario is the structure as it was; loading one leaves the aspects as they are.

Member types ​

A system can declare its own kinds of member, in one formalism:

js
sys.registerMemberType('approval', { formalism: 'process', kind: 'branch', label: 'Approval', meta: { fill: '#be123c' } });
sys.setAspectMember('order', { id: 'ok', type: 'approval', label: 'Approved?' });

A member of a type behaves as the kind the type names. meta is how it is drawn; features (as on an asset type) are the signals it can bear.

How an aspect is stored ​

In the graphNode or edgeTypeUidData
an aspectnodesystem-aspect$sys/aspect/<id>{ id, formalism, label?, … }
a membernodeits member type, else its kind$sys/aspect/<id>/<memberId>the member as written
membershipedgecontainsaspect → member{ order }
a lineedgethe line's kindmember → member{ order, label?, … }
a linkedgethe link's (performed-by)member → what it names— (follows from the member's field; not stored)

In snapshot() the aspects are under aspects: { nodes, edges }, present only when a system has some — a system without aspects is stored exactly as before. Member types are under types.memberTypes.

Earlier forms are still read. 0.14 stored processes under occurants (nodes of type system-process and system-step) and their types under types.stepTypes; 0.13 stored activities. restore() reads each as an aspect of the process formalism, and snapshot() stores it in the present form. A system that is handed no formalisms still keeps every aspect whole and stores it back unchanged; it cannot check or analyse it.

The formalisms ​

This section is generated from the declarations (node docs/generate.mjs).

Process — formalism process ​

Use it for: Things that happen in an order — a procedure, a workflow, a flowchart, a checklist with decisions in it.

Not for: Parts that exist and connect (that is the map's assets and connections), or how a number changes over time (a behaviour).

Its members are called steps, its lines flows.

Kind of stepWhat it isflows inflows out
startwhere the process begins01
endwhere it finishes1 or more0
taska piece of work1 or more1
branchone way out is taken — each flow out says its case in its label1 or more2 or more
splitevery way out is taken, side by side1 or more2 or more
joinwaits for all the ways in, then goes on2 or more1
Kind of flowWhat it says
thenfrom happens, then to

A step may name: performedBy — the uid of the asset that does the step — the steps one asset does are its lane.

A member type of this formalism is a task unless it says another kind.

Checked (beside the lines in and out above):

  • A process begins at one start
  • A process finishes at an end
  • Each way out of a branch says the case it is taken in, and no two say the same
  • A step is performed by an asset of the system
  • Every step can be reached from the start
  • From every step, an end can be reached

Its own analyses: lanes — who performs which steps, and where the work changes hands.

State machine — formalism state-machine ​

Use it for: The conditions one thing can be in and the events that move it between them — an order (placed, paid, shipped), a connection, a door, a document under review.

Not for: Work done in an order by several parts (that is a process), or a choice someone must make (a decision).

Its members are called states, its lines transitions.

Kind of stateWhat it istransitions intransitions out
initialwhere it starts — a marker, with one transition to the first state01
statea condition it stays in until an event takes it elsewhereanyany
finalwhere it ends — nothing leaves itany0
Kind of transitionWhat it says
transitionon the event in label, from gives way to to (guard: a condition that must also hold)

A member type of this formalism is a state unless it says another kind.

Checked (beside the lines in and out above):

  • A state machine starts at one initial marker
  • Each transition out of a state says the event it is taken on, and no two out of one state say the same
  • Every state can be reached from the initial marker

Petri net — formalism petri-net ​

Use it for: Things that are used up and made, or shared and waited for — stock and orders, machines and jobs, a protocol where two sides must both be ready.

Not for: A plain order of steps with no counting of what is available (that is a process).

Its members are called nodes, its lines arcs.

Kind of nodeWhat it isarcs inarcs out
placeholds tokens — a stock, a free machine, a waiting order (tokens: how many it starts with)anyany
transitionsomething that happens when every place leading into it has enough tokensanyany
Kind of arcWhat it says
arcfrom a place to a transition (it takes tokens) or from a transition to a place (it gives them); weight: how many — only place → transition, transition → place

A member type of this formalism is a place unless it says another kind.

Checked (beside the lines in and out above):

  • A Petri net has at least one place
  • A Petri net has at least one transition
  • Each arc joins the kinds it may: arc place → transition or transition → place
  • No node is cut off from the rest

Behaviour tree — formalism behaviour-tree ​

Use it for: How an agent — a robot, a game character, an automated responder — picks what to do next from what is true right now: priorities, fallbacks, checks before acting.

Not for: A fixed order of steps (that is a process), or the conditions a thing can be in (a state machine).

Its members are called nodes, its lines links.

Kind of nodeWhat it islinks inlinks out
sequencedoes its children in order; fails at the first that fails0 to 11 or more
selectortries its children in order; succeeds at the first that succeeds0 to 11 or more
paralleldoes all its children; succeeds when all succeed0 to 11 or more
inverterone child; succeeds when it fails and fails when it succeeds0 to 11
repeatone child, done again (times: how many; forever when not said)0 to 11
actiona leaf: something done0 to 10
conditiona leaf: something checked — true or false0 to 10
Kind of linkWhat it says
childto is a child of from; a node's children are tried in the order of its lines

A member type of this formalism is a action unless it says another kind.

Checked (beside the lines in and out above):

  • It is a tree: each node has at most one parent, and none leads back to itself
  • It is one tree: no node is cut off from the rest

Syntax tree — formalism syntax-tree ​

Use it for: The structure of something written — a formula, a line of code, a query, a sentence — shown as parts within parts.

Not for: Choosing what to do (a behaviour tree), or steps in an order (a process).

Its members are called nodes, its lines links.

A node is of any kind you name.

Kind of linkWhat it says
childto is a part of from; parts are in the order of the lines, and label may say what part it is

Checked (beside the lines in and out above):

  • It is a tree: each node has at most one parent, and none leads back to itself
  • It is one tree: no node is cut off from the rest

Declaring a formalism ​

A formalism is one object. This is the whole of a made-up one — places and gates joined by arcs:

js
const net = {
    id: 'net', label: 'Net',
    words: { member: 'node', line: 'arc', self: 'net' },      // what findings and refusals are said in
    nodeKinds: {
        place: { label: 'Place', fields: { tokens: { type: 'number', min: 0 } } },
        gate:  { label: 'Gate' },
    },
    lineKinds: {
        arc: { label: 'Arc', pairs: [['place', 'gate'], ['gate', 'place']], fields: { weight: { type: 'number', min: 1 } } },
    },
    rules: [{ rule: 'joins' }, { rule: 'connected' }],
    links: { heldBy: { to: 'asset', edge: 'held-by', says: "an asset's uid" } },
    roles: { entry: 'place' },                                 // where an aspect begins (what cascade starts from)
    guide: { when: 'Things consumed and produced.', not: 'Steps in an order — that is a process.' },
};
sys.registerFormalism(net);
  • nodeKinds — the kinds of member, each with the fields it may carry (type: number | text | boolean; min, max, oneOf, required). openKinds: true instead lets a member be of any kind (a syntax tree's kinds are its language's).
  • lineKinds — the kinds of line: pairs says which kinds each may join; self: true lets a member have a line to itself; fields as above.
  • rules — from the fixed list below. A rule may carry an id (what a finding's rule says) and says (the rule in a sentence, for guides).
  • links — fields in which a member names an asset: edge is the type of the edge made for it.
  • memberTypeKind — the kind a member type of this formalism is when it names none.
  • analyses — the formalism's own, by name: { says, run(aspect) }.
  • guide — when to use it and what it is not for: what a guide shows to whoever chooses a formalism.

The rules a formalism may declare ​

Checked in this order, so findings read the same way for every formalism: what is counted over the whole aspect, then each member in turn, then the shape of the whole.

  • count — a kind of member occurs exactly / at least / at most n times — { rule: 'count', kind, exactly | min | max }
  • degree — how many lines come into and go out of each member of a kind — { rule: 'degree', kind, in: [min, max], out: [min, max], line? } (null max: any number)
  • labelled-out — every line out of a kind of member carries a label, and no two the same — { rule: 'labelled-out', kind, line? }
  • link — what a member names in a link field is there — { rule: 'link', field } (checked against the system: a task's performer is one of its assets)
  • joins — every line joins kinds of member its kind of line allows — { rule: 'joins' }, with pairs on the line kinds
  • reachable-from — every member can be reached from the one member of a kind — { rule: 'reachable-from', kind, line? }
  • can-reach — from every member, a member of a kind can be reached — { rule: 'can-reach', kind, line? }
  • acyclic — no line of a kind leads back to where it started — { rule: 'acyclic', line }
  • tree — each member has at most one line of a kind coming in, and none leads back to itself — { rule: 'tree', line }
  • connected — no member is cut off from the rest — { rule: 'connected' }

Drawing ​

computeAspectScene(system, aspectId) (system-ui-compute) gives an aspect as the same render scene computeScene gives for the structure, so one painter draws both. Everything it needs is in the declaration: a kind's look ({ shape, w, h, fill }), a line kind's look ({ stroke, dashed, arrow }), layout ('rows' or 'tree'), a field a kind shows beside its label (a place's tokens), and a link marked lane. A formalism with no layout gives null.