11 — Mother of Task (MOT) → Queue Mapping
11 — Mother of Task (MOT) → Queue Mapping
DESIGN-ONLY. Cites Điều 45 §13.4, §11.5, §6.7. MOT is NOT an executor. Design seam only; no MOT runtime authored.
§1. Goal
Make MOT compatible with the queue substrate without violating §13.4. MOT must:
- Generate a job graph (the workflow template instance).
- Hand off execution to registered §11.5 executors.
- Never claim a
job_queuerow itself. - Never hold a lease, write a heartbeat, or register as a consumer.
- Make its workflow's lifecycle observable in PG (per §11.5 rule
executor_state_must_be_reconstructable_from_PG_after_restart).
This document is a design seam. Concrete MOT runtime schema (template definition, parameter binding, graph DSL) is out of scope — those belong in the MOT sub-design pack when MOT spec stabilises.
§2. Current state
- MOT spec: not stable.
- No
mot_*table exists in PG. - No live MOT instance is running.
- §13.4 of Điều 45 v1.0 codifies the MOT clause; this design implements only what that clause permits at Phase 1.
§3. Proposed seam — job_workflow and one MOT generator function
§3.1 Workflow parent row (job_workflow)
Recap from DP2:
NON-EXECUTABLE DESIGN SKETCH — DO NOT APPLY
table public.job_workflow (
workflow_id uuid PRIMARY KEY,
workflow_kind text NOT NULL, -- vocab: 'mark_cut_pipeline', 'mot_template_v1', 'customer_inbound', ...
generator text NOT NULL, -- 'MOT:<template_id>' for MOT graphs
status text NOT NULL, -- {active, succeeded, failed, partially_failed, cancelled}
...
);
MOT emits exactly one job_workflow row + N job_queue rows where job_queue.workflow_id = job_workflow.workflow_id and job_queue.parent_job_id carries the DAG edge.
§3.2 MOT graph emit function
NON-EXECUTABLE DESIGN SKETCH — DO NOT APPLY
function fn_mot_graph_emit(
p_template_id text,
p_template_params jsonb, -- signal-only; CHECK forbids body/content/raw/vector/...
p_correlation_id text,
p_actor text
) RETURNS jsonb
-- 1. Validate template_id ∈ mot_template registry (separate MOT pack).
-- 2. Validate actor has MOT_template_generator role.
-- 3. INSERT INTO job_workflow (...).
-- 4. For each step in template:
-- INSERT INTO job_queue (
-- job_kind = step.job_kind,
-- executor = step.executor (validated against §11.5 — MOT explicitly refused),
-- idempotency_key = workflow_id||':'||step.local_id,
-- source_ref = step.source_ref,
-- target_ref = step.target_ref,
-- payload_ref = step.payload_ref,
-- safe_payload = {step.local_id, template_id, params_keys_only},
-- process_after = step.delay_relative_to_workflow_start,
-- priority = step.priority,
-- workflow_id = new_workflow_id,
-- parent_job_id = parent step's job_id (for DAG edges)
-- );
-- 5. RETURN {workflow_id, step_job_ids[], count}.
MOT calls this function; it does NOT call fn_job_claim or fn_job_succeed. Workflow lifecycle advances purely through constituent jobs.
§3.3 Workflow lifecycle derivation
NON-EXECUTABLE DESIGN SKETCH — DO NOT APPLY
function fn_job_workflow_refresh(p_workflow_id uuid) RETURNS jsonb
-- Reads job_queue rows for the workflow.
-- Derives status:
-- active = any step in {queued, leased, in_progress, retry_waiting}
-- succeeded = all steps succeeded
-- failed = any step in dead_letter
-- partially_failed = some succeeded, some dead_letter, none in_progress
-- cancelled = parent or all steps cancelled
-- Writes status + finished_at into job_workflow.
-- Called as a worker job: job_kind='job_workflow_refresh' on cadence (DP1 Layer 1).
This refresh is also called from fn_job_succeed, fn_job_fail_*, and fn_job_cancel (DP2/DP3) as a side-effect, so workflow status converges in near-real-time.
§4. MOT step interactions
§4.1 Same-DAG step interaction
parent_job_id carries the edge. A worker claiming a step checks that parent_job_id (if not NULL) is in succeeded before processing. This is the simplest DAG semantics; richer (any-vs-all parents) belongs in the MOT pack.
§4.2 Trigger-in / trigger-out
- MOT step may receive input via trigger-in (DP5
iu_sql_event_routerow mapping an event to enqueueing a step) — same path as any other consumer. - MOT step may emit events via standard
fn_iu_emit_eventcalls from its executor — same path as any other producer. - MOT itself never bypasses the queue substrate.
§4.3 IU step interaction
A MOT step that is an Information Unit operation (e.g. cut, restage) MUST go through the existing operator alias surface (fn_iu_op_*), not bypass it. §13.4 rule: "MOT step CANNOT create IU bypass Điều 37 cut alias / Điều 35 DOT pipeline".
§5. Enforcement that MOT is NOT an executor
Recapping from DP6 §3.4:
job_queue.executorCHECK refuses'MOT'.consumer_registry.executorCHECK refuses'MOT'.job_subscription.executor_kindCHECK refuses'MOT'.queue_heartbeat.executor_kindCHECK refuses'MOT'.
Any attempt to register MOT as an executor fails INSERT at the DB level. There is no path through the design that lets MOT claim or run a job_queue row.
§6. Vocabulary
mot_vocab_at_phase_1:
workflow_kinds:
- mot_template_v1 # placeholder, refined in MOT pack
generator_format: # job_workflow.generator
pattern: 'MOT:<template_id>'
step_local_id:
type: text # MOT-internal, used only inside one workflow scope
No new event_type. No new event_domain. No new event_stream.
§7. Observability
NON-EXECUTABLE DESIGN SKETCH — DO NOT APPLY
view v_job_workflow_health AS
SELECT w.workflow_id, w.workflow_kind, w.generator, w.status,
count(j.*) FILTER (WHERE j.status='queued') AS queued_steps,
count(j.*) FILTER (WHERE j.status='in_progress') AS in_progress_steps,
count(j.*) FILTER (WHERE j.status='succeeded') AS succeeded_steps,
count(j.*) FILTER (WHERE j.status='dead_letter') AS dead_letter_steps,
w.enqueued_at, w.finished_at
FROM job_workflow w
LEFT JOIN job_queue j ON j.workflow_id = w.workflow_id
GROUP BY w.workflow_id;
Feeds v_queue_health (DP4).
§8. Compatibility with Điều 45 v1.0
| Clause | Compliance |
|---|---|
| §13.4 MOT-clause | ✅ verbatim: MOT generates graph, delegates to executors, never claims |
§13.4 forbidden labels (future_MOT_executor) |
✅ CHECK refuses MOT in 4 surfaces |
| §11.5 executor whitelist | ✅ MOT not in set; cannot register heartbeat |
| §6.7 work state machine | ✅ each MOT step is a job_queue row obeying 9-state machine |
| §4 signal-not-data | ✅ template_params CHECK forbids forbidden keys |
| §13.3 Phase 1 NOT bring MOT runtime | ✅ this doc is design seam only |
§9. Implementation prerequisites
- DP2 (
job_queue,job_workflow) in place. - DP3 (lease/retry) so step failures retry per policy.
- DP6 (executor enforcement) so the MOT-CHECK is wired in 4 places.
- MOT template registry (out of scope; a future MOT pack).
§10. Open questions
| # | Question | Routed to |
|---|---|---|
| MOT-Q1 | Approve job_workflow table at Phase 1, or defer to MOT-pack? |
Council |
| MOT-Q2 | Approve fn_mot_graph_emit as the only MOT entry-point? |
Council |
| MOT-Q3 | DAG semantics: any-parent vs all-parents at Phase 6? | Council + MOT pack |
| MOT-Q4 | Should job_workflow support nested workflows (workflow-of-workflows)? |
Council |
| MOT-Q5 | Should MOT template registry be vocab-only in event_type_registry style, or its own table? |
MOT pack |
| MOT-Q6 | partially_failed semantics — does it freeze the workflow or allow retry of failed steps? |
Council |
MOT mapping. Design seam only. No MOT runtime authored. No mutation. Authored 2026-05-26.