fix(core): refine + duplicate create into the resolved intake lane, not the deleted triage column
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
7
.changeset/refine-duplicate-intake-lane.md
Normal file
7
.changeset/refine-duplicate-intake-lane.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Refined and duplicated tasks land in the workflow's Planning column instead of a deleted legacy column.
|
||||||
|
category: fix
|
||||||
|
dev: task_refine (update-task-deps.ts) and task_duplicate (project-store-ops.ts) hardcoded column "triage"; both now resolve resolveWorkflowIntakeFacts().intake with the literal as last resort. Symptom: amber PLANNING badge (badge color keys off raw column id) on cards in an undeclared column.
|
||||||
@@ -43,7 +43,9 @@ pgDescribe("refineTask / duplicateTask backend mode (PostgreSQL)", () => {
|
|||||||
expect(refined.id).not.toBe(source.id);
|
expect(refined.id).not.toBe(source.id);
|
||||||
expect(refined.sourceType).toBe("task_refine");
|
expect(refined.sourceType).toBe("task_refine");
|
||||||
expect(refined.sourceParentTaskId).toBe(source.id);
|
expect(refined.sourceParentTaskId).toBe(source.id);
|
||||||
expect(refined.column).toBe("triage");
|
// FNXC:MergedPlanningColumn 2026-07-31-22:40: refine resolves the workflow intake lane; the
|
||||||
|
// default coding workflow's intake is the merged `todo` Planning column ("triage" is deleted).
|
||||||
|
expect(refined.column).toBe("todo");
|
||||||
expect(refined.dependencies).toEqual([source.id]);
|
expect(refined.dependencies).toEqual([source.id]);
|
||||||
expect(refined.description).toContain("Please tighten the empty-state copy");
|
expect(refined.description).toContain("Please tighten the empty-state copy");
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
import {TaskStore, storeLog, WORKFLOW_COMPILED_STEP_TEMPLATE_PREFIX, WORKFLOW_MOVE_POLICY_TIMEOUT_MS} from "../store.js";
|
import {TaskStore, storeLog, WORKFLOW_COMPILED_STEP_TEMPLATE_PREFIX, WORKFLOW_MOVE_POLICY_TIMEOUT_MS} from "../store.js";
|
||||||
import { resolveCapacityPoolId } from "../workflow-capacity.js";
|
import { resolveCapacityPoolId } from "../workflow-capacity.js";
|
||||||
|
import {resolveWorkflowIntakeFacts} from "./task-creation.js";
|
||||||
import {TransitionRejectionError} from "./errors.js";
|
import {TransitionRejectionError} from "./errors.js";
|
||||||
import * as schema from "../postgres/schema/index.js";
|
import * as schema from "../postgres/schema/index.js";
|
||||||
import {and, eq, isNull, ne, or, sql} from "drizzle-orm";
|
import {and, eq, isNull, ne, or, sql} from "drizzle-orm";
|
||||||
@@ -208,7 +209,12 @@ export async function duplicateTaskImpl(store: TaskStore, id: string): Promise<T
|
|||||||
title: normalizedTitle.title ?? undefined,
|
title: normalizedTitle.title ?? undefined,
|
||||||
description: `${sourceTask.description}\n\n(Duplicated from ${id})`,
|
description: `${sourceTask.description}\n\n(Duplicated from ${id})`,
|
||||||
priority: normalizeTaskPriority(sourceTask.priority),
|
priority: normalizeTaskPriority(sourceTask.priority),
|
||||||
column: "triage",
|
/*
|
||||||
|
FNXC:MergedPlanningColumn 2026-07-31-22:35 (missed creation surface — duplicate):
|
||||||
|
Same fix as refine: resolve the default workflow's intake lane instead of the legacy
|
||||||
|
`"triage"` literal, which the merged coding workflow no longer declares.
|
||||||
|
*/
|
||||||
|
column: ((await resolveWorkflowIntakeFacts(store)).intake ?? "triage") as Task["column"],
|
||||||
modelPresetId: sourceTask.modelPresetId,
|
modelPresetId: sourceTask.modelPresetId,
|
||||||
sourceType: "task_duplicate",
|
sourceType: "task_duplicate",
|
||||||
sourceParentTaskId: id,
|
sourceParentTaskId: id,
|
||||||
|
|||||||
@@ -100,7 +100,17 @@ directly and survives any rename or merge.
|
|||||||
Unresolvable workflow returns `{ manual: false }` with no intake, so callers keep their existing
|
Unresolvable workflow returns `{ manual: false }` with no intake, so callers keep their existing
|
||||||
conservative behavior rather than acting on a guess.
|
conservative behavior rather than acting on a guess.
|
||||||
*/
|
*/
|
||||||
async function resolveWorkflowIntakeFacts(
|
/*
|
||||||
|
FNXC:MergedPlanningColumn 2026-07-31-22:30 (missed creation surfaces):
|
||||||
|
Exported so refine (`update-task-deps.ts`) and duplicate (`project-store-ops.ts`) resolve the same
|
||||||
|
intake lane as the main create. Both built Task rows directly inside `createTaskWithId` callbacks
|
||||||
|
with a hardcoded `column: "triage"` — a column the merged coding workflow no longer declares — so a
|
||||||
|
refined/duplicated card landed in an UNDECLARED column: rendered with the legacy amber badge,
|
||||||
|
invisible to trait-driven sweeps until the undeclared-column re-home rescued it. #2589/#2603 fixed
|
||||||
|
the main create; these two surfaces were the enumeration gap (found via a two-tone Planning badge on
|
||||||
|
the live board).
|
||||||
|
*/
|
||||||
|
export async function resolveWorkflowIntakeFacts(
|
||||||
store: TaskStore,
|
store: TaskStore,
|
||||||
workflowIdOverride?: string,
|
workflowIdOverride?: string,
|
||||||
): Promise<{ intake?: string; hold?: string; manual: boolean }> {
|
): Promise<{ intake?: string; hold?: string; manual: boolean }> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {resolveWorkflowIrForTask} from "../workflow-ir-resolver.js";
|
|||||||
import {buildRefinementSeedPrompt} from "../mesh-task-replication.js";
|
import {buildRefinementSeedPrompt} from "../mesh-task-replication.js";
|
||||||
import {SelfDefeatingDependencyError, detectSelfDefeatingDependency} from "./errors.js";
|
import {SelfDefeatingDependencyError, detectSelfDefeatingDependency} from "./errors.js";
|
||||||
import {resolveTaskLifecycleColumns} from "../workflow-lifecycle-traits.js";
|
import {resolveTaskLifecycleColumns} from "../workflow-lifecycle-traits.js";
|
||||||
|
import {resolveWorkflowIntakeFacts} from "./task-creation.js";
|
||||||
import type {WorkflowIr} from "../workflow-ir-types.js";
|
import type {WorkflowIr} from "../workflow-ir-types.js";
|
||||||
import {mkdir, readFile, writeFile} from "node:fs/promises";
|
import {mkdir, readFile, writeFile} from "node:fs/promises";
|
||||||
import {join} from "node:path";
|
import {join} from "node:path";
|
||||||
@@ -111,6 +112,15 @@ export async function refineTaskImpl(store: TaskStore, id: string, feedback: str
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:MergedPlanningColumn 2026-07-31-22:35 (missed creation surface — refine):
|
||||||
|
Resolve the inherited workflow's intake lane instead of the legacy `"triage"` literal. The
|
||||||
|
hardcoded id landed refinements in a column the merged coding workflow no longer declares —
|
||||||
|
surfaced on the live board as an amber PLANNING badge (badge color keys off the raw column id)
|
||||||
|
on a card invisible to trait-driven sweeps until the undeclared-column re-home. Literal survives
|
||||||
|
only as the last resort for a store that cannot resolve any workflow, matching createTask.
|
||||||
|
*/
|
||||||
|
const refineIntakeColumn = (await resolveWorkflowIntakeFacts(store, pendingWorkflowSelection?.workflowId)).intake ?? "triage";
|
||||||
const newTask = await store.createTaskWithDistributedReservation({ description: feedback.trim() }, {
|
const newTask = await store.createTaskWithDistributedReservation({ description: feedback.trim() }, {
|
||||||
createTaskWithId: async (newId) => {
|
createTaskWithId: async (newId) => {
|
||||||
// FN-5077: keep deterministic "Refinement" fallback when normalized refinement label is unusable (null).
|
// FN-5077: keep deterministic "Refinement" fallback when normalized refinement label is unusable (null).
|
||||||
@@ -138,7 +148,7 @@ export async function refineTaskImpl(store: TaskStore, id: string, feedback: str
|
|||||||
title: normalizedTitle.title ?? "Refinement",
|
title: normalizedTitle.title ?? "Refinement",
|
||||||
description: `${feedback.trim()}\n\nRefines: ${id}`,
|
description: `${feedback.trim()}\n\nRefines: ${id}`,
|
||||||
priority: normalizeTaskPriority(sourceTask.priority),
|
priority: normalizeTaskPriority(sourceTask.priority),
|
||||||
column: "triage",
|
column: refineIntakeColumn as Task["column"],
|
||||||
dependencies: [id],
|
dependencies: [id],
|
||||||
sourceType: "task_refine",
|
sourceType: "task_refine",
|
||||||
sourceParentTaskId: id,
|
sourceParentTaskId: id,
|
||||||
|
|||||||
Reference in New Issue
Block a user