Lab Journal
The processflow rewrite lands — graph engine, RBAC, and tenant scoping in one long Monday

The processflow rewrite lands — graph engine, RBAC, and tenant scoping in one long Monday

lab-journalhackerlabs.devprocessflow

So much for a quiet bank holiday. What started as a brainstorm about multi-tenancy turned into the single biggest day of work processflow has seen — roughly a hundred commits spanning a schema rewrite, a new graph engine, a full canvas UI, RBAC, and tenant scoping. I'm a bit dazed writing this up.

hackerlabs.dev

Tiny footprint here: just published Sunday's lab journal and dropped in the recap image for the 24th. Standard markdown-into-content/posts/ flow, build picks it up. The interesting stuff today all lived in processflow.

processflow — schema rewrite to a graph model

The day really began by tearing out the old hierarchical stage model and replacing it with a proper directed graph: TemplateNode/TemplateEdge for definitions and InstanceNode/InstanceEdge for running processes. This is the foundation everything else built on top of. Once the schema flipped, the legacy advance.ts and hierarchical builder/process-view components came out — there was no point keeping them limping along beside the new model.

I also got vitest wired up against an isolated Postgres test DB before writing any of the engine code, which paid off enormously over the next few hours. Having a real DB in tests (not a mock) means the graph engine tests actually exercise Prisma queries the way production will.

processflow — validation rules

Before building the engine itself I wrote the structural validators: start/end/edge rules, fork branch label rules, parallel split/join structural rules, and indirect call-template cycle detection via validateTemplateDeep. That last one matters — a single template can't directly call itself, but A→B→A is the kind of thing that quietly breaks production. Better to catch it at save time than at runtime.

processflow — the graph engine

This was the meatiest piece. Built up in deliberate layers:

  1. Module skeleton + shared template fixtures
  2. fireEdge primitive and Task activation (with email assignment)
  3. completeTask plus a completeNode dispatcher for non-Fork/End types
  4. completeFork with branch selection and SKIPPED propagation down dead paths
  5. Headless types that auto-activate (START, PARALLEL_SPLIT, JOIN)
  6. END handling — both root completion and sub-process return
  7. CALL_TEMPLATE spawning a sub-instance and emailing the called template's owner
  8. Cycle re-activation (Task/Fork reset on re-entry, Join clears stale fires)
  9. cancelInstance with ACTIVE-node SKIPPED transitions
  10. Node notes/dueDate edits and add/remove links

The trickiest bug was around JOIN re-entry on cycles. Originally I had two branches handling re-entry differently and they fought each other. The fix was consolidating JOIN re-entry into the early-return branch and resetting the JOIN node to PENDING when it's re-entered — otherwise stale "fired" state from the previous loop iteration leaked through and the join would complete immediately on the second pass.

processflow — API surface

Flattened the routes to match the new model: template node + edge routes, GET /api/templates/[id]/validate, and the process lifecycle (create/list/get/cancel plus node complete/update/links). Removed the old nested stages routes entirely. createInstanceFromTemplate now validates the template before spawning a root instance, so you can't start a process from a broken template.

Also seeded two sample graph templates — Legal Review and Vendor Onboarding — which doubled as smoke tests for the whole pipeline.

processflow — canvas UI on xyflow

The frontend rewrite mirrors the backend: a real canvas builder instead of the old hierarchical form. Built on @xyflow/react. Rough order:

  • Canvas node types (7 of them) with status colors
  • Draggable block palette
  • Custom edges with labels and fired-state coloring
  • Config side panel that works in both builder and instance modes
  • TemplateCanvas itself — editable canvas with palette, panel, and persistence
  • Routes for /templates, /templates/[id], /processes, /processes/new, /processes/[id], and /dashboard with template/active/completed counts
  • A read-only running-process view with active list and cancel

Plus the smaller UX wins: top-to-bottom flow direction, parallel split showing multiple output dots with a configurable count, Join showing multiple input dots, persisted edge handles, completion timestamps on Task nodes, completion + picked branch shown on Fork and Call Template nodes, tabs + search on /processes, archived tab on /templates.

A couple of fiddly fixes worth noting:

  • Dropped nodes were landing top-left at the cursor instead of centered on it. xyflow's screenToFlowPosition returns where the node's top-left should go, so I shifted by roughly half a typical node (75, 20) to get the cursor near the visual center.
  • The Split/Merge icons in the palette needed to be rotated 90° to match the in-canvas orientation now that flow is top-to-bottom. Small thing, but visually jarring without it.
  • Had to resolve the selected node/edge from live state (not the stale snapshot) so panel inputs were actually editable.
  • xyflow's Controls were getting clipped — fixed by shrinking the canvas height a touch.

processflow — RBAC (Phase A)

Once the core was working I shifted into the tenant/RBAC plan I'd handed off to myself the night before. Phase A is roles and