From 0b0a5f8c766ef5a8bc39d16452835bbbe4e4af0d Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 25 Jun 2026 09:09:05 -0400 Subject: [PATCH 1/3] QA: run_qa v1.6 form + ExplicitImports Convert test/qa/qa.jl from the hand-rolled Aqua body to the SciMLTesting run_qa v1.6 form and enable the ExplicitImports checks. Aqua mapping (preserves every prior check): - test_ambiguities(recursive = false) -> aqua_kwargs ambiguities recursive = false - find_persistent_tasks_deps / deps_compat / project_extras / stale_deps / unbound_args -> default Aqua.test_all sub-checks - test_piracies(treat_as_own = []) -> default (empty), dropped - test_undefined_exports(broken = true) -> aqua_broken = (:undefined_exports,), tracked in SciML/EasyModelAnalysis.jl#300 (`Variable`/`rotate!` leak dead through @reexport) ExplicitImports (explicit_imports = true): - no_stale_explicit_imports, all_explicit_imports_via_owners, all_explicit_imports_are_public: pass - all_qualified_accesses_via_owners / all_qualified_accesses_are_public: pass with documented per-name ignore-lists for dependency-internal qualified accesses (AbstractSystem, DynamicPPL, unwrap, AbstractMCMCEnsemble, LN_SBPLX, successful_retcode) - no_implicit_imports: many heavy reexport / bulk-using names; left @test_broken via ei_broken, tracked in SciML/EasyModelAnalysis.jl#301 test/qa/Project.toml: SciMLTesting compat floor -> "1.6" (the broken-marker and ExplicitImports kwargs require 1.6.0). Aqua kept as a direct dep (the ambiguities sub-check spawns a child process that needs it); ExplicitImports comes transitively through SciMLTesting. Verified locally on Julia 1.10 against released SciMLTesting 1.6.0 (no dev-from-branch): QA group is 15 Pass, 2 Broken (undefined_exports + no_implicit_imports), 0 Fail/0 Error. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/qa/Project.toml | 2 +- test/qa/qa.jl | 56 +++++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 03e281b..0a6b61b 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -11,5 +11,5 @@ EasyModelAnalysis = {path = "../.."} [compat] Aqua = "0.8" SafeTestsets = "1, 0.1" -SciMLTesting = "1" +SciMLTesting = "1.6" julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 3b25a94..00879cb 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,24 +1,32 @@ -using EasyModelAnalysis, Aqua -@testset "Aqua" begin - # find_persistent_tasks_deps Pkg.develop's each dependency. On Julia 1.12 a Pkg - # regression (JuliaLang/Pkg.jl#4587) makes Pkg.develop honor a developed - # dependency's relative [sources] and resolve it against the depot, so - # OptimizationBBO (which pins its in-repo OptimizationBase via [sources], as - # every Optimization.jl sublibrary does) errors with "expected package - # OptimizationBase to exist at path .../OptimizationBBO/OptimizationBase". The - # [sources] is correct and load-bearing for the monorepo; the bug is upstream. - # Remove this version gate once Pkg.jl#4587 is fixed (see EasyModelAnalysis#303). - if VERSION < v"1.12" - Aqua.find_persistent_tasks_deps(EasyModelAnalysis) - end - Aqua.test_ambiguities(EasyModelAnalysis, recursive = false) - Aqua.test_deps_compat(EasyModelAnalysis) - Aqua.test_piracies( - EasyModelAnalysis, - treat_as_own = [] - ) - Aqua.test_project_extras(EasyModelAnalysis) - Aqua.test_stale_deps(EasyModelAnalysis) - Aqua.test_unbound_args(EasyModelAnalysis) - Aqua.test_undefined_exports(EasyModelAnalysis, broken = true) -end +using SciMLTesting, EasyModelAnalysis, Test + +run_qa( + EasyModelAnalysis; + explicit_imports = true, + aqua_kwargs = (; ambiguities = (; recursive = false)), + # undefined_exports: `Variable` and `rotate!` leak in (dead) via `@reexport` + # (SciML/EasyModelAnalysis.jl#300) + aqua_broken = (:undefined_exports,), + ei_kwargs = (; + all_qualified_accesses_via_owners = (; + ignore = ( + :AbstractSystem, # ModelingToolkitBase (accessed via ModelingToolkit) + :DynamicPPL, # DynamicPPL (accessed via Turing) + :unwrap, # SymbolicUtils (accessed via Symbolics) + ), + ), + all_qualified_accesses_are_public = (; + ignore = ( + :AbstractMCMCEnsemble, # AbstractMCMC (not public) + :AbstractSystem, # ModelingToolkit (not public) + :DynamicPPL, # Turing (not public) + :LN_SBPLX, # NLopt (not public) + :successful_retcode, # SciMLBase (not public) + :unwrap, # Symbolics (not public) + ), + ), + ), + # no_implicit_imports: heavy reexport / bulk-using of the SciML stack + # (SciML/EasyModelAnalysis.jl#301) + ei_broken = (:no_implicit_imports,), +) From 1c139a2e4c6f236bb75f2a462178f446a5028f49 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Mon, 29 Jun 2026 12:51:10 -0400 Subject: [PATCH 2/3] QA: drop now-resolvable ExplicitImports exceptions ExplicitImports findings against the released stack (SciMLBase 3.30, ModelingToolkit 11.29, Symbolics 7.29) no longer flag three names that the conversion commit had ignored, so the ignore-lists are trimmed to only the genuinely-irreducible external API: - all_qualified_accesses_via_owners: dropped :AbstractSystem (now owned by ModelingToolkit) and :unwrap (now owned by Symbolics). Only :DynamicPPL remains (reached through Turing's re-export; DynamicPPL is not a direct dependency). - all_qualified_accesses_are_public: dropped :AbstractSystem, :successful_retcode (now public in SciMLBase) and :unwrap (now public in Symbolics). The kept three are genuinely non-public external names: :AbstractMCMCEnsemble (AbstractMCMC), :DynamicPPL (Turing submodule re-export), :LN_SBPLX (NLopt algorithm constant). No source change was needed: src already qualifies these through their public owners (SciMLBase.successful_retcode, ModelingToolkit.AbstractSystem, Symbolics.unwrap), so the public/owner checks now pass without an ignore. no_implicit_imports stays @test_broken (ei_broken): the module deliberately `@reexport`s the SciML stack (DifferentialEquations, ModelingToolkit, Distributions, Plots), so converting the 47 implicit names to explicit imports would break the intended public surface and mis-attribute owners (e.g. solve/remake to SciMLExpectations). Tracked in SciML/EasyModelAnalysis.jl#301. undefined_exports stays aqua_broken (#300). Verified GROUP=QA locally on Julia 1.12 against released SciMLTesting 1.7.0: QA/qa.jl is 15 Pass, 2 Broken (no_implicit_imports + undefined_exports), 0 Fail / 0 Error. The two trimmed public/owner checks pass with no Unexpected Pass. Co-Authored-By: Chris Rackauckas --- test/qa/qa.jl | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 00879cb..3da5e9e 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -9,20 +9,15 @@ run_qa( aqua_broken = (:undefined_exports,), ei_kwargs = (; all_qualified_accesses_via_owners = (; - ignore = ( - :AbstractSystem, # ModelingToolkitBase (accessed via ModelingToolkit) - :DynamicPPL, # DynamicPPL (accessed via Turing) - :unwrap, # SymbolicUtils (accessed via Symbolics) - ), + # DynamicPPL.acclogp!! is reached through Turing's re-export; DynamicPPL + # is not a direct dependency, so accessing it via Turing is intentional. + ignore = (:DynamicPPL,), ), all_qualified_accesses_are_public = (; ignore = ( - :AbstractMCMCEnsemble, # AbstractMCMC (not public) - :AbstractSystem, # ModelingToolkit (not public) - :DynamicPPL, # Turing (not public) - :LN_SBPLX, # NLopt (not public) - :successful_retcode, # SciMLBase (not public) - :unwrap, # Symbolics (not public) + :AbstractMCMCEnsemble, # AbstractMCMC: no public alias for the ensemble type + :DynamicPPL, # Turing: submodule re-export, no public alias + :LN_SBPLX, # NLopt: algorithm constant, not marked public ), ), ), From 191fe4c45239b5ae0469ad5cbb13d54f112e7eee Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 3 Jul 2026 05:20:58 -0400 Subject: [PATCH 3/3] QA: ignore ModelingToolkit.value in ExplicitImports (rebase fixup) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebasing onto the advanced main (65 sibling PRs merged) shifted the ExplicitImports landscape again: `value` — the Symbolics unwrap API re-exported through ModelingToolkit and used as `ModelingToolkit.value` in src/threshold.jl — is now flagged by two EI checks that previously passed: - all_qualified_accesses_via_owners: `value` has owner `Symbolics` but is accessed from `ModelingToolkit` (the direct dependency). - all_qualified_accesses_are_public: `value` is not declared public in `ModelingToolkit`. `value` is the standard, intended Symbolics unwrap call; ModelingToolkit re-exports it and does not mark it public, so both are genuine external-API ignores in the same family as :DynamicPPL / :LN_SBPLX. Added :value to both ignore-lists (not a source change: threshold.jl correctly uses the re-exported API). Verified GROUP=QA locally on Julia 1.12 against released SciMLTesting 1.7.0 after the rebase: QA/qa.jl is 15 Pass, 2 Broken (no_implicit_imports + undefined_exports), 0 Fail / 0 Error. Co-Authored-By: Chris Rackauckas --- test/qa/qa.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 3da5e9e..c0fc243 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -9,15 +9,21 @@ run_qa( aqua_broken = (:undefined_exports,), ei_kwargs = (; all_qualified_accesses_via_owners = (; - # DynamicPPL.acclogp!! is reached through Turing's re-export; DynamicPPL - # is not a direct dependency, so accessing it via Turing is intentional. - ignore = (:DynamicPPL,), + ignore = ( + # DynamicPPL.acclogp!! is reached through Turing's re-export; DynamicPPL + # is not a direct dependency, so accessing it via Turing is intentional. + :DynamicPPL, + # `value` is the Symbolics unwrap API, re-exported through ModelingToolkit + # (the direct dependency); accessing it as `ModelingToolkit.value` is intended. + :value, + ), ), all_qualified_accesses_are_public = (; ignore = ( :AbstractMCMCEnsemble, # AbstractMCMC: no public alias for the ensemble type :DynamicPPL, # Turing: submodule re-export, no public alias :LN_SBPLX, # NLopt: algorithm constant, not marked public + :value, # ModelingToolkit: Symbolics unwrap re-export, not marked public ), ), ),