Pass Transitions – The Complete Guide to Token Routing in AgetnticOS
The Pass transition is the foundational routing primitive in AgetnticOS Petri nets. It examines token data and decides where each token should go – without transforming it, without calling external systems, without side effects. Think of it as a railway switch operator: tokens arrive, get inspected, and are sent down the correct track.
Pass transition routing tokens based on status field – the token data is unchanged, only its destination varies.
Anatomy of a Pass Transition Inscription
Every Pass transition inscription follows this structure:
{
"id": "t-my-router",
"kind": "task",
"mode": "SINGLE",
"presets": {
"input": {
"placeId": "input-queue",
"host": "myModel@localhost:8080",
"arcql": "FROM $ LIMIT 1",
"take": "FIRST",
"consume": true
}
},
"postsets": {
"outputA": { "placeId": "place-a", "host": "myModel@localhost:8080" },
"outputB": { "placeId": "place-b", "host": "myModel@localhost:8080" }
},
"action": { "type": "pass" },
"emit": [
{ "to": "outputA", "from": "@input.data", "when": "fieldName == 'value'" },
{ "to": "outputB", "from": "@input.data", "when": "fieldName != 'value'" }
]
}
Key components:
kind: "task"– Pass transitions use the “task” kindpresets– where to read tokens from (with ArcQL query and consumption rules)postsets– symbolic names for output destinationsaction.type: "pass"– marks this as a pure routing transition (no transformation)emit– array of routing rules with conditions
Pattern 1: Multi-Destination Routing
The most common use case: route tokens to different places based on field values with compound conditions.
Multi-destination routing with compound conditions – one token can emit to multiple places when conditions match.
Complete Order Routing Inscription
{
"id": "t-route-orders",
"kind": "task",
"mode": "SINGLE",
"presets": {
"input": {
"placeId": "p-new-orders",
"host": "pass-demo@localhost:8080",
"arcql": "FROM $ LIMIT 1",
"take": "FIRST",
"consume": true
}
},
"postsets": {
"high": { "placeId": "p-high-priority", "host": "pass-demo@localhost:8080" },
"standard": { "placeId": "p-standard", "host": "pass-demo@localhost:8080" },
"audit": { "placeId": "p-audit-log", "host": "pass-demo@localhost:8080" }
},
"action": { "type": "pass" },
"emit": [
{ "to": "high", "from": "@input.data", "when": "priority == 'high' AND amount > 1000" },
{ "to": "standard", "from": "@input.data", "when": "priority != 'high'" },
{ "to": "audit", "from": "@input.data" }
]
}
Token routing examples:
// Token 1: High priority + high amount → HIGH + AUDIT
{ "orderId": "ORD-001", "priority": "high", "amount": 5000 }
// Token 2: Medium priority → STANDARD + AUDIT
{ "orderId": "ORD-002", "priority": "medium", "amount": 500 }
// Token 3: High priority but low amount → STANDARD + AUDIT
{ "orderId": "ORD-003", "priority": "high", "amount": 200 }
Pattern 2: No-Match Behavior
What happens when a token doesn’t match any emit condition? The token is consumed but not emitted anywhere. This is useful for filtering out unwanted tokens.
// Inscription with specific conditions
"emit": [
{ "to": "known", "from": "@input.data", "when": "status == 'active' OR status == 'pending'" }
]
// Token with status='unknown' → CONSUMED but NOT EMITTED
{ "id": "NOMATCH-1", "status": "unknown" }
// Result: Token removed from input, nothing in output
Warning: If you want unknown values to go somewhere, add an explicit catch-all rule:
"emit": [
{ "to": "known", "from": "@input.data", "when": "status == 'active'" },
{ "to": "error", "from": "@input.data", "when": "status != 'active'" }
]
Pattern 3: Multi-Emit (Same Token, Multiple Destinations)
Unlike traditional if/else, Pass transitions can emit the same token to multiple places when multiple conditions match:
"emit": [
{ "to": "orders", "from": "@input.data", "when": "type == 'order'" },
{ "to": "priority", "from": "@input.data", "when": "priority == 'high'" },
{ "to": "all", "from": "@input.data" } // Always emit
]
// Token matching multiple conditions:
{ "type": "order", "priority": "high", "id": "ORD-001" }
// Result: Token emitted to ALL THREE places!
// - orders: receives token (type=='order' matched)
// - priority: receives token (priority=='high' matched)
// - all: receives token (always emits)
Understanding the Emit Semantic
The from Expression
For Pass transitions, from specifies what data to emit:
@input.data– the token’s business payload (what you stored)@input._meta– token metadata (id, name, parentId)@input– the entire token structure (data + _meta)
The when Condition
The when condition is evaluated against what from resolves to. For @input.data, field names are accessed directly:
// Token data: { "status": "active", "amount": 100 }
// Emit rule: { "from": "@input.data", "when": "status == 'active'" }
// "status" refers to the field in the resolved data
// NOT @input.data.status - just "status"
Complete Condition Syntax Reference
ArcQL Preset Filtering
The arcql field in presets allows pre-filtering tokens before the transition even fires:
"presets": {
"input": {
"placeId": "input-place",
"host": "model@localhost:8080",
"arcql": "FROM $ WHERE $.status==\"active\" LIMIT 1",
"take": "FIRST",
"consume": true
}
}
ArcQL Syntax Rules:
- Paths MUST start with
$(e.g.,$.status,$.amount) - Use DOUBLE equals
==not single= - String values use DOUBLE QUOTES:
$.status=="active"
Summary
Key takeaways for Pass transitions:
- Pass transitions route without transforming – pure decision logic
- Each emit rule is independent – tokens can go to multiple places
- No-match means consumed but not emitted – useful for filtering
- Omit “when” for always-emit behavior – catch-alls and pass-throughs
- Use
kind: "task"for Pass transitions - emit.from is
@input.data– the original token data
In the next section, we’ll explore Map transitions for data transformation.