agentsclimarketplace

Checkpoint resume

Skill ulpi-io/autonomous-engineering/checkpoint-resume

Make long autonomous work durable and resumable: a live .ulpi/runs/<id>.json status file (per-unit + per-phase state, atomic locked writes via the bundled scripts/checkpoint.mjs CLI) that a resume reads to SKIP everything already done — session-independent. Status writes are non-fatal observability. Use for any multi-unit run worth resuming after a stop or crash.From its SKILL.md

Install
npx -y skills add ulpi-io/autonomous-engineering --skill checkpoint-resume

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 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.

SKILL.md

18.9 KB, ~4.5k tokens by cl100k_base, as published. Nobody here has run it

<EXTREMELY-IMPORTANT> The status file is the durable-primary record — but observability writes are never a work gate. For a git-integrating pipeline, reachable `Task-Id` trailers are the durable integration log and the status file is its reconciled cache. Non-negotiable: 1. RESUME MEANS RECONCILE, THEN SKIP-DONE. Read the existing status file; for a git pipeline reconcile it from reachable trailers; rebuild only units not proven `done`. NEVER overwrite it with a fresh all- `pending` document — that erases the checkpoint and redoes everything. 2. Durability is on DISK and session-independent. The canonical coordinator and legacy Workflow both recover a lost status write from `Task-Id` commits reachable on their integration branch. The portable Codex path is `node autonomous-pipeline/scripts/pipeline.mjs resume --run <id>` (surfaced by `run-status.mjs --resume`). Claude's `Workflow(...resumeFromRunId)` cache and external session journal are optional overlays — never a source of durable completion and never a resume dependency. 3. Status writes are NON-FATAL. A failed/racing write is logged and ignored — it MUST NOT block or fail the underlying work. Never gate delivery on a status write; never report a run failed solely because its status file is stale — reconstruct state from the actual artifacts instead. 4. Mark a unit done ONLY when it is actually complete and verified (its own check passed / it integrated) — never optimistically. The coordinator blocks a `done` integration with no reachable trailer; generic runners without that backstop can otherwise skip a false `done` forever. 5. A unit is eligible only when its dependencies are actually done. Never skip a unit as "done" whose prerequisite never landed — that builds on a missing base. </EXTREMELY-IMPORTANT>

Checkpoint Resume

Inputs

  • $target: For a NEW run, a short label (used to build the run id). For a RESUME, the run id or the path to an existing .ulpi/runs/<id>.json.

Goal

Turn a long, multi-unit task into one that can be stopped and restarted at will — from any session, after any interruption — without losing finished work or redoing it. The status file is the durable- primary progress view; git-integrating pipelines reconcile it from their reachable trailer log.

Step 0: New run or resume? (decide FIRST)

  • Resume if $target names an existing run id/file, or the user says "resume / continue". Do NOT re-initialize. Read the file, reconcile any git-backed integration state, and proceed to Step 3.
  • New run otherwise. Create a fresh status file (Step 1) and proceed.

Getting this wrong is the whole failure mode: a "resume" that writes a fresh pending document throws away the checkpoint. When in doubt, check for the file first.

Success criteria: mode determined; on resume, the existing file is loaded, not clobbered.

Step 0.5: Use the bundled CLI — don't hand-roll the file operations

This skill ships scripts/checkpoint.mjs (zero-dependency Node). It implements the whole contract — atomic writes, refusal to clobber a live checkpoint, refusal to demote a done unit, fail-closed finalize, and the resume-set computation — so USE IT instead of hand-rolling jq:

node <skill-dir>/scripts/checkpoint.mjs init  <file> --task "<desc>" [--units "a,b,c"] [--id <id>] [--launch '<json>']
node <skill-dir>/scripts/checkpoint.mjs unit  <file> <unit> <pending|in_progress|done|blocked|dep_blocked> [--note "…"] [--deps "x,y"]
node <skill-dir>/scripts/checkpoint.mjs phase <file> <phase> <pending|running|done|blocked|skipped>
node <skill-dir>/scripts/checkpoint.mjs get   <file> --summary
node <skill-dir>/scripts/checkpoint.mjs resume <file>     # → { skip, eligible, dep_blocked }
node <skill-dir>/scripts/checkpoint.mjs item  <file> --json '<object-or-array>'   # append durable openItems
node <skill-dir>/scripts/checkpoint.mjs finalize <file> <done|needs_attention|aborted> [--result "…"]
node <skill-dir>/scripts/checkpoint.mjs gc    <runs-dir> [--keep-days 7]  # archive old TERMINAL runs

