WorkflowHub

How it works

The engine, end to end.

What you author, what the engine enforces, what an agent receives, where a person stands in the way, and how the whole thing is put together underneath.

1 · Design time

What you author

A workflow owns two sibling things: the shapes of the items that flow through it, and the map they flow over. Fields, routes and rules hang off the shape; queues and junctions hang off the workflow and are shared by every shape in it.

Workflow
├── WorkItemType            the "shape" of an item
│   ├── Tab                 one per stage, usually
│   ├── Field               15 types + list values
│   ├── Route               this type's path over the queues
│   ├── Rule ─ RuleAction   the enforcer
│   └── Scenario ─ Criterionwhen a rule fires
│
├── Queueworkflow-level:
├── Junction ─ JunctionPath │  SHARED by every
├── ComponentLink           │  type in the workflow
└── PermissionGroup ─ Grant ┘

Queues are keyed to the workflow; routes are keyed to the type. So two item types — say Code Change and Project — can share one set of queues and travel through them along completely different paths.

The invariants worth knowing before you design

  • A lifecycle is one workflow. Splitting a lifecycle across linked items in separate workflows means no single record carries the whole rule set — you can no longer evaluate whether the process was followed.
  • An item never changes type or workflow. Field values are bound to the type's field definitions; relocation would orphan every value. Cross-workflow coordination is association and dependency, never relocation.
  • Queues do not move between workflows. To reuse one elsewhere you create a queue with the same attributes — deliberate, because a queue carries permissions and contracts with it.
  • Start, finish and “which way is forward” are declared, not inferred from the graph. An audit tool checks the declaration against the routes, so a workflow that cannot be completed says so before anyone tries.
  • Fields are runtime data. Adding a field to a live type is a data change with no source file to diff — so it is verified the way code is: re-read it and confirm existing values round-trip unchanged.

What a work item carries

Captured on the item, and when

Half of it is defined by the item's type; the other half is universal and behaves identically in every workflow you will ever author.

CapturedWhenWhat it is for
Field valuesAt creation, then edited at any stage that shows the fieldThe stage's artifact. A rule decides whether it is required, visible, read-only, or human-only at that queue.
The outcome fieldAt the end of a stage, before the item advancesThe one enumerated value the stage exists to produce. It selects the route — the engine moves the item, not the worker.
AttachmentsAny time; also on ingest from an external sourcePDFs, Office files, images, .eml/.msg — 50 MB cap, extension allow-list, stored via the blob store.
NotesAny time, by anyone working the itemFree narrative. Rules can add automatic notes on entry, exit, or field change.
History / eventsAutomatically, on every save, route, finish, lock and field changeThe audit trail. Each entry carries actor and provenance (human / ai / app) stamped server-side from the auth path — never client-supplied.
Typed linksWhen one item realizes, verifies, covers or supersedes anotherTraceability edges across items — including across workflows.
Estimated durationAt intake or any time afterA system attribute, not a form field, so every item is schedulable regardless of workflow. Feeds the project critical path.
Time entriesWhile the item is workedActual effort, against estimate.
DependenciesInside a projectFinish-to-start and friends, with lag, across workflows. The only place cross-item gating exists.
Cost telemetryWhen an agent session endsTokens, model, duration and dollars per stage, so cost-per-outcome is a measured number rather than a hope.

2 · Run time

The life of one work item

The same eight moves whether the worker is a person in a browser or a model on the other end of an MCP connection. See what steps 3 to 7 look like on screen →

It arrives

Created in the UI, imported from CSV, filed by another workflow, or posted to the ingestion port by a source — a folder watcher, a mail webhook, a cloud event, your own application. Ingestion is idempotent on a dedupe key, so re-delivery is safe, and the file rides along as an attachment.

It is claimed

A worker pulls the next item from a queue it is permitted to work. The claim is atomic — a competing pull skips a locked row rather than blocking — and it leases the item so two workers can never hold the same one. Ordering respects project dependencies first, then the earlier project due date, then arrival.

It arrives with its context

One call returns the item, its field values, the stage playbook bound to that queue, the reference documents that stage needs, and the queue contract. Context is scoped to the queue on purpose: the narrower the brief, the cheaper and more accurate the work.

It is worked

Fields are written, notes added, attachments uploaded, time logged. Every save runs the rules for this queue: required becomes required, invisible stays invisible, a human-only field refuses an agent's write before the engine even evaluates it, and each write is stamped with actor and provenance.

The outcome is set

The stage's one enumerated outcome field is filled — Pass, Fail, NeedsHuman, whatever this stage declares. That value is the routing decision. A session that ends without setting it did not complete the stage, and the item goes to the queue the contract nominates for exactly that case.

The gates run

BeforeWorkItemRouted rules evaluate. Missing evidence cancels the transition with a message. Because the rules see the whole item, a gate can require something set five stages earlier — which is what makes “this item followed the process” a checkable claim rather than an assumption.

