KB-47E8

Branch C — Node / Card Data Contract

7 min read Revision 1
mowui-handoffdata-contractcanvas-nodenode-cardbackend-mappingdieu-282026-05-29

Branch C — Node / Card Data Contract

The canonical shape of a single canvas node-card, every field mapped to a backend view/function. Reconciles the v1 CanvasNode object with the MOWD Phase 1 read views (v_mow_design_workflow, v_mow_design_step) and the additive EXTEND columns. Contract only — no implementation.


1. CanvasNode (one card)

{
  "node_id":          "uuid|int",          // identity of the node at its tier
  "tier":             "T1|T2|T3|T4|T5|T6",
  "code":             "string",            // "WF-001", "NV01", "T03", "PB002"
  "title":            "string",
  "subtitle":         "string|null",       // owner name / category / specialty
  "parent_id":        "uuid|int|null",
  "order_index":      "integer",
  "child_count":      "integer",
  "child_preview":    [ {"code","title","status","traffic_light"} ],  // max 3
  "status":           "draft|active|deprecated|retired",
  "traffic_light":    "green|yellow|red|gray",        // rolled up (4-state for nav)
  "automation_level": "full_auto|agent_in_loop|human_in_loop|manual|null",
  "owner_gov_code":   "string|null",       // GOV-MOW etc.
  "active_version":   "integer|null",      // active_design_version
  "freeze_status":    { "active": "bool", "reason": "string|null" },
  "health_summary":   { /* design_health jsonb: owner_set, version_set, bound_pct, dangling[] */ },
  "iu_ref":           "uuid|null",         // design_iu_ref (wf) or step_iu_ref (step)
  "workflow_ref":     "uuid|int|null",
  "task_ref":         "uuid|int|null",
  "dot_refs":         [ "command_name" ],
  "event_refs":       [ {"domain","type"} ],
  "proposal_count":   "integer",           // open workflow_change_requests for this node
  "permission_actions":[ "view","propose","admin" ],
  "last_updated_at":  "timestamptz",
  "instance_data":    null                  // populated only in runtime/instance mode (Phase 2)
}

2. CanvasContext (the frame state, not a card)

{
  "active_tier":   "T3",
  "breadcrumb":    [ {"tier","id","code","title"} ],
  "current_node_id":"uuid|int|null",
  "view_mode":     "template|instance",
  "proposal_mode": false,
  "instance_id":   "uuid|null",
  "as_of":         "timestamptz"            // query time, for stale-data control
}

3. instance_data (Phase 2 runtime mode — same card, extra block)

"instance_data": {
  "instance_id":      "uuid",
  "assignee_avatar":  "url|null",
  "assignee_name":    "string|null",
  "sla_due_at":       "timestamptz|null",
  "sla_traffic_light":"green|yellow|red",
  "progress_ratio":   0.0,                  // 0.0 → 1.0
  "state":            "not_started|ready|in_progress|waiting|blocked|overdue|failed|cannot_complete|completed|paused|cancelled"
}

The card's traffic_light is 4-state for navigation rollup; instance_data.state is the 9-state floor + 2 derived (doc 04 / state machine). The UI maps the 9+2 states onto the 6-color chip set (doc 07 token table). These two scales are deliberately distinct: nav rollup answers "is this subtree healthy?", instance state answers "what is this one run doing right now?".

4. Field → backend mapping

Field Design-side source Notes
node_id workflows.id / workflow_steps.id / tbl_*.id uuid for IU/tbl, int for legacy workflows/steps
tier derived from source table / workflows.level T2=workflow, T1=step/task
code workflows.process_code / workflow_steps.step_key / tbl_*.code
title *.title
subtitle v_mow_design_workflow.owner_name / category from governance_registry join
parent_id workflows.parent_workflow_id / workflow_steps.workflow_id / tbl parent
order_index workflow_steps.sort_order
child_count pre-aggregated step_count pre-agg join, not correlated subquery at scale
child_preview top-3 children by order server-truncated
status workflows.status active/draft/retired/deprecated
traffic_light design_health rollup / fn_workflow_rollup_compute R1–R6 rollup rules
automation_level derived from step actor_type mix full_auto if all system/agent; human_in_loop if any human checkpoint
owner_gov_code workflows.owner_gov_code (+8 EXTEND) FK → governance_registry.code
active_version workflows.active_design_version (+8)
freeze_status workflows.freeze_active/freeze_reason (+8)
health_summary workflows.design_health jsonb (+8) owner_set, version_set, bound_pct, dangling refs
iu_ref workflows.design_iu_ref / workflow_steps.step_iu_ref (+8/+7) uuid → information_unit
workflow_ref workflows.id
task_ref tasks.id via workflow_steps.task_id read-only Phase 1
dot_refs workflow_steps.dot_ref (+7) dot_iu_command_catalog.command_name
event_refs workflow_steps.event_domain_ref/event_type_ref (+7) composite → event_type_registry
proposal_count count(workflow_change_requests WHERE status IN(draft,submitted,review)) per node
permission_actions server-computed per role (Điều 37 predicate) never hardcoded
last_updated_at *.date_updated

5. Contract rules (carried from Phase 1 doc 04 §B)

  1. Backend-only visibility: every list is a view + Directus policy + per-tier Điều-37 predicate; the API filters by role before serialization. Action chips are rendered from permission_actions, not hardcoded.
  2. Stale-data control: every payload carries as_of + per-row last_updated_at; UI re-fetches 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 + refine guard.
  4. No write path outside DOTs: the UI never issues raw SQL or Directus item writes to design columns; every mutation goes through a named DOT (doc 05) that enforces gate/owner/approval.
  5. IU bodies via ref: iu_ref is a uuid pointer; the card renders the body through render_iu_body / dot_iu_reconstruct_source, never inline-stored prose.

6. Data contract verdict

Concrete + backend-mapped. Every one of the 22 contract fields (plus instance_data) maps to a named view/column/function on the existing+EXTEND substrate. The two traffic-light scales are reconciled. CanvasContext carries the frame state and stale-data control. No implementation performed; this is the single contract both the Design brief (doc 07) and the Code brief (doc 08) consume.

Back to Knowledge Hub knowledge/dev/reports/architecture/mow-unified-canvas-master-ui-handoff-pack-2026-05-29/03-node-card-data-contract.md