Nightly automation
What opens the automation window, what it dispatches while you sleep, what a normal night looks like, and the order to check things in when it produced nothing.
The pipeline described on The pipeline can run without you. During a configured window, the app captions what is uncaptioned and feeds finished captions to ComfyUI, so the morning's job is sorting output rather than starting it.
This page is about that unattended half: what starts it, what it will and will not do on its own, and how to work backwards when you wake up to nothing.
What opens the window
Nothing inside the app is on a timer. The window is opened from outside, by a repeating bearer-authed request to POST /api/internal/schedule/window-start from a caller that lives outside this repo. Each time that request lands, one orchestrator runs and asks two questions in order:
- Is the Automation schedule master switch on? If not, it returns immediately, having dispatched nothing.
- Is one of the configured schedule windows active right now, evaluated in America/Chicago? If not, same — it returns having done nothing.
Only when both answers are yes does a window exist. That is why the timezone matters more than it looks: the windows are Chicago-local regardless of where your browser thinks it is, and a window written against the wrong hours simply never matches.
Two housekeeping sweeps run on every trigger ahead of both of those checks, so they happen even when automation is switched off entirely: generation jobs wedged in captioning by an executor-run caption job that never bridged back get released, and run notifications whose window has closed get finalised. Those are the reason a job can quietly un-wedge itself overnight with no window in sight.
What a window dispatches
The first trigger to land inside a window does the heavy work. It samples recent completed jobs to estimate how long a caption and a render each take, sizes both sides against the time remaining, records that plan, and then dispatches the caption side and the generation side in parallel. Neither side failing stops the other.
The caption sweep happens once, at window start. It dispatches its whole planned batch of caption jobs onto the queue in one go, and the caption executor on the home host then drains them across the rest of the night — pulling ten messages at a time and working five of them in parallel, then pulling again once that batch has settled. Those two numbers, Queue pull batch size and Queue processing concurrency on Settings → Captioning, are what set the pace of the night; the window itself only decides how much goes onto the queue. Later triggers in the same window do not sweep again.
Generation is paced completely differently, and this is the part that surprises people. Every later trigger inside the window re-enters and tops up, but the seeder dispatches at most one job per trigger, and only onto a ComfyUI that is completely idle. If anything is queued or running it stops there and waits for the next tick; it also flushes VRAM before handing over the job it does dispatch. That is deliberate — ComfyUI runs prompts one at a time but does not free VRAM between them, so firing a planned batch all at once packs the card and the back half of the batch dies out of memory.
The practical consequence: the planned generation count is a ceiling, not a forecast. What a night actually renders is however many jobs fit through the GPU one at a time, and it will normally come in under the plan.
If the captioned pool is too small to fill the window, the window also refills it — seeding random captioned clips per platform so the generator has something to work from. That is checked at window start and again on later ticks when the pool has nearly run dry, so a window that drains its pool mid-night does not just go idle.
A window never publishes anything, and never reviews anything. Captioning and generation are the only two things it dispatches. Publishing to CivitAI is always a button you press, on the Completed tab of Jobs.
The controls that govern it
All of them live on Settings → Automation, and that page has no autosave — leaving without pressing Save discards the edit. Settings covers each field; what matters here is which ones change the shape of a night.
- Automation schedule — the master switch, and a genuine gate. Off means no window opens at all.
- Pause this window — not a setting, but a button on each open row under Recent windows and on that window's detail page. It stops the window dispatching anything new; whatever is already queued or rendering finishes normally, and nothing is cancelled. It is scoped to that one window, so tonight's pause expires on its own — the next window starts unpaused, and the master switch above is left alone. Press Resume on the same row to pick the night back up while the window is still open.
- Caption sweep — off zeroes the caption side of every window. Generation still runs, on whatever is already captioned.
- ComfyUI generation — off zeroes the generation side of every window. Captioning still runs.
- Schedules — the windows themselves, days plus hours, Chicago time. Overlapping windows are rejected on save rather than silently resolved.
- Buffer minutes, Percentile, and Sample size — how conservatively each side is sized. Buffer comes off the window length before anything else is computed.
- Caption concurrency — the direct lever on how many captions a window plans for. Raising it raises the planned batch proportionally, bounded by what LM Studio can actually keep up with.
- Max generation batch — despite the name, it caps both sides. It is a hard ceiling on the generation plan, and it is also the limit the caption-candidate probe is run with, so it caps how many captions a window plans too: set it to 5 and the window plans at most five captions as well as five generations.
0means unlimited, which is the default, so it does not bite until you set it. - OOM risk threshold — skips generation candidates whose width × height × duration exceeds it, before they ever reach the GPU. A quiet generation night with a full captioned pool can be this filter doing its job.
- Refill when queue is empty and Refill count per platform — the pool top-up described above.
Caption sweep and ComfyUI generation switch off one side of the night each, and they do it the same way: the estimator plans zero for that side and nothing is dispatched, but the window still opens and still records a row. So a night with either one off is not a missing window — it is a window whose plan reads 0 with a reason of disabled on that side. That distinction matters when you are working the checklist below, because "no row at all" and "a row that planned nothing" have completely different causes.
What a normal night produces
Recent windows at the bottom of Settings → Automation is the record. One row per window, with counts that are live rendered jobs rather than dispatch attempts, so they reflect what happened rather than what was intended. Click a row for that window's plan and its per-side results.
A row badged Paused is a window you stopped by hand: it is still open and still counting whatever finishes, but it is not dispatching anything new until you press Resume or the window closes on its own.
A healthy night looks like: one row for the window, a caption run dispatched at the start whose jobs complete steadily, and a generation count that is smaller than the planned count — because the GPU, not the plan, set the pace. Skip reasons recorded against the generation side are normal too; comfyui_busy on later ticks is exactly the serial gate working.
Nightly caption runs are recorded as Manual, not Scheduled. This is the single most misleading thing on the screen. The window and the Start Auto-Captioning button go through different entry points, but both stamp every run they create manual. scheduled is written only when the internal caption endpoint is driven without one of the manual trigger sources — which is the old worker-cron path, retired when the unified window orchestrator took over. So on Auto-Caption Runs, do not go looking for a Scheduled row from last night. Match the run up by its start time instead.
It produced nothing — what to check
In this order. Each step tells you whether to keep going or stop.
- Settings → Automation, Recent windows. Is there a row for last night at all? No row means the window never opened — and the reasons are all upstream of anything else on this list: the master switch is off, no schedule covers those hours in Chicago time, or the external trigger never fired. That last one is outside the app entirely, so nothing on any screen will show it; the absence of the row is the signal.
- Open the row, and check the backlog on Stats alongside it. The row carries the plan the estimator produced, the per-side results, and — when anything was skipped — a Skipped reasons panel listing each reason with a count. A planned count of zero on a side comes with a reason next to it, and the reason is where to look first:
disabledmeans that side's switch — Caption sweep or ComfyUI generation — is off, and nothing else on this list applies. Otherwise the sizing decided there was nothing to do, usually an empty candidate pool or no duration history to size against yet; if the caption backlog cards on Stats are also near zero, stop here, because the quiet night was correct. A planned count with nothing dispatched is the other case, and the Skipped reasons panel names which. - If generation is the empty side, read the two ComfyUI keys in that panel.
comfyui_probe_failedmeans the app could not reach ComfyUI at all — the seeder probes ComfyUI's own HTTP API before every dispatch and treats a failed probe as busy, so an unreachable ComfyUI produces a window that dispatched nothing and raised no error.comfyui_busyis the opposite and is benign: something was still rendering, which is the serial gate working as designed. To test reachability live, press Test Connection on Settings → Workflow. Do not use ComfyUI Queue for this — that screen reads the app's own job records by status, not ComfyUI, so an unreachable ComfyUI leaves it looking empty, exactly like an idle one. It answers "is something already running", and only while ComfyUI is up. - If captioning is the empty side, find the run on Auto-Caption Runs by its start time, remembering it will read
Manual. A badge of Empty is a success — the sweep ran and everything eligible was already captioned. Anything else, press the Failed counter on the run detail and read a tile's Failure reason: panel; an LM Studio timeout and an unfetchable media document need entirely different fixes. - Check the Failed tab on Jobs. Work that was dispatched and then died shows up there rather than on the automation screen — and remember
publishingsits under that tab too.
If every step comes back clean and the window row still shows nothing dispatched, the remaining candidates are all on the home Ubuntu host rather than in the app: the caption executor, the ffmpeg service, or ComfyUI itself being down. The app cannot tell you about any of them, which is why the row exists in the first place.