Skip to content

Use dependabot and drop support for Julia < 1.10 - #292

Merged
devmotion merged 13 commits into
masterfrom
dmw/dependabot
Sep 17, 2026
Merged

devmotion merged 13 commits into
masterfrom
dmw/dependabot

Conversation

@devmotion

@devmotion devmotion commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Drops support for Julia < 1.10 and replaces CompatHelper with dependabot.

  • .github/dependabot.yml gains the julia ecosystem, with all Julia package updates grouped into one daily PR. The CompatHelper workflow is removed.
  • A Downgrade workflow runs the test suite against the declared lower bounds.
  • Test dependencies move from [extras]/[targets] to test/Project.toml, with [workspace] projects = ["test", "docs"] in the root.
  • julia = "1.10", and the CI matrix tests min-patch instead of min. Base.div(::TrackedReal, ::TrackedReal, ::RoundingMode) is now defined unconditionally, and SPARSE_CAT_AMBIGUITY drops its VERSION >= v"1.10" half.

The lower compat bounds are raised to versions that work. One of them is required, not bookkeeping:

StaticArrays = "1.6.4". JacobianConfig builds its tracked output buffer with similar(output, TrackedReal{D,D,Nothing}) and fills it element-wise in track!. TrackedReal is never isbitstype: it is a mutable struct, and it holds the InstructionTape. MArray fakes element assignment with unsafe_store! into its single NTuple field, so it only supports setindex! for isbits eltypes. StaticArrays returns a SizedArray from similar for non-isbits eltypes from 1.6.4 on; 1.6.3 and earlier return an MArray, whose setindex! errors with

setindex!() with non-isbitstype eltype is not supported by StaticArrays. Consider using SizedArray.

Pinning both versions confirms it: 1.6.3 reproduces the error, 1.6.4 returns a SizedArray. So hessian!(::DiffResult, f, ::MVector) and the corresponding jacobian! path require 1.6.4. Without the bound the Downgrade job resolves a StaticArrays that cannot run them. #295 fails on the same code path, on Julia 1.0, where the resolver picks StaticArrays 1.3.6.

Statistics becomes a package extension

Statistics was a hard dependency for exactly one method, mean(::TrackedArray), and its two instruction hooks. Those move to ext/StatisticsExt.jl.

Nothing is lost by making it weak. ReverseDiff does not export or re-export mean, so reaching the method requires the name, and every route to it already loads Statistics: using Statistics, a qualified Statistics.mean, or a package such as StatsBase that re-exports it and depends on it. The extension loads exactly when the method can be called.

In return ReverseDiff stops loading Statistics for everyone, which also stops it triggering StaticArraysStatisticsExt downstream, since StaticArrays is a hard dependency that takes Statistics as a weakdep. Measured with @time_imports, that is ~1.2 ms plus ~1.3 ms against a ~1740 ms using ReverseDiff — small, so the argument is the accurate dependency declaration rather than the load time.

The tests assert the extension is absent before using Statistics and present after. Without that, a broken extension would go unnoticed: TrackedArray <: AbstractArray, so the generic mean falls through to the tracked sum and still returns the right value and gradient while recording a different instruction. This is not hypothetical — it happened during development against a stale manifest.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.53%. Comparing base (33cd287) to head (5fb518c).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #292      +/-   ##
==========================================
- Coverage   86.07%   85.53%   -0.54%     
==========================================
  Files          18       19       +1     
  Lines        1967     1964       -3     
==========================================
- Hits         1693     1680      -13     
- Misses        274      284      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

devmotion and others added 8 commits September 15, 2026 20:56
The downgrade test resolved FunctionWrappers 1.0.0 and errored in every
testset that exercises a `CompiledTape` (ChainRulesTests, GradientTests,
JacobianTests, HessianTests):

    LoadError: Module IR does not contain specified entry function
    Stacktrace:
      [1] assume @ FunctionWrappers/src/FunctionWrappers.jl:11 [inlined]
      ...
      [5] forward_pass!(compiled_tape::ReverseDiff.CompiledTape{...})
          @ ReverseDiff src/api/tape.jl:124

FunctionWrappers <= 1.1.1 defines `assume` with the pre-Julia-1.6 form of
`Base.llvmcall` in which the second element of the tuple is the function
body. Since v"1.6.0-DEV.663" Julia interprets that element as the name of
the entry function in the supplied module IR, so the call fails at run
time. FunctionWrappers 1.1.2 is the first release that branches on
`VERSION` and emits a named `@fw_assume` definition.

ReverseDiff therefore never worked with FunctionWrappers < 1.1.2 on any
Julia version we still support, so the lower bound was simply wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`JacobianConfig` builds its tracked output buffer with
`similar(output, TrackedReal{D,D,Nothing})` and then fills it
element-wise in `track!`. `TrackedReal` is never `isbitstype`: it is a
`mutable struct`, and it holds the `InstructionTape`. `MArray` only
supports `setindex!` for isbits eltypes, since it fakes element
assignment with `unsafe_store!` into its single `NTuple` field.

StaticArrays only returns a `SizedArray` from `similar` for non-isbits
eltypes from 1.6.4 on; 1.6.3 and earlier return an `MArray` whose
`setindex!` errors with

    setindex!() with non-isbitstype eltype is not supported by
    StaticArrays. Consider using SizedArray.

So `hessian!(::DiffResult, f, ::MVector)` and the corresponding
`jacobian!` path need StaticArrays >= 1.6.4. Without the bound the
Downgrade job would resolve a version that cannot run them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
devmotion and others added 3 commits September 17, 2026 12:50
`Statistics` was a hard dependency for exactly one method,
`mean(::TrackedArray)`, and its two instruction hooks.

Nothing is lost by making it weak. `ReverseDiff` does not export or
re-export `mean`, so reaching the method at all requires the name, and
every route to it already loads `Statistics`: `using Statistics`,
a qualified `Statistics.mean`, or a package such as StatsBase that
re-exports it and depends on it. The extension therefore loads exactly
when the method can be called.

In return `ReverseDiff` stops loading `Statistics` for everyone, which
also stops it triggering `StaticArraysStatisticsExt` downstream, since
`StaticArrays` is a hard dependency and takes `Statistics` as a weakdep.

The tests assert the extension is absent before `using Statistics` and
present after. Without them a broken extension would go unnoticed:
`TrackedArray <: AbstractArray`, so the generic `mean` falls through to
the tracked `sum`, records a different instruction and still returns the
right value and gradient.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`julia-downgrade-compat` merges the main and test projects and promotes
weakdeps to dependencies:

    Info: Promoting Statistics from weakdep to dependency in merged project

so `Statistics` loads with `ReverseDiff` there and `StatisticsExt` is
always present. The job sets `DOWNGRADE_TEST` for the test step and the
assertion is skipped when it is set. The check that the extension does
load after `using Statistics` still runs everywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devmotion
devmotion merged commit 2b1bbb5 into master Sep 17, 2026
7 of 8 checks passed
@devmotion
devmotion deleted the dmw/dependabot branch September 17, 2026 12:56
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.

1 participant