Skip to content

Production-harden loader, multi-package dependency tracking, exhaustive tests#15

Open
felix-andreas-finccam wants to merge 3 commits into
mainfrom
claude/production-readiness-testing-88icbf
Open

Production-harden loader, multi-package dependency tracking, exhaustive tests#15
felix-andreas-finccam wants to merge 3 commits into
mainfrom
claude/production-readiness-testing-88icbf

Conversation

@felix-andreas-finccam

Copy link
Copy Markdown
Contributor

Summary

Brings loadfast into production shape: fixes real multi-package and lifecycle bugs found by probing, makes the package a clean installable artifact (R CMD check --as-cran: 0 errors, 0 warnings), and extends testing from 259 to 299 harness checks plus a new testthat suite that runs against the installed package during R CMD check.

Loader changes (R/loadfast.R)

Multi-package dependency tracking (the main gap): previously, after reloading a dependency, importer packages kept running against a stale snapshot and a plain load_fast() on them short-circuited with "No changes". Now:

  • Reloading a package flags every cached package that (directly or transitively) imports it.
  • A flagged package's next load_fast() auto-upgrades to a full reload, with a message explaining why.
  • Full loads recursively reload flagged dependencies first, so a chain C -> B -> A converges with a single load_fast(C) after A is reloaded.
  • No-change loads do not invalidate dependents.

Lifecycle fixes:

  • Depends: packages are attached during full loads (clear error when missing) — previously ignored, so package code relying on them crashed.
  • .onUnload runs best-effort when unloadNamespace() refuses (namespace imported by other loaded packages) before the registry entry is force-replaced.
  • A failed full load no longer leaves a half-built namespace registered/attached; recovery on the next call still works.
  • Renaming the package in DESCRIPTION in place now detaches/unloads the old identity instead of leaving it on the search path forever.
  • Incremental re-sourcing follows Collate-aware full-load order instead of alphabetical basenames.
  • Namespace spec version comes from DESCRIPTION, so getNamespaceVersion() reports correctly.
  • Re-entrance guard is now a path stack: public load_fast() stays strict, internal dependency-reload recursion is allowed.

Packaging

  • DESCRIPTION: version 1.0.0; methods/tools declared in Imports; dev deps moved from the non-standard DevDeps field to Suggests.
  • roxygen2-generated NAMESPACE and man/ pages for both exports.
  • Top-level message() moved to a suppressible .onAttach; sources are ASCII-only.
  • library("testthat") replaced with a guarded attachNamespace(); tiny methods::: internals (.hasS4MetaData/.getGenerics/.TableMetaPrefix) inlined as equivalents so --as-cran is warning-free.
  • Complete .Rbuildignore; renv settings track Suggests.
  • R CMD check --as-cran: 0 errors, 0 warnings; the only inherent NOTE is the attach() call (same NOTE pkgload carries).

Testing

  • Stage 6 (new) — multi-package dependency invalidation: direct importer invalidation, transitive chain convergence, no-change loads, whole-namespace import(), S4 class redefinition across packages, forced-unregister path incl. .onUnload.
  • Stage 7 (new) — production hardening: Depends attachment + missing-Depends error, rename-in-place cleanup, failed-full-load cleanup and recovery, namespace version fidelity, Collate order on incremental reload, re-entrance guard, verbose logs.
  • tests/testthat/ (new) — self-contained smoke/error-path tests that run against the installed package during R CMD check, so the check proves the built artifact works.
  • Harness result: 299 passed, 0 failed (verified locally on R 4.3.3/Linux).

CI

  • Harness job now also runs on macOS (Linux/Windows/macOS, R 4.5.2 via renv).
  • New R CMD check --as-cran job on Linux/Windows/macOS at R release, plus oldrel-1 on Linux.

🤖 Generated with Claude Code

https://claude.ai/code/session_01S8v6K52c7AJwuDkyyETMU7


Generated by Claude Code

claude added 3 commits July 6, 2026 12:38
…ve tests