A person decides, where a person should

At a queue flagged as a human checkpoint, an agent trying to advance the item out is refused with a 403 — route, finish, or batch, it fails closed. The item waits somewhere staffed and visible. Human-only fields record the decision, and the audit trail names who made it.

The engine moves it — and the lesson is kept

The engine performs the transition, not the worker; two mechanisms moving one item is how work lands in the wrong queue. On the way out, anything a future worker would want is contributed to the knowledge base, where it merges into the existing record on that topic instead of becoming a second copy of it.

3 · The interface between a stage and its worker

The queue contract

One call answers the only question a worker actually has: what do I set here, and where does each value send the item? A route that the contract cannot explain is a decision with no basis — so it should not exist.

{
  "outcomeField": "TestResult",
  "deliverables": ["TestPlan", "TestEvidence"],
  "outcomes": [
    {
      "value": "Pass",
      "destination": "Verify",
      "whenToUse": "Everything the TestPlan requires
                     passed; evidence recorded."
    },
    {
      "value": "Fail",
      "destination": "Development",
      "whenToUse": "Any planned case failed; quote the
                     failing cases in TestEvidence."
    }
  ],
  "noOutcome": "Management",
  "engineRoutes": true
}
DeclarationMeaning
outcomeFieldThe one field this stage exists to produce. Enumerated, never free text.
deliverablesThe evidence the stage owes besides the outcome. Advancing without it is visible to supervision.
outcomesFor each value: the destination queue and one sentence saying when to use it. This is the routing table.
noOutcomeWhere the item goes when a session ends with the outcome unset — a staffed queue, not a quarantine flag nobody looks at.
engineRoutesStates that the engine moves the item. The worker sets the outcome and stops.

The deliverables live on the workflow itself, not in an agent's instructions. A field name written into shared code is a workflow-specific literal in a platform — the moment a second workflow arrives, that code is wrong for one of them.

4 · The enforcer

The rules engine

A rule is: a trigger, a set of scenarios (all true or any true), and one or more actions. It is data on the work-item type — so changing what your process guarantees is an authoring change, and it takes effect without a deployment.

11 triggers

Where in the item's life the rule gets its say.

BeforeWorkItemCreated AfterWorkItemCreated BeforeWorkItemSave AfterWorkItemSave OnFieldUpdate OnWorkItemOpen OnButtonClick BeforeWorkItemRouted AfterWorkItemRouted BeforeWorkItemFinished AfterWorkItemFinished

37 actions

What the rule does when its scenarios hold.

  • Shape the form — require, un-require, show, hide, make read-only, set a value, set a list's data source, set a grid's data source.
  • Stop the action — cancel with a message, or ask for confirmation. This is how a gate is built.
  • Move the item — route it to a named queue, or enable/disable an individual routing option.
  • Reach outside — send email, run a query against a configured source, set fields from query results, create a trigger record, launch a URI.
  • Leave a trace — add an automatic note, set priority, control sub-item entry.

Worth stating plainly

An automatic route is put through the same gate rules, destination validation and human checkpoint as a hand-made one. Auto-routing stops a move being forgotten; it never grants permission for a move that would have been refused.

5 · The workers

How an agent connects

Everything that acts on WorkflowHub — a person's browser session, an agent, an ingestion source, an external worker — authenticates as an ordinary principal with ordinary permissions. There is no separate, weaker machine door.

Mode A

Autonomous

An API key is configured and the agent works queues end to end: claim, read context, do the work, write evidence, set the outcome, stop. Unattended, but never past a human gate.

Mode B

Directed

No key: a person drives the model interactively through the MCP tools, step by step. Same API, same permissions, same audit trail — a human is simply in the loop for each move.

Path 2

Deterministic tasks

Work that needs no judgement — move a file, run an on-prem query, read a blob only your network can reach — goes to the leased task fabric instead: register, lease, heartbeat, complete or fail, with retries and a dead-letter.

An API key is a principal, not a bypass

  • Stored hash-only. The raw key is shown once and never persisted.
  • It carries an acts-as identity and resolves permissions exactly like the user it acts as — so the audit trail names an actor, not an anonymous integration.
  • Acting as someone else is administrator-only. Self-service key creation forces the caller's own identity and cannot be talked out of it.
  • The acts-as identity is fixed at creation. There is no update path — re-pointing a live secret at a different account would silently change who it is, so you revoke and re-mint instead.
  • Being an agent is a claim on the request, decided at authentication. That is what the human-gate checks read; nothing the agent sends can change it.

What the agent is told, and what it is prevented from doing

  • Told: the item, its history, the stage playbook, the bound reference documents, the queue contract, and the operating directives that travel with every delivery.
  • Told: what the organisation already knows — a knowledge search before the work, not after the mistake.
  • Prevented: from advancing an item out of a human checkpoint queue.
  • Prevented: from writing a human-only field.
  • Prevented: from overriding workflow direction or field requirements, even holding the permission.
  • Prevented: from touching a repository its item's application is not bound to. No binding, no work — the refusal is the feature.

