ops-deploy-fix — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ops-deploy-fix (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill is the operator console for the post-merge + build-failure auto-fix loop installed by /ops:setup Step 6.5a. The underlying daemons, hooks, and prompts live in ${CLAUDE_PLUGIN_ROOT}/scripts/, ${CLAUDE_PLUGIN_ROOT}/hooks/, and ${CLAUDE_PLUGIN_ROOT}/prompts/. State lives in ~/.claude/state/ops-deploy-fix/. Logs live in ~/.claude/logs/ops-deploy-fix/.
Plugin rules apply (see `claude-ops/CLAUDE.md`). In particular:
AskUserQuestionconfigureThe first positional argument selects the subcommand. If absent, present:
/ops:deploy-fix — what do you want to do?
[status]
[tail]
[configure]
[test]Subcommands:
| Subcommand | Purpose |
|---|---|
status | Dashboard of recent monitor runs, fixer dispatches, locks, budget |
tail | Follow the latest fixer log live |
configure | Re-run the wizard from /ops:setup Step 6.5a |
test | Send a synthetic failure through the pipeline (dry-run, no real fix) |
statusPrint a compact dashboard. Read all data via Bash; favor parallel reads.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OPS ► DEPLOY-FIX STATUS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Master switch: <on|off> (deploy_fix_enabled)
Auto-dispatch: <on|off> (auto_dispatch_fixer)
Danger flag: <on|off> (allow_dangerous)
Budget cap: <N>/hour/repo (max_fixes_per_hour)
Notify channel: <macos|ntfy|discord|none>
Fix model: <haiku|sonnet|opus>
Last 5 monitor runs:
2026-04-26T08:14Z owner/repo-a:dev merge-watch deploy-success
2026-04-26T08:11Z owner/repo-b:main build-watch transient → rerun
...
Last 5 fixer dispatches:
2026-04-26T08:09Z owner/repo-b:main build-fix.md in-flight pid 41123
2026-04-26T07:42Z owner/repo-a:dev deploy-fix.md done log:fix-...log
Active locks:
owner/repo-b:main:build pid=41123 (alive)
Hourly budget remaining (this hour, <YYYYMMDD-HH>):
owner/repo-a 2 / 3
owner/repo-b 1 / 3
──────────────────────────────────────────────────────Data sources:
PREFS=~/.claude/plugins/data/ops-ops-marketplace/preferences.json
STATE=~/.claude/state/ops-deploy-fix
LOGS=~/.claude/logs/ops-deploy-fix
# Config snapshot (all keys read with defaults from plugin.json userConfig)
jq -r '{
deploy_fix_enabled, auto_dispatch_fixer, allow_dangerous,
max_fixes_per_hour, notify_channel, fix_model
}' "$PREFS" 2>/dev/null
# Monitor runs — last 5 across all repos
ls -t "$LOGS"/monitor-*.log 2>/dev/null | head -5 | while read f; do
printf '%s %s\n' "$(stat -f '%Sm' -t '%FT%RZ' "$f" 2>/dev/null || stat --format='%y' "$f" 2>/dev/null | awk '{print $1"T"$2"Z"}')" "$(basename "$f")"
done
# Fixer dispatches — last 5
ls -t "$LOGS"/fix-*.log 2>/dev/null | head -5 | while read f; do
pid_file="$STATE/lock-$(basename "$f" | sed 's/^fix-//; s/-[0-9]*\.log$//')"
status="done"
[ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" 2>/dev/null && status="in-flight"
printf '%s %s %s\n' "$(stat -f '%Sm' -t '%FT%RZ' "$f" 2>/dev/null || stat --format='%y' "$f" 2>/dev/null | awk '{print $1"T"$2"Z"}')" "$status" "$(basename "$f")"
done
# Active locks
for f in "$STATE"/lock-*; do
[ -f "$f" ] || continue
pid=$(cat "$f")
if kill -0 "$pid" 2>/dev/null; then
echo "$(basename "$f" | sed 's/^lock-//') pid=$pid (alive)"
fi
done
# Hourly budget — read all budget files for current hour
hour=$(date +%Y%m%d-%H)
cap=$(jq -r '.max_fixes_per_hour // 3' "$PREFS")
for f in "$STATE"/budget-*-"$hour"; do
[ -f "$f" ] || continue
slug=$(basename "$f" | sed "s/^budget-//; s/-$hour\$//")
used=$(cat "$f")
echo "$slug $((cap - used)) / $cap"
doneAfter the dashboard, if any lock is older than the configured watcher_timeout_seconds, surface a ⚠️ Stale lock: line and offer:
Stale lock detected for <repo>. Action?
[Inspect log]
[Clear lock (Rule 5 — destructive)]
[Leave it]
[Skip]Only on Clear lock should you delete the file (per-action confirmation per Rule 5).
tailTail the most-recent fixer log. Resolve via ls -t:
LATEST=$(ls -t ~/.claude/logs/ops-deploy-fix/fix-*.log 2>/dev/null | head -1)
if [ -z "$LATEST" ]; then
echo "No fixer logs yet. Trigger one with /ops:deploy-fix test or wait for a real failure."
exit 0
fi
echo "▸ Tailing $LATEST (Ctrl-C to stop)"
tail -f "$LATEST"Run via Bash with run_in_background: true and a long timeout so the user can read the stream as it grows. Print the resolved log path and the spawned shell PID up-front so the user knows what's running.
If a second positional arg --lines N is passed, do tail -n N -f. If --no-follow, drop -f.
configureRe-run the wizard from /ops:setup Step 6.5a, plus optionally 6.5b/c/d. Implementation: route into the setup skill with section filter.
Flow:
$PREFS_PATH. Print compact summary (same fields as status, no logs). What do you want to reconfigure?
[Deploy auto-fix wizard (6.5a)]
[Recap marquee (6.5b)]
[Task* reminder (6.5c)]
[Account rotation toggle (6.5d)]skills/setup/SKILL.md — same questions, same persistence, same Rule-3 "never silently skip" semantics. ✓ Reconfigured. Daemons & hooks pick up the new prefs on the next event — no restart required.Use run_in_background: true (Rule 4) for any CLI install / brew / curl / tmux source-file triggered along the way.
testSynthetic dry-run through the pipeline. No real fix dispatch — no agent runs. Confirms wiring: hooks fire → monitor classifies → notify channel pings → state files written → would-dispatch path logged.
Steps:
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-}"
[ -z "$PLUGIN_ROOT" ] && PLUGIN_ROOT=$(find ~/.claude -type d -name claude-ops 2>/dev/null | head -1)
COMMON="$PLUGIN_ROOT/scripts/lib/deploy-fix-common.sh"
[ -f "$COMMON" ] || { echo "✗ deploy-fix-common.sh missing — re-install the plugin"; exit 1; } What should the synthetic failure look like?
[Build failure (npm run build:* exit 1)]
[Deploy workflow failure (gh actions check_run)]
[Health check failure (curl /health 503)]
[Version mismatch (served SHA != merged SHA)]OPS_DEPLOY_FIX_DRY_RUN=1 and invoke the relevant trigger script: export OPS_DEPLOY_FIX_DRY_RUN=1
export OPS_DEPLOY_FIX_TEST_REPO="your-org/your-repo" # placeholder per Rule 0
export OPS_DEPLOY_FIX_TEST_BASE="dev"
case "$kind" in
build) bash "$PLUGIN_ROOT/bin/ops-deploy-fix-build-trigger" --synthetic ;;
deploy) bash "$PLUGIN_ROOT/bin/ops-deploy-fix-merge-trigger" --synthetic ;;
health) bash "$PLUGIN_ROOT/scripts/ops-deploy-monitor.sh" --synthetic-health ;;
version) bash "$PLUGIN_ROOT/scripts/ops-deploy-monitor.sh" --synthetic-version ;;
esacTrigger scripts must honor OPS_DEPLOY_FIX_DRY_RUN=1 by:
claude -p ... dispatch in dispatch_fix_agentwould-dispatch-<id>-<ts>.log to $LOGS_DIR instead[DRY-RUN] would notify <channel> instead of firing real notify (no actual outbound messages during test — Rule 6)_If the trigger scripts don't yet support --synthetic / OPS_DEPLOY_FIX_DRY_RUN, surface that as a known TODO in the output and degrade to "would-have-dispatched" log-only._
would-dispatch-*.log path and a one-line classification result (transient? dedup-hit? budget-exhausted? would-dispatch-template=<file>). Test complete. Next?
[Tail the would-dispatch log]
[Run another test]
[Show status dashboard]
[Done]/ops:setup (or /ops:deploy-fix configure) first.brew install jq in background (Rule 4) and retry.status flow above, requires Rule-5 confirmation to clear.discord selected but no webhook) → on status, surface as ⚠️ notify_channel=discord but no webhook URL configured and offer [Reconfigure now].~/.claude/plugins/data/ops-ops-marketplace/preferences.json, ~/.claude/state/ops-deploy-fix/*, ~/.claude/logs/ops-deploy-fix/*, ${CLAUDE_PLUGIN_ROOT}/scripts/lib/deploy-fix-common.sh, ${CLAUDE_PLUGIN_ROOT}/prompts/{build-fix,deploy-fix}.md~/.claude/state/ops-deploy-fix/lock-* (clear on stale)configure subcommand): the prefs file, via the merge pattern in Step 6.5~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.