KB-37E3

MOWD Phase 1 — Master UI Data Contract (Branch D)

8 min read Revision 1
mowdphase1uidata-contractdieu-28read-model2026-05-29

Branch D — Master UI Data Contract (read models only; NO implementation)

Four surfaces. Backend-enforced visibility (Directus permissions + view-level filtering); the UI never sees rows/actions it lacks rights to. No Nuxt code here (Điều 28 — contract only). Must scale to thousands of workflows / 10k+ tasks → all lists are server-paginated, server-sorted, server-filtered; no client-side full scans; traffic-lights computed in SQL/DOT, not the browser.


A. View definitions backing the contract

v_mow_design_workflow and v_mow_design_step (doc 02 §3). Scale note: before exposing to UI at thousands of rows, replace the two correlated subqueries in v_mow_design_workflow with a pre-aggregated join:

LEFT JOIN (SELECT workflow_id, count(*) step_count,
                  count(*) FILTER (WHERE step_iu_ref IS NOT NULL) bound_step_count
           FROM workflow_steps GROUP BY workflow_id) sc ON sc.workflow_id=w.id

A third view v_mow_governance_cockpit (doc 07) backs surface 4.


Surface 1 — MOW Tree / List

  • Source: v_mow_design_workflow (+ parent_workflow_id, level, category_id from base for tree).
  • Fields: id, process_code, title, status, owner_gov_code+owner_name, active_design_version, freeze_active, step_count, bound_step_count, design_health(rollup), level, parent.
  • Filters: status (active/draft/retired), owner_gov_code, frozen (y/n), category_id, has-unbound-steps (bound<step_count), free-text on title/process_code.
  • Sorting: process_code, title, status, bound_step_count/step_count ratio, date_updated. Default: status then process_code.
  • Traffic-light: 🟢 owner set + version set + 100% steps bound + not frozen-for-error · 🟡 partial binding or no active_design_version · 🔴 no owner_gov_code OR validation failures OR dangling refs.
  • Drill-down: row → Surface 2 (detail) for that workflow_id; tree expand via parent_workflow_id.
  • Actions → DOT: Open=dot_mow_design_get · Validate=dot_mow_design_validate · Freeze=dot_mow_design_freeze · Unfreeze=dot_mow_design_unfreeze.
  • Permission: read = any MOW-scoped role; Freeze/Unfreeze = MOW owner role; mutating actions hidden for read-only users.
  • States: empty="No workflows match"; loading=skeleton rows; error=banner + retry, never partial silent list.

Surface 2 — MOW Design Registry List / Detail

  • Source: header v_mow_design_workflow; steps v_mow_design_step; relations from workflow_step_relations (incl. condition_iu_ref); change history via dot_mow_design_audit.
  • Fields (detail): header (as Surface 1) + per-step: step_key, step_type, actor_type, title, step_iu_ref, guide_iu_ref, dot_ref, output_table_ref, event_domain_ref/event_type_ref, step_version, is_iu_bound; relation list (from→to, relation_type, condition_expression, condition_iu_ref, label).
  • Filters (steps): step_type, actor_type, is_iu_bound, has-dot-ref, has-event-ref.
  • Sorting: sort_order (DAG order), step_type.
  • Traffic-light (per step): 🟢 bound (step_iu_ref set) + dot_ref resolves + (event refs resolve or N/A) · 🟡 inline-only / not yet bound · 🔴 dangling ref (dot_ref/iu_ref/event not found).
  • Drill-down: step_iu_ref → IU viewer; dot_ref → DOT catalog entry; output_table_ref → table_registry; render DAG via dot_mow_design_render_tree.
  • Actions → DOT: Bind IU=dot_mow_design_bind_step_iu · Bind DOT=dot_mow_design_bind_dot · Bind Event=dot_mow_design_bind_event · Bind design IU (header)=dot_mow_design_bind_iu · Propose change=dot_mow_design_propose_change · Activate=dot_mow_design_activate (council) · Rollback=dot_mow_design_rollback (council).
  • Permission: bind = MOW owner; activate/rollback = council only (button disabled+explained for non-council); proposing allowed for any author incl. agent (queued, never auto-applied).
  • States: empty steps="Design not yet decomposed"; activate disabled until validation 🟢 + approval present; error per-action toast with the failing check.

