Skip to contents

The problem

The HVTI R package family is twelve packages spread across twelve GitHub repositories. Setting up a machine used to mean twelve pak::pak() calls, in an order you had to know, against repository names that have not always matched the package names: hvtiRpropensity lived in ehrlinger/hvtiPropensityScores and TemporalHazard in ehrlinger/temporal_hazard until the 2026-08 renames.

hvtiR reduces that to one call, and adds a way to ask whether you are current.

The members

The registry is the package’s only piece of data. It maps each package to the repository it installs from:

hvtiR::members()
#>            package                      repo
#> 1   hvtiRutilities  ehrlinger/hvtiRutilities
#> 2   hvtiRdatabuild  ehrlinger/hvtiRdatabuild
#> 3      hvtiRtables     ehrlinger/hvtiRtables
#> 4   hvtiRtemplates  ehrlinger/hvtiRtemplates
#> 5        hvtiPlotR       ehrlinger/hvtiPlotR
#> 6  hvtiRlifetables ehrlinger/hvtiRlifetables
#> 7   hvtiRbootstrap  ehrlinger/hvtiRbootstrap
#> 8  hvtiRpropensity ehrlinger/hvtiRpropensity
#> 9  hvtiRimputation ehrlinger/hvtiRimputation
#> 10  ggBoostedTrees  ehrlinger/ggBoostedTrees
#> 11  TemporalHazard  ehrlinger/TemporalHazard
#> 12 ggRandomForests ehrlinger/ggRandomForests

What you have

hvtiR::status() compares what is installed against what is on each repository’s main branch. When pak recorded the installed GitHub commit, the comparison catches new commits even when the package version did not change:

hvtiR::status()
hvtiR 1.2.3 - 12 members

  v hvtiRutilities   1.4.3      1.4.3      ok
  v hvtiRdatabuild   0.2.4      0.2.4      ok
  ! hvtiRtables      1.0.2      1.0.3      stale
  x hvtiPlotR        -          2.8.2      missing
  ...

i 2 members need updating. Run hvtiR::update().

The status column takes one of six values:

Status Meaning
ok installed version and recorded commit are current
stale installed version or recorded commit is behind
missing not installed
ahead you have a local development build
unknown GitHub could not be reached
ok-local installed; the remote was not consulted

Packages installed without GitHub commit provenance fall back to the version comparison. The table stays version-focused; commit SHAs are used internally only to resolve a tie between equal versions.

status() checks hvtiR itself separately because the installer is not a family member. It stays outside the table. When it is behind, the report names the available version and the command that updates it:

pak::pak("ehrlinger/hvtiR")

The object is a plain data frame underneath, so you can use it in a script:

st <- hvtiR::status()
st$package[st$status == "stale"]

On a machine with no outbound network, skip the remote entirely:

hvtiR::status(remote = FALSE)
hvtiR 1.2.3 - 12 members

  - hvtiRutilities   1.4.3      -          ok-local
  - hvtiRdatabuild   0.2.4      -          ok-local
  - hvtiRtables      1.0.3      -          ok-local
  - hvtiRtemplates   1.2.2      -          ok-local
  - hvtiPlotR        2.8.2      -          ok-local
  - hvtiRlifetables  0.1.4      -          ok-local
  - hvtiRbootstrap   0.9.4      -          ok-local
  - hvtiRpropensity  0.1.8      -          ok-local
  - hvtiRimputation  0.1.1      -          ok-local
  - ggBoostedTrees   0.0.7      -          ok-local
  - TemporalHazard   1.2.12     -          ok-local
  - ggRandomForests  4.0.0      -          ok-local

i Remote was not consulted; versions shown are installed versions only.

Installing and updating

On a new machine, install everything:

hvtiR::install()

Thereafter, install only what has fallen behind:

hvtiR::update()

hvtiR::update() reuses the installer check made by status(), but it never installs hvtiR itself. Its namespace is already loaded, which is exactly what the guard below refuses. When the report says it is behind, reinstall it the way you first installed it:

pak::pak("ehrlinger/hvtiR")

Why it may refuse

hvtiR::update() warns and installs nothing if a required member is already loaded in the session:

