@pxp-nd/flow 1.1.29 → 1.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BPM_Propossal.md +566 -0
- package/README.md +437 -1
- package/dist/controllers/Action.d.ts +1 -1
- package/dist/controllers/Action.js +8 -2
- package/dist/controllers/Flow.d.ts +1 -0
- package/dist/controllers/Flow.js +39 -2
- package/dist/controllers/FlowInstance.d.ts +1 -0
- package/dist/controllers/FlowInstance.js +10 -14
- package/dist/controllers/Node.d.ts +1 -0
- package/dist/controllers/Node.js +9 -0
- package/dist/controllers/NodeInstance.d.ts +1 -0
- package/dist/controllers/NodeInstance.js +11 -15
- package/dist/helpers/FlowNdApiDocs.d.ts +19 -0
- package/dist/helpers/FlowNdApiDocs.js +299 -0
- package/dist/helpers/dbLock.d.ts +4 -0
- package/dist/helpers/dbLock.js +45 -0
- package/package.json +2 -2
package/BPM_Propossal.md
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# BPM + Automation Platform — Design Proposal
|
|
2
|
+
|
|
3
|
+
**Status:** Draft
|
|
4
|
+
**Base:** Current Tempo Flows (`twf_*`, `EventHq`, SQS worker) — see [FLOWS.md](./FLOWS.md)
|
|
5
|
+
**Stack lean (v1):** NestJS + React + PostgreSQL + SQS/BullMQ
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Summary — what we want to build
|
|
10
|
+
|
|
11
|
+
We want a **new process platform** that keeps everything that already works in Tempo Flows (visual automation, action catalog, event + queue, multi-tenant templates) and extends it into a real **BPM** product.
|
|
12
|
+
|
|
13
|
+
**In short:**
|
|
14
|
+
|
|
15
|
+
| Today (Flows) | Tomorrow (BPM + Automation) |
|
|
16
|
+
|---------------|-----------------------------|
|
|
17
|
+
| Event → delay → email/push/controller | Same automations, cleaner engine |
|
|
18
|
+
| Mostly linear graphs | Gateways (branch / parallel) with clear UI |
|
|
19
|
+
| Little human workflow | Forms, approvals, assignees, task inbox |
|
|
20
|
+
| No first-class documents | Require / upload / generate documents |
|
|
21
|
+
| Internal controllers mainly | HTTP / webhooks / external connectors |
|
|
22
|
+
| Soft versioning | Immutable process versions |
|
|
23
|
+
| Magic vendor `55` | Explicit **SYSTEM / GROUP / TENANT** catalog scopes |
|
|
24
|
+
|
|
25
|
+
**Product shape (same engine, two modes):**
|
|
26
|
+
|
|
27
|
+
1. **Automation** — fire-and-forget graphs (billing alerts, COS push, funnels).
|
|
28
|
+
2. **BPM** — long-running cases (form → approve → documents → notify → close).
|
|
29
|
+
|
|
30
|
+
**Builder UX (keep):** left = action palette · center = canvas · right = node form (`schema_json`).
|
|
31
|
+
|
|
32
|
+
**Migration:** strangler pattern — keep `twf_*` running; migrate process-by-process (e.g. Safe Families first); do not big-bang rewrite.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. ER diagram (color legend)
|
|
37
|
+
|
|
38
|
+
| Color | Meaning |
|
|
39
|
+
|-------|---------|
|
|
40
|
+
| **Amber** | Exists today as `twf_*` → **renamed / evolved** |
|
|
41
|
+
| **Green** | **Completely new** (no direct `twf_*` equivalent) |
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart TB
|
|
45
|
+
classDef renamed fill:#FFF3CD,stroke:#B8860B,color:#000000,stroke-width:2px
|
|
46
|
+
classDef newtbl fill:#D1E7DD,stroke:#0F5132,color:#000000,stroke-width:2px
|
|
47
|
+
classDef legend fill:#F8F9FA,stroke:#6C757D,color:#000000
|
|
48
|
+
|
|
49
|
+
subgraph LEGEND["Legend"]
|
|
50
|
+
L1["AMBER = existing twf_* → renamed/evolved"]
|
|
51
|
+
L2["GREEN = completely new"]
|
|
52
|
+
L1:::legend
|
|
53
|
+
L2:::legend
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
subgraph TENANCY["Tenancy"]
|
|
57
|
+
TG["bpm_tenant_group"]:::newtbl
|
|
58
|
+
TGM["bpm_tenant_group_member"]:::newtbl
|
|
59
|
+
AG["bpm_action_group"]:::newtbl
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
subgraph DESIGN["Design-time"]
|
|
63
|
+
AT["bpm_action_type<br/>← twf_action_type"]:::renamed
|
|
64
|
+
A["bpm_action<br/>← twf_action"]:::renamed
|
|
65
|
+
PD["bpm_process_definition<br/>← twf_flow"]:::renamed
|
|
66
|
+
PV["bpm_process_version<br/>← split from twf_flow"]:::renamed
|
|
67
|
+
N["bpm_node<br/>← twf_node"]:::renamed
|
|
68
|
+
E["bpm_edge<br/>← twf_node_connection"]:::renamed
|
|
69
|
+
FD["bpm_form_definition"]:::newtbl
|
|
70
|
+
FV["bpm_form_version"]:::newtbl
|
|
71
|
+
DT["bpm_document_type"]:::newtbl
|
|
72
|
+
AP["bpm_approval_policy"]:::newtbl
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
subgraph RUNTIME["Runtime"]
|
|
76
|
+
PI["bpm_process_instance<br/>← twf_flow_instance"]:::renamed
|
|
77
|
+
AI["bpm_activity_instance<br/>← twf_node_instance"]:::renamed
|
|
78
|
+
TK["bpm_token"]:::newtbl
|
|
79
|
+
T["bpm_task"]:::newtbl
|
|
80
|
+
TA["bpm_task_action"]:::newtbl
|
|
81
|
+
DOC["bpm_document"]:::newtbl
|
|
82
|
+
MSG["bpm_message<br/>← twf_event"]:::renamed
|
|
83
|
+
JOB["bpm_job"]:::newtbl
|
|
84
|
+
AUD["bpm_audit_log<br/>← twf_flow_log"]:::renamed
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
TG --> TGM
|
|
88
|
+
A --> AG
|
|
89
|
+
TG --> AG
|
|
90
|
+
AT --> A
|
|
91
|
+
A --> N
|
|
92
|
+
PD --> PV
|
|
93
|
+
PV --> N
|
|
94
|
+
PV --> E
|
|
95
|
+
FD --> FV
|
|
96
|
+
FV -.-> N
|
|
97
|
+
AP -.-> N
|
|
98
|
+
DT -.-> N
|
|
99
|
+
PV --> PI
|
|
100
|
+
PI --> AI
|
|
101
|
+
PI --> TK
|
|
102
|
+
N --> AI
|
|
103
|
+
AI --> T
|
|
104
|
+
T --> TA
|
|
105
|
+
PI --> DOC
|
|
106
|
+
DT --> DOC
|
|
107
|
+
MSG --> PI
|
|
108
|
+
PI --> JOB
|
|
109
|
+
PI --> AUD
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
erDiagram
|
|
114
|
+
bpm_tenant_group ||--o{ bpm_tenant_group_member : has
|
|
115
|
+
bpm_tenant_group ||--o{ bpm_action_group : scopes
|
|
116
|
+
bpm_action_type ||--o{ bpm_action : categorizes
|
|
117
|
+
bpm_action ||--o{ bpm_action_group : visible_in
|
|
118
|
+
bpm_action ||--o{ bpm_node : used_by
|
|
119
|
+
bpm_process_definition ||--o{ bpm_process_version : has
|
|
120
|
+
bpm_process_version ||--o{ bpm_node : contains
|
|
121
|
+
bpm_process_version ||--o{ bpm_edge : contains
|
|
122
|
+
bpm_form_definition ||--o{ bpm_form_version : has
|
|
123
|
+
bpm_form_version ||--o{ bpm_node : bound_to
|
|
124
|
+
bpm_approval_policy ||--o{ bpm_node : configures
|
|
125
|
+
bpm_document_type ||--o{ bpm_node : required_by
|
|
126
|
+
bpm_process_version ||--o{ bpm_process_instance : instantiates
|
|
127
|
+
bpm_process_instance ||--o{ bpm_activity_instance : has
|
|
128
|
+
bpm_process_instance ||--o{ bpm_token : active_at
|
|
129
|
+
bpm_node ||--o{ bpm_activity_instance : executes_as
|
|
130
|
+
bpm_activity_instance ||--o{ bpm_task : creates
|
|
131
|
+
bpm_task ||--o{ bpm_task_action : records
|
|
132
|
+
bpm_process_instance ||--o{ bpm_document : owns
|
|
133
|
+
bpm_document_type ||--o{ bpm_document : typed_as
|
|
134
|
+
bpm_message ||--o{ bpm_process_instance : starts_or_signals
|
|
135
|
+
bpm_process_instance ||--o{ bpm_job : schedules
|
|
136
|
+
bpm_process_instance ||--o{ bpm_audit_log : audited_by
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Color checklist**
|
|
140
|
+
|
|
141
|
+
| Amber — renamed / evolved | Green — new |
|
|
142
|
+
|---------------------------|-------------|
|
|
143
|
+
| `bpm_action_type` ← `twf_action_type` | `bpm_tenant_group` |
|
|
144
|
+
| `bpm_action` ← `twf_action` | `bpm_tenant_group_member` |
|
|
145
|
+
| `bpm_process_definition` ← `twf_flow` | `bpm_action_group` |
|
|
146
|
+
| `bpm_process_version` ← split from `twf_flow` | `bpm_form_definition` / `bpm_form_version` |
|
|
147
|
+
| `bpm_node` ← `twf_node` | `bpm_document_type` |
|
|
148
|
+
| `bpm_edge` ← `twf_node_connection` | `bpm_approval_policy` |
|
|
149
|
+
| `bpm_process_instance` ← `twf_flow_instance` | `bpm_token` |
|
|
150
|
+
| `bpm_activity_instance` ← `twf_node_instance` | `bpm_task` / `bpm_task_action` |
|
|
151
|
+
| `bpm_message` ← `twf_event` | `bpm_document` |
|
|
152
|
+
| `bpm_audit_log` ← `twf_flow_log` | `bpm_job` |
|
|
153
|
+
|
|
154
|
+
> Tenant identity maps to today’s `vendor_id`. Optional bridge tables `twf_origin_name` / `twf_field_map` can remain for legacy view-based automations; BPM case data lives in `bpm_process_instance.variables`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 3. Data dictionary
|
|
159
|
+
|
|
160
|
+
Conventions used below:
|
|
161
|
+
|
|
162
|
+
- `tenant_id` = vendor id (unless noted).
|
|
163
|
+
- JSON examples are illustrative.
|
|
164
|
+
- `visibility_scope`: `SYSTEM` | `GROUP` | `TENANT`.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### 3.1 Tenancy
|
|
169
|
+
|
|
170
|
+
#### `bpm_tenant_group` *(new)*
|
|
171
|
+
|
|
172
|
+
Groups of tenants that share a catalog slice (e.g. business type).
|
|
173
|
+
|
|
174
|
+
| Column | Type | Description | Example |
|
|
175
|
+
|--------|------|-------------|---------|
|
|
176
|
+
| `id` | PK | Group id | `1` |
|
|
177
|
+
| `code` | varchar | Stable code | `SF` |
|
|
178
|
+
| `name` | varchar | Display name | `Safe Families` |
|
|
179
|
+
| `is_active` | bool | Soft enable | `true` |
|
|
180
|
+
|
|
181
|
+
#### `bpm_tenant_group_member` *(new)*
|
|
182
|
+
|
|
183
|
+
| Column | Type | Description | Example |
|
|
184
|
+
|--------|------|-------------|---------|
|
|
185
|
+
| `id` | PK | Row id | `10` |
|
|
186
|
+
| `tenant_id` | int | Vendor id | `674` |
|
|
187
|
+
| `tenant_group_id` | FK | Group | `1` (`SF`) |
|
|
188
|
+
|
|
189
|
+
#### `bpm_action_group` *(new)*
|
|
190
|
+
|
|
191
|
+
Links a GROUP-scoped action to one or more tenant groups.
|
|
192
|
+
|
|
193
|
+
| Column | Type | Description | Example |
|
|
194
|
+
|--------|------|-------------|---------|
|
|
195
|
+
| `action_id` | FK | Action | `111` |
|
|
196
|
+
| `tenant_group_id` | FK | Group that may use it | `1` (`SF`) |
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 3.2 Catalog (palette)
|
|
201
|
+
|
|
202
|
+
#### `bpm_action_type` *(← `twf_action_type`)*
|
|
203
|
+
|
|
204
|
+
Category in the left palette (Delay, Email, Controller, HTTP, Approval, …).
|
|
205
|
+
|
|
206
|
+
| Column | Type | Description | Example |
|
|
207
|
+
|--------|------|-------------|---------|
|
|
208
|
+
| `id` | PK | Type id | `28` |
|
|
209
|
+
| `code` | varchar | Stable type code | `CONTROLLER_EXECUTION` |
|
|
210
|
+
| `name` | varchar | Palette group label | `CONTROLLER EXECUTION` |
|
|
211
|
+
| `schema_json` | json | Shared UI schema for the type | `{ "timeout": { ... } }` |
|
|
212
|
+
| `is_start` | char(1) | Can be a start/trigger type | `N` |
|
|
213
|
+
| `is_delay` | char(1) | Timer-like behavior | `N` |
|
|
214
|
+
| `on_duplicate` | varchar | Hook when process is cloned | `duplicateEmailTemplate` |
|
|
215
|
+
| `is_active` | bool | Visible in palette | `true` |
|
|
216
|
+
|
|
217
|
+
#### `bpm_action` *(← `twf_action`)*
|
|
218
|
+
|
|
219
|
+
Reusable step/event definition (stable contract via `code`).
|
|
220
|
+
|
|
221
|
+
| Column | Type | Description | Example |
|
|
222
|
+
|--------|------|-------------|---------|
|
|
223
|
+
| `id` | PK | Action id | `111` |
|
|
224
|
+
| `action_type_id` | FK | Category | `28` |
|
|
225
|
+
| `code` | varchar | **Stable API key** (never hardcode numeric id in callers) | `SF SEND COS PUSH` |
|
|
226
|
+
| `name` | varchar | Builder label | `Send Push Notification to COS members` |
|
|
227
|
+
| `description` | text | Help text in palette | `Sends a push when a COS message is posted` |
|
|
228
|
+
| `schema_json` | json | Right-panel form for this action | `{ "subject": {...}, "message": {...} }` |
|
|
229
|
+
| `config_json_template` | json | Defaults / `{{ FIELD }}` placeholders | `{ "subject": "{{ FAMILY_NAME }}" }` |
|
|
230
|
+
| `controller_function` | varchar | Internal handler or connector key | `hq-nd/BusinessTypeSF/sendCircleOfSupportNotification` |
|
|
231
|
+
| `visibility_scope` | varchar | Who can use it | `GROUP` |
|
|
232
|
+
| `owner_tenant_id` | int null | Set when scope = `TENANT` | `null` |
|
|
233
|
+
| `is_enabled` | bool | Kill switch | `true` |
|
|
234
|
+
| `icon` | varchar | Palette icon | `mdi:bell-ring-outline` |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### 3.3 Process definition (design-time graph)
|
|
239
|
+
|
|
240
|
+
#### `bpm_process_definition` *(← `twf_flow` metadata)*
|
|
241
|
+
|
|
242
|
+
Logical process (many versions).
|
|
243
|
+
|
|
244
|
+
| Column | Type | Description | Example |
|
|
245
|
+
|--------|------|-------------|---------|
|
|
246
|
+
| `id` | PK | Definition id | `100` |
|
|
247
|
+
| `key` | varchar | Stable process key | `sf-new-cos-message` |
|
|
248
|
+
| `name` | varchar | Display name | `New COS Message` |
|
|
249
|
+
| `category` | varchar | `automation` or `bpm` | `automation` |
|
|
250
|
+
| `owner_tenant_id` | int null | Owning vendor; null for platform template | `674` |
|
|
251
|
+
| `visibility_scope` | varchar | Who may clone/subscribe | `GROUP` |
|
|
252
|
+
| `icon` | varchar | UI icon | `chatbubbles-outline` |
|
|
253
|
+
| `is_active` | bool | Soft delete | `true` |
|
|
254
|
+
|
|
255
|
+
#### `bpm_process_version` *(split from `twf_flow`)*
|
|
256
|
+
|
|
257
|
+
Immutable published graph. Running instances pin a version.
|
|
258
|
+
|
|
259
|
+
| Column | Type | Description | Example |
|
|
260
|
+
|--------|------|-------------|---------|
|
|
261
|
+
| `id` | PK | Version id | `45` |
|
|
262
|
+
| `definition_id` | FK | Parent definition | `100` |
|
|
263
|
+
| `version` | int | Monotonic version number | `3` |
|
|
264
|
+
| `status` | varchar | `draft` \| `published` \| `retired` | `published` |
|
|
265
|
+
| `published_at` | timestamp null | Publish time | `2026-07-15T12:00:00Z` |
|
|
266
|
+
| `graph_checksum` | varchar | Optional integrity hash | `sha256:…` |
|
|
267
|
+
|
|
268
|
+
#### `bpm_node` *(← `twf_node`)*
|
|
269
|
+
|
|
270
|
+
One element on the canvas.
|
|
271
|
+
|
|
272
|
+
| Column | Type | Description | Example |
|
|
273
|
+
|--------|------|-------------|---------|
|
|
274
|
+
| `id` | PK | Node id | `18200` |
|
|
275
|
+
| `version_id` | FK | Process version | `45` |
|
|
276
|
+
| `action_id` | FK null | Catalog action (null for pure gateways/start/end) | `111` |
|
|
277
|
+
| `element_type` | varchar | BPMN-ish type | `serviceTask` / `userTask` / `exclusiveGateway` / `startEvent` |
|
|
278
|
+
| `name` | varchar | Label on canvas | `Send Push Notification to COS members` |
|
|
279
|
+
| `is_init` | bool | Entry / trigger node | `false` |
|
|
280
|
+
| `config_json` | json | Values from right-panel form (`action_config_json` today) | `{ "subject": "{{ REGISTERED_BY_NAME }}", "message": "{{ MESSAGE }}" }` |
|
|
281
|
+
| `form_version_id` | FK null | Bound form (user tasks) | `12` |
|
|
282
|
+
| `approval_policy_id` | FK null | Approval rules | `3` |
|
|
283
|
+
| `document_type_id` | FK null | Required doc type | `null` |
|
|
284
|
+
| `position_x` / `position_y` | int | Canvas coordinates | `120`, `340` |
|
|
285
|
+
|
|
286
|
+
#### `bpm_edge` *(← `twf_node_connection`)*
|
|
287
|
+
|
|
288
|
+
Directed connection; may carry a condition (gateway branch).
|
|
289
|
+
|
|
290
|
+
| Column | Type | Description | Example |
|
|
291
|
+
|--------|------|-------------|---------|
|
|
292
|
+
| `id` | PK | Edge id | `900` |
|
|
293
|
+
| `version_id` | FK | Process version | `45` |
|
|
294
|
+
| `from_node_id` | FK null | Source; null = link into start | `18199` |
|
|
295
|
+
| `to_node_id` | FK | Target | `18200` |
|
|
296
|
+
| `name` | varchar null | Branch label | `approved` |
|
|
297
|
+
| `condition_expr` | varchar null | Expression for XOR/default | `variables.decision == "approved"` |
|
|
298
|
+
| `is_default` | bool | Default flow on gateway | `false` |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### 3.4 Forms, approvals, documents (design-time)
|
|
303
|
+
|
|
304
|
+
#### `bpm_form_definition` *(new)*
|
|
305
|
+
|
|
306
|
+
| Column | Type | Description | Example |
|
|
307
|
+
|--------|------|-------------|---------|
|
|
308
|
+
| `id` | PK | Form id | `5` |
|
|
309
|
+
| `key` | varchar | Stable key | `sf-need-review` |
|
|
310
|
+
| `name` | varchar | Display name | `Need review form` |
|
|
311
|
+
| `visibility_scope` | varchar | Catalog scope | `GROUP` |
|
|
312
|
+
| `owner_tenant_id` | int null | If `TENANT` | `null` |
|
|
313
|
+
|
|
314
|
+
#### `bpm_form_version` *(new)*
|
|
315
|
+
|
|
316
|
+
| Column | Type | Description | Example |
|
|
317
|
+
|--------|------|-------------|---------|
|
|
318
|
+
| `id` | PK | Form version id | `12` |
|
|
319
|
+
| `form_id` | FK | Parent form | `5` |
|
|
320
|
+
| `version` | int | Version number | `2` |
|
|
321
|
+
| `schema_json` | json | JSON Schema of fields | `{ "type":"object", "properties": { "notes": { "type":"string" } } }` |
|
|
322
|
+
| `ui_schema` | json | UI layout hints | `{ "notes": { "ui:widget":"textarea" } }` |
|
|
323
|
+
| `status` | varchar | `draft` \| `published` | `published` |
|
|
324
|
+
|
|
325
|
+
#### `bpm_approval_policy` *(new)*
|
|
326
|
+
|
|
327
|
+
| Column | Type | Description | Example |
|
|
328
|
+
|--------|------|-------------|---------|
|
|
329
|
+
| `id` | PK | Policy id | `3` |
|
|
330
|
+
| `code` | varchar | Stable code | `SF_AREA_ANY` |
|
|
331
|
+
| `policy_type` | varchar | `ANY` \| `ALL` \| `N_OF_M` | `ANY` |
|
|
332
|
+
| `quorum_n` | int null | Required when `N_OF_M` | `null` |
|
|
333
|
+
| `assignee_resolver` | json | How to resolve approvers | `{ "type":"expression", "resolver":"sfAreaApprovers", "args": { "familyAreaId": "{{ variables.familyAreaId }}" } }` |
|
|
334
|
+
| `outcomes` | json | Allowed outcomes | `["approved","rejected","changes_requested"]` |
|
|
335
|
+
|
|
336
|
+
#### `bpm_document_type` *(new)*
|
|
337
|
+
|
|
338
|
+
| Column | Type | Description | Example |
|
|
339
|
+
|--------|------|-------------|---------|
|
|
340
|
+
| `id` | PK | Doc type id | `7` |
|
|
341
|
+
| `code` | varchar | Stable code | `HOST_AGREEMENT` |
|
|
342
|
+
| `name` | varchar | Label | `Host agreement PDF` |
|
|
343
|
+
| `mime_allowlist` | json | Allowed MIME types | `["application/pdf"]` |
|
|
344
|
+
| `max_size_mb` | int | Upload limit | `10` |
|
|
345
|
+
| `visibility_scope` | varchar | Catalog scope | `GROUP` |
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
### 3.5 Runtime
|
|
350
|
+
|
|
351
|
+
#### `bpm_process_instance` *(← `twf_flow_instance`)*
|
|
352
|
+
|
|
353
|
+
One running (or completed) case.
|
|
354
|
+
|
|
355
|
+
| Column | Type | Description | Example |
|
|
356
|
+
|--------|------|-------------|---------|
|
|
357
|
+
| `id` | PK | Instance id | `5001` |
|
|
358
|
+
| `version_id` | FK | Pinned process version | `45` |
|
|
359
|
+
| `tenant_id` | int | Owning vendor | `674` |
|
|
360
|
+
| `business_key` | varchar | External correlation id | `cos-message:8891` |
|
|
361
|
+
| `status` | varchar | `running` \| `waiting` \| `completed` \| `failed` \| `cancelled` | `running` |
|
|
362
|
+
| `variables` | json | Case data (replaces heavy reliance on SQL views) | `{ "referralId": 321, "familyName": "The Smith Family", "familyInitials": "SM", "message": "Can someone cover Friday?" }` |
|
|
363
|
+
| `started_at` / `ended_at` | timestamp | Lifecycle | `2026-07-16T14:01:00Z` / `null` |
|
|
364
|
+
|
|
365
|
+
#### `bpm_activity_instance` *(← `twf_node_instance`)*
|
|
366
|
+
|
|
367
|
+
One execution of a node inside an instance.
|
|
368
|
+
|
|
369
|
+
| Column | Type | Description | Example |
|
|
370
|
+
|--------|------|-------------|---------|
|
|
371
|
+
| `id` | PK | Activity id | `80001` |
|
|
372
|
+
| `instance_id` | FK | Process instance | `5001` |
|
|
373
|
+
| `node_id` | FK | Definition node | `18200` |
|
|
374
|
+
| `status` | varchar | `pending` \| `active` \| `completed` \| `failed` \| `skipped` | `completed` |
|
|
375
|
+
| `input_json` | json | Snapshot of inputs | `{ "subject": "Jane Doe", "message": "…" }` |
|
|
376
|
+
| `output_json` | json | Snapshot of outputs | `{ "sent": 3, "skipped": 1 }` |
|
|
377
|
+
| `error_message` | text null | Failure detail | `null` |
|
|
378
|
+
| `started_at` / `completed_at` | timestamp | Timing | … |
|
|
379
|
+
|
|
380
|
+
#### `bpm_token` *(new)*
|
|
381
|
+
|
|
382
|
+
Active position(s) in the graph (required for parallel gateways).
|
|
383
|
+
|
|
384
|
+
| Column | Type | Description | Example |
|
|
385
|
+
|--------|------|-------------|---------|
|
|
386
|
+
| `id` | PK | Token id | `60` |
|
|
387
|
+
| `instance_id` | FK | Process instance | `5001` |
|
|
388
|
+
| `node_id` | FK | Current node | `18200` |
|
|
389
|
+
| `status` | varchar | `active` \| `consumed` | `active` |
|
|
390
|
+
| `parent_token_id` | FK null | For parallel forks | `null` |
|
|
391
|
+
|
|
392
|
+
#### `bpm_task` *(new)*
|
|
393
|
+
|
|
394
|
+
Human inbox item (form / approval).
|
|
395
|
+
|
|
396
|
+
| Column | Type | Description | Example |
|
|
397
|
+
|--------|------|-------------|---------|
|
|
398
|
+
| `id` | PK | Task id | `900` |
|
|
399
|
+
| `activity_id` | FK | Related activity | `80010` |
|
|
400
|
+
| `instance_id` | FK | Process instance (denormalized for queries) | `5001` |
|
|
401
|
+
| `tenant_id` | int | Tenant | `674` |
|
|
402
|
+
| `task_type` | varchar | `FORM` \| `APPROVAL` | `APPROVAL` |
|
|
403
|
+
| `title` | varchar | Inbox title | `Approve posted need #441` |
|
|
404
|
+
| `assignee` | varchar null | Claimed user id/email | `approver@sf.org` |
|
|
405
|
+
| `candidate_users` | json | Potential assignees | `["a@sf.org","b@sf.org"]` |
|
|
406
|
+
| `candidate_groups` | json | Role/group keys | `["SF_AREA_12"]` |
|
|
407
|
+
| `due_at` | timestamp null | SLA due | `2026-07-17T18:00:00Z` |
|
|
408
|
+
| `status` | varchar | `open` \| `completed` \| `cancelled` | `open` |
|
|
409
|
+
| `form_version_id` | FK null | Form to render | `12` |
|
|
410
|
+
| `payload_json` | json | Task-specific data | `{ "needId": 441, "kind": "post" }` |
|
|
411
|
+
|
|
412
|
+
#### `bpm_task_action` *(new)*
|
|
413
|
+
|
|
414
|
+
Audit of human actions on a task.
|
|
415
|
+
|
|
416
|
+
| Column | Type | Description | Example |
|
|
417
|
+
|--------|------|-------------|---------|
|
|
418
|
+
| `id` | PK | Action id | `1200` |
|
|
419
|
+
| `task_id` | FK | Task | `900` |
|
|
420
|
+
| `actor` | varchar | Who acted | `a@sf.org` |
|
|
421
|
+
| `action` | varchar | `approve` \| `reject` \| `comment` \| `reassign` \| `complete` | `approve` |
|
|
422
|
+
| `comment` | text null | Optional note | `Looks good` |
|
|
423
|
+
| `data_json` | json null | Submitted form fields | `{ "notes": "OK" }` |
|
|
424
|
+
| `created_at` | timestamp | When | `2026-07-16T15:00:00Z` |
|
|
425
|
+
|
|
426
|
+
#### `bpm_document` *(new)*
|
|
427
|
+
|
|
428
|
+
Uploaded or generated file for an instance.
|
|
429
|
+
|
|
430
|
+
| Column | Type | Description | Example |
|
|
431
|
+
|--------|------|-------------|---------|
|
|
432
|
+
| `id` | PK | Document id | `70` |
|
|
433
|
+
| `instance_id` | FK | Process instance | `5001` |
|
|
434
|
+
| `document_type_id` | FK | Type | `7` |
|
|
435
|
+
| `tenant_id` | int | Tenant | `674` |
|
|
436
|
+
| `file_name` | varchar | Original name | `host-agreement.pdf` |
|
|
437
|
+
| `s3_key` | varchar | Storage key | `bpm/674/5001/host-agreement.pdf` |
|
|
438
|
+
| `mime_type` | varchar | MIME | `application/pdf` |
|
|
439
|
+
| `status` | varchar | `required` \| `uploaded` \| `approved` \| `rejected` | `uploaded` |
|
|
440
|
+
| `uploaded_by` | varchar null | Actor | `coach@sf.org` |
|
|
441
|
+
|
|
442
|
+
#### `bpm_message` *(← `twf_event`)*
|
|
443
|
+
|
|
444
|
+
Inbound event / signal that starts or resumes a process.
|
|
445
|
+
|
|
446
|
+
| Column | Type | Description | Example |
|
|
447
|
+
|--------|------|-------------|---------|
|
|
448
|
+
| `id` | PK | Message id | `56953` |
|
|
449
|
+
| `action_code` | varchar | Trigger action code (or process key) | `SF NEW COS MESSAGE` |
|
|
450
|
+
| `tenant_id` | int null | Known tenant if resolved | `674` |
|
|
451
|
+
| `business_key` | varchar null | Correlation | `cos-message:8891` |
|
|
452
|
+
| `payload_json` | json | Event body / origin row snapshot | `{ "id": 8891, "referralId": 321, "familyName": "The Smith Family", "familyInitials": "SM" }` |
|
|
453
|
+
| `desc_job_name` | varchar | Caller context (like today) | `CircleOfSupport/createMessage` |
|
|
454
|
+
| `status` | varchar | `pending` \| `processed` \| `failed` | `processed` |
|
|
455
|
+
| `process_instance_id` | FK null | Instance created/signaled | `5001` |
|
|
456
|
+
|
|
457
|
+
#### `bpm_job` *(new)*
|
|
458
|
+
|
|
459
|
+
Async work: timers, HTTP retries, delayed continuations (worker queue rows).
|
|
460
|
+
|
|
461
|
+
| Column | Type | Description | Example |
|
|
462
|
+
|--------|------|-------------|---------|
|
|
463
|
+
| `id` | PK | Job id | `300` |
|
|
464
|
+
| `instance_id` | FK null | Related instance | `5001` |
|
|
465
|
+
| `activity_id` | FK null | Related activity | `80001` |
|
|
466
|
+
| `job_type` | varchar | `TIMER` \| `HTTP` \| `EMAIL` \| `PUSH` \| `CONTINUE` | `PUSH` |
|
|
467
|
+
| `run_at` | timestamp | When eligible | `2026-07-16T14:01:05Z` |
|
|
468
|
+
| `attempts` | int | Retry count | `1` |
|
|
469
|
+
| `max_attempts` | int | Cap | `4` |
|
|
470
|
+
| `payload_json` | json | Worker payload | `{ "actionCode": "SF SEND COS PUSH", "config": { ... } }` |
|
|
471
|
+
| `status` | varchar | `scheduled` \| `running` \| `done` \| `failed` | `done` |
|
|
472
|
+
| `last_error` | text null | Last failure | `null` |
|
|
473
|
+
|
|
474
|
+
#### `bpm_audit_log` *(← `twf_flow_log`)*
|
|
475
|
+
|
|
476
|
+
Immutable trail for support and compliance.
|
|
477
|
+
|
|
478
|
+
| Column | Type | Description | Example |
|
|
479
|
+
|--------|------|-------------|---------|
|
|
480
|
+
| `id` | PK | Log id | `10001` |
|
|
481
|
+
| `instance_id` | FK null | Process instance | `5001` |
|
|
482
|
+
| `tenant_id` | int null | Tenant | `674` |
|
|
483
|
+
| `event_type` | varchar | `INSTANCE_STARTED` \| `TASK_COMPLETED` \| `NODE_FAILED` \| … | `NODE_FAILED` |
|
|
484
|
+
| `message` | text | Human-readable summary | `Push connector timeout` |
|
|
485
|
+
| `details_json` | json | Structured detail / stack excerpt | `{ "activityId": 80001, "error": "ETIMEDOUT" }` |
|
|
486
|
+
| `created_at` | timestamp | When | `2026-07-16T14:02:00Z` |
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## 4. Multi-tenancy (catalog visibility) — short
|
|
491
|
+
|
|
492
|
+
| Scope | Who sees the action/form/template |
|
|
493
|
+
|-------|-----------------------------------|
|
|
494
|
+
| `SYSTEM` | All tenants |
|
|
495
|
+
| `GROUP` | Tenants in linked `bpm_tenant_group` (via `bpm_action_group`) |
|
|
496
|
+
| `TENANT` | Only `owner_tenant_id` |
|
|
497
|
+
|
|
498
|
+
Builder palette = `SYSTEM ∪ GROUP(member) ∪ TENANT(owner)`.
|
|
499
|
+
**Always re-validate on publish and execute** (never trust UI alone).
|
|
500
|
+
Instance/task/document data is always hard-scoped by `tenant_id`.
|
|
501
|
+
|
|
502
|
+
Replace today’s “vendor 55 runs for everyone” with **platform/group templates + tenant clone or subscription**.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Appendix A — Keep / improve from current Flows
|
|
507
|
+
|
|
508
|
+
**Keep:** stable action `code`, action catalog + `schema_json`, node graph, event + SQS worker, template duplicate, instance history, 3-pane builder.
|
|
509
|
+
|
|
510
|
+
**Improve:** human tasks, approvals, forms, documents, gateway UX, case variables, HTTP connectors, immutable versions, observability.
|
|
511
|
+
|
|
512
|
+
Details of the current engine: [FLOWS.md](./FLOWS.md).
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Appendix B — Target capabilities (MVP → later)
|
|
517
|
+
|
|
518
|
+
| Capability | Approach |
|
|
519
|
+
|------------|----------|
|
|
520
|
+
| Forms | `bpm_form_*` + User Task node |
|
|
521
|
+
| Approvals | `bpm_approval_policy` + `bpm_task` |
|
|
522
|
+
| Branching | `exclusiveGateway` / `parallelGateway` + `bpm_edge.condition_expr` + `bpm_token` |
|
|
523
|
+
| Documents | `bpm_document_type` + `bpm_document` + S3 |
|
|
524
|
+
| External APIs | Action types `HTTP_REQUEST`, `WEBHOOK_WAIT`, connectors |
|
|
525
|
+
| Automations | Same engine; category=`automation`; service tasks only |
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Appendix C — Stack & migration
|
|
530
|
+
|
|
531
|
+
| Layer | Choice |
|
|
532
|
+
|-------|--------|
|
|
533
|
+
| API + engine v1 | **NestJS** modular monolith |
|
|
534
|
+
| Workers | NestJS + SQS/BullMQ |
|
|
535
|
+
| UI | React (evolve Harmony builder + task inbox) |
|
|
536
|
+
| DB | PostgreSQL preferred |
|
|
537
|
+
| Later | Optional Go workers if metrics demand |
|
|
538
|
+
|
|
539
|
+
**Migration:** keep `twf_*` live → adapter from `EventHq.add` → migrate SF processes first → retire legacy write path when ready.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## Appendix D — Open decisions
|
|
544
|
+
|
|
545
|
+
1. Expression language: JSONLogic vs CEL vs JMESPath
|
|
546
|
+
2. Form tech: RJSF vs Formily vs custom
|
|
547
|
+
3. Postgres vs MySQL for the new DB
|
|
548
|
+
4. Inbox inside Harmony vs separate app
|
|
549
|
+
5. BPMN XML export in v1 or JSON-only
|
|
550
|
+
6. Tenant groups: reuse `business_type` vs dedicated packs
|
|
551
|
+
7. Global automations: subscription (recommended) vs fan-out listener
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Appendix E — Example process (Safe Families)
|
|
556
|
+
|
|
557
|
+
```text
|
|
558
|
+
Start (SF NEW COS MESSAGE / SF NEW POSTED NEED)
|
|
559
|
+
→ Service Task: notify (push)
|
|
560
|
+
→ [BPM] User/Approval Task (optional)
|
|
561
|
+
→ Exclusive Gateway (approved / rejected)
|
|
562
|
+
→ Service Task: decision notification
|
|
563
|
+
→ End
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Same trigger model as today; richer graph when human steps are needed.
|