mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-03 03:51:35 +00:00
* feat(cron): stream schedule sources (supervised command stdout) Add gated argv stream schedules with bounded line batching and trigger.streamBatch composition. Reuse the gateway ProcessSupervisor for source ownership, deterministic teardown, capped restart backoff, and schedule-key guarded batch execution. Expose additive protocol, CLI, tool, UI, docs, and generated snapshot surfaces without storage DDL. Contract: stream schedules are event-driven, require cron.triggers.enabled, reject command payloads, and retain at most one bounded pending batch. * fix(cron): reject retired stream source epochs at run admission Thread an invalidatable per-owner source-generation token (ownerNonce.generation) through cron.run admission alongside the schedule key. A batch handed to cron.run under one source epoch can wait behind another run while its owner is stopped; a disable→re-enable or A→B→A edit leaves the schedule key unchanged, so the key check alone would admit the retired epoch's batch. The token is persisted in job.state on every lifecycle write and compared at every admission site plus the executeJobCore guard, so a stale epoch's batch is skipped. Also fix direct stream-job mutations recording the wrong lifecycle status when global cron is off but triggers are on: extract resolveStreamStopReason so the direct path reports the remediable cron-disabled state like reconcile does. * fix(cron): close stream admission windows from round-10 review - Persist the retired source generation before draining stop teardown, so a batch queued behind another cron run cannot gain admission during the up-to-10s in-flight-batch wait (server-cron routed stop path). - Add streamSourceGeneration to the closed gateway response schema (excluded from the writable patch schema) so a running stream job passes strict result validation without letting callers spoof source identity. - Close the mutation-epoch ABA: track an eviction epoch so a snapshotted absent-0 is trusted as unchanged only when no LRU eviction happened during the await. * refactor(cron): stream sources own a durable logical identity Split the conflated restart-generation/admission token into two concepts: a persisted streamSourceIdentity owned by cron store mutations (rotates on enable/disable, source replacement, once-trigger auto-disable, and explicit retirement; stable across supervised child restarts) and a watcher-local process generation used only to fence stale child callbacks. Admission now requires schedule key + identity together at every window, closing the A-to-B-to-A and restart-flush races from review rounds 8-11. Also: match-mode regexes now see raw source text (the [truncated] marker is applied after matching), stop-timeout failures set the live restartExhausted mirror so shutdown preserves the terminal diagnostic, and the watcher is split into owner/output/registry modules under the max-lines budget. * fix(cron): harden stream teardown and intake from round-4 review An exit queued ahead of a requested stop no longer counts toward restart exhaustion (the synchronous stop fence owns it), overlapping cron.stop and stopAndDrain share one memoized shutdown drain instead of double-stopping every owner, raw output intake is bounded at 4x the batch cap so normal 64 KiB pipe reads stop losing complete lines to OS chunk boundaries, and persisted-shape quarantine coverage for unsafe match expressions is pinned. * fix(cron): keep watcher-internal owner disposal from retiring live identity Disposing an obsolete owner while a start replaces it is not a durable removal; a retiring stop there rotated the live job's identity and stranded the replacement behind the CAS ownership guard. Also align the docs with the implemented match semantics: complete lines match on full text past the batch cap, only intake-cut prefixes are unmatchable. * fix(cron): make oversized-line matching independent of pipe chunking Partial lines are retained up to the raw-intake bound rather than the delivery cap, so a complete over-cap line matches identically whether it arrives in one callback or several; only a line the intake bound itself cut remains an unprovable prefix. Reconcile also contains schedule-replacement stop failures per job, matching the other stop branches. * fix(cron): bound assembled lines, drop stale payload override, barrier stopAll - enforce the 4x raw-intake per-line cap while assembling split callbacks, so an oversized line stays an unprovable prefix regardless of chunking - stop passing the watcher-cached payload as a cron.run override; the run snapshots the persisted payload under its admission lock - stopAll waits for every owner stop to settle before surfacing failures - split cron-stream-output interleaving tests into their own file (max-lines) * fix(cron): address first full-CI round (lint, knip, schema test, unused param) * chore(cron): refresh codex prompt snapshots for stream schedule schema * fix(cron): keep the first clean line after an intake drop ending at a newline * fix(cron): fence stream reconcile list snapshots against direct mutation routes A cron.list snapshot captured across the reconcile await could be applied after a direct add/update route already started the owner, stopping it as removed and retiring its live identity. A mutation revision bumped at every direct route start invalidates the stale snapshot; reconcile re-lists (bounded) instead of applying it. * style(cron): format stream owner imports * fix(cron): discard severed stream prefixes at EOF * fix(cron): retry failed stream shutdown drains * fix(cron): honor stream stop fence after output drain * chore(cron): refresh landing checks