resume — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resume (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.
The recovery entry point. State lives entirely in state.json + the filesystem work queues, so resume is deterministic: re-read state, repair the few crash-fragile spots, and hand back to run. It is idempotent - running it twice is safe. It is a standalone skill, distinct from run: the dir is skills/resume/, the frontmatter name is resume, the suffix is /claude-migrate:resume, and commands/resume.md's Skill-tool target is the string resume (Repo H-3). It NEVER forces a state change that the controller would refuse - a FAILED gate stays failed until the user fixes the cause.
/claude-migrate:resume <run-name>
<run-name> is required. RUN_PATH=".planning/claude-migrate/<run-name>". Abort if state.json is missing - tell the user to /claude-migrate:init first.
Invoke the progress skill via the Skill tool (read-only) so the user sees exactly where the run paused: current_step, status, blocked_reason, the queue counters, and the gate verdicts. This is the orientation step before any repair.
EXIT traps in claim.sh / state.sh / release.sh / requeue.sh do NOT fire on kill -9 or power loss, leaving orphan lock dirs that make every subsequent state.sh inc/set/dec spin for 30s. Clear them BEFORE any state write. Candidate lock dirs:
$RUN_PATH/state.json.lock.d$RUN_PATH/units/.claim.lock.d$RUN_PATH/seed/.claim.lock.d$RUN_PATH/project/<PNN__slug>/.create.lock.dFor each, apply the heuristic: if a live holder PID is recorded and still running, leave it; else if the lock dir is older than 30s with no live holder, rmdir it (orphan from a crash); a fresh lock from a live worker is preserved:
heal_orphan_lock() {
local lockdir="$1"
[[ -d "$lockdir" ]] || return 0
local pidfile="$lockdir/holder.pid"
if [[ -f "$pidfile" ]]; then
local pid; pid=$(cat "$pidfile" 2>/dev/null)
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
echo "[resume] lock $lockdir held by live PID $pid - leaving alone" >&2; return 0
fi
fi
local age_s mtime
if stat -f '%m' "$lockdir" >/dev/null 2>&1; then mtime=$(stat -f '%m' "$lockdir") # macOS / BSD
else mtime=$(stat -c '%Y' "$lockdir"); fi # GNU / Linux
age_s=$(( $(date +%s) - mtime ))
if [[ "$age_s" -gt 30 ]]; then
echo "[resume] orphan lock at $lockdir (age=${age_s}s, no live holder) - removing" >&2
rmdir "$lockdir" 2>/dev/null || rm -rf "$lockdir"
else
echo "[resume] lock $lockdir is fresh (${age_s}s) - likely a live worker; leaving alone" >&2
fi
}Idempotent: a live worker's lock survives, a crash orphan is cleared. Deeper diagnostics (counter drift) belong to /claude-migrate:health; resume's heal is intentionally narrow.
Crashed workers leave units in units/in-progress/ (preflight/distill) and seed items in seed/in-progress/ (apply). For each, requeue back to pending/ via release.sh so the counters stay invariant - never mv by hand:
for f in "$RUN_PATH"/units/in-progress/*.md; do
[[ -e "$f" ]] || continue
bash ${CLAUDE_PLUGIN_ROOT}/bin/release.sh "$f" requeue resumed-orphan
doneFor seed/in-progress/ items, do NOT blindly requeue - they may be mid-apply and require the per-seed inspection in Step 4 first. If a live apply worker may still be running in this session, do NOT requeue its in-progress units; only requeue after confirming no apply is active.
seed/UNNN.json is the only resume authority; apply/UNNN.result.json is a report artifact only. For each seed/UNNN.json, branch on status (resume rules §3.5):
done → skip.renamed → skip (rename already applied).awaited_ok (not renamed) → re-run rename ONLY. Never re-seed. Rename is idempotent.seeded (not awaited_ok) → re-poll for the first assistant turn, bounded by ok_wait_ms. On timeout, leave status=seeded + last_error=ok_timeout (never failed).opened → AMBIGUOUS (crash between submit and the seeded write). Run SINK dedupe_probe FIRST via the sink adapter; if a matching destination chat exists, adopt it (status=seeded, record dest_chat_url) and DO NOT re-submit (C-2). Only if no match exists may the unit be re-seeded.rate_limited → set status=pending (re-claimable; never failed).in-progress/pending → leave for apply to re-claim.Drive these through apply-unit (invoked via the Skill tool in-session) so the same write-ahead logic applies; do not hand-edit seed/UNNN.json.
If status == blocked AND blocked_reason is a user gate (filter-gate, auto-reoffer, login, browser-lost, cost), hand to confirm rather than run:
Run /claude-migrate:confirm <run-name> to clear the <blocked_reason> gate.Invoke skill claude-migrate:confirm with argument <run-name> via the Skill tool. (For login/browser-lost, confirm re-probes the browser per references/login-policy.md.) Do NOT clear a FAIL machine-gate or skip it - resume never magically advances past a failed gate.
If input.mode == "live" and the run was parked waiting for an export ZIP, and the user has now placed the unzipped export, set input.mode=export + input.export_path, stage source/ (NEVER users.json), swap source-connector.md to the export template, and continue. This is the preferred live path (trigger the official export, drop it, resume).
If the run is not blocked on a user gate, invoke skill claude-migrate:run with argument <run-name> via the Skill tool. It picks up from current_step.
name, invocation suffix, and command target are ALL the string resume (Repo H-3) - resume is a standalone skill, not an alias of run.seed/UNNN.json is the SOLE resume authority - never trust apply/UNNN.result.json for resume decisions, and never hand-edit seed/UNNN.json.opened seed unit MUST run dedupe_probe before any re-seed (C-2) - never blind-resubmit.awaited_ok unit re-runs ONLY rename; a seeded unit re-polls (never re-seeds); a seeded await timeout stays seeded (never failed).release.sh (counter-safe) - never mv by hand; never requeue a unit a live worker may still hold.confirm; everything else hands to run.state.json outside bin/state.sh. Never reference any specific domain; labels come from config.yaml.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.