# courieR 0.3.1

## Bug fixes

* Fixed a second cause of shipped packages never showing up as installed, on
  the *copy* path (offline/preserve modes and online mode's local/private
  packages). A manifest's `libpath` is the library directory shared by every
  package, but `.copy_plan()` passed it through as if it were the per-package
  source, so each "copy" recursively cloned the entire source library into
  `target/<pkg>/` — leaving no valid package where R looks and reporting a
  silent success. The package name is now joined onto the library path, which
  also restores the cross-major compiled-package warning (its `libs/` check was
  always false before). Covered by a `.copy_plan` + `copy_packages` regression
  test and an opt-in browser end-to-end test (`COURIER_E2E=true`).
* R installation detection no longer drops installs intermittently on slow
  machines. `find_routes()` probed each candidate with a hard 3 s timeout and
  silently discarded any that exceeded it, so a cold R start (antivirus,
  OneDrive-synced installs) made versions flicker in and out between runs. The
  default probe timeout is now 30 s, configurable via
  `options(courier.probe_timeout = )`, and a probe that does time out is
  reported with a warning instead of vanishing; the `rig list` timeout was
  also raised from 5 s to 15 s.
* Fixed shipped packages never showing up as installed: `find_target_lib()`
  and the pak install subprocess inherited the parent R session's
  `R_LIBS_USER`, so `ship()` installed into the *parent's* library while
  Compare scanned the real target. All subprocesses that launch another R now
  share one environment-stripping helper (`child_r_env()`), and the install
  destination is guaranteed to be a library that `manifest()` scans.
* Library scans that time out are no longer cached as "empty library" for the
  rest of the session; timeouts are reported loudly in the log and the scan
  timeout was raised from 30 s to 5 min for slow machines.
* The "both installations share one library" warning no longer fires
  vacuously when both scans returned no packages.

## Routing

* Online mode now means online: every package resolvable from CRAN /
  Bioconductor / GitHub is reinstalled via pak (binaries preferred). Only
  local/private packages with no online source are copied from the source
  library. (Previously pure-R CRAN packages were file-copied even in online
  mode, which is much slower than pak on synced/network libraries.)

## Performance

* One manifest scan per library per session, shared across all tabs: a ship
  right after Compare reuses the Compare scans and spawns no scan
  subprocesses; the post-ship refresh rescans only the shipped-into library.

## Dashboard UX

* **Bulk Dispatch** now shows a live hero panel during shipping (matching
  Custom Dispatch): packages delivered so far, the current package being
  installed, elapsed time, and the estimated total. The detailed log in the
  R console is also mirrored in the dashboard log pane.
* Both **Bulk Dispatch** and **Custom Dispatch** tables now default to
  showing only packages missing from the target after Compare, instead of
  all diff statuses. The filter chip bar lets you toggle other statuses.
* **Custom Dispatch** filter chips are now colour-coded — orange for
  source-side differences (newer in source, not in target), teal for
  target-side differences — matching Bulk Dispatch.
* **Custom Dispatch** comparison table gains a **Repo** column showing where
  the package comes from (CRAN, Bioconductor, GitHub, etc.). Packages with
  an unknown source are shown in red in both tables.
* `report_issue()` — new exported function. Call it from the console after
  any error to open a pre-filled GitHub issue form in your browser with your
  R version, platform, courieR version, and the error message already
  populated. The dashboard's error modal gains a **Send Report** button that
  does the same in one click.
* Custom Dispatch shows a live green hero panel while shipping: packages
  delivered so far, the package currently being copied/installed, elapsed
  time, and the estimated total.
* Time estimates (plan summary, ship confirmation, preview invoice) now come
  from per-machine calibrated rates persisted across sessions — every
  completed ship updates them — instead of fixed constants that were off by
  100x on synced/network drives.
* Custom Dispatch source/target header now shows the R version, install
  location, and library path of both installations (was the bare executable
  path).
* After a Custom Dispatch shipment the comparison tables refresh
  automatically and the depot log says so; the refresh is logged when it
  completes.
* `library(courieR)` startup message now includes a link to the user guide
  vignette (`vignette("get-started", package = "courieR")`).

# courieR 0.3.0

## Dashboard UX

* Flattened the dashboard navigation from three nested tab levels to a single
  row of five top-level tabs: **Bulk Dispatch**, **Browse**, **Custom
  Dispatch**, **Manifest**, and **Maintenance**. The former **Advanced** wrapper
  tab is gone.
* Renamed **Dispatch** → **Bulk Dispatch**, and the former **Ship** sub-tab is
  now the top-level **Custom Dispatch** tab.
* **Custom Dispatch** now uses a two-pane layout — the package table on the left
  and a live log panel (with a delivery receipt) on the right — mirroring Bulk
  Dispatch. Its comparison columns are labelled **Source** / **Target** (was
  *Version A* / *Version B*), its filter chips read "newer in source / newer in
  target / not in target / not in source", and the redundant `A → B` context bar
  was removed.

## Performance

