Review follow-up:
- Reject loop files whose frontmatter name exceeds MAX_TASK_NAME_LENGTH
(80): task names are clamped at storage time, so a raw name longer than
the limit could never match the stored task identity. The file is treated
as malformed (definition: null) instead of creating an unreachable
definition; MAX_TASK_NAME_LENGTH is now exported from project-config.js
and shared with loops.js.
- Surface loop-sourced tasks in the scheduled-tasks dialog: tasks carrying
loopFile show a 'Managed by loop file <path>' note, and the enable
toggle / edit / delete actions are disabled with an explanatory tooltip,
since the file remains authoritative and would revert any such change.
run-now stays available. New locale keys added to all 11 message files
(i18n parity test enforces exact key sets).
- ScheduledTask type gains an optional loopFile field (additive, unknown
to older clients).
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.
Adds markdown-based scheduled-task definitions ("loops") discovered from
.agents/loops/*.md (project scope, ancestor directories up to the
worktree root) and ~/.agents/loops/*.md (user scope), mirroring the
skills discovery pattern.
File format: YAML frontmatter (name, schedule cron, enabled, model as
provider/model, optional agent/timezone) plus the markdown body as the
execution prompt. Discovery and parsing live in
scheduled-tasks/loops.js; project-config gains reconcileLoopTasks which
runs inside the project write lock on every syncProject:
- identity by task name; a loop takes over a matching task, preserving
its id and runtime state (markdown wins on conflict with JSON)
- tasks whose loopFile is gone are unscheduled; JSON tasks are never
removed
- new loops are created under deterministic loop:<scope>:<name> ids
- project scope shadows user scope on name collisions
- malformed files are skipped with a warning and never block valid ones
Runtime state stays in the project config/state store; it is never
written to the markdown files. Module documentation updated with the
file format and reconciliation rules.
Fixes#2583