fix(tasks): harden loop reconciliation against renames and malformed files
Review fixes for the markdown loop feature: - Loop-owned tasks now adopt by loop file path, not task name, so renaming a loop (frontmatter name or UI rename) renames the task in place instead of leaving a stale duplicate that keeps running the old definition; orphan duplicates of the same file are unscheduled. - Unparseable loop files are reported to the scheduler as definition:null entries: a task whose file still exists is kept with its last good definition, and only a genuinely removed file unschedules it. Transiently malformed files (mid-edit, bad merge) no longer delete tasks or their runtime state. - Adoption preserves UI-only execution fields (goalEnabled, goalTokenBudget, permissionAutoAccept, variant) that the portable format does not define. - DELETE on a loop-sourced task now returns 400 with guidance to remove the loop file, instead of being silently undone by the next reconcile. - Loops default to enabled: false; discovery of repository content never auto-executes scheduled sessions unless the file explicitly enables them. Regression tests for each fix; DOCUMENTATION.md updated.
This commit is contained in:
@@ -78,6 +78,17 @@ export const createScheduledTaskService = (dependencies) => {
|
||||
await findProjectByID(projectID);
|
||||
const normalizedTaskID = asNonEmptyString(taskID);
|
||||
if (!normalizedTaskID) throw new OpenChamberControlError('taskId is required', 400);
|
||||
const current = await projectConfigRuntime.listScheduledTasks(projectID);
|
||||
const existing = current.find((task) => task.id === normalizedTaskID) || null;
|
||||
if (existing?.loopFile) {
|
||||
// Loop tasks are owned by their `.agents/loops` markdown file: deleting
|
||||
// the JSON row would be silently undone by the next reconcile. The file
|
||||
// itself is the removal surface.
|
||||
throw new OpenChamberControlError(
|
||||
'Loop task is managed by its .agents/loops markdown file; delete the file to remove the task',
|
||||
400,
|
||||
);
|
||||
}
|
||||
const result = await projectConfigRuntime.deleteScheduledTask(projectID, normalizedTaskID);
|
||||
if (!result.deleted) throw new OpenChamberControlError('Task not found', 404);
|
||||
await scheduledTasksRuntime.syncProject(projectID);
|
||||
|
||||
Reference in New Issue
Block a user