All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-runners (push) Has been skipped
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m51s
CI/CD / test-sqlite (push) Successful in 2m51s
CI/CD / build-and-deploy (push) Successful in 39s
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
194 lines
6.3 KiB
Markdown
194 lines
6.3 KiB
Markdown
# Workflows
|
|
|
|
Workflows are multi-stage processes with team assignment, validation gates,
|
|
SLA enforcement, and optional Starlark automation. They are managed in
|
|
**Team Admin > Workflows**.
|
|
|
|
## Core concepts
|
|
|
|
| Concept | Description |
|
|
|---------|-------------|
|
|
| **Definition** | A named template: stages, entry mode, staleness timeout. Created per-team or adopted from global definitions. |
|
|
| **Stage** | One step in the workflow. Has a mode, audience, optional team assignment, and optional SLA. |
|
|
| **Instance** | A running copy of a definition. Pins a published version snapshot and tracks accumulated stage data. |
|
|
| **Assignment** | A queue entry linking an instance stage to a team member. Claim → work → complete. |
|
|
| **Signoff** | An approval or rejection recorded against an instance stage (multi-party validation). |
|
|
|
|
## Entry modes
|
|
|
|
| Mode | Description |
|
|
|------|-------------|
|
|
| `team_only` | Only authenticated team members can start instances. |
|
|
| `public_link` | Anyone with the public URL can start an instance. The first stage must have `audience: public`. An `entry_token` is issued for the anonymous submitter to resume later. |
|
|
|
|
Public entry URL format:
|
|
```
|
|
{origin}/api/v1/public/workflows/{workflow_id}/start
|
|
```
|
|
|
|
## Stage modes
|
|
|
|
Each stage has a **mode** that determines how it progresses:
|
|
|
|
| Mode | Description |
|
|
|------|-------------|
|
|
| `form` | User submits structured data. Stage data is accumulated into the instance. |
|
|
| `review` | Multi-party sign-off gate. Requires configured approvals before advancing. |
|
|
| `delegated` | Assigned to a team member queue. The assignee claims, works, and completes. |
|
|
| `automated` | Starlark hook executes without user interaction. Can chain up to 10 consecutive automated stages. |
|
|
|
|
## Stage types
|
|
|
|
| Type | Description |
|
|
|------|-------------|
|
|
| `simple` | Linear — always advances to the next ordinal. |
|
|
| `dynamic` | Conditional — evaluates branch rules against stage data to pick the next stage. |
|
|
| `automated` | Combined with mode `automated` for fully scripted stages. |
|
|
|
|
## Audiences
|
|
|
|
| Audience | Description |
|
|
|----------|-------------|
|
|
| `team` | Only authenticated team members can interact. |
|
|
| `public` | Anonymous users can interact (used with `public_link` entry). |
|
|
| `system` | System-generated stages, no direct user interaction. |
|
|
|
|
## Team assignment
|
|
|
|
When a stage has `assignment_team_id` set, the engine creates an
|
|
**assignment** record:
|
|
|
|
1. Assignment enters the queue with status `unassigned`.
|
|
2. A team member **claims** the assignment (status → `claimed`).
|
|
3. The assignee works the stage and **completes** it (status → `completed`).
|
|
4. The engine auto-advances to the next stage.
|
|
|
|
A **required role** can restrict who may claim:
|
|
- Set `stage_config.required_role` to a team role name (e.g. `"reviewer"`).
|
|
- Only members with that role can claim the assignment.
|
|
|
|
Team roles are configured in **Team Admin > Settings > Roles**.
|
|
|
|
## Signoff gates (multi-party validation)
|
|
|
|
Review-mode stages can require multiple approvals before advancing.
|
|
Configure via `stage_config.validation`:
|
|
|
|
```json
|
|
{
|
|
"validation": {
|
|
"required_approvals": 2,
|
|
"required_role": "approver",
|
|
"reject_action": "cancel"
|
|
}
|
|
}
|
|
```
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `required_approvals` | Minimum approve decisions needed to advance. |
|
|
| `required_role` | Only members with this team role can sign off. Empty = any member. |
|
|
| `reject_action` | What happens on rejection: `"cancel"` (default) cancels the instance, or a stage name to reroute. |
|
|
|
|
Each signoff records: user, decision (`approve` or `reject`), optional comment, timestamp.
|
|
|
|
## SLA enforcement
|
|
|
|
Two timeout mechanisms run in a background scanner (every 5 minutes):
|
|
|
|
### Per-stage SLA
|
|
|
|
Set `sla_seconds` on a stage. When an instance has been in that stage
|
|
longer than the threshold:
|
|
|
|
- `sla_breached` flag is set in instance metadata.
|
|
- A `workflow.sla_breach` WebSocket event is emitted.
|
|
- The instance is **not** auto-cancelled — breaches are informational.
|
|
|
|
### Per-workflow staleness
|
|
|
|
Set `staleness_timeout_hours` on the workflow definition. When an instance
|
|
has not been updated for longer than the threshold:
|
|
|
|
- Instance status is set to `stale`.
|
|
- All open assignments are cancelled.
|
|
- A `workflow.stale` WebSocket event is emitted.
|
|
|
|
## Branch rules
|
|
|
|
Dynamic stages evaluate conditions against accumulated `stage_data`
|
|
to determine the next stage. Rules are a JSON array on the stage:
|
|
|
|
```json
|
|
[
|
|
{ "field": "priority", "op": "eq", "value": "high", "target_stage": "escalation" },
|
|
{ "field": "amount", "op": "gt", "value": 10000, "target_stage": "manager-review" }
|
|
]
|
|
```
|
|
|
|
First matching rule wins. If no rules match, the next ordinal stage is used.
|
|
|
|
### Operators
|
|
|
|
| Op | Description |
|
|
|----|-------------|
|
|
| `eq` | Equal (string-normalized) |
|
|
| `neq` | Not equal |
|
|
| `gt`, `lt`, `gte`, `lte` | Numeric comparisons |
|
|
| `exists` | Field is present in stage data |
|
|
| `not_exists` | Field is absent |
|
|
| `in` | Value is in a list |
|
|
| `contains` | String contains substring |
|
|
|
|
`target_stage` can be a stage name (case-insensitive) or a numeric ordinal.
|
|
|
|
## Publishing
|
|
|
|
Workflows have a draft/publish lifecycle:
|
|
|
|
1. Edit stages and configuration in the workflow editor (draft state).
|
|
2. **Publish** creates a versioned snapshot of all stages.
|
|
3. New instances pin the latest published version.
|
|
4. Editing stages after publishing does not affect running instances.
|
|
|
|
Version numbers auto-increment. The snapshot preserves the complete
|
|
stage definition array at publish time.
|
|
|
|
## Starlark hooks
|
|
|
|
Automated stages execute a Starlark script via the `starlark_hook` field:
|
|
|
|
```
|
|
package_id:entry_point
|
|
```
|
|
|
|
For example: `my-automation:on_review` calls the `on_review` function
|
|
in the `my-automation` package. If no entry point is specified,
|
|
`on_run` is used.
|
|
|
|
The hook receives a context dict:
|
|
|
|
```python
|
|
{
|
|
"instance_id": "...",
|
|
"current_stage": "...",
|
|
"workflow_id": "...",
|
|
"started_by": "...",
|
|
"stage_data": { ... }
|
|
}
|
|
```
|
|
|
|
The hook returns a dict controlling what happens next:
|
|
|
|
| Key | Effect |
|
|
|-----|--------|
|
|
| `advance: True` | Auto-advance to the next stage |
|
|
| `data: { ... }` | Merge into stage data for the next stage |
|
|
| `error: "msg"` | Set instance status to `error` and halt |
|
|
|
|
Up to 10 consecutive automated stages can chain before the engine
|
|
stops with an error (cycle guard).
|
|
|
|
See the [Starlark Reference](STARLARK-REFERENCE) for available
|
|
sandbox modules.
|