diff --git a/.changeset/fn-8907-promote-planning.md b/.changeset/fn-8907-promote-planning.md new file mode 100644 index 0000000000..ba90cd49b2 --- /dev/null +++ b/.changeset/fn-8907-promote-planning.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Hide Promote on board cards that are still being planned. +category: fix +dev: Gates the TaskCard Promote render path on existing planning signals. diff --git a/packages/dashboard/app/components/TaskCard.tsx b/packages/dashboard/app/components/TaskCard.tsx index 69dd0aaf37..ad4ffc7e3c 100644 --- a/packages/dashboard/app/components/TaskCard.tsx +++ b/packages/dashboard/app/components/TaskCard.tsx @@ -1585,6 +1585,18 @@ function TaskCardComponent({ two independent conditions disjoint. */ const awaitingPlanning = task.awaitingPlanning ?? ((task.steps?.length ?? 0) === 0); + /* + FNXC:TaskCardPromote 2026-08-09-19:00: + Post-U11, the hold column is also the planning lane, so Promote must not be offered while a card is unplanned, being planned, in Plan Review, or awaiting plan approval. That click is rejected as `unplanned-for-execution` and the force path would start implementation against an incomplete plan. + + `awaitingPlanning` is absent from SSE payloads, so its step-count fallback deliberately matches the Ready / Queued to plan badge pair. `isAwaitingApproval` only applies on an intake-trait merged planning lane or for the `plan-review-replan-cap` reason. + */ + const isStillInPlanning = awaitingPlanning + || task.status === "planning" + || task.status === "needs-replan" + || planReviewRunning + || isAwaitingApproval; + const showPromoteAction = Boolean(onPromote) && !isStillInPlanning; const showIdleTodoBadge = !isPaused && isHoldColumn && !visualStatus @@ -2161,7 +2173,7 @@ function TaskCardComponent({ ); return (next?.id ?? "todo") as ColumnId; }, [taskMoveColumns, task.column]); - const shouldRenderActionRow = Boolean(onPromote) || showCreatePrQuickAction || showAddressPrFeedbackAction || showStartAction; + const shouldRenderActionRow = showPromoteAction || showCreatePrQuickAction || showAddressPrFeedbackAction || showStartAction; const enterEditMode = useCallback((e?: React.MouseEvent) => { e?.stopPropagation(); @@ -3210,7 +3222,7 @@ function TaskCardComponent({ && Boolean(githubTrackedIssue); const footerHasLeadingContent = Boolean(filesChangedButton) || (isGitHubImportedTask && !showLinkedIssueChipForImport); - const costBadgeBelowPromote = Boolean(onPromote && cardCostLabel); + const costBadgeBelowPromote = Boolean(showPromoteAction && cardCostLabel); const costBadgeChip = cardCostLabel ? ( )} - {onPromote && ( + {showPromoteAction && (