Append || true at call sites — status writes are non-fatal. The CLI exits 2 (refuses) on the contract-violating operations: re-init over a live checkpoint, demoting a done unit, and finalize done while units are open. Those refusals are the guardrails, enforced in code.

Large-run opt-in. Keep the single atomic JSON file as the default. For plans where rewriting the snapshot on every transition is too costly, import scripts/lib/event-log.mjs, call initializeEventLog(file, doc, { enabled: true }), then appendTransition(...). It fsyncs one JSONL transition before atomically replacing the same reader-compatible .json snapshot; rebuildSnapshot replays after a crash and discards only a torn final non-newline fragment. The event path is explicitly opt-in and does not change run-status.mjs or the ordinary checkpoint CLI contract.

Everything is timestamped (ISO-8601 UTC): the doc (createdAt/updatedAt/finishedAt), each unit (createdAt/updatedAt + startedAt/finishedAt), each phase (startedAt/updatedAt/finishedAt), and each register item (at) — so a reader can show real durations and "updated 3m ago", not guesses.

The typed launch descriptor. Pass init --launch '<json>' to persist the exact relaunch recipe in the file, so the run can be resumed from the status file alone (session-independent). It is a typed object { scriptPath, args? }init validates it before touching disk (scriptPath must be a non-empty string; args, when present, a JSON object) and refuses an array/string/missing-scriptPath descriptor. To make a run RUNNABLE under the Codex coordinator, persist the coordinator recipe — a scriptPath whose basename is pipeline.mjs and args:{ command:"resume", run:"<id>" }:

node <skill-dir>/scripts/checkpoint.mjs init <file> --task "…" --units "a,b,c" --id my-run \
  --launch '{"scriptPath":"autonomous-pipeline/scripts/pipeline.mjs","args":{"command":"resume","run":"my-run"}}'

The pipeline coordinator's own approve stamps exactly this descriptor for you, so its runs are runnable out of the box. Any other descriptor (a Claude Workflow() script such as pipeline-workflow.js, or an absent/malformed one) is treated as MIGRATION-ONLY / non-runnable — see Step 0.6.

Step 0.6: Query a run the easy way — run-status.mjs (READ-ONLY)

To SEE where a run is (yours or one a pipeline left behind), use the bundled legible reader — it never writes, so it can't disturb a run in flight:

node <skill-dir>/scripts/run-status.mjs                 # newest run for this project, rendered
node <skill-dir>/scripts/run-status.mjs <id>            # a specific run (id prefix is enough)
node <skill-dir>/scripts/run-status.mjs --list          # every run, one line each, newest first
node <skill-dir>/scripts/run-status.mjs --json [id]     # the raw durable doc
node <skill-dir>/scripts/run-status.mjs --resume [id]   # print the Codex resume command for this run
node <skill-dir>/scripts/run-status.mjs --resume --json  # emit ONLY the typed resume descriptor (argv, no shell)

It auto-discovers .ulpi/runs/ by walking up from the cwd, renders phases + a per-task progress bar + the open findings register + a resume affordance. The default render is durable-primary, then adds a best-effort Live workflow overlay and a visible live-agent-vs-durable-unit divergence line. The overlay reads only external Claude journal.jsonl started/result envelopes; absence or format drift prints an honest no live workflow … use /workflows note and never changes durable status. The reader never opens agent transcripts, spawns Git, or writes. --resume classifies the persisted launch descriptor into exactly one of three resume recipes (it never fabricates a command):

  • Runnable (codex-cli) — the persisted launch is the coordinator recipe (basename pipeline.mjs, args.command==="resume"). --resume prints the shell-safe Codex command node pipeline.mjs resume --run <id> (the id is a discrete, quoted argv token — never string- interpolated into a shell); --resume --json adds the typed { runnable:true, kind:"codex-cli", argv:[…], shell, resumeSet } descriptor.
  • Legacy Workflow (legacy-workflow) — the launch is a Claude Workflow() script (e.g. pipeline-workflow.js). It is labeled MIGRATION-ONLY / non-runnable and is NEVER printed as a runnable shell command; the reader echoes the persisted {scriptPath,args} (with statusFile re-pinned to this run) for migration reference only. --resume --json{ runnable:false, migrationOnly:true, kind:"legacy-workflow" }.
  • No / malformed launch (no-launch) — nothing runnable was persisted (pre-coordinator, hand-rolled, or a malformed descriptor): non-runnable, with the computed skip/eligible resumeSet so a human can relaunch via pipeline.mjs approvepipeline.mjs start --run <id>.

