Skip to content

feat(sync): abort a running sync from the profile card - #100

Merged
dash0-dev[bot] merged 4 commits into
mainfrom
feat/abort-running-sync
Oct 7, 2026
Merged

dash0-dev[bot] merged 4 commits into
mainfrom
feat/abort-running-sync

Conversation

@dash0-dev

@dash0-dev dash0-dev Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Summary

While a Two-Way / One-Way profile is syncing, its Pause button becomes Abort, so a sync can be stopped, its settings changed and restarted with Sync Now. Save and Reinstall refuse while a run is live (Rule 8), so until now a long initial sync left nothing to do but wait.

Changes

  • SyncAbort (pure core next to SyncRunLock): finds the run from its two lock holders plus their descendants (ps -A). A holder is only used once identified as this profile's run: the script by its <configPath> argument, or an app-launched rclone as the app's child syncing this profile's folder. A reused PID, a mount daemon, the app itself and launchd are never signalled.
  • Graceful first: SIGINT goes to rclone only, so bisync can save its listings. It never goes to tee (rclone would SIGPIPE) or to the lsjson probe. After 90 s, or on Force Stop (behind a confirmation), every process of the run gets SIGTERM, and SIGKILL follows 10 s later.
  • Rule 8 holds: the abort finishes only once no lock holder is alive, and only then calls removeStaleLocks (stale-only). No new run can start while an abort is in flight.
  • Not a failure: the run's last log lines are read before the abort ends, and they raise no error banner, notification or auto-fix --resync. The directory watcher doesn't restart the sync because of the run's own final writes.
  • Aborted initial sync: its RunAtLoad agent load waits for the next Sync Now (or a Save), so the sync doesn't restart the moment it was stopped.
  • ConfigSelfTest AC-AB1…AC-AB4, plus CLAUDE.md rule 9 and README.

Verification

  • Not compiled or run: this was written in a Linux sandbox with no Xcode/Swift. The CI build and SyncTray --self-test are the first real compile and test.
  • Syntax: tree-sitter-swift parse of the changed files, with no new errors.
  • Tests: the AC-AB1/AB4 logic and source pins were mirrored in Python. Each AB4 negative assertion fails when the code it pins is mutated.
  • Review: two independent review passes (no compile issues found; findings fixed).
  • Not verified: rclone's graceful-shutdown timing and exit code on a real bisync. Worth a manual run: abort a large initial sync, then a regular bisync, and check the next run has no --resync.

Known limits

  • The deferred agent load is in memory only. After an app restart, the schedule comes back at the next login or Save.
  • There is no Abort in the menu bar yet.

mthines and others added 4 commits October 7, 2026 05:33
While a Two-Way / One-Way run is live, the profile card's Pause button
becomes Abort, so a sync can be stopped, its settings changed (Save and
Reinstall refuse while a run is live) and restarted with Sync Now.

- SyncAbort (pure core) finds the run from its lock holders plus their
  descendants, identifies roots by name, and never targets the app or
  launchd
- graceful first: SIGINT to rclone only, so bisync saves its state;
  SIGTERM after 90 s or on Force Stop, SIGKILL 10 s later
- locks cleaned stale-only, and only after every process has exited
- an aborted run is not a failure: no error, notification or auto-fix,
  and the directory watcher does not restart it
- an aborted initial sync defers its RunAtLoad agent load to the next
  Sync Now instead of restarting at once
- ConfigSelfTest AC-AB1..AC-AB4, CLAUDE.md rule 9, README

Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
- identify run roots by identity: the script must carry this profile's
  config path, an rclone holder must be the app's own child
- finish once no lock holder is live; release non-rclone orphans (the
  watchdog sleep) instead of waiting, keep escalating a leftover rclone
- read the log to its end before the abort stops counting as in flight;
  a new run clears the trailing suppression
- refuse new runs while an abort is in flight; the card treats it as
  running
- Force Stop only escalates an in-flight abort (forceStopAbort)
- view launchers report aborted from isAborting, not the time window
- directory watcher leaves an aborted initial sync stopped

Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
- an app-launched rclone root must also name this profile's local folder
- never SIGINT the script's lsjson reachability probe
- show Stopping... at once, but the phase (and Force Stop) only once
  something of the run is visible
- refuse a script run while aborting before recording its trigger

Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
@dash0-dev

dash0-dev Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

✅ No issues found

Adds Abort / Force Stop for a live Two-Way or One-Way run: lock-holder identity, SIGINT then SIGTERM then SIGKILL, stale-only lock cleanup and run-end suppression. No blocking issues; one UX note on the timeout path.

1 advisory finding below the confidence bar — see Less certain below.

Checked: 7 of 7 changed files read · 5 possible issues → 0 confirmed → 0 posted

Less certain (1) — advisory, below the confidence bar
  • SyncTray/Services/SyncManager.swift:2097 — suggestion: On the 130 s timeout with the run still live, finishAbort returns silently: the card flips back to Abort with no notice, and timeout is logged at info. Tell the user it did not stop. (confidence 85)
Review details
Gate Status Details
Description vs. code ✅ Description matches the diff: abort flow, Rule 8 guards, log suppression, deferred agent load, AC-AB1-4, docs.
Prior review feedback ✅ Earlier review comments are resolved.
Documentation ✅ CLAUDE.md rule 9, the README entry and doc comments explain the abort flow and its limits.
Self-review signals ✅ No debug logs, leftover TODOs, or unreviewed stubs.
Code review ✅ The multi-lens review found no blocking issues.

Found

Quality — produced 5 → posted inline 0 · cleared 0 · carried forward 0 · deferred 0 · below-bar 1
Dropped — 3 below-bar
Optimality — ran — in-context pass (lens SKILL.md not loaded: tool budget); process-table identity vs. process-group signalling weighed, chosen approach judged fit (graceful phase must spare tee); 0 proposals
Standards — ran — 2 docs (CLAUDE.md, .claude/rules/telemetry.md) checked against all 7 files; 0 findings
Measurability — ran — in-context (lens SKILL.md not loaded: tool budget); abort emits profile.operation abort started/not_running/force_requested/success/forced/timeout and sync.skip_reason=aborted; no missing signal

Run

full · 1086 lines in delta · tier deep · depth checkout
⚠️ 2 prepare-time anomalies (reviewer identity unknown (no --reviewer-login / PR_REVIEWER_LOGIN; /user 401s here) — relation defaulted to cross, +1 more)
Integrations — none
Memories — no relevance rules or lessons consulted

Nothing to report — severity, reach diagram (impact graph found no consumer or importer), 0 files skipped.

pr-reviewer · commit a92a67a · full review · how these findings are produced · updated Oct 7, 2026 7:43am UTC

@dash0-dev
dash0-dev Bot marked this pull request as ready for review October 7, 2026 07:44
@dash0-dev
dash0-dev Bot requested a review from mthines as a code owner October 7, 2026 07:44
@dash0-dev

dash0-dev Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Darkplane auto-approval is enabled for this repository (mode: Auto-approve).

Trigger an evaluation for this pull request when it is ready:

Run auto-approval evaluation

@dash0-dev
dash0-dev Bot merged commit d3cfa8d into main Oct 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant