Address PR review feedback (#1890)
- Gate bootstrap prompt to entry column/triage only, not every non-execution
column, so direct createTask({column:'todo'}) keeps generateSpecifiedPrompt.
- Guard the workflow-column hold-release dispatch path (reserveSlot) against
planning-status and bootstrap-stub todo tasks, matching the legacy filter.
- Extend clearStaleSpecifyingStatuses startup sweep to the todo column so a
restarted in-place planning task does not hold a maxTriageConcurrent slot.
- Gate the Start button on the intake column flag instead of the literal
'ideas' id, so any manual-intake workflow gets the affordance.
- Add regression test: direct todo create must not get a bootstrap stub.
This commit is contained in:
@@ -48,4 +48,16 @@ describe("createTask intake-column wiring (Coding (Ideas))", () => {
|
||||
);
|
||||
expect(prompt).toBe(`# ${task.id}\n\n${task.description}\n`);
|
||||
});
|
||||
|
||||
it("keeps generateSpecifiedPrompt for a direct create into todo (not bootstrap)", async () => {
|
||||
const store = harness.store();
|
||||
const task = await store.createTask({ description: "direct todo create", column: "todo" });
|
||||
expect(task.column).toBe("todo");
|
||||
const prompt = await readFile(
|
||||
join(harness.rootDir(), ".fusion", "tasks", task.id, "PROMPT.md"),
|
||||
"utf-8",
|
||||
);
|
||||
// A direct todo create is NOT an intake column, so it must NOT get the bootstrap stub.
|
||||
expect(prompt).not.toBe(`# ${task.id}\n\n${task.description}\n`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5060,15 +5060,13 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
||||
if (this.isWatching) this.taskCache.set(id, { ...task });
|
||||
|
||||
/*
|
||||
FNXC:CodingIdeasWorkflow 2026-07-04-10:10:
|
||||
A freshly created task has no specification yet regardless of which intake/planning column it lands in (triage, ideas, or a merged todo). Use the bootstrap stub for every pre-execution column so the spec-detection helpers (isBootstrapPromptStub) treat the card as unplanned until triage replaces it with a real PROMPT.md. Execution/review/done/archived columns keep the generated specified prompt for the legacy direct-create paths.
|
||||
FNXC:CodingIdeasWorkflow 2026-07-04-10:10 (revised):
|
||||
A freshly created task needs the bootstrap stub only when it lands in a column the triage service will plan from — the legacy "triage" intake or a workflow's resolved manual intake (e.g. Coding (Ideas) → "ideas"). Gate on those two ids so legacy direct-create callers that pass an explicit column like "todo" still get the generated specified prompt. A task whose column is neither the entry column nor "triage" (custom hold/backlog columns, direct todo creates) keeps generateSpecifiedPrompt.
|
||||
*/
|
||||
const isPrePlanningColumn = task.column !== "in-progress"
|
||||
&& task.column !== "in-review"
|
||||
&& task.column !== "done"
|
||||
&& task.column !== "archived";
|
||||
const isIntakeColumn = task.column === "triage"
|
||||
|| (options?.resolvedEntryColumn !== undefined && task.column === options.resolvedEntryColumn);
|
||||
const prompt = options?.promptOverride
|
||||
?? (isPrePlanningColumn
|
||||
?? (isIntakeColumn
|
||||
? buildBootstrapPrompt(id, task.title, task.description)
|
||||
: this.generateSpecifiedPrompt(task));
|
||||
const validation = validateFileScopeInPromptContent(prompt);
|
||||
|
||||
@@ -1407,7 +1407,7 @@ function TaskCardComponent({
|
||||
|| Boolean(task.blockedBy)
|
||||
|| Boolean(task.overlapBlockedBy)
|
||||
|| Boolean(fanout && fanout.totalCount > 0);
|
||||
const showStartAction = task.column === "ideas" && Boolean(onMoveTask);
|
||||
const showStartAction = taskColumnFlags?.intake === true && task.column !== "triage" && Boolean(onMoveTask);
|
||||
const shouldRenderActionRow = Boolean(onPromote) || showCreatePrQuickAction || showAddressPrFeedbackAction || showStartAction || (showInReviewMoveControl && !metaRowVisible);
|
||||
|
||||
const renderInReviewMoveControl = () => (
|
||||
|
||||
@@ -2252,6 +2252,24 @@ export class Scheduler {
|
||||
reserveSlot: async (task): Promise<SlotReservation | null> => {
|
||||
let reservedScope = false;
|
||||
|
||||
/*
|
||||
FNXC:CodingIdeasWorkflow 2026-07-04-12:10:
|
||||
The workflow-column dispatch path is the only dispatcher when the flag is on, so the planning/bootstrap guards from the legacy todo filter must also apply here. A todo task being specified in place (status "planning") or still carrying the bootstrap stub PROMPT.md must not be released into an execution slot.
|
||||
*/
|
||||
if (task.status === "planning") {
|
||||
return null;
|
||||
}
|
||||
if (task.column === "todo") {
|
||||
try {
|
||||
const promptContent = await readFile(getPromptPath(this.store.getTasksDir(), task.id), "utf-8");
|
||||
if (promptContent === buildBootstrapPrompt(task.id, task.title, task.description)) {
|
||||
return null;
|
||||
}
|
||||
} catch {
|
||||
// Missing prompt handled by filesystem validation below.
|
||||
}
|
||||
}
|
||||
|
||||
const unmetDeps = getUnmetSchedulingDependencies(task, tasks, schedulingDependencyOptions);
|
||||
if (unmetDeps.length > 0) {
|
||||
await this.store.updateTask(task.id, {
|
||||
|
||||
@@ -336,8 +336,13 @@ export class TriageProcessor {
|
||||
}
|
||||
|
||||
private async clearStaleSpecifyingStatuses(): Promise<void> {
|
||||
const tasks = await this.store.listTasks({ column: "triage", slim: true });
|
||||
const stale = tasks.filter(
|
||||
/*
|
||||
FNXC:CodingIdeasWorkflow 2026-07-04-12:00:
|
||||
In the merged planner/capacity "todo" column a task can carry status "planning" when the triage service is specifying it in place. A crash/restart before planning completes leaves that status set, so the startup sweep must clear it from BOTH triage and todo — otherwise a stale planning todo task permanently occupies a maxTriageConcurrent slot and blocks new triage work.
|
||||
*/
|
||||
const triageTasks = await this.store.listTasks({ column: "triage", slim: true });
|
||||
const todoTasks = await this.store.listTasks({ column: "todo", slim: true });
|
||||
const stale = [...triageTasks, ...todoTasks].filter(
|
||||
(t) => t.status === "planning" && !this.processing.has(t.id),
|
||||
);
|
||||
for (const t of stale) {
|
||||
|
||||
Reference in New Issue
Block a user