Step 1: Initialize the status file (new run only)

Prefer checkpoint.mjs init. If you must write it by hand, pick a stable id: <label>-<UTC-timestamp> (get the timestamp from date -u +%Y%m%dT%H%M%SZ — never invent one). Write .ulpi/runs/<id>.json:

{
  "schemaVersion": 1,
  "id": "<id>",
  "task": "<one-line description>",
  "status": "running",
  "createdAt": "<UTC now>",
  "updatedAt": "<UTC now>",
  "units": {
    "<unit-id>": { "status": "pending", "dependsOn": [], "note": "", "createdAt": "<UTC now>", "updatedAt": "<UTC now>" }
  },
  "openItems": [],
  "result": null
}

(The CLI stamps createdAt/updatedAt on every unit and phase automatically — the fields above are what init writes; you never hand-maintain them.)

Enumerate the units up front when they're known (DAG tasks, files to migrate); for a discovery-driven run, start with units: {} and add them as they're found. Each unit's status moves pending → in_progress → done (or blocked / dep_blocked).

If the orchestrator that runs the work has no filesystem access (e.g. a sandboxed Workflow), the CALLER creates and updates this file — pass the absolute statusFile path in.

Success criteria: the file exists before any work starts, so a watcher sees the run immediately.

Step 2: Update as work lands (running)

After each unit reaches a terminal state, patch the file with the CLI (Step 0.5): node <skill-dir>/scripts/checkpoint.mjs unit <file> <unit> done || true — it owns locking, atomicity, and the refusals. The jq recipe below is the FALLBACK for environments without Node only; never prefer it when the CLI is available:

jq --arg u "<unit-id>" --arg s "done" --arg t "$(date -u +%Y%m%dT%H%M%SZ)" \
   '.units[$u].status=$s | .updatedAt=$t' .ulpi/runs/<id>.json > .ulpi/runs/<id>.json.tmp \
   && mv .ulpi/runs/<id>.json.tmp .ulpi/runs/<id>.json

Wrap writes so a failure is swallowed (|| true) — a status write must never abort the work. Update openItems with anything the run should carry forward (blocked units, findings), and status at the top level as phases progress.

Success criteria: at any instant, cat-ing the file shows the true current state; a crash here loses at most the in-flight unit.

Step 3: Resume — skip done, rebuild the rest

On resume, reconcile durable integration evidence, then compute the work set:

  1. For a git-integrating pipeline, scan Task-Id: <id> trailers reachable from the integration ref (coordinator: pipeline.integrationRef; legacy Workflow: workingBranch). A reachable trailer recovers a lost status write into done; the coordinator stamps reconciled-from-trailer:<sha> and blocks a stale checkpoint done with no reachable commit. A commit only on task/<id> did not integrate and stays eligible. Use the existing done state — never invent a separate integrated state or claim the merge and status write form one transaction.

  2. Load units. A unit is done → skip it entirely.

  3. ANY unit not donepending / in_progress / blocked / dep_blocked — whose dependsOn are all done → it's eligible; rebuild it. (in_progress was interrupted mid-flight — redo it. A dep_blocked unit whose dependency has since landed is eligible again — done is the ONLY state that skips.)

  4. A unit whose dependency is NOT done → dep_blocked, pointing at the missing root; do not build it on a partial base.

Then run only the eligible set through the same machinery as the original run, writing back to the SAME file (same id/path). The durable skip-done makes this independent of any runtime cache — a template edit or a fresh session doesn't force a full rebuild.

For a coordinator-owned run you don't recompute this by hand — the Codex-native path drives it for you:

node autonomous-pipeline/scripts/pipeline.mjs resume --run <id>   # reads the checkpoint, skips done units

run-status.mjs --resume <id> prints exactly this command when the run's persisted launch is the coordinator recipe; the coordinator re-reads the durable checkpoint and re-runs only the eligible set.

Resume behavior — the five cases (make these explicit)