Surface 3 — MOT / JFT Task surface

  • Source: tasks (10 rows live) joined to owning workflow via workflow_steps.task_id/workflows.task_id; read-only in Phase 1 (MOT runtime is out of scope). Designed for 10k+ tasks → server pagination + indexed filters mandatory.
  • Fields: task id, title/content summary, linked workflow (process_code/title), linked step (step_key), status, actor_type, date_updated.
  • Filters: workflow, status, actor_type, assignee (when assignee_policy lands Phase 2), date range.
  • Sorting: date_updated desc, status. Server-side keyset pagination (id/date) — never OFFSET at 10k+.
  • Traffic-light: 🟢 task linked to a bound step · 🟡 linked to inline step · 🔴 orphan task (no workflow/step link).
  • Drill-down: task → its workflow (Surface 2) → step.
  • Actions → DOT: Phase 1 = read/inspect only; "Open design" → Surface 2. (No task runtime DOT — doc 08.)
  • Permission: read = task-scoped role; no mutate buttons in Phase 1.
  • States: empty="No tasks"; loading=paged skeleton; error=retry; large-result guard="showing first N, refine filters".

Surface 4 — Governance Cockpit

  • Source: v_mow_governance_cockpit (doc 07) over approval_requests (211), apr_approvals (42), workflow_change_requests (3), governance_registry (9).
  • Fields: pending proposals (entity, request_type, author, age), approvals needed (cross-sign count vs ≥2), recent activations/rollbacks, frozen workflows, validation-failing workflows, gate status (fn_iu_gate_verify_closed).
  • Filters: request_type, status (draft/approved/applied/rejected/expired), entity_type, owner_gov_code, age bucket.
  • Sorting: age desc, priority.
  • Traffic-light: 🟢 no pending > SLA, gates all_safe · 🟡 proposals awaiting review · 🔴 stale proposal past SLA OR a gate not safe OR an activation attempted without ≥2 signatures.
  • Drill-down: proposal → target workflow (Surface 2) + its dsl_diff; approval → signers.
  • Actions → DOT: Approve/Reject = human/council only, writes apr_approvals (NOT a DOT the agent can call); Withdraw proposal=dot_mow_design_propose_change (status=withdrawn); View gate=dot_iu_gate_verify_closed.
  • Permission: approve/reject visible only to council; agent sees read-only; backend rejects any agent decision row (Điều 32 + automated-agent CHECK from prior G3 work).
  • States: empty="No pending governance items"; error=banner; never hide a 🔴 item.

B. Cross-cutting contract rules

  1. Backend-only visibility enforcement: every list is a DB view + Directus permission policy; the API filters by role before serialization. The UI requests a page; it cannot enumerate beyond its policy. Action buttons are rendered from a server-provided capability list per row, not hardcoded.
  2. Stale-data control: every payload carries as_of (query time) + per-row date_updated; cockpit shows gate checked_at. UI must re-fetch on action completion (optimistic updates forbidden for governance).
  3. Scale: keyset pagination, server sort/filter, pre-aggregated counts, indexed filter columns (workflows.status, workflow_steps.workflow_id, tasks link cols, approval_requests.status). Hard page cap (e.g. 100) with "refine filters" guard.
  4. No write path outside DOTs: the UI never issues raw SQL/Directus item writes to design columns; every mutation goes through a named DOT (doc 03), which enforces gate/owner/approval.

C. UI data contract verdict

Concrete. 4 surfaces fully specified with source view/function, fields, filters, sort, traffic-light rule, drill-down, action→DOT mapping, permission, backend enforcement, and empty/error/loading states; scale strategy defined for thousands of workflows / 10k+ tasks. No implementation performed.

Back to Knowledge Hub knowledge/dev/reports/architecture/mow-design-registry-phase1-ratify-commit-dot-ui-migration-acceptance-megacampaign-2026-05-29/04-master-ui-data-contract.md