feat(host-core): catch up scheduled occurrences missed while the app was not running - #1212
Open
hawchou1995 wants to merge 1 commit into
Open
hawchou1995 wants to merge 1 commit into
hawchou1995 wants to merge 1 commit into
Conversation
…was not running Automatic dispatch only fires while PI-Desktop is running, and the scheduler deliberately dropped anything it found late: due() rearmed an occurrence more than 90 seconds old into the future, and recover() rearmed every task at startup. Closing the app across a scheduled minute therefore lost that run silently and permanently. Admit the single newest miss instead, while it is still inside the task's catch-up window. The window is the boundary rather than a retry count, because a recurring task's later occurrences supersede the older ones: one miss produces at most one run, at most two catch-ups start per poll, and anything older is rearmed as before. Paused tasks, tasks without a schedule, manual cadence and a task with an unfinished run never catch up, and a catch-up is dispatched through the same scheduled.run path as an automatic occurrence. catchUp (on by default) and catchUpWindowMinutes (three hours hourly, one day daily and weekly, clamped to 5-10080) ride the task's existing config_json boundary, so there is no schema, RPC, wire or dependency change. Refs vastsa#1178
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1178
Responding to the maintainer's invitation in #1178 ("可否 pr 一下?"). Left as
featdeliberately: R6.1 exempts work maintainers direct and states that relabelling another type
as
fix/perfdoes not qualify it (SPEC 06-delivery/03-ai-development-workflow.mdR6.1).What
Automatic scheduling now catches up an occurrence missed while the app was not running.
Why
due()rearmed anything more than 90 seconds late into the future, andrecover()rearmedevery armed task at startup. The intent was to avoid a burst of stale runs after downtime, but
the effect is that closing the app — or letting the machine sleep — across a scheduled minute
loses that run silently and permanently. Both sites are changed to admit one catch-up when the
task's newest miss is still inside its window, and to rearm as before when it is not.
Boundaries — the answer to "最大重试天数或次数?"
A retry count answers the wrong question. For a recurring task only the newest miss is worth
running, because later occurrences supersede the older ones: replaying
ndaily occurrences isnot
nretries of one run, it isnstale runs. So the policy is a window, not a count:catchUptruecatchUpWindowMinutesmanualcadence and a task with an unfinished run nevercatch up, and a catch-up goes through the same
scheduled.runpath as an automatic occurrence,so the run ledger, overlap suppression and
SCHEDULE_NOT_DUEadmission all still apply.Both keys ride the task's existing
config_jsonboundary, so there is no new table, column,migration, RPC method or wire field.
Scope
crates/host-core/src/scheduled/automation.rs— catch-up policy, rewrittendue()/recover(), testscrates/host-core/src/scheduled/timing.rs—Schedule::latest_missed()and its testsguide/automations,spec/04-ux/01-ui-iaand the decisions log (D635), English and Simplified Chinese in step
Cargo.toml,Cargo.lock, schema, RPC or JS/TS change.reschedule,runningand
begin_runkeep their signatures and semantics.existing Agent tools can set both keys per task. A follow-up can add controls.
Verification
Local Windows candidate, rustc/cargo 1.98.1 (
x86_64-pc-windows-gnu), branch head6dedf39fonorigin/main7f6c2cd9:cargo test -p host-core --lockedcargo test -p host-core --locked scheduledcargo fmt --all -- --checkcargo clippy -p host-core --all-targetsnode docs/scripts/check-docs.mjsnode docs/scripts/check-locales.mjsnode scripts/check-architecture.mjsnode scripts/check-pr-base-main.mjsorigin/mainis an ancestor of HEADThe three failures are not caused by this change: the unchanged baseline fails the same three
with the same assertion,
CONFIG_SYNC_LIMIT_EXCEEDED: instruction file is too large. Root cause isthat
config_sync::domains::global_instruction_path()readsdirs::home_dir()/.pi/agent/AGENTS.mdfrom the real home directory rather than an isolated temp root, and that file is 33,559 B here
against
MAX_INSTRUCTION_BYTES= 32 KiB — the repository's ownAGENTS.md(33,277 B) is over thesame limit. A clean CI home directory has no such file.
New tests cover: catch-up once inside the window; rearm outside it;
catchUp: false;manualandunarmed tasks; paused tasks; a running task suppressing its own catch-up; the two-per-poll cap;
startup keeping an in-window miss for exactly one catch-up; the default windows and the clamp;
legacy tasks never catching up; and the calendar arithmetic (daily/weekly collapse to the newest
occurrence, hourly anchored at the armed instant, invalid input) against an injected timezone.
NOT RUN
pnpm test:e2e:scheduled(E2E-SCHEDULED-desktop-automation-lifecycle). That suite runs the realdesktop, preload, Host SQLite and agent sidecar, so it needs a built Electron candidate; this
candidate has no
node_modulesor desktop build, and installing/building to produce one wouldwrite several GB to a system drive that is already near full. Reason recorded rather than guessed.
Alternative validation: the scenario's Expected clause delegates stale/missed occurrence handling
to Host tests — "Host tests additionally prove duplicate admission rejection, stale/missed
occurrence handling, invalid input rejection and recovery" — which is the path extended here and
is covered by the 12 new Host tests above. Remaining risk: the renderer-facing behaviour of a
catch-up run (run-history row, conversation link, no foreground focus change) is unverified end
to end, though it reuses the existing automatic dispatch path unchanged.
pnpm install,pnpm build:js,pnpm lint,pnpm -r test. No JS/TS file is touched, and thetwo dependency-free docs checkers were run instead (above). CI covers the rest.
Compatibility
Merging is additive: existing tasks keep their cadence,
nextRunAtsemantics and run history, andgain catch-up with defaults. Reverting restores the previous behaviour, and a task carrying
catchUp/catchUpWindowMinuteswhile reverted is unaffected — they are two keys the older codeignores in
config_json, so no data cleanup is needed.Affected specs and records: ADR 0310 (new), ADR
scheduled-desktop-automations(amended),ADR 0305 (unchanged),
04-ux/01-ui-ia§3.3,guide/automations, decisions log D635,E2E-SCHEDULED-desktop-automation-lifecycle.