From 819288d36c9834877a26492d20ff612cb442e243 Mon Sep 17 00:00:00 2001 From: DJP Date: Fri, 15 May 2026 19:29:41 -0400 Subject: [PATCH] Auto-advance fix + NONE stages = Open/Close only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The earlier auto-advance commit (f0eb29d) didn't actually fire on dynamic pipelines because the dependency engine was still reading template.dependsOn — the legacy seed graph, almost always empty for pipelines built in the new editor. Producers saw downstream stages stay BLOCKED forever no matter how many terminal transitions fired. dependency-engine.ts: - Resolves prerequisites against three sources in priority order: 1. stageDefinition.dependsOn (V2 dynamic edges, by stageDefinitionId) 2. template.dependsOn (legacy seed graph) 3. order fallback (depend on the immediately previous-order stage) - getStageIdsToUnblock now also considers NOT_STARTED stages, not just BLOCKED ones — when a gate completes, every waiting stage flips to IN_PROGRESS. stage-service.ts: - All four allStages.map blocks now include `order` + a normalised `stageDefinition` so the engine sees the dynamic graph. - Stage fetches include stageDefinition.dependsOn. - Bulk update path: unblock target is IN_PROGRESS now (was NOT_STARTED) to match the single-update behaviour. deliverable detail page: - NONE-approval stages use a minimal transition set: NOT_STARTED → IN_PROGRESS → APPROVED → IN_PROGRESS (reopen). No more Submit-for-Review / Approve / Mark-Delivered / Request- Changes / Skip noise on stages that have no review concept. - The APPROVED button on a NONE stage reads "Mark Complete" so the affordance signals workflow close, not a review approval. Net effect on a typical 5-stage pipeline: Click Start Work on stage 1 → Mark Complete → stage 2 auto-opens → Mark Complete → stage 3 auto-opens, and so on. One terminal click per stage. FORMAL/SIMPLE stages still go through the StageReviewPanel for the review flow. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../deliverables/[deliverableId]/page.tsx | 37 ++++- src/lib/pipeline/dependency-engine.ts | 136 +++++++++++------- src/lib/services/stage-service.ts | 56 ++++++-- 3 files changed, 168 insertions(+), 61 deletions(-) diff --git a/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/page.tsx b/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/page.tsx index bf4896a..6b71a74 100644 --- a/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/page.tsx +++ b/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/page.tsx @@ -351,7 +351,34 @@ export default function DeliverableDetailPage() {
{stages.map((stage: any) => { - const available = TRANSITIONS[stage.status] ?? []; + // Per-stage approval type drives both the inline transition + // set AND whether the StageReviewPanel below the row owns + // the action buttons. NONE = open/close only (Start Work, + // Mark Complete, Reopen — nothing else). SIMPLE/FORMAL hand + // off to the review panel. + const stageApprovalType = + stage.stageDefinition?.approvalType ?? + stage.template?.approvalType ?? + "NONE"; + const isNoneApproval = stageApprovalType === "NONE"; + + // For NONE stages, override the transition set with a minimal + // open/close shape so producers never see Submit-for-Review, + // Approve, Request-Changes, Mark-Delivered on a non-review row. + const NONE_TRANSITIONS: Record = { + BLOCKED: [], + NOT_STARTED: ["IN_PROGRESS"], + IN_PROGRESS: ["APPROVED"], + IN_REVIEW: ["APPROVED"], + CHANGES_REQUESTED: ["IN_PROGRESS"], + APPROVED: ["IN_PROGRESS"], + DELIVERED: ["IN_PROGRESS"], + SKIPPED: ["NOT_STARTED"], + }; + const available = isNoneApproval + ? NONE_TRANSITIONS[stage.status] ?? [] + : TRANSITIONS[stage.status] ?? []; + const assignments = stage.assignments ?? []; const isGate = stage.stageDefinition?.isCriticalGate ?? stage.template.isCriticalGate; const isOptional = stage.stageDefinition?.isOptional ?? stage.template.isOptional; @@ -474,9 +501,15 @@ export default function DeliverableDetailPage() { stage.status === "DELIVERED")) || (nextStatus === "NOT_STARTED" && stage.status === "SKIPPED"); + // NONE-approval stages don't have the review + // concept — relabel APPROVED to "Mark Complete" + // so the affordance reads as a workflow close, + // not a review approval. const label = isReopen ? "Reopen" - : (TRANSITION_LABELS[nextStatus] ?? nextStatus); + : isNoneApproval && nextStatus === "APPROVED" + ? "Mark Complete" + : (TRANSITION_LABELS[nextStatus] ?? nextStatus); return (