stowe 0.3.2

where git chokes, stowe stows - versioned, deduped big and binary files, pushed to backups you can still play (mirror) or compact blob stores (S3)
<!--
AI-ONLY DOCUMENT. This file exists to give an AI agent the COMPLETE operating picture for this repo. Optimize for completeness and precision for the agent, not for human readability. Humans read README.md instead. Do not remove detail to make this nicer — err toward more explicit, not less. FORMAT: machine-read, not a formatted human doc. Do NOT hard-wrap lines to a column width for readability; put each rule/point on ONE line, however long.
-->
# AGENTS.md

## Hard rules
- **Commit only when the user says ship.** Commits go in once the changes are
  tested, which is normally right when they're about to ship; never as a mid-work
  checkpoint.
- Release flow, in this exact order: `cargo clippy` warning-clean + `cargo test`
  green → bump `version` in `Cargo.toml` → one commit (short conventional
  message, never co-authored) → `git push origin main``cargo publish`
  (dry-run first; publishing is irreversible) → **tag only after publish
  succeeds**: `git tag vX.Y.Z && git push origin --tags`. A tag must never
  point at a version that failed to publish.
- Fix the root cause. If a workaround must ship, say the word "workaround" out
  loud, so a silent patch never passes as a real fix. Same for lints: never
  `#[allow]` a warning away; delete or fix the code it points at.
- **Never test against real user data.** Use throwaway scratch dirs and the
  release binary, never the real library or an external local copy; don't
  reinstall to test.
- **No em-dashes** anywhere user-facing (README, --help, crate description,
  commit messages, prose) - they read as AI-generated text.
- Every bug fix gets a test - throwaway bash checks let real regressions
  through; that's why tests/cli.rs exists.
- Linux-first; Windows deliberately unsupported until it can actually be
  tested on Windows.

## Invariants and gotchas
- When checking whether a remote is available: **a folder existing proves
  nothing** - unmounting leaves the mountpoint dir behind, so a bare directory
  check can target the wrong disk. Proof is the remote's on-disk `.stowe/`
  marker plus the local last-push record (`.stowe/remotes/<name>`); a known
  remote whose marker is gone must error, never be recreated.
- When touching `--mount` handling: the script is the sole authority - stowe
  runs it and trusts the exit code, no folder-based second-guessing. Scripts
  must be idempotent (instant no-op when already mounted).
- When changing mirror sync: plan against the mirror's *actual* files, not
  only its manifest - otherwise it can't repair a drive someone deleted or
  corrupted files on.
- When changing drift detection: judge drift against the commit being
  *pushed*, not just the recorded snapshot - otherwise adapt → commit → push
  dead-ends on the file that was just adopted.
- When touching any tree walk: `.stoweignore` (parsed in `src/ignore.rs`) applies to the working tree *and* to every mirror walk (`mirror_sizes`, `adapt`) - a phone regenerates `.thumbnails/` constantly, so junk that is ignored locally but not remotely reads as drift and demands `--force` on every push. Ancestors are checked, so a file inside an ignored dir is ignored even when the caller didn't prune. An explicitly named path (`stowe add junk.tmp`) is still staged.
- When touching scan/status: `status` never decodes audio; only `add`
  fingerprints (decoding dominates import cost), cached by size+mtime. The
  fingerprint is blake3 of the first ~30s of decoded PCM - survives
  rename/re-tag, not re-encode.
- When optimizing tree walks: keep `read_dir` + `DirEntry::metadata`
  (dirfd-relative stat; full-path stats are ~5x slower on deep FUSE trees) and
  keep the walk sequential - a FUSE daemon serializes, so parallel walking is
  *slower*. Only content hashing is parallel.
- When writing files to a mirror: names legal on ext4 can be unstorable on
  exFAT/NTFS (control chars etc.) - push probes the target FS empirically and
  offers a rename fix; never let a raw `os error 22` reach the user.
- tokio stays quarantined in the object-store remote code; the rest of the
  program is synchronous by design.
- Known, accepted bug: mtime is cached at whole-second resolution, so a
  same-size in-place edit within one second is missed.

## Build / test (verified)
    cargo build --release       # binary at target/release/stowe
    cargo test                  # unit tests in src files + end-to-end in tests/cli.rs

## Overview
`stowe` is a Rust CLI on crates.io: git for the files git chokes on (music,
photos, video, datasets). Content-addressed, linear history (one `main`, no
branches, no content diffs). A remote is either a **mirror** (real playable
folders on a drive/phone, bookkeeping hidden in a `.stowe/` beside them) or a
**backup** (deduped blobs, e.g. S3). Local remotes default to mirror, s3 to
backup; `--format` overrides, `convert` flips a remote in place. AGPL-3.0-only.

## Self-repair
If anything here contradicts the code, the code wins - fix this file in the
same session you notice the drift.