Skip to content

Make tuple-node getindex type-stable; replace @test_broken @inferred with @inferred - #172

Merged
ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas:green/MultiScaleArrays.jl
Oct 16, 2026
Merged

ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas:green/MultiScaleArrays.jl

Conversation

@ChrisRackauckas

@ChrisRackauckas ChrisRackauckas commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

🚧 UNREVIEWED — awaiting review by @ChrisRackauckas. Filed by an AI agent running as @ChrisRackauckas; Chris has not seen this.
Harness: Devin CLI 3000.11.92 (diagnosis and fix), Claude Code 2.1.300 (PR) · Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Conversation: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq (fleet master)

This fixes a failure on the default branch's CI. The diagnosis and the evidence below come from the investigating agent's report; "confirmed by running" and "read in logs" are kept apart there.

Failure 1: test/tuple_nodes.jl:51-52 @test_broken @inferred errors with "Expression evaluated to non-Boolean" on Julia 1.13.2

Error (read in logs)

Core/tuple_nodes.jl: Error During Test at .../test/tuple_nodes.jl:51
  Expression evaluated to non-Boolean
  Expression: @inferred getindex(community, 4)
Core/tuple_nodes.jl: Error During Test at .../test/tuple_nodes.jl:52
  Expression evaluated to non-Boolean
  Expression: @inferred getindex(scenario, 8)
Some tests did not pass: 20 passed, 0 failed, 2 errored, 0 broken.

Two jobs fail with the same error, both on julia version 1.13.2: Core (julia 1, ubuntu-latest) and Core (julia 1, ubuntu-latest, x86).

Cause: a Julia release (1.13.1 inference improvement), so the test expectation is now wrong. This is not a dependency change.

getindex(m::AbstractMultiScaleArray, i::Int) calls nodeselect(m.nodes, idx, i) = ns[idx][i] with a runtime idx. m.nodes here is a heterogeneous tuple (two Plants with different settings/organ types). Julia 1.13.1 and later union-split this and infer Float64. Earlier versions infer Any. So @inferred now returns 4.1 instead of throwing, and @test_broken rejects the non-Boolean value. The code path uses only Base (tuples, Vector{Int}, searchsorted), so package versions cannot change the result.

Confirmed by running (scratch/infer.jl against unmodified master 4611c53 copied to scratch/master_src; it prints Base.return_types(getindex, (typeof(x), Int)) and the @inferred result):

Julia community / scenario return type @inferred
1.10.12 (lts) Any throws
1.12.7 Any throws
1.13.0 (official tarball) Any throws
1.13.1 (official tarball) Float64 passes
1.13.2 (release) Float64 passes

The behaviour changes between 1.13.0 and 1.13.1. I did not bisect to the individual Julia commit. The GitHub julia 1 lanes moved from 1.13.0 to 1.13.2 between the 2026-09-11 green run and today.

Existing PRs

None. The only open PRs in SciML/MultiScaleArrays.jl are #74 (ChrisRackauckas, "simple arrayinterface overloads", 2023) and #54 (ChrisRackauckas, "Fix diffeq tag", 2021). Neither touches this. A search for inferred OR tuple_nodes OR test_broken finds only merged QA PRs.

Fix

The test is not version-gated. Instead, the code is made type-stable on every supported Julia version, so the test can assert inference unconditionally.

  • src/indexing.jl: add Tuple methods for nodeselect. They recurse over the tuple, so a runtime index into heterogeneous tuple nodes infers as the union of each element's result (concrete when the leaves share an eltype):
    nodeselect(ns::Tuple, i, I...) = i == 1 ? first(ns)[I...] : nodeselect(Base.tail(ns), i - 1, I...)
    nodeselect(ns::Tuple{}, i, I...) = throw(BoundsError(ns, i))
    Vector nodes keep using the generic ns[i][I...] method.
  • test/tuple_nodes.jl:51-52: @test_broken @inferred getindex(...) becomes @inferred getindex(...), matching lines 49-50. The old expectation ("this is not inferable") is false on Julia ≥ 1.13.1, and with the fix it is false on every supported version. The test is now strictly stronger: it fails if inference regresses on any version.

With the fix, return_types gives [Float64] for both community and scenario on 1.10.12, 1.12.7 and 1.13.2 (confirmed by running).

Test before and after (confirmed by running; logs in scratch/):

  • Before, master test on 1.13.2 (master_release_nonboolean.log): Error During Test ... Expression evaluated to non-Boolean ... Value: 4.1, exit 1. This reproduces the CI failure.
  • Before, new test with the src fix stashed, on 1.12.7 (failbefore_1.12.log): ERROR: LoadError: return type Float64 does not match inferred return type Any at tuple_nodes.jl:51, exit 1.
  • After, include("test/tuple_nodes.jl") against the dev'd branch (tuple_release.log, tuple_1.12.log, tuple_lts.log): exit 0 on 1.13.2, 1.12.7 and 1.10.12.
  • After, GROUP=Core Pkg.test() on 1.13.2 (core_release.log): all pass, including Core/tuple_nodes.jl | 20 20 and indexing_and_creation_tests.jl | 134 134.
  • After, GROUP=QA Pkg.test() on 1.13.2 (qa_release.log): QA/qa.jl | 20 20, passed.
  • Runic --inplace on both files: no changes. typos on both files: clean.

Not run locally: the 32-bit Core lane (x86), and the full Core group on lts and 1.12. On lts and 1.12 only tuple_nodes.jl was run.


🤖 Posted by an AI agent — harness: Devin CLI 3000.11.92 (fix), Claude Code 2.1.300 (PR) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium / claude-opus-5-5
Conversation: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq (fleet master)

🤖 Generated with Claude Code

Independent review (added by the fleet master)

Risk assessment

  • Risk: low
  • Blast radius: Tuple-backed node selection, including linear and hierarchical indexing; vector-backed nodes retain the generic method. No public name, dependency, or compatibility bound changes.
  • Evidence: Independently tested the actual indexing/shape-construction source and the tuple fixture on Julia 1.10.12 and 1.13.2: community/scenario inference, all 12 values, and lower/upper node bounds pass. With original indexing source and the new assertions on LTS, observed return type Float64 does not match inferred return type Any. Read the default-branch log independently: both release architectures report non-Boolean results at the two broken-inference assertions. Also ran the full tuple_nodes.jl on Julia 1.10.12, including its ODE solve: exit 0. PR CI remains pending.
  • Independent review: codex gpt-6.1-sol rated it low, high confidence: tuple recursion exposes each concrete node's indexing result to LTS inference while retaining valid integer-index results.
  • Merge: auto-merge candidate — after Core (including LTS/x86), QA and downgrade CI pass.

🤖 Section added by an AI agent — harness: Claude Code 2.1.300 · model: claude-opus-5-5 (review by codex-cli 0.162.1, gpt-6.1-sol)
Conversation: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq

Julia 1.13.2 infers getindex through heterogeneous tuple nodes, which
turned the @test_broken @inferred checks into errors. Recursing over the
tuple makes inference succeed on all supported versions, so the tests
assert @inferred.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.92
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless Devin run (fleet master green lane; no session URL)
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review October 16, 2026 00:39
@ChrisRackauckas
ChrisRackauckas merged commit d00dacc into SciML:master Oct 16, 2026
13 checks passed
ChrisRackauckas referenced this pull request Oct 16, 2026
Agent-Harness: Claude Code 2.1.301
Agent-Model: claude-opus-5-5
Claude-Session: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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