Warning:
Cannot install required hvtiPlotR: already loaded in this session.
i No members were installed. Restart R and run `hvtiR::update()` before anything attaches it.
i Pass `force = TRUE` to install anyway (unsafe on Windows).

This is not fussiness. A package whose namespace is loaded cannot be safely overwritten — on Windows the write fails outright and leaves a half-installed library. The returned empty character vector carries a blocked attribute with the loaded package names. Restart R and run the update before you attach anything.

Set up a study

install.packages("pak")
pak::pak("ehrlinger/hvtiR")
hvtiR::setup(
  study = "EDIT: the Study Tracker title",
  study_tracker_id = 0L,  # EDIT: the Study Tracker ID
  root = "EDIT: the study directory"
)

Run this once per study, before anything else, from an R session started outside any project, passing root =. From inside the study’s RStudio project it stops before installing anything and names the command to run: it replaces packages that session has loaded. It also stops the study using renv’s per-user cache, so the shared library holds real copies a teammate can read, and reinstalls any package that was only a link into someone’s home. It cleans up template copies first and only then locks the packages, so the lockfile records what the study declares and not what the templates happened to mention. For an existing (legacy) study pass adopt = TRUE, which is what cleans up its template copies. A study that already has a renv.lock keeps its locked versions unless the declared family needs newer ones, in which case setup() moves them, lists each move and records them (upgrade = TRUE, the default; pass upgrade = FALSE for a study in revision, whose versions must not move), and every package that lockfile recorded is declared on first conversion, so none drop out. No personal GitHub token is needed: the packages are installed with pak, which carries a shared token, and renv keeps the environment (lockfile, restore, snapshot). It is safe to rerun.

Reproducible installs

hvtiR::install() resolves every member from GitHub main, so it gives you the newest code rather than a fixed one. Two people running it a week apart can end up on different commits under the same version number. When an analysis has to be reproducible, pin the versions with renv:

renv::init()      # once per project
hvtiR::install()  # installs into the project library
renv::snapshot()  # records every package, and each member's commit

renv.lock then names the exact commit behind each member, and renv::restore() rebuilds that library on another machine. This works because pak records the commit it installed from, which is the field renv reads.

A member installed from a local working copy rather than from GitHub carries no such commit, so renv has none to record for it.

When something will not install

hvtiR::doctor()

The doctor reports on your environment ahead of the status table: the installed hvtiR version, your R version against the family’s strictest requirement, your platform, whether pak is installed to install members with, the package repositories R is configured to use, and whether an renv project is active.

ggRandomForests and hvtiRlifetables both need R 4.4.0 or newer, which is the single most common reason a member will not install. Note that hvtiR itself requires only R 4.1.0 — deliberately, so that the diagnostic still runs on a machine whose R is too old for the members it is reporting on.

The second most common reason is a repository that is frozen in time. RStudio Server sites often point R at a dated Posit Package Manager snapshot, and a dependency released after the snapshot date is invisible to pak. The install then fails with “Could not solve package dependencies”. The doctor warns when a configured repository is such a snapshot, and its “Dependency floors” section names each requirement the repositories cannot meet: ggRandomForests needs varPro (>= 3.3.0), for example, which reached CRAN on 2026-09-22. Reading those floors takes the network, so hvtiR::doctor(remote = FALSE) skips that section.

Why GitHub and not CRAN

Every member installs from GitHub main, including ggRandomForests, which is also published on CRAN.

Development flows from the family repositories to CRAN, not the other way around. GitHub main is therefore the leading release source, while CRAN is a downstream republication for members that are published there.

One consequence is worth knowing: because ggRandomForests came from GitHub, a later update.packages() can quietly downgrade it to the CRAN version. If a version looks wrong, run hvtiR::status().

The same replacement can happen to TemporalHazard, and there it may not be harmless. hvtiRlifetables requires TemporalHazard (>= 1.2.0) in its Imports: field, a requirement R enforces when the namespace loads. If a CRAN downgrade no longer satisfies that requirement, hvtiRlifetables will not load until hvtiR::update() puts the GitHub release back.