ADR-0004: Unify work items on the typed-record engine
Status
Section titled “Status”Accepted — 2026-06-17. Builds on ADR-0001 and the typed-record engine; supersedes the legacy Project-management models. Operated on by ADR-0002 (agents act on records) and surfaced as events by ADR-0003.
Context
Section titled “Context”The platform carries two parallel models for work items:
- Legacy — Eloquent
Project/Epic/Task/TimeLogmodels with their Filament “Project Management” cluster (the original 1.x-era ERP). - The typed-record engine —
Record+RecordTypewith per-type workflows, parent/child links, and a requiredproject_idcontainer (F4). This is what the MCP tools and theRecordResourceUI drive, and it is where all new work already lives (the Atlas campaign iscollection → leadrecords).
Maintaining both is drift and double work — two CRUD surfaces, two notions of “status”, two places to wire workflow. We converge on one.
Decision
Section titled “Decision”Every work item is a Record of a RecordType. Concretely:
- Project stays the container — the per-company typed container from F4 (every record belongs to a project; the per-company Inbox is the default). Project is not a Record.
- Epic, Task, Subtask become RecordTypes, linked through the engine’s parent/child +
allowed_children: Project → Epic → Task → Subtask. (Leads/collections already use this same mechanism.) - Epic is a first-class, root work-record and the billing/estimation unit: a quote is composed of epics; an epic rolls its tasks’ hours up into an invoice line (“Epic — N h — X HUF”).
- Time logs attach to the Task — the leaf where work actually happens. Epic is the collector: it aggregates the hours of its task children; it never owns time directly.
- Workflow, statuses, transitions, guards, and custom fields all come from the type registry — uniform across MCP, the
RecordResourceUI, and any future surface. - The legacy Eloquent Project-management models (
Epic,Task,TimeLog) and their Filament cluster resources are retired; existing rows migrate intorecords(+member_records, + a record-attached time log).
Consequences
Section titled “Consequences”Enables: one engine, one UI, one MCP surface for every work item — dev tasks, leads, campaigns alike. Billing becomes a query over records (epic → Σ task hours). Humans and autonomous agents operate on the same primitives, so the agent execution contract (ADR-0002) and the record lifecycle events (ADR-0003) apply uniformly.
Requires building (phased — production stays live throughout):
- P1 — define
epic/task/subtaskRecordTypes (CLI-seeded YAML) with their workflows +allowed_children(Epic→Task→Subtask). - P2 —
RecordResourcehandles the hierarchy (parent/child navigation, create-child) on top of the JIRA-style UX. - P3 — data migration: legacy
epics/tasks→records(assignees →member_records), preserving codes and relations. - P4 — time logging: re-point
TimeLogonto task records; epic hours = the rollup of its tasks. - P5 — retire the legacy Eloquent models + the Project-management cluster resources.
Deferred: per-transition required-field “screens” (JIRA-style transition forms) — authored later via the platform form builder. Today every transition uses one consistent move form (to_status + optional reason).