Skip to content

docs(specs): task loops — sequential multi-session batch runs - #1

Draft
miquido-adamk wants to merge 2 commits into
mainfrom
spec/task-loops
Draft

miquido-adamk wants to merge 2 commits into
mainfrom
spec/task-loops

Conversation

@miquido-adamk

Copy link
Copy Markdown
Owner

Source doc: .ai/specs/2026-08-19-task-loops.md

Design only — no implementation in this PR.

What this specifies

A loop: an ordered list of independent work items run as a strict sequence of ordinary cezar tasks, one session/worktree/branch per item, advancing only when the previous item's run reaches a terminal state. The counter lives in persisted project-local state driven by the workspace coordinator, so a restart resumes instead of losing the loop.

The framing that shapes everything else: a scheduled task fires when the clock strikes; a loop fires when the previous run finishes. That barrier — and the set of ways an awaited run can fail to finish — is the whole spec.

Why it is a draft

Four assumptions carry ⚠ NEEDS HUMAN CONFIRMATION; merge is gated on confirming them. See the Open Questions comment below.

The most consequential: on a zero-config install a loop delivers N branches, not N landed changes. reviewGateEnabled is off by default and settleSuccess skips the gate entirely for autonomous runs, so every autonomous run today settles to done with no PR. That is a general defect of autonomous runs, not of loops, so this spec deliberately does not fix it — which makes the delivered feature smaller than the original request. If that is unacceptable, this spec should not ship first.

Blocked on

Not implementable until open-mercato#846 (postponed tasks, Phase 1 of epic open-mercato#771) lands. open-mercato#771 requires "one coordinator, one occurrence model, one ordinary run launch adapter" and forbids "a second scheduler, store, route family, or launch path", so this consumes that foundation rather than cloning the automations module a third time (measured: 39 files, +2872 lines). Every reference to those seams is written conditionally, because none exist in this tree yet.

The foundation spec defers "a source-neutral extraction … when a third caller needs it" — this feature is that caller, so Phase 0 performs the extraction as its own landed, behavior-preserving change.

Scope calls worth reviewing

Three earlier direction choices were reversed after the design review; each is flagged as reversible in the spec:

Corrections found by review, verified in code

  • A stalled monitoring item now pauses the loop instead of advancing past it — advancing left the run alive, which breaks the width-1 invariant, and busySlots() only exempts monitoring up to maxMonitoringSessions().
  • The barrier gained a reconciling floor for three unbounded waits, the worst being that pruneOldRuns deletes runs with no emit at all (store.ts:1177-1180) — an awaited run can vanish in total silence.
  • waiting is not one of the traps: armIdleTimer ends the session and it settles within IDLE_TIMEOUT_MS. The real ones are monitoring, failed + autoResumeAt, and restart-transit through failed.

Evidence

Current-state screenshots captured from the running app (CEZ_DRY_RUN=1) are committed under .ai/specs/assets/task-loops/ and inline in the spec's UI/UX section. They corrected the design: the composer already carries ×1 (parallel variants) and a Start/Plan first control, so Loop is specified as the sequential sibling of ×1 and mutually exclusive with it.

Breaking Changes

None — design only.

@miquido-adamk miquido-adamk added the documentation Improvements or additions to documentation label Aug 19, 2026
@miquido-adamk

Copy link
Copy Markdown
Owner Author

🤖 om-auto-write-spec — Open Questions

The spec was written autonomously. Every question it resolved is listed here for override. Four carry ⚠ NEEDS HUMAN CONFIRMATION, so the PR stays a draft until they are confirmed.

# Question Applied default Why Confirm?
Q1 One deployable capability, or a bundle? One slice: item list + loop object + barrier + UI. Issue-sourced queues, per-item landing and per-item draft PRs all excluded. Each functions without the others; bundling produces an unsplittable spec. Reverses an earlier "issues + list in v1" direction.
Q2 Build on unmerged open-mercato#846, or ship standalone? Build on it; not implementable until open-mercato#846 lands. open-mercato#771 forbids a second scheduler/store/route family/launch path; standalone would clone a 1231-LOC module a third time.
Q3 What does a finished item deliver? A branch, and nothing more. Draft-PR-per-item removed and reassigned to a prerequisite spec covering autonomous runs generally. settleSuccess leaves every autonomous run as done with no PR — a general defect. Fixing it inside loops/ would create two divergent settle behaviors. Consequence: less than the original request asked for.
Q4 What counts as "item finished"? done | review | cancelled | (failed with no pending autoResumeAt). Everything else is a bounded wait with a deadline and a reason string. Forced by the code. ok
Q5 A stalled monitoring item — advance past it? No. Pause the loop and surface it. Advancing while the run stays alive puts two live children in a width-1 loop, and past maxMonitoringSessions() they consume real capacity. Cancelling would destroy possibly-good work. Pausing costs unattended progress — the only option that neither lies nor destroys.
Q6 Per-loop knobs? None. Deadlines are module constants; forge availability is discovered. "When a feature seems to need configuration, the design is wrong." Reverses two earlier per-loop-switch directions, which were chosen against a state machine that is not the one in the code. ok
Q7 Width > 1? No. Strictly sequential, no knob. Sequencing is the point; parallel fan-out is what ×1 variants and automations already do. ok
Q8 Gating CEZ_LOOPS=1, permanently off by default, like CEZ_AUTOMATIONS. No removal phase. A capability spawning N unattended paid sessions is exactly "widens exposure or cost → opt-in, off by default". Removing the flag later would breach BACKWARD_COMPATIBILITY §1. ok
Q9 New provenance field, or generalize? Extract the source-neutral launch adapter as its own landed change first, then add loop? additively. automation? untouched. The foundation spec defers this extraction "when a third caller needs it" — this is that caller. ok
Q10 Confirmation before starting? Required. "Review and start" opens a step stating item count and that nothing is merged. The one consequential, hard-to-reverse action in the feature. Without it the composer's cheapest gesture is its most expensive. Not a preference, so not a setting. ok
Q11 Reuse groupId for parentage? No — and no route change, because loop children carry no groupId, so the variant loser-sweep is already unreachable. Keep one cheap regression test; the real work is hiding Compare for loop parents. groupRuns filters on groupId and the route 404s on an empty set, so a guard would be a no-op. ok

