# 排程

本頁涵蓋自動排程，以及 scheduler skill 被改壞後的復原方式。

## Scheduler skills（隔離 namespace）

由 scheduler 觸發的 skill 位於獨立樹狀結構，**不**被 `agents.Scanner()` 掃描：

```
~/.config/agenvoy/skills/scheduler/<short>-<hash8>/SKILL.md
```

| 面向 | 一般 skill | Scheduler skill |
|---|---|---|
| 路徑 | `~/.config/agenvoy/skills/<name>/SKILL.md` | `~/.config/agenvoy/skills/scheduler/<short>-<hash8>/SKILL.md` |
| Frontmatter `name` | `<name>` | `<short>-<hash8>`（無 `scheduler-` 前綴） |
| `/<name>` 自動補全 | 有 | 無 —— 於 picker 底部以 `/sched-<name>`（warn-purple）呈現 |
| 觸發 | `runtime.MatchSkill`（於 `exec.Prepare`）後合成 `run_skill` | (a) 由 daemon `runtime.SetRunner` 觸發 cron / one-shot，(b) 從 TUI 手動 `/sched-<name>` 或 `/schedule` |

### 建立流程

`scheduler-skill-creator` skill 是**新增**排程的正規入口。它會：

1. Pre-flight gate（Step 0）：若使用者訊息缺少時間 token（`+5m` / `HH:MM` 等）或任務 token，必須先呼叫 `ask_user` —— 不預設 `+10m`、不推測「大概是早上 9 點」。兩項都缺時，兩題寫進同一次 `ask_user` 的 `questions` 一起送，不再一次問一題。
2. 執行 `python3 ~/.config/agenvoy/skills/.system/scheduler-skill-creator/scripts/init_scheduler_skill.py <short>` 建立帶 hash 後綴的 skill 目錄。路徑刻意用絕對路徑：`run_command` 的 CWD 是使用者的工作目錄而非 skill 目錄，相對的 `scripts/...` 一定找不到。
3. 內容改**委派 `/skill-creator`**（走它的「編輯現有 skill」路徑），不再於此處直接 patch SKILL.md。目錄與名稱在步驟 2 已定案，這一步只做內容。
4. 呼叫 `schedules(mode=write)` 綁定排程。

直接呼叫 `schedules(mode=patch)` 僅允許用於**重新綁定**既有排程（變更已建立之 scheduler skill 的時間）。`mode=write` 屬於 creator 流程內部步驟——其 `skill_name` 帶有生成的 hash 後綴，手寫的名稱必定失敗。

### 手動執行（`/sched-<name>`）

TUI 命令 picker 將 `scheduler/` 下、其 cron 或單次項目綁定當前 session 的目錄列為 `/sched-<name>`。選取後讀取其主體並派送給當前 agent，**帶一段 preamble**，阻止較弱的模型將 SKILL.md 形狀的主體誤讀為建立排程請求而重跑 creator。`/schedule` 把所有 cron 與單次項目列在同一 popup：`enter` 以相同路徑立即觸發、`d` 刪除；新增或編輯排程請直接交代 agent（原本的 `/cron` 與 `/task` 指令已移除）。

該 preamble 強制：

- 立即執行既有 scheduler skill 並輸出結果
- **不**啟用 `scheduler-skill-creator`
- **不**執行 `init_scheduler_skill.py`
- **不**呼叫 `schedules(mode=write)`

### Daemon 觸發

Daemon 呼叫 `runtime.SetRunner(app.RunSkill)`，由 `app.RunSkill(ctx, sessionID, skillName)` 擔任 runner。當 scheduler 觸發（cron tick 或 one-shot 到期）時，runner：

1. 透過 `skill.GetSchedule(skillName)` 讀取主體。
2. 確保 session 目錄與其 SQLite 資料列存在。
3. 以 `exec.ExecWithSubagent` 執行主體 —— 一個帶排程 context 的 in-process subagent。

One-shot 任務成功觸發後會被移除，skill 目錄則被丟棄。

## 修復壞掉的編輯

執行後自動改寫 skill 的機制已移除——失敗的執行不會再在背後修改 skill。修復改為建立在檔案層級的快照層上：工具對檔案的每次變更都有紀錄，修復為明確操作：

| 步驟 | 工具 |
|---|---|
| 檢視某個 skill 或 tool 檔的版本紀錄 | `file_history(mode=list)` |
| 將最新紀錄版本與磁碟現況做 diff | `file_history(mode=read)` |
| 還原單一版本 | `edit_file(mode=restore, version=...)` |
| 回滾某個任務動過的所有檔案 | `edit_file(mode=restore, task_id=...)` |

被移除的 skill 會落在 `.Trash/`，可從該處復原。