SituationState in the fileWhat resume does
Fresh session — no runtime memory of the prior runany live checkpoint on diskDrive off durable DISK state, not a session cache. node pipeline.mjs resume --run <id> reads the file, reconciles reachable Task-Id trailers, then skips proven-done units; Workflow(...resumeFromRunId) is a Claude-only cache, never the resume you rely on.
Interrupted unit — died mid-flighta unit left in_progressRe-run it. in_progress is NOT done — only done skips. The at-most-one in-flight unit is redone; everything done before it is skipped.
Dependency-blockeda unit whose dependsOn are not all doneMark it dep_blocked pointing at the missing root and do NOT build it — never build on a partial base. When the prerequisite later lands, it becomes eligible again automatically.
Malformed / absent recipelaunch missing, an array/string, or not the coordinator shaperun-status.mjs classifies it no-launchnon-runnable; it emits the computed resumeSet and points at pipeline.mjs approve/start. It NEVER fabricates a runnable command.
Legacy recorda v1 doc, or a launch that is a Claude Workflow() scriptA v1 doc loads, resumes and finalizes UNCHANGED (see below). A Workflow launch is classified legacy-workflowMIGRATION-ONLY / non-runnable: echoed for reference, never printed as a runnable Codex command.

v1 → v2 migration (add-only, non-breaking)

New runs are written at schemaVersion: 2 (stable-id findings + durable resolvedItems + the typed launch). A schemaVersion: 1 run is fully supported: it loads, resumes and finalizes byte-for-byte unchanged on a READ. The ONLY change ever applied is on a mutating write, where the store does an idempotent, ADD-ONLY in-place upgrade — it adds the missing resolvedItems: [] and bumps schemaVersion — and NEVER rewrites existing units/phases/openItems/launch. An in-flight v1 run is therefore never broken by an upgrade; its finished units stay skipped and its open work stays open. A v1 run with no persisted launch simply resumes via the no-launch path above.

Success criteria: finished units are provably not redone; only the remaining/eligible set runs; a legacy/v1 record resumes without loss and without being shown a fabricated command.

Step 4: Finalize

When the work set is exhausted, write the terminal state: status = done (all units done) / needs_attention (some blocked/open) / aborted; populate result and the final openItems. This file is the run's durable record — tell the user where it lives.

If a run died without a final write, reconstruct the terminal state from the artifacts (git log, the units' own checks) rather than trusting a stale running — see references/status-schema.md.

Success criteria: the file reflects the real end state and enumerates anything still open.

Guardrails

  • Never overwrite an existing run's file with a fresh pending document on resume.
  • For git-integrating pipelines, reconcile reachable Task-Id trailers before computing the resume set; never make the live session journal a resume dependency.
  • Never mark a unit done before it is complete AND verified.
  • Never skip a unit whose dependency didn't actually land.
  • Never let a status-write failure block, abort, or fail the underlying work.
  • Never fabricate a timestamp — read the clock (date -u).
  • Never treat a stale/running file as ground truth after a crash — reconstruct from artifacts.
  • Keep writes atomic (tmp + mv) and incremental (jq patch, not full rewrite) to survive races.

When To Load References

  • scripts/checkpoint.mjs The runnable implementation of this contract — init/unit/phase/get/resume/finalize with atomic writes, timestamped mutations, and code-enforced refusals. Prefer it over hand-rolled file operations, always.
  • scripts/run-status.mjs The READ-ONLY legible reader (Step 0.6): renders the newest run (or <id>/--list/--json), and --resume classifies the persisted launch and prints the Codex-native coordinator resume command (node pipeline.mjs resume --run <id>) for a runnable run, or a migration-only/non-runnable pointer otherwise — never a fabricated command. Never writes — safe to run against a live run.
  • references/status-schema.md The full status-file schema (per-unit states, dependsOn, openItems, phase blocks), the three verbs (status / stop / resume), atomic-write recipes, and how to rebuild the file from artifacts after a crash. Load when defining a run's schema or writing the resume logic.

Output Contract

Report:

  1. run id + status-file path
  2. new run vs resume; on resume, how many units were skipped-done vs rebuilt
  3. final status (done / needs_attention / aborted) and where the durable record lives
  4. any open/blocked units carried in openItems

What ships with it: 6 files

78.9 KB alongside SKILL.md, 5 of them executable

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.