6 · Memory

Documents, retrieval, and knowledge that is curated rather than accumulated

Retrieval quality is not a storage detail — it decides whether a worker trusts the archive or rebuilds the answer from scratch at full price. So the corpus is treated as a maintained artifact with its own workflow.

Ingest file · mail · event Clean & validate a workflow, with gates Chunk & embed pgvector Retrieve search + browse index Work happens search before · contribute after contributions merge into the existing record · contradictions are reported, then withdrawn from retrieval

Ingestion, curation and retrieval are all just workflows and API calls — there is no separate, unaudited pipeline.

Provenance on every record

Verified, asserted, or unknown — and “nobody knows who wrote this” is stated honestly rather than rounded up to trusted. Trust starts from that state and moves with evidence.

Corrections are visible

A record found wrong is contradicted through a channel that raises a case, not quietly rewritten. A contradicted record stops being served.

A token budget per queue

What a stage delivers to its worker is the context window that matters. Binding decides what each queue receives, and an over-budget manifest is a defect — not untidiness.

7 · Underneath

Architecture

C# on .NET 10, PostgreSQL 16 with pgvector, a React + TypeScript single-page app, and an MCP server that is a façade over the same API every other client uses.

CLIENTS React SPA AI agents MCP server Ingestion sources folder · mail · webhook Your integrations External workers leased tasks WORKFLOWHUB API — THE ONLY PATH TO DATA auth middleware permission service rules engine router / junctions human-gate checks work items · queues · workflows · projects · knowledge · reports · keys · orgs — one authorisation model for all of it Domain models · enums · validation · contracts Data repositories · migrations · provider-selected backend PostgreSQL 16  ·  pgvector  ·  least-privilege application role that cannot change schema dual keys on every entity: a fast internal id and a stable public uuid, so a workflow can move between environments

Deployed on Cloud Run against Cloud SQL, or self-hosted with Docker Compose. The same image and the same schema either way.

Why one gateway

Because the alternative is two places that decide who may do what. Every authorisation check, every rule evaluation, every audit stamp lives on one path. The SPA holds no database credential; neither does an agent. MCP is a façade over the API, not a second door into the data.

Why dual keys

Every entity carries a fast internal identifier and a stable public UUID, and every foreign key is doubled. That is what lets a workflow definition be exported from one environment and imported into another with its identities intact — the basis of test environments, tenant portability, and shareable workflow bundles.

8 · Security

Enforced at every layer, independently

Identity is external; the application owns authorisation. No layer assumes the one above it did its job.

LayerWhat it enforces
Identity OIDC single sign-on — Microsoft Entra ID, Google, Okta, Auth0. MFA and conditional access stay the identity provider's job. The caller is anchored to the provider's immutable subject, not an email address that changes, and a first-time user is provisioned from the token.
Principals API keys are first-class principals stored hash-only, each carrying a fixed acts-as identity. A break-glass administrator key exists deliberately, so a misconfigured SSO cannot lock you out of your own system — without reintroducing a local password store.
Authorisation 25 permission types, granted to groups over components (a queue, or a whole workflow), scoped to the workflow. Membership is explicit or resolved from an SSO group claim. Because groups belong to the workflow, they travel with its definition.
Agency Being an agent is a claim established at authentication. Human checkpoints, human-only fields and direction overrides are checked against it server-side, so no client — and no prompt — can route around them.
Tenancy The organisation is the tenant and the trust boundary: it owns members, workflows, groups and secrets. Cross-organisation sharing is an explicit, mutual, non-transitive trust link per shared resource — A trusting B and B trusting C never implies A trusts C.
Data The database runs under a least-privilege login that cannot alter schema. Secrets are envelope-encrypted and never committed to a file that can be pushed. Attachments are validated by size and extension before anything is stored.
Audit Every state change writes an event with actor, provenance and timestamp, stamped server-side from the authentication path. The audit trail is not a feature bolted on for compliance — it is the same data the reports and the supervision workflow read.

9 · Scale

Growing from one workflow to an estate

The org is the unit

Isolation, billing and placement all follow the organisation. Every user has at least a personal org, so there is no separate solo-user path to maintain.

Environments are cells

An organisation lives wholly inside one environment. A control plane provisions environments, moves organisations between them, and transports workflow definitions.

The server tier stays light

Agents and external workers execute; the platform brokers. That keeps the scaling problem a matter of partitioning organisations rather than sizing a compute farm.

Supervision is itself a workflow

Stuck items, looping items and contradicted knowledge raise cases into a supervision pipeline — so operational drift becomes tracked work instead of something a person happens to notice.

Author the process once. Let the fleet work it.

The engine is what makes autonomous work trustworthy: evidence before advance, separate eyes on review, and a person at every decision that has consequences.