Tinyverse Toolchain: Updates and Thanks

Posted by Troy Hernandez on Wed, Jun 24, 2026

Back in February we released the tinyverse development toolchain: a handful of small, low-dependency packages for documenting, formatting, checking, and introspecting R packages. Since then they’ve been through their first real CRAN cycles, and, better than that, people who aren’t me started filing issues and sending pull requests!

Here’s what changed, and who to thank for it.

PackageVersionHighlight
tinyrox0.4.0ESS ##' comments, DESCRIPTION linter removed, stale-Rd pruning
rformat0.2.0idempotence fix, smarter call re-wrapping
tinypkgr0.2.2document(), load_all() on the search path
saber0.7.2cache_dir on fn_graph(), CRAN hardening

tinyrox 0.4.0

The documentation generator got the most attention this round.

It now reads ESS-style ##' doc comments in addition to the usual #', which came in as a clean pull request from Dirk Eddelbuettel (#27). If you write your roxygen blocks in Emacs Speaks Statistics, they parse now.

Grant McDermott opened two issues that shaped the release. One asked for @returns (the plural) as an alias for @return (#24). Easy yes. The other (#23) was subtler: tinyrox shipped a DESCRIPTION linter that was flagging ordinary English words like “graphics” as unquoted package names. The fix was to delete the linter. A documentation generator has no business policing your DESCRIPTION prose, and the false positives cost more than the check was worth.

Two more. document() now prunes stale .Rd files when you rename or delete a topic, but only files it generated itself (it looks for its own marker line, so your hand-written Rd is safe, #22). And an unknown tag now warns and skips instead of aborting the whole run.

rformat 0.2.0

rformat is the R Core-style formatter. The headline property for any formatter is idempotence: formatting already-formatted code should change nothing. format(format(x)) has to equal format(x), or the thing churns your diffs forever.

We check that against every R file in 126 CRAN and base packages, with randomized style options per file. A few combinations turned up a case where joining else onto a preceding closing brace left a stray blank line on the first pass that the second pass then removed. Not idempotent. It’s fixed now, in both the C++ fast path and the pure-R fallback (the two backends are kept in sync, on purpose, with parity tests).

The rest is wrapping behavior. wrap_long_calls() re-packs an already-wrapped call so the first argument lands on the call line instead of hanging by itself (#30), the gap between a collapsed function signature and a bare one-line body is closed (#29), and rformat_file() / rformat_dir() no longer tack a blank line onto the end of the file (#34). Dirk has been a steady source of rformat issues and PRs since early on, and several of these trace back to his feedback.

tinypkgr 0.2.2

tinypkgr’s release closes two issues Grant graciously filed. document() now exists as a thin wrapper, and load_all() attaches your package to the search path so its functions are callable after you load it (#6, #7). A follow-up fixed load_all()’s argument order, which had quietly broken the positional call (#9).

saber 0.7.2

saber is the context-engineering package: AST symbol indices, blast-radius tracing, call graphs, and project briefings for LLM coding agents. This update is mostly CRAN hardening. fn_graph() takes a cache_dir now, so checks write to a temp directory instead of ~/.cache (#32). briefing_git() stopped leaking a git “status 128” warning when you point it at something that isn’t a repository (#33). And the session-start hook is pickier about when it sources a local package’s R/ (#31). Christophe Regouby asked saber’s blast_radius() to scan @examples and vignettes, not just R/, which landed earlier, as the include argument (#19).

Thanks

The best part of putting these out was that other people picked them up and told me where they break. If you’ve filed an issue or sent a PR on any of these, thank you. They started as tools for just my workflow. They’re better for the friction of other people’s.

install.packages(c("tinyrox", "rformat", "tinypkgr", "saber"))