From 54a67c6b626ad25f36f19913a1250ad4406cb21e Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Thu, 26 Mar 2026 13:27:30 -0400 Subject: [PATCH] fix(publishing): surface approval handoff state --- pmoves/n8n/flows/approval_poller.json | 2 +- pmoves/ui/app/dashboard/studio-board/page.tsx | 34 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pmoves/n8n/flows/approval_poller.json b/pmoves/n8n/flows/approval_poller.json index 08b30d3591..dcc2fb44cb 100644 --- a/pmoves/n8n/flows/approval_poller.json +++ b/pmoves/n8n/flows/approval_poller.json @@ -80,7 +80,7 @@ }, { "parameters": { - "functionCode": "const row = item.json || {};\nconst meta = (row.meta && typeof row.meta === 'object') ? row.meta : {};\nconst requestedAt = new Date().toISOString();\nconst publishRequestId = `${row.id}:${requestedAt}`;\nconst payload = {\n studio_board_id: row.id,\n publish_request_id: publishRequestId,\n artifact_uri: row.content_url,\n title: row.title || meta.title || 'Untitled Artifact',\n namespace: row.namespace || undefined,\n description: meta.description || undefined,\n tags: Array.isArray(meta.tags) ? meta.tags : undefined,\n meta: {\n ...meta,\n studio_board_id: row.id,\n publish_request_id: publishRequestId,\n publish_requested_at: requestedAt,\n publish_request_source: 'n8n-approval-poller'\n }\n};\n\nif (!payload.namespace) {\n delete payload.namespace;\n}\nif (!payload.description) {\n delete payload.description;\n}\nif (!payload.tags || payload.tags.length === 0) {\n delete payload.tags;\n}\n\nconst patch = {\n status: 'publishing',\n meta: {\n ...meta,\n studio_board_id: row.id,\n publish_request_id: publishRequestId,\n publish_requested_at: requestedAt,\n publish_request_source: 'n8n-approval-poller'\n }\n};\n\nreturn {\n json: {\n envelope: {\n topic: 'content.publish.approved.v1',\n payload\n },\n rowId: row.id,\n patch\n }\n};" + "functionCode": "const row = item.json || {};\nconst meta = (row.meta && typeof row.meta === 'object') ? row.meta : {};\nconst requestedAt = new Date().toISOString();\nconst publishRequestId = `${row.id}:${requestedAt}`;\nconst publishMeta = {\n ...meta,\n studio_board_id: row.id,\n publish_request_id: publishRequestId,\n publish_requested_at: requestedAt,\n publish_approval_event_sent_at: requestedAt,\n publish_request_source: 'n8n-approval-poller'\n};\nconst payload = {\n studio_board_id: row.id,\n publish_request_id: publishRequestId,\n artifact_uri: row.content_url,\n title: row.title || publishMeta.title || 'Untitled Artifact',\n namespace: row.namespace || undefined,\n description: publishMeta.description || undefined,\n tags: Array.isArray(publishMeta.tags) ? publishMeta.tags : undefined,\n meta: publishMeta\n};\n\nif (!payload.namespace) {\n delete payload.namespace;\n}\nif (!payload.description) {\n delete payload.description;\n}\nif (!payload.tags || payload.tags.length === 0) {\n delete payload.tags;\n}\n\nconst patch = {\n status: 'publishing',\n meta: {\n ...publishMeta,\n publish_failed_at: null,\n publish_failure_reason: null,\n publish_failure_stage: null,\n publish_failure_meta: null\n }\n};\n\nreturn {\n json: {\n envelope: {\n topic: 'content.publish.approved.v1',\n payload\n },\n rowId: row.id,\n patch\n }\n};" }, "id": "prepare_event", "name": "Prepare Publish Envelope", diff --git a/pmoves/ui/app/dashboard/studio-board/page.tsx b/pmoves/ui/app/dashboard/studio-board/page.tsx index d8859ebee6..a254f46ecd 100644 --- a/pmoves/ui/app/dashboard/studio-board/page.tsx +++ b/pmoves/ui/app/dashboard/studio-board/page.tsx @@ -18,10 +18,14 @@ interface StudioBoardRowMeta { rejection_reason?: string | null; reviewed_at?: string | null; reviewed_by?: string | null; + publish_started_at?: string | null; + publish_completed_at?: string | null; publish_event_sent_at?: string | null; publish_requested_at?: string | null; + publish_approval_event_sent_at?: string | null; publish_failed_at?: string | null; publish_failure_reason?: string | null; + publish_failure_stage?: string | null; tags?: string[]; persona?: string; workflow?: string; @@ -172,6 +176,12 @@ export default function StudioBoardDashboardPage() { reviewed_by: reviewer || null, rejection_reason: rejectionReason, publish_state: action === "approve" ? null : meta.publish_state, + publish_requested_at: action === "approve" ? null : meta.publish_requested_at, + publish_approval_event_sent_at: + action === "approve" ? null : meta.publish_approval_event_sent_at, + publish_started_at: action === "approve" ? null : meta.publish_started_at, + publish_completed_at: action === "approve" ? null : meta.publish_completed_at, + publish_event_sent_at: action === "approve" ? null : meta.publish_event_sent_at, publish_failed_at: action === "approve" ? null : meta.publish_failed_at, publish_failure_reason: action === "approve" ? null : meta.publish_failure_reason, publish_failure_stage: action === "approve" ? null : meta.publish_failure_stage, @@ -390,6 +400,18 @@ export default function StudioBoardDashboardPage() { {row.namespace || "—"}
{row.status || "unknown"}
+ {row.status === "publishing" && meta.publish_started_at ? ( +
+ publisher started @ {formatDate(meta.publish_started_at)} +
+ ) : null} + {row.status === "publishing" && + !meta.publish_started_at && + meta.publish_approval_event_sent_at ? ( +
+ approval relayed @ {formatDate(meta.publish_approval_event_sent_at)} +
+ ) : null} {meta.publish_event_sent_at ? (
published @ {formatDate(meta.publish_event_sent_at)} @@ -400,9 +422,19 @@ export default function StudioBoardDashboardPage() { publish requested @ {formatDate(meta.publish_requested_at)}
) : null} + {row.status === "publish_failed" && meta.publish_failed_at ? ( +
+ failed @ {formatDate(meta.publish_failed_at)} +
+ ) : null} {meta.publish_failure_reason ? (
- publish failed: {meta.publish_failure_reason} + publish failure: {meta.publish_failure_reason} +
+ ) : null} + {meta.publish_failure_stage ? ( +
+ failure stage: {meta.publish_failure_stage}
) : null} {meta.rejection_reason ? (