* Shipping no longer upgrades the entire dependency tree of each selected
  package: the dashboard now calls `ship()` with `upgrade = FALSE`. Selected
  packages still install or upgrade to the latest compatible version, but
  their already-installed dependencies are left alone unless a version
  requirement forces a change. This removes the main cause of multi-minute
  single-package shipments.
* Custom Dispatch now scans each library at most once per Ship click and
  passes the scans to `ship()` (`source_pkgs`/`target_pkgs`), instead of
  re-scanning both libraries for every batch.

## Bug fixes

* `manifest()` no longer leaks the parent R session's library/home environment
  into the target R subprocess. Previously `processx` inherited `R_LIBS_USER`
  (and `R_HOME`) from the R running courieR, so every probed installation
  reported the parent's library — making distinct R versions appear to share one
  library and compare as 100% identical. The subprocess now strips
  `R_LIBS_USER`, `R_LIBS`, `R_LIBS_SITE`, and `R_HOME` while still reading the
  target R's own `.Renviron`/`.Rprofile`.
* Dashboard: fixed "Operation not allowed without an active reactive context"
  on startup, caused by reading a `reactiveVal` outside a reactive consumer in
  the sync log helper (now wrapped in `isolate()`).

## Dashboard UX

* Renamed the core actions to match courieR's shipping vocabulary:
  **Scout** (detect installations), **Inventory** (compare libraries), and
  **Ship** (transfer packages). The main tab is now **Dispatch**, and the
  Advanced tabs are **Restock**, **Depot**, **Delivery Receipt**, **Route**,
  and **Manifest**.
* Detection is no longer automatic on startup; click **Scout** to scan. A
  `Scout → Inventory → Ship` workflow note appears in the control panel, and the
  result is shared across tabs.
* The logo twinkles while the app is busy (tied to Shiny's busy/idle events).
* Comparison table: per-column filters (search boxes for package/versions, a
  dropdown for status), pagination grouped compactly below the table, and the
  global search box removed. The log panel sits beside the comparison, spanning
  its full height.
* Transfer mode options shortened (Online reinstall / Offline copy / Preserve
  version) with a live description of the selected mode.
* `find_routes()` is called once per scan and shared across modules instead of
  being re-run by each, reducing startup/detection time.

# courieR 0.2.3

## New features

* `ship()` and `migrate()` gain a `mode` argument:
  - `"online"` (default) — reinstall packages via pak from CRAN/GitHub/Bioconductor.
  - `"offline"` — copy package directories by file; packages with no valid source path are skipped and reported.
  - `"preserve"` — copy first to keep exact versions; fall back to a pinned pak spec (`pkg@version`) for packages that cannot be copied.
  The dashboard Sync tab exposes the same three options as a dropdown.
* `migrate(from, to)` — one-call CLI migration. Pass version strings (`"4.5.2"`, `"4.6.0"`) or full Rscript paths; courieR resolves the installations and runs `ship()` automatically.
* `hub()` — short alias for `open_hub()`. Run `hub()` to launch the dashboard with less typing.
* `library(courieR)` now prints the version number and a reminder to run `hub()` or see `?ship`.
* `ship()` gains a `log_callback` argument for real-time progress messages from the pak subprocess, including a notice when first-time metadata loading may take 1–2 minutes.

## Bug fixes

* `manifest()` now reliably excludes base and recommended packages on Windows. The previous path comparison was case-sensitive and could let packages like `translations` slip through into a sync plan, causing a pak error. The filter now uses case-insensitive path comparison and a name-based guard via `installed.packages(priority = c("base", "recommended"))`.

## Performance & UX

* `find_routes()` per-candidate subprocess timeout reduced from 5 s to 3 s, shortening detection time when multiple R versions are installed.
* Sync dashboard: replaced the floating `withProgress()` modal with an inline Bootstrap progress bar inside the log pane. Log lines now append to the DOM immediately, removing the need for `shiny:::flushReact()`.
* Sync dashboard: detection phase shows a Bootstrap info alert while scanning and records "Detection complete: found N installation(s)." in the sync log.

# courieR 0.2.2

* CRAN resubmission addressing reviewer feedback.

# courieR 0.2.1

* Fixed correctness bugs identified during CRAN review.
* Added centralised error reporting in the Shiny module.

# courieR 0.2.0

* CRAN readiness: hardened documentation, CRAN-safe examples, and CI workflow.
* `ship()` gains a `@section Safety:` block documenting subprocess and library write behavior.
* `manifest()` now uses `deparse()` for library path quoting, fixing edge cases with special characters.
* `manifest()` CSV fallback parsing now handles multi-line CSV output correctly.
* Added `cran-comments.md` and `.github/workflows/R-CMD-check.yaml`.
* DESCRIPTION title simplified; version bumped.

# courieR 0.1.0

* Initial release.
* `find_routes()` detects R installations on Windows, macOS, and Linux,
  including user-local installs.
* `manifest()` scans installed packages from any R installation via subprocess.
* `inventory()` compares two package libraries and reports missing, outdated,
  and newer packages.
* `ship()` migrates packages from one R installation to another using pak.
* `open_hub()` launches a Shiny dashboard for point-and-click migration.
