Skip to content

v0.4.4 — Quality release (DW-Production v0.4.3.1 fanout findings)

Choose a tag to compare

@jpazvd jpazvd released this 29 May 11:58
· 128 commits to develop since this release
99d672d

v0.4.4 (2026-05-29)

Quality release. Three v0.4.4 milestone issues land in one cycle (PRs
#39,
#41,
#43). All three
surfaced empirically during the DW-Production v0.4.3.1 fanout audit
(IM / WS / HVA install + reviewer-mode runs on 2026-05-28). No public
API breaks; the bumped-default behaviour stays backwards-compatible
for v0.4.3.1 consumers.

Follow-up issue #42
tracks the remaining un-prefixed exports for a single naming-cleanup
PR (proposed v0.4.5).

Three carry-forward bugs + dw_-prefix aliases (issue #36)

Closes two of the three sub-fixes flagged on #36 during the HVA
scaffold-install Copilot review on 2026-05-28. The third (a value-arg
propagation bug in dw_regions.R) is moot — dw_regions is being
redesigned to consume the new unicef-drp/Country-and-Region-Metadata-API
package in #40 (v0.5.0); the affected code path is removed.

Sub-fix 1: aggregate_data_v2.R is now safe to source standalone

Pre-fix, aggregate_data_v2.R called .cso_require() from zzz.R.
Sourcing aggregate_data_v2.R directly (without sourcing zzz.R
first) left .cso_require undefined; the first call to
aggregate_data_v2(...) errored with could not find function .cso_require.

The file now defines a local fallback for .cso_require() at source
time, gated by exists(".cso_require", mode = "function", inherits = TRUE).
When zzz.R has already been sourced into .GlobalEnv, the shared
helper wins and nothing is redefined; when only aggregate_data_v2.R
is sourced, the local fallback provides the same behaviour.

Sub-fix 2: create_sector_script() profile sentinel check relaxed (and aligned with create_profile())

Pre-fix, the generated 00_run_<sector>.R template checked
isTRUE(profile_DW_Production). The DW-Production profile
(profile_DW-Production.R) does not set profile_DW_Production, so
the generated script errored at the sentinel check even after the
profile was sourced successfully.

The check is now relaxed from isTRUE(<name>) to !is.null(<name>),
which accepts any non-null value — character paths, numeric values,
or the boolean sentinel that create_profile("DW-Production") emits
(profile_DW_Production <- TRUE). The default profile_name stays at
"profile_DW_Production" so the documented scaffold flow
(create_profile()create_dw_sector_script()) works out of the
box without additional configuration.

The new error message names the missing variable so future
profile-vs-template mismatches surface a concrete fix. The roxygen
@param doc also clarifies that the generated template uses
projectFolder directly for input/output paths, so the profile MUST
set projectFolder for the runner to do useful work — the sentinel
check only confirms the profile was sourced.

For DW-Production consumers (whose existing profile_DW-Production.R
doesn't set the sentinel), the one-line profile_DW_Production <- TRUE
must be added to the profile. Tracked as a separate DW-Production-side
follow-up PR.

dw_-prefixed canonical aliases for the two touched exports

Toolkit-export naming consolidates around the dw_ prefix in v0.4.x;
the non-prefixed names predate that convention. While in this PR's
files anyway, added:

  • dw_aggregate_data_v2 (alias for aggregate_data_v2)
  • dw_create_sector_script (alias for create_sector_script)

Both names point to the same function and share the same \\code{\\link{}} man page (via roxygen @rdname). The non-prefixed names continue to work — no breaking change. Follow-up issue tracks the rest of the un-prefixed exports (aggregate_data, generate_markdown_report, apply_time_window, generate_agg_footnote, create_profile, review_profile, test_scripts, create_dw_sector_script) as a single cleanup PR.

dw_default_unicef_allowlist() helper for consumers (issue #37)

New exported helper returns a character vector of ^...-anchored
regex patterns covering UNICEF DRP GitHub-raw and repository URLs.
Consumers seed dw_url_allowlist from this constant instead of
re-deriving the patterns per project:

# In profile_<consumer>.R
dw_url_allowlist <- c(
  dw_default_unicef_allowlist(),
  # Project-specific extras:
  "^https://yourorg\\.github\\.io/"
)

Surfaced empirically by the DW-Production reviewer-mode audit on
2026-05-28 (IM 01_immunization.R): every URL-using sector script
hand-wrote the same ^https://raw\\.githubusercontent\\.com/unicef-drp/
pattern. The helper consolidates the duplication and lets future
UNICEF-DRP additions land in one place upstream rather than in each
consumer's profile.

Purely additive — consumers must opt in by composing the helper into
their dw_url_allowlist. The URL-freeze safety contract is unchanged
(no URL is fetchable without explicit ratification).

.dw_frozen_root() resolution is now discoverable (issue #38)

.dw_frozen_root() falls through a 3-tier resolution chain when
locating the URL-freeze cache root:

  1. dw_frozen_root global (opt-in; preferred)
  2. <githubFolder>/_frozen (fallback)
  3. <getwd()>/_frozen (last-resort fallback)

Pre-v0.4.4 the helper resolved silently — consumers whose project
layout didn't match the fallback heuristic had to grep dw_io.R to
discover why dw_use("https://...") couldn't find their frozen file.

v0.4.4 adds two discoverability improvements:

  • A new internal helper .dw_frozen_root_resolved() returns a
    (path, source) pair so downstream callers can surface the chosen
    tier in messages and error envelopes.
  • .dw_frozen_root_notify_once() emits a session-scoped notice the
    first time the helper falls back beyond tier #1:
    message() for tier #2 (<githubFolder>/_frozen),
    warning() for tier #3 (<getwd()>/_frozen).
    Consumers that explicitly set dw_frozen_root get no notice.

The missing-frozen-copy error envelope in .resolve_remote_url()
now includes the resolution tier so consumers see which fallback
fired (or that the explicit global picked the path that's wrong):

[cso_toolkit.dw_use:remote] Reviewer mode forbids fetching from the network.
 Missing frozen copy: <path>
 URL: <url>
 Frozen-root resolution: <chosen-root> (<tier-name>)
 Fix:
   1. If the path above is wrong, set `dw_frozen_root <- '<your-canonical-frozen-path>'` in your profile.
   2. Otherwise, a producer must call dw_use(...) once and commit the frozen file + sidecar.

Surfaced empirically by the DW-Production IM reviewer-mode audit on
2026-05-28: the fallback resolved to <githubFolder>/_frozen instead
of DW-Production's convention of <projectFolder>/01_dw_prep/011_rawdata/_frozen/.
Three runs (75+ min of slow Teams network) were needed to diagnose
what a single message could have surfaced at session start.

No public-API changes. The internal .dw_frozen_root() (path-only)
is preserved for backward compatibility with v0.4.3.1 callers.