Skip to content

refactor(api): tasks and processes become core primitives features plug into - #233

Merged
artem-broccoli merged 19 commits into
artem/bro-262-never-lose-a-record-change-that-starts-afrom
artem/bro-270-tasks-and-processes-become-shared-foundations
Nov 15, 2026
Merged

artem-broccoli merged 19 commits into
artem/bro-262-never-lose-a-record-change-that-starts-afrom
artem/bro-270-tasks-and-processes-become-shared-foundations

Conversation

@artem-broccoli

@artem-broccoli artem-broccoli commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Tasks and processes become shared foundations in core/, beside records, and import no business module. Features plug in instead:

  • Record types register what a process needs from their records: who a record is about (readSubjects) and the dates a record-date trigger may start on (dateFields, findByDateBetween). Employees registers employee (its startDate); tasks registers task.
  • Triggering events are registered by the module that publishes them (employees.added by employees; tasks.task_created and tasks.task_status_changed by tasks), collected once at startup; a key registered twice stops startup.
  • People and working days are read through PeoplePort and WorkingCalendarsPort, which employees fills with adapters, together with the bank holiday rule. Ports live in a module's ports/ folder and their implementations in the implementing module's adapters/ folder, both enforced as lint layers.
  • A process step's task is marked done with the ordinary tasks.markDone. Processes listens to the task's status event before the commit and completes the step and the run in the same transaction. Every transaction that locks a task and its run takes the task first; the daily check now locks a run's open tasks before the run. A run that can no longer go on (its record or person is gone) is left for the daily check, and a task done meanwhile stays done.
  • Tasks no longer store which process step created them; Task.processSource is served by processes, and the Employee fields of Task by employees, so the GraphQL shape the web app reads stays the same.
  • Tasks and runs are domain aggregates. Task owns who sees it, who marks it done or deletes it, and that done and cancelled are final. ProcessRun holds its steps and owns completing them, finishing, the removal check and cancelling; changes load it with lockRun and write it back with saveRun.
  • Process definitions: a step keeps key and kind at its root and its kind's fields under it (task: { assignee, title, description, due }); startableByAdmin is gone, so an admin may start any process by hand with create on process_run. The run steps table is now process_run_step_progress: the steps live in the version's definition, the table holds a run's progress on each.
  • Counting working days moves to utils (addWorkingDays); processes keeps what a day offset means and its limit.
  • New rule RULE77: a module.md opens with JSON examples of its main flow; processes/module.md shows a run started by a change and by a date.

Behaviour changes:

  • Done and cancelled tasks are final: Reopen, the Undo of Mark done and the reopen step command are gone.
  • People no longer cancel tasks. The person who asked can delete an open task they asked for (tasks.delete), with a confirmation; only a process cancels the tasks it created, with a reason.
  • Cancelling a run by hand is gone from the process service (it had no API or screen).

Stacked on #227.

Testing

  • pnpm check, all API integration tests (1546) and end-to-end tests (304) pass; web unit tests pass.
  • New tests: deleting a task (asker, assignee, unrelated caller, done, cancelled, process task), finishing steps through tasks.markDone (both steps, one step, concurrent completion, removed subject or assignee, manual tasks untouched), the daily check cancelling a run whose task record was deleted, definition parsing against registrations, and refusing a key registered twice.
  • The migrations are edited in place; drizzle-kit generate reports no change for tasks and processes. The Docker image builds and pnpm dev starts on a fresh database.
  • docs/qa/tasks.md is updated for Delete and the removed Reopen and Undo.
  • After the review changes: processes, tasks and employees integration tests (544) and processes unit tests pass on a fresh database; typecheck and lint pass.
  • Not done: screenshots of the Delete menu and dialog.

Release Notes:

  • Improved: marking a process task done now works from Tasks like any other task.
  • Changed: a task you asked for can be deleted while it is open; done tasks can no longer be reopened and tasks can no longer be cancelled by hand.

…s in through registrations

Tasks and processes import no module. Processes reads people and working
calendars through ports the employees module fills, record dates and
subjects through record type registrations, and triggering events from
the modules that publish them. A process step's task is marked done with
the ordinary task command; processes completes the step before the
commit. Reopen and cancelling a task by hand are gone; the person who
asked deletes an open task instead.
A run whose subject record or person was removed no longer finishes when
its last task is marked done, and the daily check cancels a run whose
record, such as a deleted task, is gone. Manual starts accept the record
id in any case. Tasks register their own triggering events.
Also drop the unused single-person subject read and inline mark done's
task change.
@artem-broccoli
artem-broccoli requested a review from a team as a code owner November 14, 2026 15:17
Task becomes a class: who sees it, who marks it done or deletes it, and
that done and cancelled are final live on it; every change returns a new
task.
ProcessRun becomes a class with its steps: completing a step's tasks,
finishing, the removal check and cancelling live on it. Changes load the
run with lockRun and write it back with saveRun.
… to the database

Saving a run rewrote every step's task id, which could restore a task id
the database had just cleared and fail a concurrent mark done.
Processes' ports move to processes/ports and the employees module's
implementations of them to employees/adapters, both enforced as layers,
so a module's outside dependencies show in its folders.
Processes keeps what a day offset means, its search limit and the
refusal; utils counts working days for any caller.
The steps live in the version's definition; the table holds a run's
progress on each of them.
…ocess by hand

Starting by hand stays behind create on process_run for the subject.
…nd's fields under it

A task step's assignee, title, description and due move under `task`,
so another kind of step brings its own fields without touching the root.
…nts start it

parseProcessDefinition restores the stored on_event trigger as an
EventTrigger; isTriggeredBy moves onto it.
@artem-broccoli
artem-broccoli merged commit 968d16b into artem/bro-262-never-lose-a-record-change-that-starts-a Nov 15, 2026
7 checks passed
@artem-broccoli
artem-broccoli deleted the artem/bro-270-tasks-and-processes-become-shared-foundations branch November 15, 2026 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant