pueue-job-queue¶
Terminology rule (zh-TW pages)
技術名詞首次出現以「中文 (English original)」格式呈現,例:依賴注入
(dependency injection)。不自創翻譯——若無公認譯名直接保留英文
(如 embedding、tokenizer)。代碼、API 名、CLI flag、套件名、檔名一律不翻。
驅動 Nukesor/pueue —— 一個由 daemon
撐起來的 shell job queue —— 做佇列 (queued)、平行、排程、輕度 DAG 化的
shell 工作。這個 skill 是個 CLI 橋接器,不是自製排程器:它包裝
pueue add --after、pueue status --json、pueue log --json,讓 agent
可以提交、批次、串接、等待、重試,而不會搞丟正在跑的東西。
| 介面 | 它回答的問題 |
|---|---|
check-daemon.sh |
「pueued 在跑嗎?這個 OS 上 log 在哪?」 |
submit.sh |
「提交一個任務並給我可解析的 {task_id, label, group, after}。」 |
submit-dag.py |
「把整條 fan-out / fan-in pipeline 提交、依賴接好 —— 在一個依 DAG 寬度開大小的、全新隔離的 group 中。」 |
wait.py |
「Block 直到這些任務結束,並摘要成功 / 失敗。」 |
cleanup.sh |
「回收 disk + status 延遲:清掉舊任務、空的 group、log 檔。」 |
references/cli-cheatsheet.md |
「我該抓哪個沒被包裝的 pueue subcommand?」 |
references/json-schema.md |
「pueue status --json 長什麼樣?QUERY DSL 語法是什麼?」 |
references/dag-patterns.md |
「fan-out / fan-in / 菱形怎麼表達?」 |
references/daemon-and-config.md |
「macOS / Linux 怎麼自動啟動 pueued?」 |
references/remote-daemon.md |
「daemon 在另一台機器上 —— 我能驅動它嗎?該不該?」 |
這個 skill 存在是為了讓 agent 不必煩三件事:
- 記帳 (Bookkeeping) —— task id、依賴接線、group 自動建立、
pueue add --print-task-id的 id 防禦性解析。 - Schema 漂移 ——
pueue status --json用 serde tagged enum ({"Done": {"result": "Success" | "DependencyFailed" | "Killed" | {"Failed": <int>}}})。wait.py分類所有變體,回傳可預測的 exit code (0ok、5任何失敗、6timeout)。 - Footgun ——
pueue add -- bash -c 'sleep 60'不會保留內層 引號 (pueue 會 re-shell);pueue kill --all也會暫停每個 group;pueue restart(預設) 建立新的 id;pueue clean(預設) 也會 清掉失敗。SKILL.md 有一份 10 條的 gotcha 清單涵蓋每一個。
skill 觸發時機¶
- 「跑這 30 個指令,最多 4 個同時」→ 設 group 平行度,迴圈
submit.sh --group sweep。 - 「啟動一個長訓練 job 然後我可以闔上筆電」→
submit.sh -- ./train.sh(pueue 跨 reboot 持久)。 - 「task B 只在 task A 成功後跑」→
submit.sh --after $A_ID -- ./b.sh。 - 「fan out 4 個訓練,再 evaluate」→
submit-dag.py dag.yaml。 - 「今晚排程跑這個」→
submit.sh --delay 6h -- ./nightly.sh。 - 使用者說 pueue / pueued / pueue add / pueue queue / pueue group 或問「shell job 的 task queue」。
何時不該用¶
- 一個短的 shell 指令 —— 直接跑就好,pueue 帶 daemon 開銷。
- 跨主機排程、OR-deps、條件分支、retry-with-backoff、typed task IO
—— 升級到 Airflow / Prefect / Dagster / DVC / Slurm。
references/dag-patterns.md有決策表。 - 長時間執行的 service —— 那是
systemd/launchd的事。
結構¶
skills/local/pueue-job-queue/
├── SKILL.md # ~275 行
├── scripts/
│ ├── check-daemon.sh # bash; daemon 健康 + auto-start
│ ├── submit.sh # bash; submit-one 包裝、JSON out、group 自動建立
│ ├── wait.py # PEP 723; block 直到終止狀態、state-change events、JSON 摘要
│ ├── submit-dag.py # PEP 723 (pyyaml); DAG submitter 含 --isolated-group / --auto-parallel
│ └── cleanup.sh # bash; 清 task + 空 group + 老 log 檔
├── references/
│ ├── cli-cheatsheet.md # 沒被包裝的指令
│ ├── json-schema.md # 觀察到的 status --json 形狀 (4.0.2) + QUERY DSL + jq 食譜
│ ├── dag-patterns.md # fan-out / fan-in / diamond + 升級表
│ ├── daemon-and-config.md # 各 OS 的 pueued setup、config 旋鈕、log 路徑
│ └── remote-daemon.md # SSH socket forwarding、TCP+TLS、client 端解析 cwd 的陷阱
├── assets/
│ ├── dag.example.yaml # 5-task fan-out / fan-in fixture
│ └── pueue.yml.example # config with `pause_group_on_failure: true`
└── tests/
├── conftest.py # 隔離的 pueued fixture,無 pueue 時 skip
├── test_submit.py # submit.sh 路徑 + 依賴失敗
├── test_dag.py # 拓撲 invariant、cycle/unknown/missing-cmd、isolated-group
├── test_wait.py # 成功/失敗/timeout exit code
├── test_contracts.sh # bash --help/error-code 契約
└── fixtures/simple-dag.yaml # 4-task diamond
永遠帶 label¶
pueue status 會把 label 欄位顯示在 command 之前。在繁忙佇列中,
掃 label 是一眼分辨多個 train.py run 的唯一方法。skill 的 gotcha 區段
的開頭就是:
提交時永遠帶
--label,且偏好能把這個 task 跟它的兄弟區分開的名字。
慣例:<verb>-<subject>-<key> (≤30 字元)。把區別 (seed、dataset slice、
model variant) 編進去 —— 不是命令本身:
| 好 | 壞 |
|---|---|
train-baseline-seed1 |
task1 |
eval-prod-2026q1 |
python eval.py --quarter 2026q1 |
nightly-featurize (DAG) |
step-2-of-5 |
submit-dag.py --label-prefix <run>- 會產生 <run>-<task_name>,
這樣 wait.py --label-prefix <run>- 可以選整張圖、pueue clean
也可以稍後依此過濾。
經驗 schema (Empirical schema)¶
Pueue 的 JSON 輸出在 wiki 沒有正式文件化。skill 在
references/json-schema.md 出貨在 pueue 4.0.2 上觀察到的形狀,
含一個用來在不同主版本上重新驗證的診斷片段。關鍵形狀:
{
"tasks": {
"<id>": {
"id": 17,
"label": "train-baseline-seed1",
"group": "ml",
"dependencies": [12, 14],
"status": {
"Done": {
"enqueued_at": "...", "start": "...", "end": "...",
"result": "Success" // 或 "DependencyFailed" | "Killed" | {"Failed": 1}
}
}
}
},
"groups": {
"default": {"status": "Running", "parallel_tasks": 1}
}
}
wait.py 分類每一種變體;如果未來 pueue release 新增一種(比方
Suspended),測試套件會在 skill 變得啞掉前抓到。
驗證 (Verification)¶
skill 出貨 22 個 pytest 案例,會在 tempdir-scoped config 底下啟動
隔離的 pueued —— 你的真實佇列永遠不會被碰到 —— 加上一個 bash
exit-code 契約測試。pueue / pueued 不在 PATH 時兩者都會自動 skip。
lint-skill --strict 是乾淨的 (0 error、0 warning)。
uv run --extra dev pytest skills/local/pueue-job-queue/tests/ -q
bash skills/local/pueue-job-queue/tests/test_contracts.sh
bash skills/local/skill-author/scripts/lint-skill.sh skills/local/pueue-job-queue --strict
pytest fixture 在測試之間用 pueue reset --force,因為樸素的
pueue kill --all 也會暫停每個 group —— 一個值得文件化、非顯而易見的
失敗模式(詳見 SKILL.md gotcha)。
遠端 daemon:可以,但提交請走 SSH¶
Pueue 沒辦法跨主機排程,但本機 client 可以驅動單一台遠端的 pueued
—— 而且是完整控制(add、kill、log、follow),不只是看 status。
references/remote-daemon.md 有完整設定;簡短版是:建議路徑完全不用動伺服器。
# 轉發 daemon 自己的 unix socket —— 不用重設伺服器、不用 TLS
ssh -f -N -L ~/.config/pueue/remote/remote.sock:/run/user/1000/pueue_you.socket myhost
pueue -c ~/.config/pueue/remote/client.yml status
以 macOS client (4.0.4) 對 Linux daemon (4.0.2) 做過端到端驗證。動手前值得知道的:
- client 必須設
read_local_logs: false,否則log/follow會去自己的 磁碟找 daemon 的 log,然後什麼都找不到。 - 版本不一致會警告但能用 —— 每個指令都會印
Different protocol version detected '0.30.1',然後一切正常。把它從 stderr 濾掉即可;stdout 的 JSON 是乾淨的。 - 「提交」才是不能跨機器的那一半。
pueue add會記錄工作目錄,而且是 在 client 端做 canonicalize,所以遠端提交會用三種方式失敗:本機 cwd 被送 過去、任務以FailedToSpawn收場;只存在於遠端的路徑會被直接拒絕 (Failed to canonicalize given working directory path);在 macOS 上/tmp會被悄悄改寫成/private/tmp。只有在兩台機器上都存在、且解析結果相同的路徑 才行得通。
所以誠實的分工是:用 ssh host 'pueue add ...' 提交,用轉發的 client 讀取與控制。
這次調查也挖出這個 skill 裡一個真正的 bug(已修):pueue 的 result 是 tagged
enum,而 FailedToSpawn 是 dict 形狀的
({"FailedToSpawn": "<os error>"}),但 wait.py 是拿一份列舉好的「壞」變體
清單去比對 —— 於是它漏了,wait.py 對一個從未啟動的任務回傳 exit 0。
現在改成只允許 Success,其他一律 fail closed,這同時也讓它對未來新增的 pueue
變體是安全的。詳見
pitfall。