Geometric Memory and Process Trajectories:
How Priostack Detects Unusual Workflows
Every workflow engine can tell you what happened in a process: which tasks ran, which gateway fired, what variables were set. Very few can tell you whether what happened was normal.
The difference matters enormously in regulated industries. A loan approval that skips the risk assessment task is not just a bug - it is a potential fraud vector. A document approval that bypasses two required sign-offs may violate compliance obligations. The challenge is detecting these deviations automatically, without enumerating every possible bad path in a rules engine.
This article explains the idea behind Priostack's geometric memory: how an execution becomes a path through a space, why the distance between paths catches deviations nobody wrote a rule for, and - the part most vendor articles leave out - the specific class of problems this method cannot see at all.
Contents
- The problem with rule-based anomaly detection
- From execution step to position in memory
- Shape space: what a position actually is
- Process paths as geometric trajectories
- Learning what normal looks like
- Scoring: how unusual is this run?
- Fréchet distance: why minimum leash is the right metric
- What the numbers look like
- What running this at scale actually taught us
- Comparison with rule-based and statistical alternatives
- Beyond processes: memory as a context store
1. The problem with rule-based anomaly detection
The most common approach to detecting anomalous process execution is to write
rules. A rule might say: "if the assess_risk task is skipped and
the loan amount exceeds €100 000, raise an alert." This works for the specific
case you thought of. But process models have a combinatorial explosion of
possible execution paths, and writing a rule for every bad combination is
intractable.
Consider a BPMN process with 12 tasks, 3 XOR gateways, and 2 parallel splits. The number of distinct execution paths is in the hundreds. The number of abnormal paths is the total minus the handful of normal ones - still hundreds. You cannot enumerate them all, and even if you could, each new version of the process definition invalidates your ruleset.
There is a second problem with rules: process paths are sequential, not independent. A rule that operates on individual task completions misses the fact that it is the combination that is suspicious - two tasks that are individually normal but occur in the wrong order, or at the wrong position in the broader sequence. Geometric trajectory comparison captures the entire path as a unit.
2. From execution step to position in memory
When Priostack executes a BPMN process, it does not only record which task completed. Each time the engine advances an instance to its next resting point, it writes the whole execution state at that moment into a shared geometric memory as a single point. One point per step of the execution - not one per diagram element: a gateway evaluated on the way to the next wait state is folded into the point that follows it. A five-step loan application leaves roughly five points behind.
The position of that point is derived from the execution state itself: which parts of the process graph hold tokens once the step has fired. This is the property everything else rests on. The same execution path through the same process definition always lands in the same place in the space, so two runs of that path trace the same shape and the distance between them is approximately zero. That reproducibility is what makes distance meaningful - it is why a measured divergence is evidence of something rather than noise.
Writing a point returns a handle - an address, not a measurement. A handle says where a position was stored; it says nothing about what happened. Handles are allocation slots in a store that every process, every case and every tenant in a session writes into, so consecutive handles routinely belong to completely unrelated executions, and two handles far apart numerically may address positions sitting on top of each other. Nothing is ever inferred from a handle's value. Arithmetic on handles is meaningless; arithmetic on positions is the entire product.
One approved loan application, as the memory sees it:
step 1 application received → position, handle c1
step 2 identity validated → position, handle c2
step 3 risk assessed → position, handle c3
step 4 gateway + approval granted → position, handle c4
step 5 applicant notified → position, handle c5
the execution = the ordered path c1 → c2 → c3 → c4 → c5
what is compared = the five positions, in order
what is never compared = the handles themselves
Memory has regions, not one number line
Geometric memory is organised into regions - specialised spaces holding related state - and a point's identity names the region it lives in as well as its place within that region. That is what lets the memory shard as it grows without identities colliding, and it is why a bare number is not an identity: the same number means different things in different regions. The flat single-line picture in the original version of this article was the view from one small store, and the engine has since outgrown it.
What variables do, and do not, do
Process variables reach the geometry only through the branches they decide. A guard asking whether an amount exceeds a threshold contributes a decision, not a magnitude. Two applications for €5 000 and €6 000 that fall on the same side of every guard in the process produce identical positions - distance zero, not "the same region". Only an amount that flips a guard moves the trajectory, and it moves it structurally, to a genuinely different part of the space, rather than by some offset proportional to the number.
That is a feature, not a flaw. A €1 000 loan and a €5 000 000 loan that walk the same path through the same checks are the same execution, and the system says so. It is precisely what lets you ask "did this behave like the thousand cases we have already seen?" without the answer being dominated by how big the numbers were. If magnitude should itself be geometrically meaningful - if a very large application should sit far from a small one even when both walk the same path - then magnitude has to be modelled as a dimension of the space in its own right. That is a deliberate modelling decision, and the memory supports it: application values can be projected into the space alongside execution state, so what counts as "similar" is something you design rather than something you inherit.
Why positions, not task names?
Strings are expensive to compare at scale and have no notion of distance.
"validate_identity" and "assess_risk" are exactly as
different from each other as either is from "approve_loan".
Positions have a real geometry: distance, direction, density and neighbourhood
all mean something, and all of them can be computed in bulk.
More importantly, a position carries the full execution context, not
just the name of the step that ran. A notify_applicant reached
after a completed risk assessment sits somewhere different from a
notify_applicant reached by skipping it, because the state that
produced it is different. The history is in the position. That is what a
task-name log cannot give you, and it is what makes whole-path comparison
possible.
A step always runs; it is not always remembered
One honest limit, and operationally the most important sentence in this article. Geometric memory has a finite capacity that is fixed when the engine starts. A step always executes correctly; it does not always get remembered. If the memory is full, or if the geometric layer is not attached to that deployment, the step completes normally and simply leaves no point behind - and the recorded trajectory then has fewer points than the execution had.
This is silent at the process layer by design: nothing errors, business execution is unaffected, and the instance finishes exactly as it should. But short or empty trajectories score as unremarkable, so an undersized memory degrades detection towards "everything looks normal" rather than towards false alarms. Any production deployment has to monitor the health of the memory itself, not only the health of the processes running against it.
3. Shape space: what a position actually is
Every point in the memory is a fixed-width vector of numbers - the same width for every step of every process, whatever the process looks like. Fixed width is what makes comparison uniform and cheap: any two points can be compared with the same operation at the same cost, and the hardware does that work in bulk.
An execution is not one of these vectors. It is the ordered path through several of them. This is the detail most often lost in summaries of the approach: the system does not fold a run into a single signature and then compare signatures. It keeps the path and compares paths. A process that routes to manual review has one more point than one that auto-approves, and the comparison handles that directly rather than by flattening both into something of equal size first.
There is no hashing step, and execution positions are not normalised to unit length. That is deliberate: magnitude carries "how much happened", which is exactly the signal a runaway loop or a bypassed control shows up on. A loop that ran forty times should not look identical to one that ran four times. Where direction alone is the right comparison - comparing identities, where two records of the same customer should match regardless of how much evidence each carries - the system compares by direction and discards magnitude. Execution is compared by direction and magnitude; identity by direction. Those are different questions and they get different geometry.
Tolerance for executions of different lengths therefore comes from the comparison, not from the vector. The trajectory metrics align two paths of unequal length and allow non-uniform speed along each, which is what absorbs extra loop iterations without smoothing away a genuine reordering. Section 7 explains why that particular trade-off is the right one for business processes.
The shape of the walk is itself a measurement
Because an execution is a path, it has a geometry of its own, and the engine will describe it in a single pass: how far the run travelled in total, how far it ended up from where it started, how straight it ran on average, and its sharpest single turn. Four numbers, and they read as plain language. A long path with almost no net displacement is a process going round in circles. A short path with a large displacement and one very sharp turn is a run that jumped somewhere it does not normally go.
On synthetic paths these come out exactly as the geometry demands: a straight out-and-back of two unit legs measures total travel 2.0 with average straightness 1.0, while a zig-zag of the same total length measures straightness -1.0 with a net displacement of about zero. This matters because it turns a verdict into something you can read. Most anomaly detection hands you a score and stops; a description of the shape tells you what kind of unusual you are looking at, and lets you drill from the verdict down to the geometry underneath it.
4. Process paths as geometric trajectories
A step is a point. An execution is the ordered path through the points it wrote - a geometric trajectory through the shape space.
This is not a metaphor. Normal approved loans trace nearly the same curve every time, because they pass through the same execution states in the same order: application received, identity validated, risk assessed, approved, notified. The curve is stable enough that the distance between any two of them is small and boring, which is exactly what you want a baseline to be.
An execution that skips the risk assessment does not trace a slightly shorter version of that curve. It never enters the region where risk-assessed states live, and - the part that matters - every point after the skip is different too, because the state carried forward says the assessment never happened. One missing step displaces the entire remainder of the trajectory.
Normal approved path (schematic, 2-D projection of the shape space):
received ▶ identity checked ▶ risk assessed ▶ approved ▶ notified
└─ every normal run passes through here
Bypassed path (risk assessment skipped):
received ▶ identity checked ──────────────▶ approved ▶ notified
▲ │ │
│ └──────────┴─▶
never enters this region these points differ too: the
state they carry forward says
"not risk assessed"
That is the whole basis of detection, and it is why the method needs no rule describing the bypass. Nobody told the system that skipping a risk assessment was suspicious. The skip put the trajectory somewhere the corpus of normal runs has never been, and being somewhere nothing has ever been is a measurable, process-independent property. The history is in the position, not in any identifier.
5. Learning what normal looks like
The system is instance-based: it learns what normal looks like by keeping the trajectories of completed normal executions in a reference corpus. There is no training run, no model artefact and no feature engineering step. The corpus is the model.
It answers two different questions, and the difference between them matters:
- How does this compare to one specific known-good run? A pairwise, order-preserving distance between two whole trajectories. This is the Fréchet distance of section 7.
- How unusual is this against everything we have seen? A novelty score in [0, 1] computed against the corpus. Section 6 explains why the way we compute this changed.
In development you seed the corpus by hand with a set of known-good executions. In production you register completed instances automatically, and the important discipline is to register only outcomes you have confirmed were legitimate. A corpus that absorbs unreviewed fraud learns that fraud is normal. This is the one place where the method genuinely needs human judgement, and no amount of geometry substitutes for it.
The learned normal is durable
The corpus is not ephemeral. It survives restarts and deployments, and it can be checkpointed while the system is running, without stopping traffic - which matters more than it sounds, because a "learned normal" you have to rebuild after every deployment is not a learned normal, it is a warm-up period you pay for repeatedly.
Process version transitions
When you deploy a structural change to a process, its executions occupy different positions - necessarily, because position is derived from execution state, and the states have changed. Runs of the new version will therefore read as unfamiliar against a corpus built from the old one. That is correct behaviour, not a bug, but it does mean the transition needs a decision:
- Separate corpora per process version is the recommendation for anything more than a cosmetic change. Route scoring to the corpus that matches the definition the instance is running.
- Carry the corpus over only when the change does not alter the execution states - a renamed label, a repositioned diagram element. Anything that adds or removes a step will widen the cluster and blunt sensitivity until the new normal dominates.
Drift
Some anomaly systems degrade over time because the definition of normal drifts: during a downturn the risk profile of a typical application changes, and what was once unusual becomes routine. An instance-based corpus tracks that naturally as new executions accumulate. Priostack also treats drift as a first-class verdict rather than something to be silently absorbed - a region of the space that has stopped being reinforced decays, and a region that is contested by new evidence can be voided, with re-opening a voided region requiring strictly stronger evidence than closing it did. The point is that "normal has moved" and "this run is abnormal" are different findings and should never be reported as the same one.
6. Scoring: how unusual is this run?
The original version of this article described the score as the distance from a new trajectory's position to the centroid of the whole corpus, normalised by the largest distance the corpus had seen. It then claimed that this gets better indefinitely as the corpus grows.
It does not. We measured it. That estimator degrades past roughly three hundred reference trajectories and then collapses: as the corpus grows, the centroid drifts towards the middle of everything and the maximum distance used for normalisation is set by whatever outlier happens to be furthest out, so every score gets squeezed into a narrow band and the separation between normal and abnormal washes out. A global average is the wrong summary of a space that has structure in it.
The score is relative, and says so
A novelty score is meaningful within one corpus and one engine version and nowhere else. It is not a probability, it is not calibrated across deployments, and a score of 0.9 in your system and 0.9 in ours are not comparable quantities. What transfers is the separation: the gap between where your normal runs sit and where your deviations sit. Set the threshold from your own distribution, not from a number in an article.
Three answers, not two
The more consequential change is that "is this known territory?" now has three possible answers rather than two: inside, outside, and unanswerable. An empty or insufficient corpus returns unanswerable, and the classifier is structurally forbidden from converting that into "abnormal".
This is a small change with a large consequence, and it is where we differ most sharply from the way anomaly detection is usually shipped. Most detectors have exactly two outputs, so absence of evidence has to be squeezed into one of them - which means a cold start either floods the queue with false alarms or silently blesses everything. Neither is honest. A system that can say "I have not seen enough of this to have an opinion" is one you can put in front of a compliance officer, because when it does raise something, it is because it measured something, not because it had no other box to tick. The same principle runs through the layer above: a missing signal produces unknown, never a deviation, and the system stays quiet unless there is a genuine anomaly or a genuine drift.
7. Fréchet distance: why minimum leash is the right metric
The novelty score answers "how different is this from everything we know?" A complementary question is "how different is this from the nearest specific known-good run?" That is what Fréchet distance answers, and unlike a score it points at a comparable case you can actually go and read.
The man-and-dog analogy
The discrete Fréchet distance between two curves is the length of the shortest leash such that, if a man walks along one curve and a dog walks along the other, the leash never pulls taut - that is, neither can double back and the leash always connects them.
For process trajectories, the man walks along a known-good execution and the dog walks along the candidate. The leash is measured between the stored positions of the two executions: its unit is distance in the shape space, and it has nothing to do with how those positions are addressed or stored. If the dog must at some point be somewhere the man never goes, the leash stretches, and that stretch is the divergence.
Path divergence, agentic credit demo (one corpus, one engine version):
Alice ↔ Clara approved / approved ~1 nearly the same route
Alice ↔ Bob approved / manual review ~3 one extra step, same region
Alice ↔ Dave approved / risk bypassed ~48 a different route entirely
Read the ratio, not the decimals. Healthy peers sit within a few units of one
another; the bypassed run sits more than an order of magnitude away. Absolute
values are only comparable within one corpus and one engine version.
Why Fréchet and not other distance metrics?
Several alternatives exist, and each has a distinct failure mode for process trajectory comparison:
- Euclidean distance between centroids - ignores temporal ordering entirely. Two executions that visit the same positions in different orders would score as identical.
- Hausdorff distance - takes the maximum of the minimum distances, but does not care about order either. An execution that visits all the right positions in the wrong order scores the same as one that follows the correct order.
- Dynamic Time Warping - allows non-linear alignment between sequences, which is excellent for time-series signals but too permissive for process trajectories. A process that does step A before step B is structurally different from one that does B before A, even if the alignment can warp them onto each other. BPMN ordering is a design constraint, not a nuisance to be warped away.
- Fréchet distance - preserves temporal order strictly (neither walker can double back) while allowing non-uniform speed along each curve. This is exactly the right trade-off for BPMN processes: two executions that have different numbers of back-and-forth loops through a multi-instance task can still be compared correctly, but a fundamental reordering of tasks cannot be smoothed over.
Finding the precedent, not just the distance
Comparing against one arbitrary known-good run is a weak version of a better question: which past executions most resemble this one? The memory answers that directly and returns them ranked, backed by an index rather than by comparing against everything. That turns the corpus from a scoring device into a case-lookup store, which is usually what the person on the other end of the alert actually wants - not "this scored 0.94" but "this looks like these four cases from last quarter, and three of them were written off."
8. What the numbers look like
The agentic credit tutorial runs four loan applications through a BPMN process against a known anomaly: Dave's application bypasses the risk assessment. Summarised:
── Phase 6 - unusual pattern detection (summarised) ─────────────
reference corpus (normal executions only):
trajectory 1 5 steps (Alice: €25 000, approved)
trajectory 2 6 steps (Bob: €50 000, manual review)
trajectory 3 5 steps (Clara: €12 000, approved)
candidate:
trajectory 4 4 steps (Dave: €500 000, risk step bypassed)
unusual pattern score: at the top of the range (0 = normal, 1 = highly unusual)
path divergence: ~48 in shape-space distance, against ~1-3 between
the healthy peers in this same corpus
⚠ anomaly confirmed - triggering the CMMN fraud investigation case
wall time 13 ms / ~170 µs per engine round-trip (31 steps)
Both figures are corpus-relative and version-specific. The result to take from this run is the separation - more than an order of magnitude between normal variation and a bypassed control - not the individual decimals.
Why the bypassed run scores at the top of the range
Not because it was stored somewhere different. Every execution occupies its own storage, including Alice, Bob and Clara, whom the same run scores as near-identical to one another. The reason is geometric: Dave's trajectory never enters the region that risk-assessed states occupy, and every position after the skip carries "not risk assessed" forward, so his path sits outside the grain of everything the corpus contains - not at its edge, outside it.
The exact decimal is not a constant worth quoting. The same divergent case has measured anywhere between roughly 0.8 and 1.0 depending on engine version, cohort and estimator. The sign and the separation are the result. The fourth decimal is not, and publishing it as a constant only invites contradiction from the next person who runs it.
Why the divergence is around 48 and not around 3
Normal variation moves a trajectory by a unit or two: one extra manual review step, a different branch late in the process. Where two healthy runs differ at all, it is because some guard sent them down slightly different branches - two runs that make every decision identically land on the same positions and measure zero apart, whatever their amounts were.
Skipping a required step moves the trajectory by more than an order of magnitude, because it relocates every position after the skip rather than one. That gap between normal variation and structural deviation is a cliff, not a slope, and it is why the threshold is easy to set and stays set.
9. What running this at scale actually taught us
A four-instance demo proves a mechanism, not a system. This section is what changed once we ran the same pipeline at a hundred thousand and then a million instances, including the parts that did not survive contact.
Throughput
100 000 instances on a single node complete in about 3 minutes 45 seconds - roughly 443 instances per second at about 220 MB resident - and that figure covers the whole pipeline, not just the geometry: BPMN execution, decision evaluation, case handling, geometric scoring and narrated findings. At 10 000 instances the same pipeline takes about 68 seconds across roughly 70 000 engine round-trips, about seven per instance, each measuring 256 to 289 microseconds.
Every number here is one node with the current integration. We have not published distributed measurements, so please do not read these as a scale-out claim.
Detection rates, broken down by what kind of anomaly it is
This is the result we most want people to read, because it is the one that contradicts the simple story. We injected 100 distinct anomalies into a 100 000 instance run, and the outcome splits three ways:
- Amount outliers and bypassed controls: caught, near 100%. These bend the path. Geometry is exactly the right instrument.
- Identity forgery: caught weakly. A forged identity that gets rejected traces almost the same path as a legitimate identity that gets rejected - and about 7% of normal executions are legitimate rejections. The trajectory is genuinely not unusual, so no amount of geometric sensitivity will separate them. This needs a different signal.
- Operational faults: not caught by geometry at all. An integration timeout, a faulty decision table, a case that fails to open - these bend no path whatever. They surface through typed failures reported by the execution layer, which is a completely separate mechanism.
Bounded memory, and what it costs
Full-fidelity retrospective scoring holds to roughly 1.15 million instances per node with the memory sized for it. Past that you need a bounded mode: the reference corpus is held at a fixed resident size while an unbounded stream flows through it, archiving the coldest regions out. That gives continuous learning at constant memory, and it costs recall - about 68% at a 6% false positive rate holding around 16 000 resident reference points while more than a million trajectories stream past, against about 80% for the unbounded variant, which is capacity-capped and therefore not an option at that volume anyway.
That is the honest trade, and it is worth stating plainly because the original version of this article promised the opposite: that the false-positive rate would fall below 0.1% within the first 500 registered instances. There was no measurement behind that sentence. The measured figures are the ones above.
10. Comparison with rule-based and statistical alternatives
| Approach | Handles combinatorial paths? | Improves with more data? | Order-sensitive? | Engineering cost |
|---|---|---|---|---|
| Business rules engine | ✗ Enumerates specific cases | ✗ Rules must be written manually | Depends on rule design | High (ongoing maintenance) |
| Statistical control chart | If projected to 1-D, partially | ✓ Mean and σ updated from data | ✗ Variable-by-variable, no sequence | Medium |
| Dynamic Time Warping | ✓ Over entire sequences | ✓ Corpus grows | Partial (allows reordering) | Low |
| Geometric memory + Fréchet | ✓ Over entire sequences | ✓ With neighbourhood scoring; a global-average score does not | ✓ Strict (no reordering) | Low (zero rules to write) |
Read that last row with section 9 in hand. Geometric memory wins decisively on the dimension it is built for - deviations that change the shape of an execution - and it does not compete at all on deviations that leave the shape intact. A rules engine and a geometric corpus are not really substitutes; the rules engine is how you encode the handful of things you already know are bad, and the corpus is how you find the ones nobody wrote down. Most serious deployments want both.
The classic objection to any distance-based detector is explainability: a score of 0.98 tells you the path deviated, not which step caused it. That was a fair criticism of the first version of this system and it is the main thing that has changed. A verdict now comes with the shape description from section 3 - how far the run travelled, how far it ended up from where it started, how straight it ran, where its sharpest turn was - and with the ranked precedents from section 7. "Unusual, sharpest turn immediately after identity validation, most similar to these four prior cases" is an explanation an investigator can act on. A bare score is not.
11. Beyond processes: memory as a context store
Nothing in sections 2 through 4 is specific to BPMN. The memory stores positions and compares them; anything you can project into the space inherits the same operations - nearest precedents, "is this known territory", "how far has this moved and in which direction". Processes were simply the first thing we projected into it.
We now use it for things that are not processes at all: device fleets, reservations and identities held as shapes rather than as rows in a side database. An entity's identity is composed across several specialised regions rather than crammed into one very wide space, so the memory gains breadth by adding views rather than by widening a vector - the same reason cities grow by adding districts rather than by widening one street.
The direction this points in is a general context store: somewhere a system - or an AI agent - keeps what it has seen and retrieves it by similarity rather than by key. A vector database gives you that for text and pays for it with a separate service, a separate scaling story and a separate consistency model. A store that already holds your execution history, your entities and your fleet state gives you something a bolt-on index cannot: the agent's memory and the system's record of what actually happened live in the same space, so "find me situations like this one" and "find me executions like this one" are the same query.
Conclusion
Geometric memory turns a process execution - a sequence of task completions and gateway evaluations - into a path through a space, where distance between paths is a measurable, process-independent property. The key ideas:
- Position, not identifier. Each step's position is derived from the execution state that produced it, so the same path always lands in the same place and two runs of it measure ~0 apart. The handles that address those positions are storage addresses and mean nothing.
- Paths, not signatures. An execution is the ordered path through its positions, and paths are what get compared. Nothing is flattened into a single fingerprint first.
- Neighbourhood, not average. Novelty is scored against the local neighbourhood in the space. The global-centroid version of this score was measured to collapse past a few hundred references, and was replaced.
- Three answers, not two. Inside, outside, or not enough evidence to say - and "not enough evidence" is never quietly promoted to "abnormal".
- Order-preserving comparison. Fréchet distance tolerates different execution lengths and non-uniform step density while refusing to smooth over a genuine reordering, and returns ranked precedents rather than only a number.
- Only path-bending anomalies. Deviations that leave the shape of an execution intact are invisible to this method and need a different mechanism entirely.
In the agentic credit demo, a single bypassed control separates from normal variation by more than an order of magnitude, which is enough to open a fraud investigation automatically from a corpus of three. At 100 000 instances on one node the same pipeline sustains roughly 443 instances per second and catches close to all path-bending anomalies, while catching none of the operational faults - which are handled somewhere else, on purpose.