fix(FN-7039): render workflow steps with proper casing in the progress UI

An enabled-but-not-yet-run workflow step has no recorded result, so the progress bar
previously showed the raw graph node id (e.g. 'code-review'). Humanize the id fallback to
Title Case ('Code Review', 'Browser Verification', 'Frontend UX Design'). Once the step
runs, the graph-recorded config.name still wins; humanization is only the pre-run fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-26 08:53:26 -07:00
parent 3ee47dec86
commit fca2ff6efb
2 changed files with 37 additions and 3 deletions

View File

@@ -5,7 +5,7 @@ import { getUnifiedTaskProgress } from "../taskProgress";
/*
FNXC:WorkflowSteps 2026-06-25-00:00 — graph-native progress model (plan U3).
These tests pin the render-state contract that the progress bar / Workflow tab rely on:
- names resolve from result.workflowStepName (no DB-row name lookup), with a raw-id fallback;
- names resolve from result.workflowStepName (no DB-row name lookup), with a humanized node-id fallback;
- a "pending" result with a startedAt and no completedAt is the `running` state, vs bare `pending`;
- advisory_failure (non-blocking) is distinct from failed (blocking) and counts as completed;
- disabled optional steps (absent from enabledWorkflowSteps) never appear in the counter/bar.
@@ -49,11 +49,25 @@ describe("getUnifiedTaskProgress", () => {
);
const item = progress.items.find((i) => i.id === "workflow-code-review");
expect(item?.name).toBe("code-review");
// Enabled-but-not-run has no recorded name → humanize the node id to proper casing,
// never render the raw lowercase id.
expect(item?.name).toBe("Code Review");
// Enabled but never run → pending.
expect(item?.status).toBe("pending");
});
it("humanizes the node id to proper casing for an enabled-but-not-run step", () => {
const progress = getUnifiedTaskProgress(
makeTask({
enabledWorkflowSteps: ["browser-verification", "frontend-ux-design", "code-review"],
workflowStepResults: [],
}),
);
expect(progress.items.find((i) => i.id === "workflow-browser-verification")?.name).toBe("Browser Verification");
expect(progress.items.find((i) => i.id === "workflow-frontend-ux-design")?.name).toBe("Frontend UX Design");
expect(progress.items.find((i) => i.id === "workflow-code-review")?.name).toBe("Code Review");
});
it("distinguishes running (started, not completed) from pending (not started)", () => {
const progress = getUnifiedTaskProgress(
makeTask({

View File

@@ -59,12 +59,32 @@ function isCompleted(status: UnifiedTaskProgressStatus): boolean {
return status === "done" || status === "skipped" || status === "advisory_failure";
}
/*
FNXC:WorkflowStepResults 2026-06-26-16:30:
An enabled-but-not-yet-run workflow step has no recorded result yet, so there is no
`workflowStepName` to show. Rather than render the raw graph node id (e.g. `code-review`,
`browser-verification`), humanize it into a Title Case label ("Code Review",
"Browser Verification"). Once the graph records the step it carries the workflow's exact
`config.name`, which always wins; humanization is only the pre-run fallback. The UI must
show proper casing for workflow steps (e.g. "Code Review"), never the lowercase hyphenated id.
*/
function humanizeWorkflowStepId(workflowStepId: string): string {
const words = workflowStepId
.replace(/^plugin:/, "")
.split(/[-_:\s]+/)
.filter(Boolean);
if (words.length === 0) return workflowStepId;
return words
.map((w) => (/^(ux|ui|qa|ai|api|pr|id)$/i.test(w) ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1)))
.join(" ");
}
function resolveWorkflowStepName(workflowStepId: string, result: WorkflowStepResult | undefined): string {
const resultName = result?.workflowStepName?.trim();
if (resultName) {
return resultName;
}
return workflowStepId;
return humanizeWorkflowStepId(workflowStepId);
}
export function getUnifiedTaskProgress(