Dispatch watchdog
Glyph is a workbench for composing the forms intelligence takes today — and discovering the language it will speak tomorrow.
npx -y skills add glyphs-ai/glyph --skill dispatch-watchdogAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Spawns a properly-detached cross-platform watchdog over a running glyph task or workflow — polls status, exits on terminal state, and reliably surfaces runtime completion notifications to the orchestrator session
SKILL.md
7.3 KB, as published. Nobody here has run it
Dispatch Watchdog Skill
Domain
A single primitive for any orchestrator agent that dispatches a
long-running glyph task or workflow: given an id, spawn a watchdog
that polls glyph <kind> show <id> --json on a configurable cadence
and exits when the entity reaches a terminal status (succeeded,
failed, cancelled). The watchdog must be spawned in a way that
the glyph runtime can observe its completion and deliver a
notification back to the orchestrator's session — not all spawn
patterns achieve this.
Boundary
In scope:
- Spawning a detached watchdog process from the orchestrator's shell
using the runtime's
mode:async + detach:trueprimitive. - A polling loop that reads task or workflow status via the glyph CLI and exits cleanly on terminal state.
- A persisted watchdog log (one line per poll) for after-the-fact inspection.
- Cross-platform invocation (Windows and macOS/Linux share one Node body; only the OS-level spawn wrapper differs).
Out of scope:
- Interpreting task output or success/failure semantics — that is the caller's job after notification.
- Cancelling stuck tasks — see the caller's stuck-task playbook.
- Replacing the orchestrator's own monitoring tick loop; this is for single long-running tasks or workflows where polling-in-foreground would block other work.
Why this skill exists
The glyph runtime delivers a completion notification to the orchestrator's session only when the watchdog is spawned through the mode:async + detach:true primitive shown below. Other spawn shapes silently break the notification path.
Primitive
The skill ships a Node script watchdog.mjs next to this file. Refer
to it via the <SKILL_DIR> placeholder — your runtime resolves the
path from context, so the body stays neutral across Copilot CLI,
Claude Code, Gemini CLI, Cursor, Windsurf, Codex, and any other
provisioner. The script depends only on the Node stdlib and runs
identically on Windows, macOS, and Linux.
<SKILL_DIR>is the directory containing thisSKILL.md. Resolve from your runtime context.
Script CLI
node <SKILL_DIR>/watchdog.mjs \
<task|workflow> <id> <abs-log-path> [poll-sec=60] [max-loops=240]
<task|workflow>— selects which glyph subcommand to poll (glyph task showvsglyph workflow show).<id>— the task or workflow id. Server-generated ids match^[A-Za-z0-9-]+$; the script rejects anything else (defence-in-depth against shell injection through the polled CLI command).<abs-log-path>— must be absolute. The script does not resolve relative paths; under detached spawnprocess.cwd()is unspecified.[poll-sec]— seconds between polls; default 60.[max-loops]— give-up bound; default 240 (≈4 hours at the default cadence). On hitting the bound the script writesmax-loops-exceededand exits 1.
Status extraction uses JSON.parse(raw).status, which indexes the
top-level field directly. This is robust against long string values,
backslash escapes (e.g. Windows paths in workflow details), and any
"status": "..." substrings that appear inside nested string content
— a regex against the raw JSON cannot make that distinction.
PowerShell (Windows) — spawn via mode:async + detach:true
# In glyph / Copilot CLI tool form:
# powershell:
# command: node <SKILL_DIR>/watchdog.mjs task <tid> "<missionDir>\watchdog.log"
# mode: async
# detach: true
# initial_wait: 10
Use workflow in place of task to poll glyph workflow show <wfid> --json instead.
The orchestrator returns immediately to other work; the runtime notifies the session when the watchdog exits.
Bash (macOS / Linux) — same pattern, runtime auto-wraps in setsid
# In glyph / Copilot CLI tool form:
# bash / shell:
# command: node <SKILL_DIR>/watchdog.mjs task <tid> "${mission_dir}/watchdog.log"
# mode: async
# detach: true
# initial_wait: 10
(On Unix-like systems the runtime wraps detached commands with
setsid automatically; the notification path is the same as
Windows.)
Watchdog log format
First line is the start marker (see Caller contract item 4). One line per poll thereafter, monotonic-timestamp-prefixed, for debugging stuck or runaway watchdogs:
2026-05-22T08:30:00+00:00 watchdog started for 20260522-abc12345
2026-05-22T08:31:00+00:00 status=running
2026-05-22T08:32:00+00:00 status=running
2026-05-22T08:33:00+00:00 status=succeeded
Anti-patterns (do not use)
- Do not use the
tasktool with a Haiku/Sonnet subagent for the poll loop. Agents describe; they don't reliably loop. - Do not use
Start-Processto background the watchdog. The process is invisible to the runtime. - Do not use
mode:asyncwithoutdetach:true. Session shutdown will kill it. - Do not poll faster than every ~15s without good reason — every poll is a CLI invocation that spawns a Node process.
- Do not reach for
child_process.execFileSync('glyph', ...)when authoring an ad-hoc watchdog variant. On Windows + Node 22+ the CVE-2024-27980 hardening refuses to spawn.cmdshims viaexecFileSyncand throwsEINVAL. The shippedwatchdog.mjsusesexecSyncwith a shell-routed command and a strict id-regex guard for exactly this reason — do not "fix" it toexecFileSync. - Do not extract
statuswith a regex over the raw JSON. Theglyph workflow show --jsonpayload includes adetailsfield that JSON-encodes the workflow brief verbatim; briefs frequently contain"status": "..."substrings, and a first-match regex picks those up instead of the top-level field.watchdog.mjsusesJSON.parse(raw).statusto dodge this; preserve that.
Caller contract
The caller (orchestrator) MUST:
- Persist the id (
task-id.txtin the mission folder is the convention for task ids; workflow ids follow the same per-mission file convention). - Invoke the watchdog once per id. Do not re-spawn if one is
already alive. Liveness =
watchdog.logmtime within 2× the configured poll interval. Older = dead; respawn. - On notification, read
watchdog.log's last line for the final status and proceed. - Verify start within 5s of spawning. Read the first line of
watchdog.log; it MUST containwatchdog started for <id>. If the marker is absent, the watchdog is dead (bad args, missing id, exec failure) — fix the invocation and respawn. Do not proceed assuming monitoring is active. - Verify status capture within 2× the configured poll interval.
Read
watchdog.logand confirm at least one non-emptystatus=<value>line exists. A line readingstatus=with no value indicates the polled CLI returned a payload without a top-levelstatusfield, or the call itself failed (look for apoll-error: …line on the previous tick for detail). Fix the underlying issue and respawn — do NOT wait for a completion notification, it will never arrive.
CHANGELOG
See CHANGELOG.md next to this file.