用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/lukemcqueen/hermes-cortex --skill detached-worker-pattern命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
Cross-server agent health monitoring using binary status vectors — deploy health endpoints on each agent, poll from orchestrator, alert on state transitions.
Wire a self-hosted Langfuse instance to Hermes Agent — generate API keys, configure env vars, enable the bundled plugin, install SDK, and verify traces flow.
Use before enforcement code changes or shared-repo commits.
| name | detached-worker-pattern |
| description | Cron tick budget kill: detached worker + result sweep. |
| version | 1.0.0 |
| category | devops |
| metadata | {"hermes":{"tags":["cron","background","worker","timeout","at-least-once","result-receipt","deadlock","decouple"],"related_skills":["cron-job-management","fleet-commands"]}} |
Any budgeted, at-least-once consumer (a cron tick, a message handler tick, a job queue worker) that must run work whose worst-case runtime can exceed the consumer's execution budget:
HERMES_CRON_TIMEOUT, no_agent script
limits) running subprocess chains (pull + deploy + doctor ≈ 390s worst case
inside a 300s budget)Fleet UPDATE_REQUEST dispatch: 5 hosts consumed the request and the updates
LANDED (verified via direct probes: repo + deploy sync at the target SHA), but
0/5 returned UPDATE_RESULTs. Root cause: the handler ran
pull+cortex-update+doctor synchronously (~390s worst case) inside a cron tick
budgeted at ~300s. The tick was killed mid-processing — after the early-archive
— so the request was never re-processed and the receipt was silently lost.
"Consumed but no result" looked like a bus failure; it was an execution-budget
kill.
subprocess.Popen(
[sys.executable, "-c", WORKER_CODE, handler_path, json.dumps(msg_body), corr],
start_new_session=True, # survives the parent's budget kill
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
start_new_session=True detaches from the parent's process group so the
cron's kill (which targets the tick's group) cannot take the worker down.state/pending-results/<corr>.json — never sends over the bus itself.<corr>.json when done (success OR crash — wrap in try/except
so a crash still yields a structured error result).<corr>.running when spawning; worker deletes it on finish.
The marker also tells concurrent readers "a deploy is mid-flight" — see #4.for f in sorted(PENDING_DIR.glob("*.json")):
result = json.loads(f.read_text())
send_result(corr, result) # bus / webhook / queue
f.unlink(missing_ok=True)
for m in sorted(PENDING_DIR.glob("*.running")):
if now - m.stat().st_mtime > TIMEOUT:
send_result(corr, {"success": False, "error": "worker died"})
m.unlink(missing_ok=True)
.running markers (worker died without writing) become explicit
timeout error results — never permanent silence.A tick that ALSO runs its own health doctor will catch the worker's mid-deploy
state (checksum mismatches, files half-rewritten) and fire false FAIL alerts.
Skip the health check while any .running marker exists.
sys.path before importing the parent module
(sys.path.insert(0, str(Path(handler_path).parent))) — cron children don't
inherit PYTHONPATH.importlib.util.spec_from_file_location)
and call its existing process function — don't duplicate the logic.Popen raises, fall through to the legacy
synchronous path rather than dropping the request.agent-message-handler.py UPDATE_REQUEST branch (commit 282045ec,
2026-08-18): _spawn_update_worker() / _send_pending_update_results() /
_update_worker_code() in ops/scripts/agent/agent-message-handler.py,
state/pending-update-results/ under CORTEX_DEPLOY_HOME. Tested end-to-end:
worker → sweep → result received; worker-death (stale marker) → timeout result;
mid-deploy health-doctor skip (killed a false 9-fail alert).