docs/backend-migration/plans/2026-04-28-cron-backend-migration-plan.md
For implementation work, use this document as the source of truth. The execution order below was strict during migration because the frontend initially still contained a live ACP cron fallback and the backend cron middleware was not yet wired into the real runtime path.
Completed:
CronBusyGuardMove all cron business logic out of the Electron main process and into aionui-backend.
Final ownership split:
ipcBridge.cron HTTP helpers to /api/cron/*.cron.job-* events for list/detail page refresh.ipcBridge.cronskill_suggestDo not delete the frontend ACP cron fallback first.
Reason:
send_message -> StreamRelay runtime path.run_now semanticsKeep the current frontend UX:
conversation_id quicklyUse Plan Z:
{data_dir}/cron/skills/cron-{job_id}/SKILL.mdaionui-extension skill lookup with cron_skills_dircron_jobs.skill_content in the schema for compatibility during migration, but stop relying on it as the source of truthSKILL_SUGGEST delivery pathReuse AgentStreamEvent::SkillSuggest.
The detector should live in backend runtime code that already knows:
conversation_idworkspacecron_job_idIt should not remain frontend-owned.
Preserve the current frontend behavior:
CronBusyGuard.tsDo not delete it as part of cron removal.
Current code shows it is still used outside pure cron logic, including:
src/process/task/WorkerTaskManager.tssrc/process/task/ConversationTurnCompletionService.tsSo the final action is:
SKILL_SUGGEST watching to backendCronBusyGuard out of the cron moduleThis order is mandatory.
crates/aionui-cron/src/skill_file.rscrates/aionui-cron/src/prompt.rscrates/aionui-cron/src/skill_suggest.rscrates/aionui-cron/tests/skill_file_test.rscrates/aionui-cron/tests/prompt_test.rscrates/aionui-cron/tests/skill_suggest_test.rscrates/aionui-extension/tests/cron_skill_resolve_test.rscrates/aionui-extension/src/constants.rscrates/aionui-extension/src/skill_service.rscrates/aionui-cron/src/lib.rscrates/aionui-cron/src/routes.rscrates/aionui-cron/src/service.rscrates/aionui-cron/src/executor.rscrates/aionui-cron/src/state.rscrates/aionui-app/src/state_builders.rscrates/aionui-ai-agent/src/middleware.rscrates/aionui-conversation/src/service.rscrates/aionui-conversation/src/stream_relay.rscrates/aionui-api-types/src/cron.rssrc/common/adapter/ipcBridge.tssrc/common/chat/chatLib.tssrc/process/utils/initBridge.tssrc/process/bridge/index.tssrc/process/task/AcpAgentManager.tssrc/process/task/OpenClawAgentManager.tssrc/process/task/AionrsManager.tssrc/process/task/NanoBotAgentManager.tssrc/process/task/RemoteAgentManager.tssrc/renderer/pages/conversation/Messages/hooks.tsDeleted in Phase 3:
src/process/bridge/cronBridge.tssrc/process/services/cron/CronService.tssrc/process/services/cron/CronStore.tssrc/process/services/cron/SqliteCronRepository.tssrc/process/services/cron/IpcCronEventEmitter.tssrc/process/services/cron/ICronRepository.tssrc/process/services/cron/ICronEventEmitter.tssrc/process/services/cron/ICronJobExecutor.tssrc/process/services/cron/WorkerTaskManagerJobExecutor.tssrc/process/services/cron/cronServiceSingleton.tsDeleted in Phase 5:
src/process/task/CronCommandDetector.tssrc/process/task/MessageMiddleware.tsDeleted in Phase 6:
src/process/services/cron/SkillSuggestWatcher.tssrc/process/services/cron/cronSkillFile.tsDelete in Phase 8:
src/process/services/cron/CronBusyGuard.tsStatus: completed
Ensure every frontend cron skill-management operation already has a backend API target before deeper migration starts.
File: crates/aionui-cron/src/routes.rs
DELETE /api/cron/jobs/{id}/skill.CronService::delete_skill.File: crates/aionui-cron/src/service.rs
Tests:
File: src/common/adapter/ipcBridge.ts
ipcBridge.cron.deleteSkill.saveSkill, hasSkill, and deleteSkill can all complete through backend APIs alone.Status: completed
Keep frontend cron detection temporarily, but remove its dependency on the local cron implementation.
This is the bridge phase.
Detection remains in frontend for now. Execution ownership moves to backend now.
File: src/process/task/MessageMiddleware.ts
cronService calls with ipcBridge.cron.*.invoke(...).ipcBridge.cron.onJobCreated.emit(...).ipcBridge.cron.onJobUpdated.emit(...).Reason:
File: src/process/task/AcpAgentManager.ts
processCronInMessage(...) yet.File: src/process/task/CronCommandDetector.ts
Status: completed
Delete the old in-process scheduler stack after Phase 2 proves that backend already owns persistence and task execution.
Delete:
src/process/bridge/cronBridge.tssrc/process/services/cron/CronService.tssrc/process/services/cron/CronStore.tssrc/process/services/cron/SqliteCronRepository.tssrc/process/services/cron/IpcCronEventEmitter.tssrc/process/services/cron/ICronRepository.tssrc/process/services/cron/ICronEventEmitter.tssrc/process/services/cron/ICronJobExecutor.tssrc/process/services/cron/WorkerTaskManagerJobExecutor.tssrc/process/services/cron/cronServiceSingleton.tsFile: src/process/bridge/index.ts
initCronBridge importinitCronBridge() callFile: src/process/utils/initBridge.ts
cronService importvoid cronService.init()Keep:
src/process/task/MessageMiddleware.tssrc/process/task/CronCommandDetector.tssrc/process/services/cron/SkillSuggestWatcher.tssrc/process/services/cron/cronSkillFile.tssrc/process/services/cron/CronBusyGuard.tsStatus: completed
Make backend runtime capable of replacing the frontend ACP cron fallback without behavior loss.
This phase has three critical gaps to close before frontend fallback can be deleted.
Current state:
aionui_ai_agent::MessageMiddleware existssend_message -> StreamRelay runtime pathRequired work:
File: crates/aionui-conversation/src/service.rs
File: crates/aionui-conversation/src/stream_relay.rs
Current state:
Required work:
File: crates/aionui-ai-agent/src/middleware.rs
CRON_UPDATE parsingFile: crates/aionui-cron/src/service.rs
Current state:
Required work:
Implemented via backend terminal replacement plus frontend replace-aware stream handling.
Frontend support change:
File: src/common/chat/chatLib.ts
replace: true stream semanticsFile: src/renderer/pages/conversation/Messages/hooks.ts
msg_id instead of always appendingBackend tests must cover:
Status: completed
Delete frontend cron command runtime logic only after Phase 4 is complete.
File: src/process/task/AcpAgentManager.ts
hasCronCommandsprocessCronInMessageFile: src/process/task/MessageMiddleware.ts
File: src/process/task/CronCommandDetector.ts
Search for and remove remaining references to:
processCronInMessagehasCronCommandsCronCommandDetectorMessageMiddlewareStatus: completed
Move cron skill persistence and SKILL_SUGGEST.md watching fully to backend.
This phase keeps the good parts of the previous implementation plan, but puts them in the correct migration order.
File: crates/aionui-extension/src/constants.rs
CRON_SKILLS_DIR_NAME = "cron/skills"File: crates/aionui-extension/src/skill_service.rs
cron_skills_dir: PathBuf to SkillPathsresolve_skill_source_path with cron skill lookupTests:
crates/aionui-extension/tests/cron_skill_resolve_test.rsCreate: crates/aionui-cron/src/skill_file.rs
Responsibilities:
SKILL.mdSKILL.mdTests:
crates/aionui-cron/tests/skill_file_test.rsFile: crates/aionui-cron/src/service.rs
save_skillhas_skilldelete_skillFile: crates/aionui-api-types/src/cron.rs
skill_content path as deprecated in comments if exposed in codeCreate: crates/aionui-cron/src/prompt.rs
Port the four prompt builders from the frontend executor.
These strings are load-bearing and should not be simplified during migration.
Tests:
crates/aionui-cron/tests/prompt_test.rsFile: crates/aionui-cron/src/executor.rs
skill_fileprompt.rsCreate: crates/aionui-cron/src/skill_suggest.rs
Responsibilities:
SKILL_SUGGEST.md after turn finishAgentStreamEvent::SkillSuggestThis detector must be invoked from real runtime code that knows conversation/workspace context.
Delete:
src/process/services/cron/SkillSuggestWatcher.tssrc/process/services/cron/cronSkillFile.tsskill_suggest cards still appear.Status: completed
Close the remaining backend behavior gaps after ownership transfer.
File: crates/aionui-cron/src/service.rs
cron_job_id relationships where neededrun_now semanticsFile: crates/aionui-cron/src/scheduler.rs or service resume path
File: src/renderer/pages/cron/ScheduledTasksPage/TaskDetailPage.tsx
CronBusyGuardStatus: completed
Rename and relocate the busy guard so the codebase no longer implies it is cron-only.
Move:
src/process/services/cron/CronBusyGuard.tsTo a conversation/task-level location such as:
src/process/task/ConversationBusyGuard.tsThen update all imports.
After each frontend phase:
bun run lint:fixbun run formatbunx tsc --noEmitbun run testIf user-facing text changes:
bun run i18n:typesnode scripts/check-i18n.jsAfter each backend phase:
cargo test -p aionui-extensioncargo test -p aionui-croncargo test -p aionui-ai-agentcargo test -p aionui-conversationcargo test -p aionui-app cron_e2e -- --nocapturehasSkillhasSkill becomes falseconversation_id quicklySKILL_SUGGEST.mdskill_suggestDo not proceed to Phase 3 unless:
Do not proceed to Phase 5 unless:
Do not proceed to Phase 6 unless:
SkillSuggest from real runtimeDo not proceed to Phase 8 unless:
This final plan intentionally combines:
The two most important corrections are:
CronBusyGuard instead of deleting itAny implementation plan that violates either rule should be treated as stale.