Labels: only documentation was applied. The pipeline set (review, skip-qa, priority-*, risk-*) does not exist on this fork, and labels are never invented.

@miquido-adamk

Copy link
Copy Markdown
Owner Author

🤖 om-auto-write-spec — run summary

Outcome: a draft spec PR. Draft rather than ready because four resolved assumptions carry ⚠ NEEDS HUMAN CONFIRMATION; merge is gated on confirming them.

📝 Spec: .ai/specs/2026-08-19-task-loops.md (324 lines), with the originating brief committed beside it at .ai/specs/briefs/2026-08-19-task-loops.md.

🌿 Branch: spec/task-loops, two commits — the spec, then the evidence and the UI correction it produced.

📸 Evidence: two current-state screenshots captured from the real app under CEZ_DRY_RUN=1 on a production build, committed to .ai/specs/assets/task-loops/ and inlined in the spec's UI/UX section. They earned their place: seeing the actual composer showed it already carries ×1 (parallel variants) and a Start/Plan first control, so Loop is now specified as the sequential sibling of ×1 and mutually exclusive with it, rather than as an invented "composer mode". Proposed-UI mockups were not rendered — the UI is specified down to literal shipping copy (headings, empty states, per-status row text, error strings), which is the artifact an implementer needs.

🏷️ Labels: documentation only. The pipeline labels this run would normally apply — review, skip-qa, one priority and one risk — do not exist in this repository, and label guards forbid creating them. No priority or risk label was set as a result.

⚠️ Two design reviews ran, and both changed the outcome. A convergence-gate challenger caught that the review-gate invariant quoted during shaping is prose the code no longer honors by default, and that the fork's remote has issues disabled while the real backlog lives upstream. A fresh-context spec review then caught two defects in the first draft: the stall policy broke the width-1 invariant it declared one line earlier, and the barrier had three unbounded wait paths — the worst being that pruneOldRuns deletes runs with no emit at all, so an awaited run can vanish silently. Every claim was verified against the source before being acted on; three cited line ranges were off by one or two and have been corrected.

🔁 Hand-off: blocked on open-mercato#846 landing. Once it does, om-auto-implement-spec .ai/specs/2026-08-19-task-loops.md — but Phase 0 (the source-neutral launch-adapter extraction) should ship as its own PR against open-mercato#846's tests first, and the Q3 gap ("an autonomous run delivers a reviewable artifact") deserves its own spec before or alongside Phase 1, or a loop's output is N branches.

@miquido-adamk

Copy link
Copy Markdown
Owner Author

🤖 om-prepare-issue — 📝 implementation tracking issue

The implementation of this spec is now tracked at #2Implement: task loops — drain a list of work items as a sequence of separate sessions.

This PR stays design-only: it adds the spec, the brief and the UI evidence, and nothing else. Implementation ships on its own PR referencing it, per the pipeline's spec/implementation split.

Issue #2 links back to this PR and to .ai/specs/2026-08-19-task-loops.md, and it records both blocking gates so a future implementer cannot start prematurely:

  1. The postponed-tasks foundation (feat(scheduled-tasks): implement one-time postponed tasks (Phase 1 of #771) open-mercato/cezar#846, Phase 1 of epic Implement postponed and recurring scheduled tasks open-mercato/cezar#771) must land first — none of the seams this spec builds on exist in this tree yet.
  2. The four ⚠ NEEDS HUMAN CONFIRMATION assumptions on this PR (Q1, Q2, Q3, Q5) must be confirmed or overruled. Q3 is the one that changes what gets delivered: as specified, a loop produces N branches rather than N landed changes.

The one exception recorded on the issue: Phase 0, the source-neutral launch-adapter extraction, is behavior-preserving and independently deployable, so it may proceed as soon as open-mercato#846 lands — it does not wait on the ⚠ confirmations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant