The Windows SQLite unit job failed because two new copyto tests asserted on
raw POSIX dest strings. On Windows `str(Path("/tmp/research"))` renders with
backslashes, so the copyto dest is `\tmp\research/notes/...` (rclone accepts the
mixed separators — the product is fine). Compare the local dest via `Path(...)`
like the other tests, which normalizes separators cross-platform.
Signed-off-by: phernandez <paul@basicmachines.co>
From the Codex and claude-review automated reviews on PR #917:
- project_diff: fail fast on fatal `rclone check` errors. A non-zero exit with
no combined listing (auth/network/missing-remote) previously produced an empty
plan, so the transfer ran as a no-op and reported success. Now raises
RcloneError with rclone's stderr. (Codex P2 / claude-review #2)
- sync-setup "Next steps": stop pointing every user at the now-Personal-only
`bm cloud sync`; lead with the Team-safe `pull`/`push` and note bisync is
Personal-only. (claude-review #1)
- Cosmetic: capitalize the push/pull abort headlines for consistency; document
the two ConflictStrategy definitions (Typer enum vs engine Literal). (nits)
- Tests: cover keep-local+pull and keep-cloud+push (preserve-destination cases),
project_transfer aborting on a mid-loop conflict-copy failure, and project_diff
raising on a fatal check error vs not raising on a normal differences exit.
Signed-off-by: phernandez <paul@basicmachines.co>
Second code-review finding (PLAUSIBLE): project_diff detects conflicts with
`rclone check` (content/hash), but project_copy used `rclone copy` with its
default size+modtime comparison. On an overwrite strategy (keep-cloud on pull /
keep-local on push), copy could skip a file the diff had flagged as a conflict
when sizes matched and the destination was not older — silently ignoring the
user's explicit choice.
Add `--checksum` to the overwrite copy so the transfer decision uses the same
content basis as detection. New-only mode is unaffected: `--ignore-existing`
skips by existence, so the comparison basis is irrelevant there. project_sync
(the Personal-only mirror) is untouched and keeps its default comparison.
Signed-off-by: phernandez <paul@basicmachines.co>
Code-review finding: project_copy_file built its `rclone copyto` command
inline and omitted `--local-no-preallocate`. On a `pull --on-conflict keep-both`
that copyto writes the conflict copy to the local filesystem, which is exactly
the case the flag guards (NUL byte padding on virtual filesystems such as
Google Drive File Stream). Every other transfer path adds it via
_build_transfer_cmd; bring copyto in line.
Signed-off-by: phernandez <paul@basicmachines.co>
Adds `bm cloud push` and `bm cloud pull` as git-style, fail-safe transfer
primitives that are usable on Team workspaces (issue #858), and restricts the
destructive `bm cloud sync` mirror to Personal workspaces.
Why
- `bm cloud sync` is a destructive local->cloud mirror; on a shared Team bucket
it can delete a teammate's files. The only pull path was two-way `bisync`,
which is already Personal-only (#849).
- Teams need a safe way to fetch teammates' notes and add their own without one
stale local tree becoming authoritative for shared cloud state.
What
- push = `rclone copy` local->cloud, pull = `rclone copy` cloud->local. Both are
additive (never delete on the destination), so neither can damage shared state.
- Conflicts (a file that differs on both sides) abort by default and list the
paths, like git refusing to clobber local changes / rejecting a stale push.
`--on-conflict {fail|keep-local|keep-cloud|keep-both}` lets the user decide;
no vague --force, no silent winner.
- `sync` now requires a Personal workspace; its guard and the bisync guard point
Team users at push/pull.
Limitations (surfaced in --help and command output, tracked by #862):
- No sync baseline yet, so deletions are not propagated and every divergence is
treated as a conflict rather than auto-resolved. Real three-way merge needs
the per-client manifest + Tigris snapshot baseline designed in #862.
Implementation
- rclone_commands.py: shared `_build_transfer_cmd`/`_transfer_endpoints`;
refactor `project_sync` onto them (no behavior change); add `project_diff`
(conflict detection via `rclone check --combined`), `project_copy`,
`project_copy_file`, and `project_transfer` (strategy dispatch).
- project_sync.py: new `push`/`pull` commands (ungated, Team-safe) with
`--on-conflict`/`--dry-run`; gate `sync` to Personal via a per-command guard
message.
- Tests at the rclone-argv and CLI-command levels; existing sync/bisync tests
updated for the new gating and messages.
Signed-off-by: phernandez <paul@basicmachines.co>