Skip to content

Running S9TerpSync

s9terpsync-release-bot edited this page Aug 5, 2026 · 1 revision

Running S9TerpSync

This page is for the operator doing a manual run. It covers the three run commands — run, run resume, and run recover-lock — and, most importantly, how --dry-run and --live differ across all three of them. It doesn't cover scheduled or service-supervised runs (that's Service & Systemd Operation), and it doesn't cover cleaning up after a partially-failed run beyond run resume itself (that's Retention, Replay & Recovery).

Dry-run vs. live

Every command on this page takes exactly one of --dry-run or --live — never both, never neither. Passing both, or passing neither, fails before anything runs, with a message to the effect of "specify exactly one of --dry-run or --live" and exit code 1.

  • DRY_RUN walks the same logic as a live run — reading the source, matching records, planning what would change — but never writes to Ethos/Banner. For run and run resume, that's the only difference from --live: both modes acquire the shared run lock before doing anything else, so a DRY_RUN still contends with any other in-progress run, run resume, or retention run, and can fail with run lock already held, exit 3, just like --live would (see Diagnostics & Troubleshooting). run recover-lock is the one exception on this page — both of its modes go through a separate recovery mechanism and never acquire the shared run lock at all; its DRY_RUN reports which locks are eligible for recovery without recovering them. Aside from that exception, dry-run is safe to run repeatedly and is the right default for validating a configuration or investigating a stuck run before committing to anything irreversible.
  • LIVE performs the real mutation: run and run resume write to Ethos (which Banner then reflects); run recover-lock actually clears a stale run lock so a new run can start.
flowchart TB
  trigger["Operator runs a command<br/>run · run resume · run recover-lock"]
  mode{"--dry-run or --live?<br/>exactly one is required"}
  dryPath["DRY_RUN<br/>plan/report only, no writes<br/>(run/run resume still take the run lock)"]
  dryOutcome["Outcome reported<br/>nothing in Ethos or Banner changed"]
  livePath["LIVE<br/>the real mutation runs"]
  liveOutcome["Outcome reported<br/>Ethos/Banner updated, or the stale lock recovered"]

  trigger --> mode
  mode -->|--dry-run| dryPath
  mode -->|--live| livePath
  dryPath --> dryOutcome
  livePath --> liveOutcome

  classDef pipeline fill:#EEEDFE,stroke:#534AB7,color:#26215C
  classDef mutation fill:#FAECE7,stroke:#993C1D,color:#4A1B0C
  classDef failure fill:#FAEEDA,stroke:#854F0B,color:#412402
  classDef readonly fill:#E1F5EE,stroke:#0F6E56,color:#04342C
  classDef neutral fill:#F1EFE8,stroke:#5F5E5A,color:#2C2C2A
  class trigger,mode neutral
  class dryPath,dryOutcome readonly
  class livePath,liveOutcome mutation
Loading

run

s9terpsync run --file <path> [--source <source>] (--dry-run | --live)

The manual intake run: reads the configured source once, matches and plans changes, and (in --live mode) writes them to Ethos.

  • --file — path to s9terpsync.yaml. Not enforced as a CLI-required option, but the command fails immediately (run failed, exit 1) if it's omitted — treat it as required.
  • --source (optional) — which configured source to read from: sftp or slate-api. Any other value fails with run source: specify sftp or slate-api. If omitted, the source is auto-detected from the config: if exactly one of sources.sftp.enabled/sources.slateApi.enabled is true, that one is used automatically; if the config has more than one source enabled, you must pass --source explicitly, or the command fails with run source: specify --source when multiple sources are enabled.
  • --dry-run / --live — exactly one is required (see Dry-run vs. live above).

On success it prints two lines:

run completed <runId> <status> files=<n> records=<n> succeeded=<n> failed=<n>
report path=<relativePath> checksum=<checksum> sizeBytes=<n>

<status> is one of NO_DATA (nothing new to process), COMPLETED (everything succeeded), or COMPLETED_WITH_ERRORS (some records failed). A COMPLETED_WITH_ERRORS status also makes the command exit 2 — see Diagnostics & Troubleshooting for what each exit code means across the CLI.

Example:

s9terpsync run --file ./s9terpsync.yaml --source sftp --dry-run

run resume

s9terpsync run resume <run-id> --file <path> (--dry-run | --live) [--retry-token <token>]... [--retry-manifest <id>] [--retry-all]

Resumes a prior Slate API run — typically one left incomplete after a failure — instead of starting a fresh one. <run-id> (positional) is the run to resume.

  • --file (required) — path to s9terpsync.yaml.
  • --dry-run / --live — exactly one is required, same rule as run above.
  • --retry-token <token> — an opaque selected-record retry token, identifying one specific record to retry. Repeat the flag to retry several records (--retry-token abc --retry-token def); duplicate tokens are rejected.
  • --retry-manifest <manifest-id> — an opaque retry manifest ID (created with replay retry-manifests create; see Retention, Replay & Recovery) selecting the set of records it lists.
  • --retry-all — retry every currently eligible record from the original run, with no explicit selection needed.

--retry-token, --retry-manifest, and --retry-all are mutually exclusive — combining any two of them (for example --retry-all with a --retry-token, or --retry-manifest with either of the others) fails with run resume failed, exit 1. Passing none of the three is allowed — it resumes the run without selecting specific records to retry.

On success it prints:

run resume sourceRunId="<run-id>" resumedRunId="<new-run-id>" status="<status>" files=<n> records=<n> succeeded=<n> failed=<n>
report path=<relativePath> checksum=<checksum> sizeBytes=<n>

As with run, a COMPLETED_WITH_ERRORS status makes the command exit 2.

Example:

s9terpsync run resume 2026-08-01T02-00-00Z --file ./s9terpsync.yaml --retry-all --live

run recover-lock

s9terpsync run recover-lock --file <path> --older-than-minutes <minutes> (--dry-run | --live)

Recovers a run lock that's been held longer than expected — for example, after a process crashed mid-run without releasing it — so a new run can start. This is the manual, CLI-driven counterpart to service recover-lock (covered on Service & Systemd Operation); this one targets the CLI's own run lock, not the service's.

  • --file (required) — path to s9terpsync.yaml.
  • --older-than-minutes (required) — a positive integer. Only locks held longer than this many minutes are eligible for recovery; anything else (zero, negative, non-numeric) fails validation.
  • --dry-run / --live — exactly one is required, same rule as above. DRY_RUN reports which locks are eligible without touching them; LIVE actually recovers them.

On success it prints:

run lock recovery <mode> status=<status> scanned=<n> eligible=<n> recovered=<n> skipped=<n> failed=<n>

In LIVE mode, if any recovery attempt failed, the command exits 2.

Example:

s9terpsync run recover-lock --file ./s9terpsync.yaml --older-than-minutes 60 --dry-run

Where to go next

  • Retention, Replay & Recovery — inspecting an incomplete run's records, creating the retry manifests run resume --retry-manifest consumes, and the separate retention commands.
  • Service & Systemd Operation — running S9TerpSync unattended on a schedule instead of by hand, and the service's own lock recovery.
  • Diagnostics & Troubleshooting — what each exit code means, and how to check a config against your live environment before you run it for real.

Clone this wiki locally