Loop Engine

Examples

Expense Approval

Expense approval demonstrates the minimal Loop Engine flow with explicit actor attribution and guard outcomes.

Loop behavior

  • States: SUBMITTED -> UNDER_REVIEW -> APPROVED | REJECTED
  • Actors: system, automation, human
  • Guard: approval_obtained on approve
  • Outcome: expense_approved

Loop definition

1import { LoopBuilder } from "@loop-engine/sdk"
2 
3const approval = LoopBuilder
4 .create("expense.approval", "finance")
5 .description("Expense report approval")
6 .state("SUBMITTED")
7 .state("UNDER_REVIEW")
8 .state("APPROVED", { isTerminal: true })
9 .state("REJECTED", { isTerminal: true })
10 .initialState("SUBMITTED")
11 .transition({ id: "start_review", from: "SUBMITTED", to: "UNDER_REVIEW", actors: ["automation"] })
12 .transition({
13 id: "approve",
14 from: "UNDER_REVIEW",
15 to: "APPROVED",
16 actors: ["human"],
17 guards: [
18 {
19 id: "approval_obtained" as never,
20 description: "Manager approval required",
21 failureMessage: "Approval missing",
22 severity: "hard",
23 evaluatedBy: "runtime"
24 }
25 ]
26 })
27 .transition({ id: "reject", from: "UNDER_REVIEW", to: "REJECTED", actors: ["human"] })
28 .outcome({ id: "expense_approved", description: "Expense approved", valueUnit: "expense_approved" })
29 .build()

Runtime path

1import { aggregateId, transitionId } from "@loop-engine/core"
2import { createLoopSystem } from "@loop-engine/sdk"
3 
4const aggregate = aggregateId("EXP-2026-001")
5const { engine } = await createLoopSystem({ loops: [approval] })
6 
7await engine.start({
8 loopId: "expense.approval",
9 aggregateId: aggregate,
10 orgId: "acme",
11 actor: { type: "system", id: "system:intake" }
12})
13 
14await engine.transition({
15 aggregateId: aggregate,
16 transitionId: transitionId("start_review"),
17 actor: { type: "automation", id: "system:router" }
18})

Guard outcomes

1const passed = await engine.transition({
2 aggregateId: aggregate,
3 transitionId: transitionId("approve"),
4 actor: { type: "human", id: "manager@acme.com" },
5 evidence: { approved: true }
6})
7"cmt">// passed.status === "executed"
8 
9const failed = await engine.transition({
10 aggregateId: aggregate,
11 transitionId: transitionId("approve"),
12 actor: { type: "human", id: "manager@acme.com" },
13 evidence: { approved: false }
14})
15"cmt">// failed.status === "guard_failed"

Full source

  • https://github.com/loopengine/loop-examples/tree/main/expense-approval