Live demo: a quarterly data collection · Reference documentation
Replacement of blockr.task
The API is still moving: function names, column names and the block interface may change.
A process definition is a data frame, one row per task. The columns say who acts, what the task waits for, and whether a script does it instead of a person.
process <- data.frame(
task = c("deliver", "validate", "review", "approve"),
name = c("Delivery", "Validate", "Review", "Approve"),
role = c("system", "system", "analyst", "management"),
depends_on = c("", "deliver", "validate", "review"),
script = c("", "validate.R", "", "")
)start_instance() opens an instance of a definition and writes an
append-only event log. A click in the task list, a script the worker ran, a
message from another system: each is one appended line. Current state is
the latest event per (task, element, field), so the log is also the audit
trail.
run_worker() is a headless R process. It runs the tasks that have a
script, and ingests the JSON files other systems drop into the inbox.
Shiny sessions hold no workflow state.
definition instance execution
────────── ──────── ─────────
a wide table ──► events.jsonl ◄── worker (a plain
in git or a append-only, one R process; runs the
process block line per event script tasks)
▲
│
inbox/*.json ◄── other systems, any language
The table flows through a
blockr board. The blocks
edit the definition, work the task list, draw the BPMN diagram and read the
log; any other blockr block also applies. A
dplyr::filter(assignee == "ana") block is one person's task list.
# install.packages("pak")
pak::pak("cynkra/blockr.process")library(blockr.process)
store <- tempfile()
jobs <- tempfile(); dir.create(jobs)
writeLines("cat('validated\n')", file.path(jobs, "validate.R"))
process <- data.frame(
task = c("each_unit", "delivery", "validate", "review"),
name = c("for each unit", "Delivery", "Validate", "Review"),
role = c("", "system", "system", "analyst"),
depends_on = c("", "", "delivery", "validate"),
script = c("", "", "validate.R", ""),
collection = c("unit", "", "", ""), # this row IS the group
parent = c("", "each_unit", "each_unit", "each_unit")
)
# open an instance over three reporting units
start_instance(process, c("north", "south", "east"), "2026Q1", store)
# the outside world delivers one of them
write_inbox_message(store, "delivery", element = "north",
instance = "2026Q1", actor = "platform", id = "d-north")
# the worker ingests it and validates that delivery, and only that one
run_worker(process, store = store, instance = "2026Q1", jobs = jobs,
wait = FALSE)
instance_view(store, "2026Q1")[, c("task", "element", "status")]
#> task element status
#> 1 delivery north done
#> 2 delivery south open
#> 3 delivery east open
#> 4 validate north done <- the script ran for north, and only north
#> 5 validate south blocked
#> 6 validate east blocked
#> 7 review north open <- a person's turn now
#> 8 review south blocked
#> 9 review east blockedNaming follows BPMN 2.0: process, task, lane, multi-instance, collection,
element, instance, assignee. Only task and name are required.
| column | what it says |
|---|---|
task, name |
the id and the label |
role |
who may act. Becomes a lane; system means nobody |
depends_on |
flow, comma separated. qa_check:false waits for an outcome |
script |
forecast.R (a file in the jobs directory) or mypkg::forecast (a function in an allowed package); the worker runs it |
collection |
this row is a multi-instance group, repeating per element |
parent |
which group a task is in |
join |
how many dependencies are enough: all, any, n=3, pct=90 |
complete_when |
how many elements close a group |
sequential |
elements run one at a time |
retry, timeout, params |
how the worker runs a script |
depends_on is flow (a DAG over tasks), parent is scope (a tree),
collection is repetition (a property of one row). The three never
overlap. A dependency may name a group, in either direction; BPMN forbids a
sequence flow across a sub-process boundary, so container edges are lowered
onto the group's entries and exits before anything reads the table.
| block | what it does |
|---|---|
new_process_block() |
the editor: the list is the process, with a rail drawing the flow |
new_start_instance_block() |
open an instance from a definition and an element list |
new_tasks_block() |
the task list: chips, filters, assignment. Writes events |
new_bpmn_block() |
the diagram, status painted on. Passes the table through |
new_instance_state_block() |
fold a store onto an incoming definition, live |
new_event_log_block() |
the audit history, newest first |
new_assign_block() |
assign people and statuses in block state (no store) |
The diagram is derived from the columns: start and end events,
gateways, lanes and multi-instance markers follow from depends_on,
role, collection and parent.
vignette("running-scripts"): the worker, the jobs directory, retries and timeouts, what a script receives, and how to deploy it (cron, systemd, Docker, Posit Connect).vignette("external-systems"): the inbox, and how a delivery platform, a database trigger or a CI job moves a process forward through it, with idempotency and an audit trail. Plus the HTTP and pull variants.
The live demo is a quarterly data collection over eight reporting units, with a worker running the scripts, a delivery platform writing into the inbox, and a rework loop. The same board runs locally:
source(system.file("examples/data-collection.R", package = "blockr.process"))dev/data-collection.md is the click script (what to show, in order),
dev/data-collection.R runs the board against local source checkouts.
The BPMN half (model, interchange XML, auto-layout, widget) lives in the
bpmn-*.R files and depends on nothing else in the package, so it can be
lifted out into a package of its own once the process model stops moving.
Rendering is
bpmn-visualization
(Apache-2.0), layout is
bpmn-auto-layout (MIT); both
are bundled, so the diagram needs no server-side Node. See
LICENSE.note for the attribution of the bundled JavaScript.
blockr.process itself is GPL (>= 3).
The design specs behind the package (the process model, script execution,
external systems) live in cynkra's internal blockr.design repository under
open/blockr-process/.