Skip to content

fix: second wave of #1578 fixes — 43 items across processor, nanoevents, analysis/jetmet/lookup/lumi/ml/btag tools, CI, and dead code - #1597

Draft
NJManganelli wants to merge 66 commits into
scikit-hep:masterfrom
NJManganelli:integration/1578-wave-fixes
Draft

fix: second wave of #1578 fixes — 43 items across processor, nanoevents, analysis/jetmet/lookup/lumi/ml/btag tools, CI, and dead code#1597
NJManganelli wants to merge 66 commits into
scikit-hep:masterfrom
NJManganelli:integration/1578-wave-fixes

Conversation

@NJManganelli

Copy link
Copy Markdown
Collaborator

Part of #1578. This integrates 15 individual small-batch fixes

First, here's the enumerate copy of Henry's review, for easier checks.

Critical bugs — silently wrong results

These produce wrong numbers without an exception, which for an analysis framework is the worst class:

Critical bugs — crashes / broken features

  • (9) muon1 - muon2 raises TypeError ✓ — numpy.subtract is never registered for candidate-derived classes (vector.py:683-687 + candidate.py); affects every NanoAOD candidate and edm4hep/fcc particles. Broken since v2024.8.0, reproduced on released 2025.12.0.
  • (10) TaskVine soft-terminate is dead code — processor/taskvine_executor.py:974: _handle_early_terminate starts with an unconditional raise KeyboardInterrupt (debugging leftover). Retry exhaustion or Ctrl-C aborts the entire run and discards all completed partial results instead of gracefully draining.
  • (11) Runner(processor_compression=None) always crashes — processor/executor.py:1592: not isinstance(pi, ProcessorABC) or not callable(pi) should be and — a plain ProcessorABC isn't callable, so uncompressed processors are unconditionally fed to lz4f.decompress.
  • (12) WeightStatistics.add/iadd return None ✓ — analysis_tools.py:188-193: add() mutates and returns nothing; ws += other rebinds to None, poisoning processor.accumulate over outputs containing weightStatistics.
  • (13) CorrectedJetsFactory raw-pt/mass fallbacks are both broken — CorrectedJetsFactory.py:151-164: treat_pt_as_raw is computed after inserting "ptRaw" (always False), and the massRaw branch assigns to name_map["ptRaw"] instead of "massRaw" — omitting either mapping warns, then crashes with KeyError in build().
  • (14) from_parquet crashes for every documented input type except str ✓ — factory.py:556-593: fs_file is only bound in the str branch → UnboundLocalError for Path, ParquetFile, and IO objects. (fix(nanoevents): from_parquet accepts Path/ParquetFile/file-like inputs again #1590)
  • (15) NanoAODSchema breaks from_preloaded ✓ — schemas/nanoaod.py:378: unguarded offsets["parameters"]["doc"]; preloaded mappings don't inject doc. The relevant test is xfail'd, hiding it. (fix(nanoevents): make NanoAODSchema work with from_preloaded (guard __doc__ access) #1589)
  • (16) PDUNE schema four-vectors are unusable ✓ — schemas/pdune.py:177-188: Px/Py/Pz stems collide into one field p and energy lands in a different record; accessing startP4D raises. Also methods/pdune.py:66-82 uses awkward-v1 APIs (awkward.materialized, awkward.layout, awkward._util.recursively_apply) that don't exist in awkward 2.x, and the schema never attaches this file's mixins anyway — the module is dead, broken, copy-pasted from an old physlite.py.
  • (17) AssociatedSV.jet (PFNano) crashes in all modes ✓ — nanoaod.py:858-866: indexes events with the Jet array instead of the name string (compare the correct sv/pf siblings). Also AssociatedPFCand.jet dask path calls dask_array.events() instead of _events() (nanoaod.py:828).
  • (18) filespec promote/demote machinery never works ✓ — dataset_tools/filespec.py:371,419: CoffeaROOTFileSpec(v) passes a model positionally → always TypeError, swallowed by except Exception: pass; PreprocessedFiles.model_validate on fully-valid data returns an InputFiles.
  • (19) Documented pileup-JSON path is unusable ✓ — lookup_tools/extractor.py:133 + evaluator.py: "json_lookup" isn't in evaluator.lookup_types → KeyError at make_evaluator(); the name-table is also corrupted (self._names[local_name] = 0).
  • (20) CI pass gate can go green on failure — .github/workflows/ci.yml:280: no if: always() + result check, so if a needed job fails, pass is skipped — and GitHub treats skipped required checks as passing. Also release (ci.yml:249) omits test-dask-client from needs, so a tag can publish with that suite red.

Medium bugs

processor/

  • (21) uproot_options={"timeout": X} crashes every open (timeout read but not popped, then passed twice — executor.py:1588,1360; with skipbadfiles=True every file is silently "bad").
  • (22) automatic_retries performs zero retries unless skipbadfiles is set (executor.py:1295), contradicting docstrings — only DaskExecutor retries.
  • (23) Failing chunks don't cancel pending futures (pool.shutdown(wait=True) without cancel_futures — error surfaces after all 10k chunks run).
  • (24) Auto-created dask Clients leak (two LocalClusters per Runner.call via .copy(), executor.py:822).
  • (25) mergepool is broken in 3 of 4 documented forms (executor.py:734-748).
  • (26) TaskVine passes concurrent_reads into _compression_wrapper's name parameter — silently ignored (taskvine_executor.py:409).

nanoevents

  • (27) Pickling NanoEventsFactory loses _mode; .events() then raises ✓ (factory.py:234-245).
  • (28) buffer_cache silently dropped for preloaded sources, twice ✓ (positional-arg mixup lands it in file_handle; _from_mapping's param is unused).
  • (29) transforms.get_index_ranges misclassifies all-zero begin/end references as empty → data replaced by [[]] ✓ (transforms.py:775).
  • (30) Buffer-cache codecs corrupt non-contiguous arrays ✓ (latent; buffer_cache.py:26-62).
  • (31) BufferCache(cache, NoCompressionCodec()) demands numcodecs unnecessarily.

schemas

  • (32) FCC _unknown_collections references nonexistent self._datatype_mixins → AttributeError (fcc.py:407, drift from the edm4hep copy).
  • (33) edm4hep OneToManyRelations = collection.get("OneToOneRelations") copy-paste (edm4hep.py:307) — OneToMany members get "unknown" types and their subcollections are silently skipped.
  • (34) FCC.get_schema("bad-version") silently returns None.

dataset / lookup / lumi

  • (35) numpy.int crashes .ea.txt parsing with ≥2 binned vars on numpy≥1.24 ✓ (txt_converters.py:500).
  • (36) validate_steps never caps/validates the first step ✓ (filespec.py:97).
  • (37) xrootd URLs with explicit ports are mangled by rsplit(":") ✓ (filespec.py:485). (fix(dataset_tools): don't split XRootD URLs at the port colon when parsing a files list #1588)
  • (38) rucio_utils mode="first": stale-variable site counting (:288) and IndexError with partial_allowed=True (:270).
  • (39) LumiData: eager path missing the astype(uint32) the dask path has ✓, and index_delayed only built if the first call is dask (lumi_tools.py:118-148).

analysis / ml

  • (40) Dead option-type check in __add_multivariation_delayed (analysis_tools.py:397: tests the array against a type class — always False; missing weights not filled with 1.0 in dask mode).
  • (41) partial_weight(modifier=...) rejects all add_multivariation modifiers (:631).
  • (42) tf_wrapper kwargs path checks arr.flags["WRITABLE"] (misspelled; KeyError) ✓ (tf_wrapper.py:117).
  • (43) Triton retry backoff sleeps in seconds using a milliseconds constant — up to ~53 min per attempt (triton_wrapper.py:347).
  • (44) rand_gauss raises IndexError on empty partitions (CorrectedJetsFactory.py:36).

Notable low-severity bugs

  • (45) PackedSelection's lru_cache returns shared mutable masks (caller &= corrupts the cache) and pins up to 128 selections alive (analysis_tools.py:2294).
  • (46) Up/Down modifier handling uses substring replace-all rather than suffix matching (:570,636).
  • (47) Recoverable mode with default use_result_type=False silently discards the exception report (executor.py:1796).
  • (48) parsl timeout decorator wraps the client-side factory so no timeout is ever enforced.
  • (49) PtEtaPhiMLorentzVector * (-1) flips space but not time ✓.
  • (50) dask/init.py:92 uses deprecated register_worker_plugin.
  • (51) manipulations.py:324 uses eval() on report strings where ast.literal_eval suffices.
  • (52) CorrectedJetsFactory.build() mutates the caller's input layout parameters as a side effect (:485).
  • (53) Missing f-prefix and wrong-variable error messages in jetmet error paths.
  • (54) A plaintext codecov token in codecov.yml.

Performance

  • (55) executor.py:497-509 — the running accumulator is decompress→merge→recompress'd on every poll cycle; O(accumulator-size) pickle+LZ4 repeated hundreds of times for large histogram dicts.
  • (56) maxchunks no longer limits preprocessing — maxchunks=1 on a 10k-file dataset still opens all 10k files (executor.py:1560).
  • (57) Parquet mapping reads the entire column (all row groups) per buffer access, twice for jagged columns, with no cache (mapping/parquet.py:38-43). (partially addressed in fix(nanoevents): correct parquet entry_start jagged reads and cache column reads #1583)
  • (58) import numba in coffea.util is dead weight (nb alias unused repo-wide) — it's one of the heaviest imports and everything imports coffea.util.
  • (59) EDM4HEP: YAML asset re-read+deepcopied on every file open; 7+ O(collections×branches) scans that could be one group-by pass.
  • (60) btagscalefactor.py:150-174 — O(cells×bins) pure-Python double loop per systematic at construction.
  • (61) Triton: O(n²) numpy.concatenate in the batch loop, and dask graph construction performs a real full-batch remote inference just to derive the meta (ml_tools/helper.py:359).
  • (62) helper cache in mapping/base.py:98 decodes each cached buffer twice per hit (in triggers getitem).

Dead code / simplifications

Substantial dead code confirmed by grep:

  • (63) _futures_handler + the doubly-broken uproot3-era parsl/detail.py:35-87 (which also means the documented tailtimeout option is never read — a silent no-op knob).
  • (64) _map_schema_parquet and the unreachable code after raise NotImplementedError in factory.py:548.
  • (65) transforms.nested_local2global and the internally-broken begin_end_mapping_with_xyzrecord.
  • (66) analysis_tools._generate_slices.
  • (67) dense_evaluated_lookup (nothing produces it anymore).
  • (68) flatten_idxs.
  • (69) _hex/_ascii in util.py.
  • (70) Unreachable NotImplementedError branches in analysis_tools.
  • (71) Every copy_behaviors call in edm4hep.py/fcc.py is a silent no-op (passes class objects where awkward compares strings) ✓. (issue edm4hep/fcc: copy_behaviors called with class objects instead of strings is a silent no-op #1594; PR fix: pass strings instead of class objects to copy_behaviors() #1596)

The biggest structural item: copy-paste duplication is the bug factory here.

  • (72) fcc.py duplicates edm4hep.py wholesale (drift already produced the _datatype_mixins crash).
  • (73) The four jetmet corrector classes each carry identical _checkConsistency/constructor boilerplate.
  • (74) promote_and_check_files is duplicated (both copies broken identically).
  • (75) _is_compat/_make_packed triplicated across schemas.
  • (76) test/test-dask-client CI jobs duplicate ~80 lines differing only in the -m marker.

Modernizations

  • (77) Collapse the lint stack into ruff: black + isort + pyupgrade all run alongside a ruff configured with defaults only ([tool.ruff] is just line-length = 160, which is inert since E501 isn't enabled — and black formats at 88 anyway). ruff-format + rules I, UP, B replace all three and would mechanically catch several findings above (B006 mutable defaults — ~10 sites; B028 missing stacklevel — 54 of 55 warnings.warn sites point at coffea internals instead of user code).
  • (78) Packaging: PEP 639 license metadata (needs hatchling>=1.26); switch PyPI publishing to Trusted Publishing (the job already has id-token: write; user/password inputs are deprecated); split the dev grab-bag into PEP 735 dependency-groups (flake8/black/nbsphinx/sphinx-rtd-theme in it are unused); ~8 runtime deps are never imported anywhere (matplotlib, tqdm, toml, mplhep, packaging, requests, ipywidgets; aiohttp may be an intentional fsspec-http activation — worth a comment if so); project.urls still point at coffeateam/; ancient untested floors (scipy>=1.1.0, 2018).
  • (79) Deprecated APIs: numpy.core.records.fromarrays (warns on every JERSF/JUNC txt parse — use numpy.rec), numpy.int (crashes), client.register_worker_plugin, ak.ak_to_packed.to_packed → ak.to_packed, awkward-v1 relics in pdune methods and transforms' hand-built forms, the cStringIO/Python-2 gzip dance in txt_converters, the uproot3 ROOTDirectory string-type guard in factory.py.
  • (80) Fragile private-API reliance worth an issue: awkward._util.copy_behaviors (a coffea-owned helper would also fix the Candidate/GenVisTau poisoning), awkward._connect.pyarrow, dask_awkward.lib.core.dak_cache, interp._forth.
  • (81) Two of three CI test jobs compute coverage that's never uploaded; codecov upload condition compares an unquoted 3.14 (float) against version strings and works only by coercion accident; pass/release fixes above.

Suggested priorities

  1. The silently-wrong-physics set: JER mass_orig (1), FsrPhoton dask path (2), TreeMaker slice (3), parquet entry_start (4), !loadallowmissing (5), Candidate charge (6), ThreeVector.unit (7) (each is a 1-5 line fix plus a regression test — the existing tests either don't cover these or codify the bug).
  2. The CI pass/release gating fix (20) — it's what lets category 1 recur.
  3. WeightStatistics.add (12), subtract registration (9), the CorrectedJetsFactory fallback (13), and the uproot_options timeout collision (21).
  4. Ruff consolidation + B006/B028 (77), which prevents several recurring classes mechanically.
  5. The dead-code purge (63–71) and edm4hep/fcc + jetmet deduplication (72–75) as a follow-up PR.

Happy to turn any slice of this into fixes with regression tests, or file GitHub issues for the findings you want to track — just say which.


Numbering key: (1)–(8) silently-wrong results · (9)–(20) crashes/broken features · (21)–(44) medium · (45)–(54) low · (55)–(62) performance · (63)–(76) dead code/duplication · (77)–(81) modernizations. If your existing tracking list uses a different numbering, paste it and I'll re-map these to match.

Area Bugs (issue numbering) Summary
processor/executor 11, 21, 22, 24, 25, 47, 55, 56 compression=None crash; uproot timeout passed twice; retries never ran; mergepool forms; auto-client leak; recoverable exceptions silently dropped; maxchunks again limits preprocessing; no idle-poll recompression
processor/taskvine 10, 26 unconditional raise KeyboardInterrupt made soft-terminate dead; concurrent_reads swallowed by name param
nanoevents mapping/factory 27, 28, 29, 30, 31, 62 pickle loses _mode; preloaded buffer_cache dropped; zero-valued index ranges → [[]]; codec corruption of non-contiguous arrays; needless numcodecs requirement; double decode per cache hit
nanoevents methods 9, 17, 49 Candidate subtraction TypeError (since v2024.8.0, charge differenced); PFNano AssociatedSV.jet/AssociatedPFCand.jet crashes; polar negative-scalar multiply now matches cartesian (returns LorentzVector)
nanoevents schemas 32, 33, 34 FCC _datatype_mixins AttributeError; OneToMany relations typed via the OneToOne key; get_schema silent None
analysis_tools 12, 40, 41, 45, 46 WeightStatistics +/+= returned None; delayed multivariation didn't fill missing weights; partial_weight rejected multivariation modifiers; PackedSelection shared mutable cache masks; Up/Down substring corruption
jetmet_tools 13, 44, 52, 53 raw pt/mass fallbacks broken; rand_gauss empty-partition IndexError; build() mutated caller layout; broken error messages
lookup_tools 19, 35 pileup-JSON path dead (json_lookup restored — maintainer call vs deleting the feature); numpy.int in EA parsing
dataset_tools/lumi_tools 38, 39 rucio mode="first" IndexError + stale site counts; LumiData dtype cast + call-order consistency
ml_tools 42, 43, 61* tf WRITABLE misspelling; triton ms-as-seconds backoff (~53 min sleeps); O(n²) batch concatenate (*meta-inference half deferred — needs live Triton)
btag/edm4hep perf 59, 60 YAML parsed once (lru_cache, mutation-guarded) instead of per file; btag dense map vectorized, bit-identical (verified on 201,204 cells), ~4×
CI 20 pass gate fails on skipped/cancelled; release needs test-dask-client
hygiene 50, 51, 54, 58 register_plugin; literal_eval; codecov token removed; dead numba import
dead code 63, 65–70 deletions gated on a public-API analysis, not just in-repo reference proofs (see below); item 64 (_map_schema_parquet) deliberately untouched (enabled by an upcoming PR)

Dead-code deletions and the public-API line. Because coffea is a library (and nanoevents.transforms functions are reachable via string tokens in user-emitted form keys, invisible to any in-repo grep), each #1578 dead-code item was triaged against external usage (GitHub code search), not just internal references:

  • Deleted — private or internal: analysis_tools._generate_slices, util._hex/_ascii, 4 provably-unreachable NotImplementedError branches.
  • Deleted — broken for years, so no working external user can exist: uproot3-era parsl chunking helpers (call APIs removed long ago), flatten_idxs (uses numpy.int, crashes on numpy ≥ 1.24), the internally-broken begin_end_mapping_with_xyzrecord family (its own _form emitter dispatches to a different token).
  • Kept — external usage found: dense_evaluated_lookup (+ its evaluator registry entry; user-maintained converters emit its type string) and transforms.nested_local2global (referenced by external custom schemas) are restored unchanged.
  • Deprecated instead of removed: tailtimeout on FuturesExecutor/ParslExecutor — documented, passed by real analyses, but never had an effect; it remains an accepted no-op field that emits DeprecationWarning when set, for removal in a later release.

Verification: every behavioral fix has a test confirmed to FAIL on unfixed master with the expected error (40 new test functions / 53 cases). Full suite on this branch: 1171 passed / 48 skipped (optional deps) / 3 xfail; the only 8 failures are the pre-existing FCC serial-run baseline (conftest dask-client default-scheduler leak — they pass standalone; fixed by #1593). pre-commit run --all-files clean.

Deliberately out of scope: 16 (PDUNE — needs a use/deprecate decision), 23 (eager future cancellation — design change, recommend opt-in), 18/36/74 (open pydantic PRs), 64 (upcoming parquet PR).

🤖 Generated with Claude Code

Nick Manganelli added 30 commits July 7, 2026 11:06
…test-dask-client

Without if: always() plus a result check, a skipped required job let the
pass check go green; release also omitted test-dask-client from needs.

Assisted-by: Claude Opus 4.8
treat_pt_as_raw was computed after ptRaw was inserted into the name map,
so it was always False, and the massRaw fallback overwrote ptRaw instead
of setting massRaw, crashing build() when raw mappings were omitted.

Assisted-by: Claude Opus 4.8
Seeding indexed the first and last elements of the input, raising
IndexError for length-zero arrays; fall back to a fixed seed since no
random numbers are drawn anyway.

Assisted-by: Claude Opus 4.8
Setting the 'corrected' parameter wrote into the parameter dict shared
with the caller's jets layout; copy it before modifying.

Assisted-by: Claude Opus 4.8
…works again

numpy.subtract was never registered for candidate-derived classes, so
subtracting any two NanoAOD candidates raised TypeError since v2024.8.0.
Mirrors Candidate.add, differencing charge.

Assisted-by: Claude Opus 4.8
…egates the time component

pt/eta/phi/mass cannot represent t < 0, so scaling by a negative number
now returns a cartesian LorentzVector matching LorentzVector.multiply;
negative() delegates to multiply(-1).

Assisted-by: Claude Opus 4.8
…minate

The soft-terminate signal handler began with an unconditional
`raise KeyboardInterrupt`, aborting the whole run and discarding
completed results on the first C-c instead of cancelling remaining
tasks and accumulating what finished.

Assisted-by: Claude Opus 4.8
`concurrent_reads` was passed as the third positional arg of
`_compression_wrapper`, which is `name`, so the option was silently
ignored and accumulation always used the default of 2 reader threads.
Bind it into `accumulate_result_files` via functools.partial.

Assisted-by: Claude Opus 4.8
WeightStatistics.add() mutates in place and returns None, so __add__ and
__iadd__ returned None. 'ws += other' rebound ws to None, poisoning any
accumulation over processor outputs.

Assisted-by: Claude Opus 4.8
The option-type check in __add_multivariation_delayed tested the dask array
object itself instead of its awkward type, so it never fired and missing
weights were left unfilled -- unlike every other add path (eager and the
delayed single-variation path) which fill None with 1.0.

Assisted-by: Claude Opus 4.8
…ariation names

partial_weight() validated modifiers with replace('Down','').replace('Up','')
against the weight-name set, which (a) rejected every add_multivariation()
modifier -- their base name is 'weight_modifier', not a stored weight key --
and (b) mangled any weight whose name contains 'Up'/'Down' mid-string.
Switch weight(), partial_weight() and variations to strip/replace only the
trailing Up/Down suffix, and validate multivariation modifiers by owning weight.

Assisted-by: Claude Opus 4.8
…ask copy

require() was lru_cached and handed the same array to every caller, so an
in-place op (e.g. mask &= other) corrupted the cached mask for later callers.
Move the computation to a cached _require and return a copy from require.

Assisted-by: Claude Opus 4.8
numpy.int was removed in numpy>=1.24, crashing convert_effective_area_file
on .ea.txt files with 2+ binned variables.

Assisted-by: Claude Opus 4.8
The documented .pileup.json path emitted a "json_lookup" type that was
absent from evaluator.lookup_types (KeyError in make_evaluator), and the
extractor overwrote the name-table index to 0, aliasing the pileup weight
onto an unrelated entry. Restore the json_lookup class, register it, and
drop the index override.

Assisted-by: Claude Opus 4.8
AssociatedSV.jet indexed events with the Jet array instead of the
collection name, crashing in every mode. AssociatedPFCand.jet's dask
path called dask_array.events() instead of _events(). Both now match
their sibling properties.

Assisted-by: Claude Opus 4.8
…ed bytes

Fixes bug 11 from scikit-hep#1578.

Assisted-by: Claude Opus 4.8
…root kwarg

Fixes bug 21 from scikit-hep#1578.

Assisted-by: Claude Opus 4.8
Fixes bug 22 from scikit-hep#1578.

Assisted-by: Claude Opus 4.8
Fixes bug 24 from scikit-hep#1578.

Assisted-by: Claude Opus 4.8
The _lookup_branch OneToManyRelations branch fetched the
"OneToOneRelations" key from the datatype definition, so one-to-many
members resolved to wrong types and their subcollections were skipped.

Assisted-by: Claude Opus 4.8
The unknown-collection RecordArray path referenced a nonexistent
self._datatype_mixins (drift from the edm4hep.py copy), raising
AttributeError whenever a leftover record branch was processed.

Assisted-by: Claude Opus 4.8
FCC.get_schema silently returned None for unrecognized versions; it now
raises ValueError listing the valid versions.

Assisted-by: Claude Opus 4.8
NanoEventsFactory.__getstate__/__setstate__ dropped _mode, so an
unpickled factory raised AttributeError from events().

Assisted-by: Claude Opus 4.8
from_preloaded never passed buffer_cache to PreloadedSourceMapping, and
PreloadedSourceMapping forwarded it into BaseSourceMapping's file_handle
slot positionally, so the cache was silently dropped.

Assisted-by: Claude Opus 4.8
get_index_ranges used awkward.sum(ranges) == 0 to detect empty ranges,
which also fired when the only produced indices were zeros, replacing
real data with a twice-nested empty array. Count elements instead.

Assisted-by: Claude Opus 4.8
Nick Manganelli added 22 commits July 8, 2026 15:04
Removes the plain nested_local2global (never dispatched: no form emits the
"!nested_local2global" token; only nested_local2global_stack/_form are used)
and the internally-broken begin_end_mapping_with_xyzrecord along with its
exclusive helpers get_array_from_indices_xyzrecord_target(_kernel). The
latter is never invoked because begin_end_mapping_with_xyzrecord_form emits
the "!begin_end_mapping" token rather than "!begin_end_mapping_with_xyzrecord".
The still-used _form variant is retained.

Assisted-by: Claude Opus 4.8
Removes the unused _generate_slices helper and four unreachable
NotImplementedError branches in NminusOne/Cutflow yieldhist methods: each
sits inside an `if not ... and not do_categorical:` block, so
`categorical is not None` (i.e. do_categorical) can never be true there.

Assisted-by: Claude Opus 4.8
Removes dense_evaluated_lookup: no converter ever produces the
"dense_evaluated_lookup" type string, so the evaluator registry entry was
unreachable. Also removes the unused flatten_idxs helper in
jme_standard_function.py (never called; also relied on the removed numpy.int).

Assisted-by: Claude Opus 4.8
Removes the unused _hex and _ascii helpers, which had no references
anywhere in the codebase.

Assisted-by: Claude Opus 4.8
The edm4hep yaml asset was re-read from disk, deepcopied, and fully
parsed on every file/schema build. Cache the (raw, parsed) dicts per
version at module level via lru_cache; the schema only reads them, so a
single parse is safely shared. Also reuse the all_collections set already
computed in _create_mixin instead of recomputing it in _build_collections.

Behavior is bit-identical (existing edm4hep + fcc suites pass). Adds a
guard test asserting the shared cache is treated read-only.

Assisted-by: Claude Opus 4.8
Building the dense correction lookup ran an O(cells x bins) pure-Python
double loop per systematic (findbin + ndenumerate). Replace it with a
vectorized numpy broadcast: match each grid cell against all bins at
once, take the first match via argmax, and apply the abseta fallback the
same way. Results are exactly equal (verified: 157 mapping arrays /
201,204 cells across 5 sample CSVs, bit-identical).

Assisted-by: Claude Opus 4.8
# Conflicts:
#	tests/test_nanoevents_edm4hep.py
…sted_local2global, deprecate tailtimeout instead of removing

GitHub code search shows external analyses pass tailtimeout to the
executors and depend on dense_evaluated_lookup (user-maintained
converters) and nested_local2global (custom schemas); internal-only
reference proofs are insufficient for public API. tailtimeout stays as
an ignored field that warns DeprecationWarning when set.

Assisted-by: Claude Opus 4.8
@NJManganelli

Copy link
Copy Markdown
Collaborator Author

I guess we need an adversarial review, and we also want to wait for wave 1 fixes to be merged in first, yeah?

@lgray

lgray commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Yeah it would be best to get everything in and then do the review.

…ts by behavior

Drop the issue-tracker references from the two vector tests introduced by this
wave; state what each verifies about the current behavior.

Assisted-by: Claude Opus 4.8
Claude-Session: https://claude.ai/code/session_01XeYa8sEdeLGa1VX2frvoNz
@ikrommyd
ikrommyd marked this pull request as draft July 17, 2026 23:11
@ikrommyd

Copy link
Copy Markdown
Member

Converted this to draft for safety

@ikrommyd

Copy link
Copy Markdown
Member

I would love to split out the individual fixes into separate PRs or group fixes that make sense to be together at least. This currently has too many unrelated changes.

@NJManganelli

NJManganelli commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator Author

I created 3 variations of PRs, the omnibus had gotten the previous votes, but if you want to look at/argue for the reduced-group, see here:

https://github.com/NJManganelli/coffea/tree/fix/1578-planB-cleanup
https://github.com/NJManganelli/coffea/tree/fix/1578-planB-nanoevents
https://github.com/NJManganelli/coffea/tree/fix/1578-planB-datatools
https://github.com/NJManganelli/coffea/tree/fix/1578-planB-edm4hep-perf
https://github.com/NJManganelli/coffea/tree/fix/1578-planB-processor

I did not push the more factorized fixes, which was around a dozen total for the 3 dozen-ish individual issues big and small

But first, as already agreed, we need to get the other fixes in. Please have a look at those. I did another pass the reduce the verbosity of Claude.

@NJManganelli
NJManganelli force-pushed the integration/1578-wave-fixes branch from 88da398 to c7c8a2f Compare July 25, 2026 16:45
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.

3 participants