Loader (R/loadfast.R):
- Track cross-package dependencies: reloading a package flags every cached
  package that (transitively) imports it; the flagged package's next
  load_fast() auto-upgrades to a full reload, and full loads recursively
  reload flagged dependencies first, so a chain C -> B -> A converges with a
  single load_fast(C) after A is reloaded. No-change loads do not invalidate.
- Attach DESCRIPTION Depends packages during full loads (with a clear error
  when one is missing), matching library()/load_all() semantics.
- Run .onUnload best-effort when unloadNamespace() refuses (dependency
  imported by other loaded packages) before force-replacing the registry
  entry.
- Clean up half-built namespaces when a full load fails partway (unregister
  and detach), while keeping failed-load recovery on the next call.
- Detach/unload the old identity when the Package field changes in place for
  a cached path.
- Re-source incremental changes in Collate-aware full-load order instead of
  alphabetical basename order.
- Seed the namespace spec version from DESCRIPTION so
  getNamespaceVersion() reports correctly.
- Replace the boolean re-entrance flag with a path stack (public load_fast()
  stays strict; internal recursion is allowed).
- Replace library("testthat") with a guarded attachNamespace(), inline the
  tiny methods::: metadata helpers (.hasS4MetaData/.getGenerics/
  .TableMetaPrefix equivalents), move the top-level message to .onAttach,
  and make the sources ASCII-only -- R CMD check --as-cran is now clean
  (0 errors, 0 warnings; only the inherent attach() NOTE remains).

Packaging:
- DESCRIPTION: version 1.0.0, declare methods/tools in Imports, move dev
  dependencies from the non-standard DevDeps field to Suggests.
- roxygen2-generated NAMESPACE and man/ pages for both exports.
- New tests/testthat suite that runs against the installed package during
  R CMD check (smoke, incremental, dependency invalidation, error paths).
- .Rbuildignore covers all non-package files; renv settings track Suggests.

Tests (test_loadfast.R, 299 checks total):
- Stage 6: multi-package dependency invalidation -- direct importer
  invalidation, transitive chain convergence, no-change loads, whole-
  namespace import(), S4 class redefinition across packages, forced
  unregister path incl. .onUnload.
- Stage 7: production hardening -- Depends attachment and failure, rename-
  in-place cleanup, failed-full-load cleanup and recovery, namespace version,
  Collate order on incremental reload, re-entrance guard, verbose logs.

CI: harness now also runs on macOS; new R CMD check --as-cran job on
Linux/Windows/macOS (R release) plus oldrel-1 on Linux.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8v6K52c7AJwuDkyyETMU7
…ix CI installs

rlang::ns_registry_env() is defunct as of rlang 1.2.0 and hard-errors, which
broke every R CMD check CI job on fresh installs (the renv.lock pinned an old
rlang, masking it locally). Replace it with .loadfast.ns_register() /
.loadfast.ns_unregister(), which construct .Internal(registerNamespace(...)) /
.Internal(unregisterNamespace(...)) calls at runtime -- the same approach
current pkgload uses. loadfast now has no third-party runtime dependencies;
rlang moves to Suggests (still imported by the devpackage test fixture, which
now imports the stable rlang::abort instead of the defunct symbol).

The harness CI jobs failed separately during renv::restore(): the lockfile
drags in dev-machine packages (keyring needs libsecret on ubuntu; a package
failed to resolve on macos). Switch those jobs to setup-r-dependencies, which
installs exactly the DESCRIPTION Imports+Suggests the harness needs and
handles system requirements; renv.lock remains for local development.

Verified locally: harness 299/299, R CMD check --as-cran clean (2 expected
NOTEs), testthat suite passes under check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8v6K52c7AJwuDkyyETMU7
normalizePath(mustWork = TRUE) wording differs by OS: "No such file or
directory" on unix vs "The system cannot find the file specified" on
Windows. Broaden the two expected-error patterns accordingly; this was the
only remaining CI failure (Windows R CMD check).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8v6K52c7AJwuDkyyETMU7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants