watch-patterns
Skill16 starsChanged 5 days agoMITView on GitHub
Correct watcher construction for long-running operations. TRIGGER when: watching a long-running operation (CI, deploy, log stream), writing poll/until loops, or using the Monitor tool. SKIP: production alerts/metrics (use monitoring-observability); log formatting (use logging-standards); diagnosing the bug (use systematic-debugging).
In this repository39 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- agent-commsSkill
- agent-memorySkill
- api-designSkill
- context-engineeringSkill
- database-optimizationSkill
- distillSkill
- docker-templatesSkill
- error-handlingSkill
- frontend-designSkill
- github-actions-templateSkill
- git-operationsSkill
- logging-standardsSkill
- mcp-toolsSkill
- monitoring-observabilitySkill
- mui-stylingSkill
- necessity-firstSkill
- pattern-recognitionSkill
- planning-methodologySkill
- python-patternsSkill
- quality-validationSkill
- react-patternsSkill
- research-methodologySkill
- reverse-engineer-anythingSkill
- security-review-checklistsSkill
- semantic-memory-mcpSkill
- skill-authoringSkill
- sofa-searchSkill
- spec-designSkill
- spec-developSkill
- spec-researchSkill
- spec-reviewSkill
- spec-workflowSkill
- state-managementSkill
- systematic-debuggingSkill
- testing-strategySkill
- ui-ux-pro-maxSkill
- verification-before-completionSkill
- worktree-managementSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 6 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 61
Every branch — success, failure, probe error, timeout — produces output.
- Distinctive: line 73
If the answer is no, the watcher is broken — fix it before arming.
- Very specific: line 142
Prefer probes that trigger/withstand the state change (stat /mnt/share/file re-triggers automount.
- Distinctive: line 150
Do not arm any background watcher until every box is checked:
- Distinctive: line 165
Run the probe once manually and confirm it returns the expected in-progress signal ("status: running", a growing byte count, a fresh log …
- Very specific: line 167
If the source can be safely stimulated (e.g.
- Distinctive: line 170
If neither is possible, at minimum confirm the watcher's own process is alive and its first iteration produced the expected "still waiting" …
- Very specific: line 180
When the condition fires, or the observation is no longer needed, stop the watcher: TaskStop for Monitor tasks, kill/collect background …
- Distinctive: line 183
NEVER end a task with an armed watcher unless the user explicitly asked for an ongoing watch — and then report its ID and how to stop it.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Watch Patterns Skill
Purpose
How to observe long-running operations correctly. Anyone can run a loop — the value is a watcher that never lies: it terminates, it detects failure as reliably as success, and its silence is never mistaken for progress.
Key architectural fact: a subagent cannot hold a long-lived observation.
Notifications from an armed Monitor land in the conversation that armed it;
a subagent finishes and dies. Observation is therefore always armed by the
main loop — this skill is loadable anywhere for exactly that reason.
Auto-Invoke Triggers
- Watching a CI run, deployment, file transfer, GC/prune job, or log stream
- Writing any poll/
untilloop over a remote or local state - Using the
Monitortool orBash(run_in_background)for observation
Decision Tree — Choose the Mechanism FIRST
The single most common failure is picking the wrong mechanism. Decide BEFORE writing any script:
| Situation | Mechanism |
| "Is it done now?" — answerable immediately | One-shot check. Single command, no loop, no monitor. |
| ONE notification when a condition becomes true (deploy finished, download complete, GC done) | Bash(run_in_background) + until loop with a terminal condition and a bounded iteration count. NOT Monitor. |
| REPEATING events, each occurrence matters (every error line, every restart) | Monitor tool with a filter covering ALL terminal states. |
| CI run status (Gitea Actions) | MCP polling: mcp__gitea__gitea-actions_run_read polled until status is terminal. Structured status beats scraping logs. |
| Metric/log condition (error rate, service down) | MCP: mcp__grafana__grafana-query_prometheus / grafana-query_loki_logs. One query returns aggregated truth; a bash loop re-derives it badly. |
| You only need the outcome eventually, no urgency | Do nothing now. Check once when the result is actually needed. |
Anti-pattern (observed failure)
Unbounded tail -f / while true piped into Monitor when only ONE
notification was needed → the tool stays armed until timeout, and every
intermediate line risks a spurious notification. Single notification =
Bash(run_in_background) + until; Monitor is for recurring events only.
Canonical single-notification pattern
# Bash(run_in_background): fires ONCE, always terminates
for i in $(seq 1 120); do # bound: 120 × 30s = 1h hard ceiling
STATUS=$(remote_status_cmd 2>&1) || STATUS="PROBE_FAILED"
case "$STATUS" in
*done*|*success*) echo "RESULT: success — $STATUS"; exit 0 ;;
*failed*|*error*) echo "RESULT: failed — $STATUS"; exit 1 ;;
esac
sleep 30
done
echo "RESULT: timeout after 1h — last status: $STATUS"; exit 2Every branch — success, failure, probe error, timeout — produces output. There is no code path that ends in silence.
Terminal-State Coverage (the filter must catch failure)
A filter that only matches the happy path makes a crash indistinguishable from "still running". Before arming ANY watcher, ask:
"If this process died right now, would my filter emit anything?"
If the answer is no, the watcher is broken — fix it before arming.
| Filter | Verdict |
grep "success" | BAD — crash = silence |
grep -E "success|complete" | BAD — still only happy path |
grep -E "success|complete|fail|error|fatal|panic|denied|timeout" | GOOD — alternation covers terminal states |
until ! kill -0 "$PID" 2>/dev/null; do sleep 1; done; echo "exited rc=$(cat rcfile 2>/dev/null)" | GOOD — process disappearance IS the event |
Rules:
- Watch the process/run lifecycle, not just log content, whenever possible
(exit code, PID disappearance, API
statusfield). Logs lie by omission; exit states don't. - For log filters, always include the failure vocabulary of the specific tool
(
FAILED,ERROR,fatal:,Traceback,panicked,unreachable) — read a sample of real output first to learn it. - Bound every loop (max iterations) instead of
while truewhenever the operation is expected to finish. Timeout is a terminal state too — emit it explicitly. Unbounded loops convert "it never finished" into silence.
Script Hygiene
| Concern | Rule | ||
| Buffering | Pipelines swallow lines: use grep --line-buffered, awk '{print; fflush()}', stdbuf -oL for stubborn tools. NEVER put head -N mid-stream — it SIGPIPEs the producer and kills the pipeline early. | ||
| Stderr | Always 2>&1 on commands you spawn — crashes print to stderr, and a filter reading only stdout misses them. | ||
| Transient errors | A remote probe MAY fail transiently: `OUT=$(curl -fsS "$URL" 2>&1) | OUT="PROBE_FAILED: $OUT"— capture and classify; a blind|| true` on every command converts persistent outage into silence. Count consecutive failures; N in a row = terminal state "target unreachable". | |
| Intervals | Remote APIs / SSH probes: ≥ 30 s. Local files/processes: 0.5–1 s. Hammering a remote API is both rude and a great way to get rate-limited into false "failures". | ||
| Idempotent probes | Each iteration must be self-contained. Do not cache a mount path, connection, or PID across iterations — re-resolve it (see autofs warning below). |
Address & Secret Acquisition (NEVER hardcode)
IP addresses in the model's context window may be MASKED (<PRIVATE_IP>).
You cannot copy an IP from context into a script — you would arm a watcher
pointed at a placeholder or an empty string. Observed failure: $NAS was
empty → /dev/tcp//22: Invalid argument — the script "ran" but observed
nothing.
Always extract addresses programmatically at runtime, then PROVE expansion:
# From fstab (NFS/CIFS targets):
NAS=$(awk '$3 ~ /nfs/ {split($1,a,":"); print a[1]; exit}' /etc/fstab)
# From ssh config (resolves aliases, keys, jumps):
PVE=$(ssh -G pve | awk '$1=="hostname"{print $2}')
# From Proxmox guest config (on the PVE host):
VMIP=$(ssh pve "qm config 119 | grep -oP 'ip=\K[0-9.]+'")
# LXC equivalent: pct config <id>
# From Vault:
TOKEN=$(vault kv get -field=api_token kv/influxdb/api)
# MANDATORY expansion test BEFORE arming anything:
echo "NAS=[$NAS] PVE=[$PVE]"
[ -z "$NAS" ] && { echo "FATAL: NAS resolution failed"; exit 1; }Heredoc gotcha (observed failure): a quoted heredoc (<<'EOF'),
single-quoted string, or single-quoted remote ssh command does NOT expand
$VAR — the script receives the literal text $NAS. If you template a
script, echo the assembled command and eyeball the resolved values before
arming.
Environment state drifts between invocations (observed failure): autofs
unmounts idle shares after ~60 s, so mount | awk ... that worked during your
probe returns empty inside the loop. /etc/fstab is the stabler source.
Prefer probes that trigger/withstand the state change (stat /mnt/share/file
re-triggers automount; /dev/tcp/$HOST/22 doesn't need the mount at all), and
re-resolve state inside each iteration.
Pre-Arm Self-Check (MANDATORY)
Do not arm any background watcher until every box is checked:
- Terminates by itself?
untilwith terminal condition + bounded iteration count. Nowhile true, no baretail -f. - Catches failure AND crash? Apply the "if it died right now" test. Failure vocabulary included. Timeout emits explicitly.
- Variables expand?
echoof every resolved variable, non-empty asserted ([ -n "$X" ]). - Target exists? ONE manual probe of the exact endpoint/path/PID BEFORE the loop. A watcher on
http://127.0.0.1:1/xruns forever and observes nothing. - Notification volume? Single-shot condition → fires exactly once. Recurring → estimate rate; dedupe or raise threshold if it would flood the channel.
- Interval sane? ≥30 s remote, 0.5–1 s local.
Post-Arm Verification (never trust silence)
Immediately after arming, verify the observation channel actually carries data:
- Run the probe once manually and confirm it returns the expected in-progress signal ("status: running", a growing byte count, a fresh log line).
- If the source can be safely stimulated (e.g.
loggera marker line into the watched journal, touch a file in the watched dir), inject a marker and confirm the watcher sees it. - If neither is possible, at minimum confirm the watcher's own process is alive and its first iteration produced the expected "still waiting" output.
A watcher that has never demonstrably seen anything is unverified. Silence after arming means "unverified", not "all quiet". Cisza ≠ sukces.
Cleanup
- When the condition fires, or the observation is no longer needed, stop
the watcher:
TaskStopfor Monitor tasks, kill/collect background Bash shells. - NEVER end a task with an armed watcher unless the user explicitly asked for an ongoing watch — and then report its ID and how to stop it.
- An orphaned watcher is a defect, not a convenience.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

