ru now needs Bash 4.4 or later and stops at startup on older Bash. Commit publication now works with the stock Git on Ubuntu 22.04 and 24.04; in v1.6.0 every publication failed there. Commands that read the repository list, discover GitHub issues and PRs, or scan dependencies now fail when they cannot read everything, instead of acting on a partial result. ru dep-update keeps only the updates ru has checked and tested itself, and agent-sweep --with-release applies the release plan instead of only tagging HEAD. See the upgrade notes.
Changed
- Bash 4.4 is the minimum. ru uses namerefs and expands arrays that may be empty under
set -u, which Bash before 4.4 cannot run, but the startup check accepted any Bash 4: on 4.2 and 4.3,ru listandru configstopped with "ARGS[@]: unbound variable". Bash 3.2 through 4.3 now get the upgrade advice and exit 3 before any command starts (7ffe91a). - A repository list that cannot be read stops the command. Before, a
repos.dlist that failed to load was skipped, andsync,statusand the other fleet commands carried on with the repos already read. Nowsync,status,list,ai-sync,dep-update,review, the fork commands andcommit-sweepexit 3 without acting on a partial list (JSON results sayconfig_read_failed), as they do when a list path exists but is not a readable regular file;ru doctorreports the configuration as unreadable, and arepos.dthat can be searched but not listed is still read.syncandreviewalso exit 3 when they cannot save their checkpoint (aad594d, 175f6c9, 6118847, 1c8b412). - Review discovery reads the whole backlog or fails.
ru reviewfollows every issue, pull request and label page before ranking; a missingghorjq, a GraphQL error, or a page that cannot be finished now exits 3 ("Review discovery failed; no complete backlog is available") instead of a warning and a partial list. Security and critical labels add 150 points (was 50) and keep an item CRITICAL after the recent-review penalty, and labels match whole names, sonon-criticalorsecurity-review-not-neededare ordinary labels.--max-reposis applied after ranking and keeps every matching item of each selected repository (26979a3, f956268, a2e8edd, 41bf7d7). - Dependency scans fail closed. Discovery runs
npm outdated,cargo outdated --workspace --root-deps-only,go list -m -uandcomposer outdated. A missing tool (includingcargo-outdated), a failed command, malformed output, or a manager without an adapter (Yarn, pnpm, pip, pipenv, Bundler, Maven, Gradle) now fails that repository with a typed error, also under--dry-run, instead of reading as "no outdated dependencies"; npm's answer is refused beside ayarn.lockorpnpm-lock.yaml. Cargo discovery covers the direct dependencies of every workspace member, and each record names itscrate_name(5abfa02, 3019f63, 44c323f). ru dep-updatekeeps only updates it verified itself (GH #19). Each requested dependency is updated in its own clone: ru reads the manifest and lockfile before and after, installs the locked environment, checks the installed version, runs the tests itself, and only then adds one commit to the original branch. A no-op, a wrong version, an inconsistent lock, an unrelated change or a failing test keeps that attempt out while earlier verified updates stay; each dependency is reported asupdated,skipped,failed,revertedorunverified, and a partial run exits nonzero. The test command follows the managers that still have updates (for examplenpm test && cargo test --workspace), and an update with no detected command and no--test-cmdfails before any AI session starts (2643e01, 1f414e8, 5161000, af23f85).- Automatic Rust test detection runs
cargo test --workspace, so a root package or a restrictedworkspace.default-memberslist no longer leaves out the member that changed. This is the command quality gates anddep-updatepick for Cargo; an explicit--test-cmdstill overrides it (44c323f). agent-sweep --with-releaseapplies the release plan. Before, it tagged HEAD as it was: the plan'stagwas ignored (v1.1.0became1.1.0), version files and the changelog were never edited, checks never ran, and a missingghstill reported a GitHub release. Now ru edits the version files and changelog, commits them aschore(release): <tag>, runs the requestedtests,lintandsecretschecks on a clean checkout of that commit, pushes the branch and an annotated tag to the scanned receivers, and creates the GitHub release with--verify-tag; a failed check publishes nothing, and a rerun continues from.git/ru-release/<tag>.json. A plan with"version": nullmeans no release instead of failing the sweep, a changelog or asset path through a symbolic link is refused, and signature files (.minisig,.sig,.asc) are exempt from thechecksumscheck (ca988b6, a3c7e4d).- Structured output covers every command.
fork-syncprinted no JSON,fork-statusprinted nothing when there was no configuration or no repository, andinit,add,removeandimportprinted nothing under--json. Each now prints one envelope with_meta.exit_code, andru robot-docs schemashas data schemas for these commands and forfork-cleanandcommit-sweep(6aea115, 30efb09, d86ad6f). - The test suite runs on macOS (beads bd-ivf6), and the review and pin fixtures match the stricter checks above (4daa226, 58e8067).
Fixed
- Commit publication failed on Git 2.34 and 2.43, the stock git on Ubuntu 22.04 and 24.04. ru bound a push to the scanned receiver by passing an empty
-c remote.<name>.url=and then the receiver; those Git versions take the empty value as one more URL instead of clearing the list, so the bind check refused and every publication (inspect, fetch and push, as used byai-sync,dep-update,review --apply,commit-sweepandagent-sweep) failed with "Cannot bind transport to the scanned publication destination". This was in v1.6.0 too. ru now binds a fresh remote name whose only URL is the receiver, copies the named remote's other settings (fetch refspecs, proxy, push options) onto it, and still passes the named remote to the repository's own pre-push hook. Measured before this fix: Git 2.34.1 and 2.43.0 failed and Git 2.56.0 worked; Apple's Git was not tested (f1f0c26). - A branch or tag pin could restore a tracked file and report success. When the pinned ref did not exist,
git checkouttreated a name such asfile1.txtas a path, restored that file and dropped its uncommitted bytes. ru now checks out only a local branch, a remote branch (as a tracking branch) or a tag, and anything else fails and leaves the clone and its files in place. A detached clone whose configured pin changed now moves to the new ref and is counted as updated (before: "No upstream", counted as a conflict) (a909d95, a2e8edd). ru --dry-run ai-syncran for real, and several documented flags were rejected. Options placed before a command reached onlyreviewandagent-sweep, soai-syncanddep-updatenever saw a leading--dry-run; every option now reaches its command wherever it appears.config --set KEY=VALUE,import --stars/--org=/--user=/--limit=,agent-sweep --show-denylist,--debugand-dexited 4 and now work,agent-sweep -j4sets the sweep's own parallelism, andreview --repos=PATTERNfilters instead of being ignored.ru import --stars,--org=NAMEand--user=NAMElist each source withghbefore writing anything, import nothing and exit 3 if a listing fails, and are no longer capped at gh's default of 30 repos (da26e99).- On macOS, ru commands could hang under pipe-memory pressure. When macOS is short of kernel pipe memory it hands out 512-byte pipes, and Bash 5.1 or later blocks writing a longer heredoc or here-string into one. The agent-sweep prompts were read that way at startup, so every ru command could hang, and about 170 other places could hang their own command; ru now has no heredocs, and stdin longer than 511 bytes comes from a separate writer process (e6254b7, 9af35e9).
- On macOS with GNU sed first on PATH,
ru config --setfailed for an existing key. ru ransed -i ''wheneverOSTYPEwas darwin; GNU sed (for example Homebrew's gnu-sed with gnubin) reads''as its script, exits 2 and leaves the file unchanged. This was in v1.6.0 too. ru now writes the edited copy beside the config file, keeping its mode, and renames it over the file, so the same code runs with either sed on every OS (e230f38). - On macOS, temporary files ignored
TMPDIR. ru's temp-file helpers calledmktempwithout a template, which macOS answers with the per-user temp directory. They now use${TMPDIR:-/tmp}/ru.XXXXXXXXXX, so files land in the chosenTMPDIR, and aTMPDIRthat does not exist fails (e26f511). - Under Bash 4.4, a second
revieworagent-sweeprefused to start, and session names kept non-ASCII letters. Before Bash 5, a subshell started inside an EXIT trap never runs its own EXIT trap, so a lock guard file survived and every later run reported "Another review is running" until the file was removed by hand; the guard is now removed explicitly. Bash 4.4 also matched ranges such as[a-zA-Z0-9]by locale collation, so a UTF-8 repo name kept "é" in its agent-sweep session name; ru now setsglobasciiranges(8d93108, 091b97f). ru agent-sweep --jsonreportedinvalid_structured_outputand exited 3 when every repository succeeded.ntm killprints "Killed session ''" on stdout, and that line landed next to the JSON summary. ru now discards ntm's stdout when it kills or interrupts a session (e6cd23d).- Some commands exited 0 after a failure.
ru status --jsonexited 0 after a failed fetch whenjqwas absent,fork-sync --pushreported a fork as synced when the push to origin failed, andru addorru removewith an invalid spec logged an error and exited 0. Now status exits 1, the fork is counted as failed and the run exits 1, and an invalid spec exits 4 (or 1 beside repos that were added or removed) (825ceb2, 6aea115, 30efb09). --format toonfell back to JSON even with toon_rust installed. ru needed atoon.shhelper that no project ships, and toon_rust's binary has beentoon(before v0.2,tru). ru now calls the installedtoonortruitself; a helper that definestoon_encodestill takes precedence, and with no encoder ru warns and prints JSON as before (22bfe55).
Upgrade notes
- Bash: ru requires Bash 4.4 or later. On Bash 3.2 or 4.0 through 4.3 it prints "ru: Bash >= 4.4 is required (found: ...)" with install advice (on macOS, a Homebrew offer) and exits 3 before running any command.
- Exit codes: these used to exit 0 and now fail: a command whose repository list cannot be read (3);
ru reviewwhen discovery cannot finish orghorjqis missing (3);ru dep-updatefor a repository whose scan fails or whose manager has no adapter, also under--dry-run, and for an update with no test command; adep-updaterun where only some updates were verified;ru addorru removewith an invalid spec (4, or 1 beside successes);fork-sync --pushwith a failed push (1); andru status --jsonafter a failed fetch withoutjq(1). Scripts that relied on the old success should read the reported reason. dep-updateJSON:deps_updatednow counts updates that were verified, tested and added to the original branch; in v1.6.0 it was set to the number of outdated dependencies found. That number is nowdeps_outdated_before, and per-dependency results are independencies.- Repository filters: an
agent-sweep --repos,review --reposorai-sync --include/--excludepattern containing*,?or[is now a glob that must match the whole repo name, owner/repo, spec or path; before, it was compared as literal text. Plain text still matches as a substring. - Release plans:
agent-sweep --with-releasenow edits and commits version files and the changelog, uses the plan'stag, and refuses a tag that does not name the version or an unknown strategy (before, treated asauto). Thesecretscheck now scans every file of the release commit; without gitleaks or detect-secrets, its heuristic patterns (token=,password:) can flag benign text and fail the release.
Known limitations
- On Ubuntu 22.04 (jq 1.6),
ru commit-sweepmay refuse valid plans with "Commit 1 has no files". Ubuntu 24.04 (jq 1.7) and macOS are unaffected. ru dep-updatediscovers updates only for npm, Cargo, Go and Composer; repositories using Yarn, pnpm, pip, pipenv, Bundler, Maven or Gradle fail withunsupported_scan. Automatic verification covers narrow layouts (for example one root npm project with a v2/v3package-lock.json, or Cargo lockfile updates within the existing requirements), installs run with npm lifecycle scripts and Composer scripts and plugins disabled, and Go and Composer updates are tested only against fixtures. GH #19 stays open for this.agent-sweep --with-release's GitHub release,ciandchecksumssteps were tested against a mockedgh, not a live GitHub release.
Testing
- Mac gate: 93 of 98 test files green; 1921 passed, 17 failed, 3 skipped. The 5 red files have environment or load causes: robot_docs (Python schema validators not installed), session_driver (dep-update runs timing out at
--timeout=10under heavy load), dep_evidence (the local build wrapper), lifecycle (a test mock's pipe race under load), ntm_driver (psblocked by the test sandbox).
Installation
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/repo_updater/main/install.sh | bashVerify
echo "$(curl -fsSL https://github.com/Dicklesworthstone/repo_updater/releases/download/v1.7.0/ru.sha256) ru" | sha256sum -c -SHA256 checksums for ru and install.sh are in checksums.txt. Built from 748299a (tag v1.7.0) and published from a gated clone with gh; no GitHub Actions.