Changelog
Source:NEWS.md
hvtiR 1.3.1
Catalog versions refreshed from CRAN and
main:hvtiRutilitiesdev 1.5.7 to 1.5.8. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Family floors refreshed from
main:hvtiRtemplatesSuggestshvtiRtables(>= 1.0.7) to (>= 1.0.8).family-floors.csvships in the package and records the version floors members declare on one another.setup()now checks that it is not running inside the study before it does anything that can install. With a renv project active, or the session library under the study root, it stops and names the command to run from a session started outside the project,hvtiR::setup(root = "<study>", ...). Before, resolving the root could installhvtiRutilitiesinto the study’s own library first.setup()finds the study root without installing anything: it usesRENV_PROJECTwhen set, and askshvtiRutilitiesonly when it is already installed.setup()stops the study using renv’s per-user cache (renv::settings$use.cache(FALSE), stored inrenv/settings.jsonso it applies to the whole team) and runsrenv::isolate()before restoring. It removes, and so reinstalls, any library link whose target the user cannot read, and pak’s leftover_cachestaging directory, and reports what it removed. This fixes “Unable to move package” failures in a library shared by several users.install()andupdate()warn, with the fix, when run from an active study project whose library holds links the user cannot read or a leftover_cachedirectory.setup()reports a failed install without crashing. The child’s output is cleaned (carriage returns, colour codes, invalid bytes, braces) before it reaches the message, and the full raw output is saved tohvtiR-setup-<time>.login the session’s temporary directory, which the message names.setup()no longer needs a personal GitHub token. It installs the family with pak into the study’s library (pak carries a shared token, so a server’s shared IP does not exhaust GitHub’s anonymous API limit) and keeps renv for the environment: restore, then the pak install, then the lockfile snapshot, in explicit mode. TheLinkingTopackages pak skips are installed from the configured repositories, which the snapshot’s validation needs. With no token it now warns instead of stopping, since only restoring a package the lockfile pins on GitHub can need one. A token held only in the git credential store is still passed on asGITHUB_PATfor the run.setup()no longer installs subdirectory remotes ahead of the rest to work around a renv 1.2.4 unpack bug: pak handles them, so the workaround and itsRemotesparsing are removed.setup()finds the study root without loadinghvtiRutilities: loading it left its namespace in use and blocked the upgrade that follows. It usesRENV_PROJECT, else the nearest_study.ymlat or above the working directory, else the working directory.A link in the study’s library whose target is readable but whose
DESCRIPTIONis not is now treated as unreadable and removed (the link only). A real package directory whoseDESCRIPTIONthe user cannot read is never deleted:setup()stops before changing anything, names the packages, and says their owner or an administrator must fix the permissions (chmod -R g+rX <library>/<pkg>) or remove them.The
install()andupdate()warning now gives the manual repair in the order that works: remove_cache, remove each broken link, thenrenv::settings$use.cache(FALSE),renv::isolate()andrenv::restore().The check that no locked package moved now runs after the
LinkingTofill, immediately before the snapshot, because the fill installs through renv and can move a locked package.That check now compares
RemoteShaas well asVersion, so a GitHub package locked at one commit and rebuilt from another is caught even when its version is unchanged.When the check trips,
setup()first removes the packages this run added to the study’s library, then restores the moved locked packages, so the library matches the lockfile again. If that restore fails (restoring a GitHub-locked package needs a personal token), the message names the packages that could not be restored and the token steps. The lockfile is unchanged either way.The pak install now uses the repositories the study’s
renv.lockrecords, falling back to the session’s, instead of the session’s alone.setup()runs its child R processes from a script file instead of an-eargument. R truncated a long-eargument, so a study with about 155 locked packages ran half a script and stopped with “The install did not report the study’s library path”.setup()installs from the binary form of a Posit Package Manager source URL. When the study’srenv.lockrecords.../cran/latestand the session has.../cran/__linux__/<distro>/latest(same host, path and snapshot), pak, the restore and theLinkingTofill use the binary URL andrenv.lockis rewritten to record it, so installs no longer compile from source (arrow failed on RHEL 8’s default compiler). No other repository is changed.setup()no longer reports false moves from its lock guard. It compares versions as versions (1.2-5is1.2.5) and looks packages up across every library on the path, so recommended packages in R’s system library (mgcv,nlme,survival, …) are found.New
upgradeargument tosetup(). The default,TRUE, lets the install move locked versions the declared family needs, lists each move and records it inrenv.lock.upgrade = FALSEkeeps a study in revision on its locked versions: the guard rolls back as before, and its message now says how to move them.setup()now resolves unmet version requirements before it snapshots. It readsDepends,ImportsandLinkingToof every package in the study library and finds packages that are absent or below a floor (>=,>,==), as renv’s snapshot validation would. Withupgrade = TRUE(the default) it installs them through renv from the binary repositories and checks again; the lock guard then reports any locked package those installs moved, and a moved package it raised is listed under “Other unmet requirements resolved”. What stays unmet, or anything at all withupgrade = FALSE, stopssetup()before the snapshot with each requirement listed andrenv.lockunchanged.
hvtiR 1.3.0
Catalog versions refreshed from CRAN and
main:hvtiRtablesdev 1.0.5 to 1.0.7. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Family floors refreshed from
main:hvtiRtemplatesSuggestshvtiRtables(>= 1.0.5) to (>= 1.0.7).family-floors.csvships in the package and records the version floors members declare on one another.New
setup()sets up a study’s environment in one command: it cleans up template copies before renv, declares the family and the packages it needs, installs them into the study’s library, and snapshots. renv runs in explicit mode, so the lockfile records what the study declares. A study with arenv.lockkeeps its locked versions, and every package that lockfile recorded is declared on first conversion.doctor()reports a member whose dependencies are not on the library path, and a study lockfile made under a different R.setup()installs the subdirectory remotes that members pin (read from their DESCRIPTIONs, such asboostmtree) in a firstrenv::install()call, which works around a renv 1.2.4 failure (“target file … already exists”) on a cold cache.setup()warns before it writes anything when no GitHub credentials are found, and says how to create them, and a failed install that reports missing authentication credentials says so too.setup()never upgrades a locked package: if declaring a new package would move a locked dependency, it restores the locked versions, leavesrenv.lockunchanged and stops, naming the change;update()is the way to move versions.Catalog versions refreshed from CRAN and
main:ggBoostedTreesdev 0.9.2 to 0.9.3;hvtiPlotRdev 2.8.11 to 2.8.14;hvtiRdatabuilddev 0.3.1 to 0.3.2;hvtiRtablesdev 1.0.7 to 1.0.8;hvtiRutilitiesdev 1.5.5 to 1.5.7. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Family floors refreshed from
main:hvtiRdatabuildImportshvtiRutilities(>= 1.5.1) to (>= 1.5.2);hvtiRtemplatesImportshvtiRutilities(>= 1.5.1) to (>= 1.5.7).family-floors.csvships in the package and records the version floors members declare on one another.
hvtiR 1.2.9
- Catalog versions refreshed from CRAN and
main:hvtiPlotRdev 2.8.10 to 2.8.11;hvtiRbootstrapdev 0.9.5 to 0.9.6;hvtiRtemplatesdev 1.4.0 to 1.4.1;hvtiRutilitiesdev 1.5.4 to 1.5.5. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.8
update()now says at once that it installed nothing when a member it needs is already loaded, naming the loaded members. Until now the only signal was a warning, which prints after the call returns and vanishes undersuppressWarnings()oroptions(warn = -1), soupdate()could stop after “hvtiR is current” without a word about the members it skipped.Catalog versions refreshed from CRAN and
main:TemporalHazarddev 1.2.15 to 1.2.16;hvtiPlotRdev 2.8.6 to 2.8.10;hvtiRdatabuilddev 0.3.0 to 0.3.1;hvtiRpropensitydev 0.1.11 to 0.2.1;hvtiRtablesdev 1.0.3 to 1.0.5;hvtiRtemplatesdev 1.3.1 to 1.4.0;hvtiRutilitiesdev 1.5.2 to 1.5.4. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Family floors refreshed from
main:hvtiPlotRSuggestshvtiRpropensityadded;hvtiRpropensitySuggestshvtiPlotRadded;hvtiRtemplatesSuggestshvtiPlotR(>= 2.8.0) to (>= 2.8.9);hvtiRtemplatesSuggestshvtiRpropensity(>= 0.1.10) to (>= 0.2.0);hvtiRtemplatesSuggestshvtiRtables(>= 1.0.1) to (>= 1.0.5).family-floors.csvships in the package and records the version floors members declare on one another.
hvtiR 1.2.7
Catalog versions refreshed from CRAN and
main:TemporalHazarddev 1.2.12 to 1.2.14. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.New
inst/extdata/family-floors.csvrecords every version floor one family member declares on another, read from each member’sDESCRIPTIONonmainby the weekly catalog-versions job. A floormaindoes not meet inDepends,ImportsorLinkingTowould breakinstall(), which resolves members frommainin one call, so that job now fails on one; aSuggestsfloor is reported only.Catalog versions refreshed from CRAN and
main:TemporalHazarddev 1.2.14 to 1.2.15;ggBoostedTreesdev 0.9.1 to 0.9.2;hvtiPlotRdev 2.8.5 to 2.8.6;hvtiRbootstrapdev 0.9.4 to 0.9.5;hvtiRdatabuilddev 0.2.4 to 0.3.0;hvtiRimputationdev 0.1.2 to 0.1.3;hvtiRlifetablesdev 0.1.4 to 0.1.5;hvtiRtemplatesdev 1.3.0 to 1.3.1;hvtiRutilitiesdev 1.5.1 to 1.5.2. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Family floors refreshed from
main:hvtiRtemplatesSuggestshvtiRdatabuild(>= 0.2.3) to (>= 0.3.0).family-floors.csvships in the package and records the version floors members declare on one another.
hvtiR 1.2.6
update()now warns and installs nothing when a required member is already loaded; this replaces the previous error. The returned empty character vector carries ablockedattribute listing the loaded members. Restart R and runupdate()before those packages attach.force = TRUEstill bypasses the guard, with the existing Windows safety risk.DESCRIPTIONnow declares the Quarto command line tool inSystemRequirements. The vignettes have always needed it to build; the field makes that visible to installers and toR CMD check.The pkgdown site’s articles put the table of contents on the left and use the full width of the window, the same layout as the HVTI Quarto books and the hvtiRtemplates jobs, through
pkgdown/extra.css. The installed vignettes keep the Quarto engine’s own minimal format.Catalog versions refreshed from CRAN and
main:ggRandomForestsCRAN 3.5.3 to 3.5.4;hvtiPlotRdev 2.8.4 to 2.8.5;hvtiRimputationdev 0.1.1 to 0.1.2;hvtiRpropensitydev 0.1.10 to 0.1.11;hvtiRtemplatesdev 1.2.4 to 1.3.0;hvtiRutilitiesdev 1.4.4 to 1.5.1. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.5
- Catalog versions refreshed from CRAN and
main:ggBoostedTreesdev 0.9.0 to 0.9.1;hvtiPlotRdev 2.8.3 to 2.8.4;hvtiRtemplatesdev 1.2.2 to 1.2.4;hvtiRutilitiesdev 1.4.3 to 1.4.4. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.4
install()andupdate()explain a failed solve on an unmet dependency floor rather than passing on pak’s raw “Could not solve package dependencies”. They name each floor that the members being installed declare and the repositories cannot meet and, when a repository is a dated Posit Package Manager snapshot, print the one-lineoptions(repos = ...)that points it atlatestfor the session. Any other failure is rethrown unchanged (#103).The getting-started vignette shows output as of 2026-09-29: the
status()examples carry the installer version in the header, the member versions in the catalog on that date and all twelve members (hvtiRimputationwas missing from the offline example), and thedoctor()section covers the repository listing, the dated-snapshot warning and the dependency floors added in 1.2.3. The README’s member table gainshvtiRimputationand itsdoctor()summary the same additions.Catalog versions refreshed from CRAN and
main:ggBoostedTreesdev 0.0.7 to 0.9.0;hvtiPlotRdev 2.8.2 to 2.8.3;hvtiRpropensitydev 0.1.8 to 0.1.10. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.3
doctor()lists the configured package repositories and warns when one is a dated Posit Package Manager snapshot, as RStudio Server sites often set. A dependency released after the snapshot is invisible to pak, soinstall()fails with “Could not solve package dependencies” when a member needs it:ggRandomForestsrequiresvarPro (>= 3.3.0), released to CRAN on 2026-09-22.doctor()also names the dependency floors the configured repositories cannot meet. It reads each member’sDESCRIPTIONfrom GitHub, compares every outsideDepends,ImportsandLinkingTofloor against the repositories’ package indexes. Against the Posit Package Manager snapshot of 2026-09-01 it reports thatggRandomForestsneedsvarPro>= 3.3.0 while the repositories offer 3.2.0. The check needs the network, sodoctor(remote = FALSE)skips it.Catalog versions refreshed from CRAN and
main:TemporalHazarddev 1.2.11 to 1.2.12;hvtiPlotRdev 2.7.17 to 2.7.18. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.Catalog versions refreshed from CRAN and
main:hvtiPlotRdev 2.7.18 to 2.8.2;hvtiRdatabuilddev 0.2.3 to 0.2.4;hvtiRtablesdev 1.0.2 to 1.0.3;hvtiRutilitiesdev 1.4.1 to 1.4.3. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.2
- Catalog versions refreshed from CRAN and
main:hvtiPlotRdev 2.7.15 to 2.7.17;hvtiRimputationdev 0.1.0 to 0.1.1;hvtiRpropensitydev 0.1.7 to 0.1.8;hvtiRtemplatesdev 1.2.1 to 1.2.2;hvtiRutilitiesdev 1.3.1 to 1.4.1.
hvtiR 1.2.1
status()now checks hvtiR itself against GitHub as well as the family members. When the installer is behind, the report names both the available version andpak::pak("ehrlinger/hvtiR")as the update command.update()reuses that check rather than making a second request.Catalog versions refreshed from CRAN and
main:hvtiRdatabuilddev 0.2.2 to 0.2.3;hvtiRpropensitydev 0.1.5 to 0.1.7;hvtiRtemplatesdev 1.2.0 to 1.2.1;hvtiRutilitiesdev 1.3.0 to 1.3.1. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.2.0
⚠️ Breaking: the job catalog leaves hvtiR, and
jobs()is removed. The catalog moves tohvtiRtemplatesasinst/extdata/templates.json, read byhvtiRtemplates::template_catalog()(hvtiRtemplates:dev/specs/2026-09-18-template-catalog-design.md). Every job type is a template there; other packages only supply the functions a template calls, so hvtiR keeps no part of it. Removed:jobs()and its help page,inst/extdata/jobs.json, thejob-catalogvignette, the catalog tests (including the temporary freeze guard added while the catalog moved), thejobs-pin-driftworkflow andtools/check_jobs_pin.py.jsonliteleaves Imports, andggRandomForests,hvtiPlotR,hvtiRpropensity,hvtiRtables,hvtiRutilitiesandTemporalHazardleave Suggests and Remotes: they were installed only to validate the catalog’sreplaced_byexports. The registry still names them all as members;install()reads the registry, not this package’sDESCRIPTION.The published-artifact catalog records
hvtiRutilities1.3.0 in itsdev_versioncolumn, refreshed frommainbytools/refresh_catalog_versions.py.
hvtiR 1.1.15
The published-artifact catalog records
hvtiRutilities1.2.1 in itsdev_versioncolumn, refreshed frommainbytools/refresh_catalog_versions.py. That catalog is presentation metadata, read by the internalread_catalog()for renderers such as the family table;status()does not read it, and takes its versions live from each repository’smain. 1.2.1 carriescache_fit()and therfr,sidandvttaxonomy rows that therfs/rfc/rfrandsid/vtcatalog rows above depend on.rfs,rfcandrfrmove fromretiretoscaffold, destined forhvtiRtemplates. Their templates are owed there: they fit with randomForestSRC and plot with ggRandomForests, so they use ggRandomForests rather than living in it.statusbecomesqueued, since a row destined for hvtiRtemplates carries a scheduling status, andbatchstays null because no ML batch has been assigned.replaced_bykeeps thegg_*functions the templates will call.retirenow holds exactlyrfandrfsrc, the legacy umbrella rowshvti_taxonomy()demoted.rfr,sidandvtleave intake. hvtiRutilities PR #127 added all three prefixes tohvti_taxonomy(); it is on that package’smainbut in no release yet, so it is NOT in 1.2.0. All three rows are destined forggRandomForests, off this catalog’shvtiRtemplatesdestination, so they takestatus: nulllike the other off-destination rows rather than a scheduling status.rfris a retire row and needs no blocker.sidandvtare build rows, which must cite a real issue once out of intake; they now citehvtiRforests#1, the issue for ML sub-project 3, opened with the newhvtiRforestsrepository because nothing citable existed before.The intake placeholder guard in
test-jobs.Rno longer goes quiet when intake is empty. It asserted inside aforover the intake rows, so with none left it made no expectation, and testthat reports an empty test as a SKIP rather than a pass. It now asserts over the whole set. Separately, the temporary pin on the three rows’hvtiRutilities#taxonomyblocker is retired:sidandvtare now pinned tohvtiRforests#1, andrfrto having no blocker at all.
hvtiR 1.1.14
-
The catalog records the current
mainversion of five members, refreshed from the live oracles bytools/refresh_catalog_versions.py:hvtiRutilities1.1.12 to 1.2.0,hvtiRtemplates1.1.0 to 1.2.0,hvtiPlotR2.7.14 to 2.7.15,hvtiRpropensity0.1.4 to 0.1.5 andhvtiRtables1.0.1 to 1.0.2. Until nowstatus()could not tell anyone that thehvtiRutilitiesandhvtiRtemplates1.2.0 releases existed, which is what a study needs before it can install them.
hvtiR 1.1.13
dc-stddiffmoves fromhvtiRutilitiestohvtiRpropensity. That package already computes standardized differences insideps_match(),ps_logistic()andps_weight(), and the port takesstddiffciandmw_varwith it. The row staysbuild, now blocked onhvtiRpropensity#34, which supersedeshvtiRutilities#103, and its spec is hvtiRtemplates’dev/specs/2026-09-16-standardized-difference-design.md.The catalog marks
dc-generalshipped, overhvtiRutilities::proc_contents()andproc_means(), with base R for the contingency tables, cumulative distributions and correlations.Catalog versions refreshed from CRAN and
main:hvtiRdatabuilddev 0.2.1 to 0.2.2. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.1.12
- Catalog versions refreshed from CRAN and
main:hvtiPlotRdev 2.7.13 to 2.7.14;hvtiRbootstrapdev 0.9.3 to 0.9.4;hvtiRdatabuilddev 0.2.0 to 0.2.1;hvtiRlifetablesdev 0.1.3 to 0.1.4;hvtiRpropensitydev 0.1.3 to 0.1.4;hvtiRtablesdev 1.0.0 to 1.0.1;hvtiRutilitiesdev 1.1.11 to 1.1.12. The versioned catalog ships in the package.
hvtiR 1.1.11
The catalog marks
dc-tables,dc-gfupanddp-postageshipped.dc-tablesnow names the correlation table and matrix engines, anddp-postageis thin overhvtiPlotR::hv_eda().Catalog versions refreshed from CRAN and
main:ggRandomForestsCRAN 3.5.2 to 3.5.3;TemporalHazarddev 1.2.9 to 1.2.11;ggBoostedTreesdev 0.0.6 to 0.0.7. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping.
hvtiR 1.1.10
pm’s row leaves the catalog, with its entry inhvti_taxonomy(). It had stayed as aretirerow after folding intolmonly becausehvtiRtemplatesrequires the catalog and the taxonomy to name the same prefixes.lmstill counts the studies using either prefix, 470. The catalog has 57 rows and 5 retired.The hazard rows name the TemporalHazard functions their templates call, in
replaced_by:achzr_kaplan();hzhazard()andhzr_phase();hmhazard(),hzr_stepwise(),hzr_deciles()andhzr_gof();hshazard()andhzr_stepwise();bhhzr_bootstrap()andhzr_stepwise(); andhpaddshazard()to itshvtiPlotRfunctions. Each is read from the shipped template inhvtiRtemplatesand validated against TemporalHazard’s exports, soTemporalHazardjoinsSuggests,Remotesand the CI installs.bcis a Cox job and calls none of them. No disposition changes.Five rows take the relabelled taxonomy names, after the same review that folded
pmintolm:bn“Bootstrap non-linear”,nd“Non-linear distributions”,nm“Non-linear model”,np“Non-linear plot” andnb“Boosting”.hvti_taxonomy()inhvtiRutilitiestakes the same labels.
hvtiR 1.1.9
pmfolds intolm, andlmisthinoverhvtiRpropensity. A review of the catalog page on 2026-09-11 put 8 or 9 in 10lmjobs as propensity models, and the corpus agrees as far as a text match can: 1,014 of the 1,872lmSAS programs name a propensity or balancing score, in 331 of 467 studies.lmnow leans onps_logistic()andbs_count(), and counts the studies using either prefix, 470 against 469.pm, whose programs are negative-binomial balancing-score fits, isretireoverbs_count(), and keeps a row only whilehvti_taxonomy()lists it.hvtiRpropensityjoinsSuggests,Remotesand the CI installs, because the routing test validates every package areplaced_bynames and fails on CI when one is absent.The catalog’s counts are corrected, and their units are written down.
dc-stddiffcounts the union of the six spellings that fold into it, 120 studies, where 59 was the 2019 spelling alone and a sum would give 157.siandmicount jobs like every other row:miits own andbd’s multiple-imputation jobs, 18 studies, andsi1, because mean imputation runs inside bootstrap jobs. The 223 and 326 studies calling the imputation macros stay in the notes. Twelve of the thirteen qualifieddcanddprows gainr_jobsandr_exemplars, by the 2026-08-29 definition, which the scan reproduces for all 42 prefixes;dp-postagestays null, because its job is named by dataset. Section 5 ofdev/specs/2026-09-04-job-catalog-design.mddefines each unit and records two rules:sas_breadth_jobsis the figure of record, and where two spellings name one job the newer macro’s names the row while the row counts both.jobs()returnssas_breadth_jobsandr_exemplars, and the job catalog vignette’s SAS column readssas_breadth_jobs. It readsas_breadth, which is null on every qualified row, so all thirteendcanddprows showed no SAS count.dc-trendsanddp-boxplotnow count studies in the field’s own unit. #61 filledsas_breadth_jobswith a token count over the whole job name, 58 and 34, where every other qualified row holds distinct studies whose second name field is the qualifier. By that unit, from the 2026-09-02 re-parse, they are 43 (descriptive/dc) and 9 (graphs/dp), both still past the two-studies gate. Theoptionsevidence strings stay token counts, and say so.Two job types join the catalog, and a top-level
optionslist starts besidejobs.dc-trends(43 studies) anddp-boxplot(9) arequeuedwith no batch yet. Both come from the 2026-09-10 triage inhvtiRtemplates, counted as distinct studies over the 2026-08-27 census.optionsrecords four constructs that span prefixes rather than being job types of their own: a landmark early / late split, repeated events, a US life-table comparison, and a matched or weighted analysis. Every reader of the catalog takes onlyjobs, so the new list changes nothing that exists. Thehsanddc-stddiffnotes now name the legacy variants that fold into them, and the tests that pinned 56 rows now pin 58.
hvtiR 1.1.8
-
dp-trendsisshipped, the first of the EDA templates from the 2026-09-10 batch.hvtiRtemplatesgains40_graphs/dp-trends.qmd, a thin template overhvtiPlotR::hv_trends(). Its design isdev/specs/2026-09-10-dp-trends-template-design.mdinhvtiRtemplates, the value the row’sspecnow holds. Aspecwith no repository prefix is a path inhvtiRtemplates, as 16 of the catalog’s 18 are;siandminamehvtiRdatabuild/explicitly because their designs live there. The row is flipped straight toshippedrather than throughin-flight:hvtiRtemplatesonly sees the catalog through its pinned tag, and the pin moves in the same pull request that adds the template, so the catalog never claims a template that is not on disk and one tag suffices.
hvtiR 1.1.7
dp-postageis no longer blocked. Itsblocked_onnamedhvtiRtemplates#97, the defect that madehvtiRtemplatesreject any template whose folder disagrees withhvti_taxonomy().hvtiRtemplates#98fixed it and closedhvtiRtemplates#97on 2026-09-10, soblocked_onis nownulland the row’s note records when and why. Nothing checksblocked_onfor aqueuedrow, which is why this is a manual step rather than something a test would have caught.-
The job catalog gains
dp-postageand pulls threedcrows into batch 3, taking it from 55 rows to 56. Scheduled for the biostats fellows training session; the design ishvtiRtemplates/dev/specs/2026-09-09-eda-templates-design.md.dc-general,dc-tablesanddc-gfupmove from batch 4 to batch 3. Nothing else moves:dc-dead,lgandrgstay in 4, and thedprows were already in 3.⚠️
dp-postageis the first row whosefolderdisagrees withhvti_taxonomy()by design. The taxonomy filesdpundergraphs; this row isdescriptive, which is the third folder the prefix occupies in the corpus.hvtiRtemplatesasserted template folders against the taxonomy alone untilhvtiRtemplates#98, so a row like this one, and the already scheduleddp-variablewithfolder: distributions, could not have shipped a template.blocked_onnames that issue.Its counts are
nullrather than zero.descriptive/dphas never been censused: the 2026-09-02 decomposition enacted rows forgraphs/dpanddistributions/dponly, and five furtherdescriptive/dplegacy templates remain unrepresented. An em dash in the rendered roadmap means unmeasured, not absent.dispositionisscaffold, notthinlike itsdpsiblings, becausehvtiPlotR2.7.13 has no faceting constructor and the grid is the template’s ownfacet_wrap(). dc-tablesrecords that pairwise correlation is in scope for it. The exemplar is/descriptive/dc.tables.ods_preoplabs_a1c.sas, oneproc corr nosimple spearman pearson fisher(biasadj=no) plots=matrixemitting a scatter-plot matrix and a Fisher-CI coefficient table.dc-generalcarries a plainproc corr nosimple ranksection with no plot and no intervals, so the two are not the same job and the note says which is which.hvtiRutilitiesis recorded at 1.1.11 ininst/extdata/catalog.csv, up from 1.1.10, refreshed fromorigin/mainbycatalog-versions.yml. The catalog ships in the package and is published asmembers.json, so its recorded versions are content rather than bookkeeping and belong under a heading like anything else.catalog-versions.ymlnow files its own NEWS entry, viatools/news_catalog_entry.py. The refresh pull request changedinst/extdata/catalog.csvand nothing else, which is exactly the shapecheck_version.pyrejects – version unchanged from the base and no standing# hvtiR (unreleased)heading to file under. Both guards are right; the machine pull request could satisfy neither, so every refresh needed a hand-written NEWS commit before it could merge.The collision is structural rather than occasional: the unreleased heading is removed by every naming commit, so a refresh landing between a bump and the next unreleased change fails every time. 1.1.6 was named minutes before the refresh that exposed it.
The tool never opens a second unreleased heading and never repeats a bullet it has already written, because the refresh branch is regenerated weekly and reruns have to converge rather than accumulate. # hvtiR 1.1.6
Added
hvtiRimputationto the registry, taking the family to twelve members. It portsPROC STANDARDwithREPLACEand theimputsubmacro: fill by a stated method, and return a row-level record of exactly which values were changed. It imports onlystatsandutils, so it needs nomember_deps()edge and noRemotes:line of its own – the third of the three things adding a member usually takes does not apply here. It is recorded aswip:impute_multiple()is unbuilt and pooling is deferred.Registered only after
ehrlinger/hvtiRimputation’smainactually carried the package. The repo existed on GitHub for some hours withmainat an empty initial commit and the package on an unmerged branch, and a registry row written then would have brokeninstall()for everyone: every spec goes topak::pak()in one call, so a single spec resolving to amainwith no package fails the whole family install rather than its own row.-
The job catalog gains
siandmi, the imputation job types, taking it from 53 rows to 55. Both aredatasets/datasets,disposition: scaffold, destined forhvtiRtemplates.siis ported and shipped ashvtiRimputation::impute_mean();miis blocked onimpute_multiple(), which is designed and not built.⚠️
sas_breadthis deliberatelynullon both. The imputation numbers that exist are STUDY counts – 223 call single, 326 call multiple, 18 both – and this field holds file/job counts everywhere else in the catalog (bdis 1,134 files). Writing study counts into it would repeat the units error the corpus census already caught once, where macro copies were reported as runs. The counts are innote, with their unit named.This row was not optional:
hvtiRtemplates’test-roadmap.Rasserts that every taxonomy prefix has a catalog row, so addingsi/miupstream inhvtiRutilitiesturned that suite red until the catalog accounted for them. That gate is working exactly as its comment says it should. 53is no longer hard-coded in two places and one docstring. The counts moved to 55, andjobs()’s test now also assertsnrow(jobs()) == length(read_jobs())– the relationship, which cannot go stale the way a literal does.New
jobs-pin-driftworkflow, withtools/check_jobs_pin.pybehind it.inst/extdata/jobs.jsonis read by sibling repositories rather than imported, sohvtiRtemplateschecks it out by tag – deliberately, so that editing the catalog here cannot fail every pull request there. That pin has a shelf life: oncemainmoves past the newest tag, the pin still resolves and both sides stay green while the consumer validates against an older catalog. Betweenv1.1.3andv1.1.5that gap was eighteen rows and nothing reported it; it closed because somebody advanced the pin by hand. Two copies of one definition, each locally valid and neither reporting the divergence, is the failure mode this migration exists to escape.Raising the alarm needs no list of consumers; clearing it does, and the two are not symmetric.
mainahead of the newest tag means every pin is stale whatever tag it names, which is local knowledge.mainmatching the newest tag means only that a tag holding the current catalog exists – it says nothing about which tag anybody checks out. So clearing reads the consumer workflows and requires everyref:to name that tag; a ref that cannot be read is pending, never current.It reports into one reused issue rather than opening a pull request, because the remedy – name a version, tag it, advance
ref:in the consumer – is a maintainer’s decision rather than a data edit, and it closes that issue only once every consumer is confirmed to read the new tag. It does not fail a build, and it stays quiet for a seven-day grace period on both halves, sincemainahead of the newest tag is the normal unreleased window under the house cadence and a freshly cut tag needs time to propagate.
hvtiR 1.1.5
- Routed the five
dc(descriptive) jobs ininst/extdata/jobs.jsonaccording to an audit, rather than the standing shorthand that all of them werehvtiRtables’ work. That shorthand held for exactly one of the five.dc-tablesis the manuscript table job: hvtiRtemplates’ decomposition design pins it to a table-building macro allocated tohvtiRtables, andhv_tbl_summary,hv_man_tableandhv_man_table_savecover it, so it staysthin.dc-general,dc-gfupanddc-deadare base-procedure jobs, “look at the data” rather than “produce the manuscript table”. One job of each of the latter two shapes was read in the corpus; both run base procedures only, with no table-building macro.hvtiRutilitiesports two of those procedures, so all three staythinwithreplaced_bynaminghvtiRutilities::proc_contentsand/orhvtiRutilities::proc_means; the remainder is tracked ashvtiRutilities#102and does not block thethinmarking.dc-stddiffbecomes abuildrow targetinghvtiRutilitiesrather thanhvtiRtables: the decomposition design decided on 2026-09-03 to ship the successor standardized-differences macro rather than its predecessor, which makes the dependencyhvtiRutilities, and the function does not exist yet. Itsstatusandbatchare nownull, per the schema rule that those hvtiRtemplates scheduling fields are null wheneverdestinationmoves offhvtiRtemplates; the work is tracked ashvtiRutilities#103.
hvtiR 1.1.4
Corrected
inst/extdata/jobs.jsondisposition for the(dp, variable)job row fromscaffoldtothin, withreplaced_bynaminghvtiPlotR::hv_trendsandhvtiPlotR::hv_ordinal. A coverage consistency check had flagged this row as the onescaffolddisposition in a group of otherwisethinrows. Reading real jobs across several studies confirmed the shape is a descriptive time-trend plot of one variable with no covariates, already served by those twohvtiPlotRexports, so no new template is needed.The scheduled catalog refresh no longer reports success when its script crashed. The workflow tested the exit code against
-gt 2, but 0 and 2 are the only codes the script returns deliberately, and an uncaught Python exception exits 1. That passed the test, so the run ended green having written nothing, opened no pull request and raised no annotation: a record that looks checked and is not, which is the failure the schedule exists to prevent. The guard is now an allowlist.cran_version()treats any crandb answer that is not a package record as an unreadable oracle rather than crashing on it. A 200 carrying a JSON array, string or number, or a non-stringVersion, raised an uncaughtAttributeError, which was the exit-1 crash above. Such a row now keeps its recorded value and is reported, like any other unreadable oracle. That includes a 200 carrying an object with noVersion:{}and an error envelope are objects, so they passed the shape check and mapped to"", which this module reserves for an authoritative CRAN 404. A recorded version was blanked and no failure was reported.The catalog refresher waits between retried fetches. It retried three times with no delay at all, which is not a retry: the case the attempts exist for is a throttled shared-IP runner, and three requests fired inside a millisecond meet the same closed window three times. The wait now widens with the attempt, matching
remote_retry_waitinR/remote.R.An unreadable oracle now says why.
fetch()collapsed every exhausted retry to status 0, so a throttle was reported with the wording reserved for permanent causes, “renamed, private, default branch moved, or the file is gone”, and pointed the reader at a rename that never happened. The last status seen survives the attempts andwhy_unreadable()turns it into the repair it actually implies.--checkno longer reports “no drift” for a run that verified nothing. A failed fetch keeps the recorded value, sobefore == afterholds just as firmly when nothing was read as when everything was read and unchanged, and the exit code was 0 either way. It is now 2 when an oracle could not be read, matching the convention the writing path already used.statusandbatchare now null on every row whosedestinationis nothvtiRtemplates, except anintakerow, which keepsstatus: "intake"and only losesbatch. Both fields arehvtiRtemplatesscheduling values, and a rowhvtiRtemplateswill never ship a template for has nothing to schedule; leaving a value there letrfcreadqueuedon the exactggRandomForestssurface that had already justified markingrfandrfsrcout-of-scope.out-of-scopehad no remaining users after the change and is removed as a status value.
hvtiR 1.1.3
status()anddoctor()report hvtiR’s own version. hvtiR is not a member, sostatus()walksmembers()and never showed the version of the package the user is running, anddoctor()’s Environment section reported the R version, the platform andpakbut not hvtiR. Both issue templates ask for that output, so a report arrived without the one version a maintainer needs first.jobs()names the row and the field when a scalar field in the catalog arrives as an array.vapply()’s own message for that named neither, and the first sign of it was the vignette failing to build. The catalog is hand edited and four of its fields are arrays, so a scalar written as one is a plausible slip.jobs()also names the row and the field when a count field holds something that is not a whole number.as.integer()made thatNAwith a warning, which reads downstream as a field the catalog simply omits rather than one written wrong. A whole number written as a string still reads.ggBoostedTreesis no longer declared inSuggests. Noreplaced_byentry names it, so nothing loaded it, butR CMD checkinstalls all ofSuggestsand it pulls a compiledboostmtreefork; seven CI jobs were building it on every run.ggRandomForestsandhvtiPlotR, which the catalog does name, stay.The catalog gains
cran_version,dev_versionanddev_ahead, refreshed weekly from crandb and each repo’sDESCRIPTIONonmainbytools/refresh_catalog_versions.py.members.jsonis unchanged, so no downstream CV sink is affected.New
jobs(), the job catalog: every job type found in the studies corpus, routed to the package that owes it. Rendered as the “The job catalog” article.AGENTS.mdrecords the branch rulesets as they actually stand. It claimed the repositories differed only inrequired_status_checks, and that the pull-request rules were uniform; checked against the API, neither held. The two repositories that disagreed were brought into line rather than the claim being softened:hvtiRbootstrapnow requires one approving review like the rest, andhvtiGraphicsno longer carriesrequire_code_owner_reviewwith noCODEOWNERSfile behind it, and now requires an approving review like the rest. All thirteen repositories underhouse-style/repos.ymlcarry an identicalprotect mainapart fromrequired_status_checks, whichTemporalHazard,ggRandomForestsandhvtiRbootstrapenforce; the reason each of the three does is written down.
hvtiR 1.1.2
ggBoostedTreesreplaceshvtiBoostmtreein the registry. The boostmtree work moved out of an HVTI-named fork of the modelling package and into a plotting package that sits besideggRandomForests: it drawsboostmtreeandBoostMLRfits rather than re-releasing them. The member count is unchanged at eleven.hvtiBoostmtreeis retired, so an installed copy is not removed by an update and should be dropped by hand.GitHub issue templates, as YAML forms rather than markdown. Three of them – an installation or update failure, a bug in
status(),doctor()ormembers(), and a change to the family registry – each requiring the diagnostics its own case needs, so an install report cannot arrive withoutdoctor()andsessionInfo()output. The chooser also links every member’s tracker: this repository is the one everyone installs, so it is where reports about the other eleven packages land..githubis in.Rbuildignore, so none of it reachesR CMD check.tools/check_version.pyaccepts a pull request that leavesVersion:alone, provided its entry is filed under this heading. It required every pull request to bump, which the house-style cadence no longer asks for. A version that goes backwards still fails, and an unchanged version with no unreleased heading still fails, which is the rebase collision the guard was built for.NEWS.mdversion headings move from##to#, matching every other package in the family. This repository was the only one at level two, which meant the version guard here and the equivalent test inhvtiRbootstrapkeyed on different heading levels. Both R’s news reader and pkgdown parse the file to the same 16 and 15 entries as before, so nothing downstream changes.
hvtiR 1.1.1
Adds
lint.yamlandtest-coverage.yaml, the two workflows every other member of the family already ran. This repository was the only one without them, so nothing here caught a style regression or a coverage drop.Clears the 26 lints that adding
lint.yamlwould have failed on. No.lintris added:hvtiRutilitiesruns the same job with no configuration file, so lintr’s defaults – including the 80-character line – are demonstrably achievable here, and this repository stays at the strictest setting in the family. Three lints are genuine false positives and carry a# nolintwith the reason instead:unknowninbuild_specs()is used inside a cli glue string that lintr cannot parse, andSELF_REPOandMIN_R_VERSIONare package constants deliberately spelled in upper case so they do not read as locals at their use sites.lint.yamlalso brings adocs-currentjob, which runsroxygenise()and fails on any diff inman/,NAMESPACEorDESCRIPTION. That gate enforces a rule the contributing notes already stated but nothing checked.
hvtiR 1.1.0
The vignette described
doctor()as adding “two environment checks” when it reports four – R version, platform,pak, and nowrenv. It had already omitted thepakcheck before this release. It now also covers reproducible installs and the installer self-report, which the README gained at the same time; the two had drifted apart.update()now reportshvtiR’s own version against GitHub. The installer is not a member of its own registry, so nothing previously mentioned it and a user could stay current on all eleven members while silently running a stale installer. It is reported and never installed:update()cannot updatehvtiRin place, because calling it means the namespace is already loaded and the loaded-namespace guard refuses. When the installer is behind, the report namespak::pak("ehrlinger/hvtiR")as the remedy.doctor()reports whether anrenvproject is active. When one is not, it says so and adds that member versions are not pinned, becauseinstall()resolves from GitHubmainand two runs a week apart can land on different commits under the same version number. Informational, not a failure:renvis optional and its absence blocks nothing.The README gains a “Reproducible installs” section:
renv::init(),hvtiR::install(),renv::snapshot(), andrenv::restore()on the other machine. It works because pak records the commit it installed from, which is the fieldrenvreads – and it notes the limit, that a member installed from a local working copy carries no commit forrenvto record.dev/specs/gains a rejected design record for asnapshot()/restore()pair that would have pinned members by commit inside this package. It was specified, approved and then rejected once it was clearrenvreads the sameRemoteShafield and so has the same ceiling while covering strictly more. The record is kept for the evidence it gathered, including why installing by release or tag was rejected: six of the eleven members have no release at all, andhvtiRlifetablesimportsTemporalHazard (>= 1.2.0)whose latest release is v1.1.0.
hvtiR 1.0.13
- Recomposed
.claude/house-style.mdagainsthouse-style-v1at64c9e23(archivedstandard-2026-08-28-3). Wording only: the Development records section now separates the.Rbuildignoreexclusion from git tracking, and says that “one directory” contrasts against thespecs/+specs/plans/pair rather than implying a per-repository subdirectory. The rule is unchanged.
hvtiR 1.0.12
- Development records moved from
design/todev/specs/, adopting the portfolio convention settled inehrlinger/house-style. Both gain anhvtiverseslug, since the house style names a file<date>-<slug>-<kind>.mdand2026-08-19-design.mdcarried a date and a kind with nothing in between.^design$became^dev$in.Rbuildignore, and the README link, a.gitignorecomment and the directory’s own README table were repointed.
hvtiR 1.0.11
- This repo now carries the composed house style and the CI check that enforces it, having been added to the
house-styleregistry. The check fails when.claude/house-style.mddrifts from the vault sources it was composed from. -
hvtiEDAreportsis recorded as archived (2026-08-27) in the README and the two design documents that list it as a non-member.
hvtiR 1.0.10
- The
HVTI Recipesrow ininst/extdata/catalog.csvnow points atehrlinger/hvtiGraphics, and its homepage at https://ehrlinger.github.io/hvtiGraphics/. The book’s repository was renamed fromhvti_graphics; GitHub redirects the repository, but GitHub Pages does not, so the homepage was a dead link rather than a redirected one. The book is not an R package, so it sits outsidemembers()and was missed when the registry was repointed in 1.0.8.
hvtiR 1.0.9
-
.remember/, the scratch directory written by therememberskill, is now excluded from the build and from git. It was listed in neither ignore file, soR CMD buildcopied it into the tarball andR CMD checkreported a “hidden files and directories” NOTE against the working tree. Nothing an installed package exposes changes.
hvtiR 1.0.8
- The registry now names three repositories by the names GitHub actually serves.
hvtiRdatasetsbecamehvtiRdatabuild, and its repository moved with it;ehrlinger/hvtiPropensityScoresbecameehrlinger/hvtiRpropensity; andehrlinger/temporal_hazardbecameehrlinger/TemporalHazard. - Only the first of those broke anything.
test-registry-live.Rfetches each member’sDESCRIPTIONand compares itsPackagefield, so the renamed package failed while the two renamed repositories kept passing on GitHub’s redirect – staleness that works until the redirect stops working. - Every member’s package name now matches its repository name. The mapping is still stored rather than derived, because these three names moved in one week and a derived repo would fail silently the next time one does.
hvtiR 1.0.7
- A pull request whose
Version:has not moved pastmainnow fails CI. Two branches bumping to the same version do not conflict in git – the identical line merges silently and onlyNEWS.mdshows a conflict – so the collision was invisible until someone read the release notes. The guard also checks thatNEWS.md’sVersion:line and its per-release heading agree withDESCRIPTION, and thatDateneither goes backwards nor sits in the future.Dateis not required to advance: same-day releases are normal here, so demanding a new day would block them.
hvtiR 1.0.6
-
member_deps()records thathvtiRtemplatesimportshvtiRutilities. The dependency was added upstream on 2026-08-27; without the entry an update that names onlyhvtiRtemplatesleavespakto resolvehvtiRutilitiesfrom CRAN, where it does not exist.
hvtiR 1.0.5
- The live registry test no longer reports a throttled CI runner as a defect.
fetch_description()gained anattemptsargument that retries a failed request with a widening wait, and the live test asks for three attempts. A renamed repository fails exactly as before, only later: retrying separates a transient stall from a persistent one by how long it lasts, which is the only signal available when both surface as the same failed connection. -
fetch_description()rejects a non-positive or malformedattemptscount instead of failing later with an unbound-object error. -
status()anddoctor()are unchanged. They keep the single-attempt default, so a member that cannot be reached still resolves inside the five-second budget rather than three times over.
hvtiR 1.0.4
Added
inst/extdata/catalog.csv, presentation metadata for every published artifact: the family members frommembers(), plus the SAS/Chazardcode and the HVTI Recipes book, which are not R packages.status,cranandroleare stored as fields rather than folded into the blurb, so each consumer can render them in its own house style.Added
tests/testthat/test-catalog.R, which ties the catalog’s member rows tomembers()exactly. A package added to the registry without a catalog entry now failsR CMD checkrather than silently shortening the package lists published downstream.Added
tools/catalog_to_json.py, which the pkgdown workflow runs to publishmembers.jsonalongside the site. Member counts are derived there, so the family-count sentence used by downstream documents is arithmetic rather than prose maintained by hand.
hvtiR 1.0.3
-
status()now detects an older GitHub installation when its version matchesmainbut pak’s recordedRemoteShadoes not. The member reports as"stale", soupdate()reinstalls it. Installs without GitHub commit provenance continue to use version comparison alone. - Commit checks use GitHub’s public Atom feeds rather than its rate-limited API. A failed commit check reports
"unknown"and its reason is retained fordoctor().
hvtiR 1.0.2
- Remote version checks now use a five-second connection timeout and retain per-repository failure reasons for
doctor()to report. -
doctor()now reports whetherpakis available before showing member status. - Documentation now explains GitHub as the family’s leading release source without hard-coded CRAN or GitHub versions, and the offline vignette example uses the qualified
hvtiR::status()call. - The pkgdown site is validated on pull requests with read-only permissions, and deployments remove pages retired by the
hvtiversetohvtiRrename. - Source-package tarballs and linked-worktree Git metadata are excluded from package builds.
hvtiR 1.0.1
The package is renamed from
hvtiversetohvtiR, matching thehvtiR*prefix the rest of the family uses. Install fromehrlinger/hvtiR.-
Every exported function is renamed, dropping the package-name prefix that no other family package carries. The package is meant to be called with
::rather than attached:was now hvtiverse::hvtiverse_install()hvtiR::install()hvtiverse::hvtiverse_update()hvtiR::update()hvtiverse::hvtiverse_status()hvtiR::status()hvtiverse::hvtiverse_doctor()hvtiR::doctor()hvtiverse::hvtiverse_members()hvtiR::members() The class returned by
status()is renamedhvtiR_status. Unlike the functions it stays package-qualified, because S3 classes are matched by string and a barestatusclass would collide.No deprecated aliases are provided.
hvtiverse1.0.0 was never depended on outside this family, so there is nothing to migrate.