TrkQual BDT - #381
Conversation
…rates "user branches"
oksuzian
left a comment
There was a problem hiding this comment.
PR Review Summary — "TrkQual BDT" (#381)
Reviewed at head 151e5beb (opened 2026-06-30, 13 commits, last updated 2026-07-24). First review of this PR; no prior reviews or comments. Companion to Mu2e/ArtAnalysis#7. 15 files, +526/−214: a schema change from trkQualTags to a richer trkQualLeaves table, a trkqual_metadata provenance histogram, and a new RooUtil UserBranch mechanism for run-time-named branches.
Decision
- 🔴 Request changes. The design is a clear improvement — explicit leaf names instead of positional
qual2/qual3, model provenance recorded in the file, and a generalUserBranchthat retires the hardcodedtrkqual_alt/trkqual3hack (#306). One defect blocks it: withtrk.fill : false, the metadata histogram is created with the right number of bins but never labelled, and the new RooUtil reader then throws on every such file. Three fcl configs shipped in this repo produce exactly that. The fix is one condition. Everything else below is minor or coordination.
Scope understood
src/EventNtupleMaker_module.cc:fhicl::Sequence<std::string> trkQualTags→fhicl::Sequence<fhicl::Table<TrkQualLeafConfig>> trkQualLeaves(leafname,inputTag,modelVersion); output branch name becomes<branchname>qual<leafname>instead of positional numbering; atrkqual_metadataTH1Irecords one labelled bin per configured algorithm.inc/TrkQualMetadata.hh(new): the{output_branch, input_tag, model_version}struct shared by the module and RooUtil.rooutil/: newUserBranch.hh(event- and track-scoped run-time branches),RooUtil::SetUserBranches/GetTrkQualMetadata/RequireTrkQualVersion, removal of the hardcodedtrkqual_alt, andCompareTrkQualTrainings.C→CompareTrkQualTrainings_UserBranches.C.bin/checkEventNtuple: prints the metadata bin labels.- fcl + docs:
prolog.fclmigrated totrkQualLeavesand gainsxgbFilename;from_mcs-reflection.fclandfrom_mcs-mixed_trkQualCompare.fclmigrated; three READMEs updated.
Findings
-
🔴 [S0] With
trk.fill : false,trkqual_metadatais written with unlabelled bins, and RooUtil throws when reading the file.- Evidence, producer side — the bin count and the labels are computed under different conditions:
The count ignores the global
for (const auto& trkFitConfig : _allTrkFitBranches) { if (trkFitConfig.fill()) nTrkQualAlgorithms += trkFitConfig.trkQualLeaves().size(); // per-branch fill only } if (nTrkQualAlgorithms > 0) { hTrkQualMetadata = tfs->make<TH1I>("trkqual_metadata", ..., nTrkQualAlgorithms, 0, nTrkQualAlgorithms); } ... if (_conf.trk().fill()) { // <-- the labels are written inside the global gate for (...) { if (!i_trkFitConfig.fill()) continue; ... SetBinLabel(++iTrkQualAlgorithm, metadataLabel); } }
trk.fill()switch; the labelling honours it. Sotrk.fill : falsewith any filled branch that has leaves produces a histogram with N bins and N empty labels. - Evidence, consumer side —
rooutil/inc/RooUtil.hh,LoadTrkQualMetadata, called from the constructor for every input file:An empty label yieldsconst std::string label = metadata_histogram->GetXaxis()->GetBinLabel(bin); const auto input_tag_pos = label.find(input_tag_marker); ... if (input_tag_pos == std::string::npos || ...) throw std::runtime_error("Invalid TrkQual metadata in " + filename + ": " + label);
nposand throws. The histogram exists, so the earlyreturnfor "no metadata" does not save it. - Evidence, that this is reached by shipped configs: the default is
trk.fits : [ @local::All ](fcl/prolog.fcl:329), andAllhasfill : truewith two leaves after this PR.fcl/from_dig-calo.fcl,fcl/from_rec-crv-kpp.fclandfcl/from_rec-crv-kpp-MC.fclall setphysics.analyzers.EventNtuple.trk.fill : falsewhile inheriting that fits list. Ntuples from all three become unopenable in RooUtil — constructor throws, before any analysis code runs. - Impact: a file that looks fine to
checkEventNtuple(it prints empty entries) and is rejected outright by the repo's own analysis library. It is also a silent trap for any user config that disables the tracker subsystem, which is the documented way to make calo- or CRV-only ntuples. - Suggested fix: gate the count the same way as the labels —
Worth also making the reader defensive: skip empty labels rather than throwing, so older or oddly-produced files degrade to "no metadata" instead of being unreadable.
if (_conf.trk().fill()) { for (const auto& trkFitConfig : _allTrkFitBranches) { if (trkFitConfig.fill()) nTrkQualAlgorithms += trkFitConfig.trkQualLeaves().size(); } }
- Evidence, producer side — the bin count and the labels are computed under different conditions:
-
🟠 [S1] This PR and ArtAnalysis#7 are mutually blocking — every merge order breaks jobs, and the window is not addressed anywhere.
- Evidence:
fcl/prolog.fcladdsxgbFilename : "ArtAnalysis/TrkDiag/data/TrkQual_BDT1_v2.0.ubj"to theTrkQualtable, and everyinputTaggains an:ANN/:BDTinstance. Neither exists in ArtAnalysismain:TrackQuality's validatedConfighas noxgbFilenamekey (unknown key → fhicl validation error), and it stillproduces<MVAResultCollection>()on the unnamed instance. Conversely, merging ArtAnalysis#7 first makesxgbFilenamerequired, which EventNtuplemain's prolog does not set. Both orders abort at module construction. - Impact: two repos, two merge buttons, no atomic operation — there will be a window in which the next Analysis musing build is broken, and nothing in either PR body says how to sequence it. ArtAnalysis#7 also currently has open change requests (reviewed at
a1920768), so the dependency is not merge-ready. - Suggested fix: state the sequencing plan in both PR bodies. The cleanest de-risking is on the ArtAnalysis side: keep the ANN on the unnamed instance and add only
"BDT"as a named one, and givexgbFilenamea default. Then #7 merges harmlessly on its own, this PR follows, and there is no broken window. If the instance rename is kept, say explicitly that the two must land within the same musing build.
- Evidence:
-
🟡 [S2] Nothing validates that
leafnamevalues are unique, and the old scheme could not produce a collision.- Evidence: the module builds
output_branch = branchname + "qual" + leafnameand calls_ntuple->Branch((outputBranch+".").c_str(), ...)once per leaf, with no check that two leaves in the same fit differ. The code this replaces auto-numbered (qual,qual2,qual3), so distinctness was structural. - Impact: a config with two leaves sharing a
leafnamewrites two TTree branches with the same name, and — becauseLoadTrkQualMetadatakeys its map onoutput_branch— the two bins from the same file then collide and throw"TrkQual metadata for <branch> differs between input files". The message names the wrong cause; the files are one file. - Suggested fix: throw a
cet::exceptionat construction when two leaves in a fit produce the sameoutput_branch, naming both input tags. That converts a confusing downstream failure into a config error at job start.
- Evidence: the module builds
-
🟡 [S2]
modelVersionhas three different naming conventions inside this one PR, andRequireTrkQualVersioncompares them by exact string.- Evidence:
fcl/prolog.fclwritesmodelVersion : "ANN1_v2"and"BDT1_v2";fcl/from_mcs-reflection.fclwrites"TrkQual_ANN1_v2"for the same ANN model;fcl/from_mcs-mixed_trkQualCompare.fcluses"TrkQual_ANN1_v1.0"/"TrkQual_ANN1_v1.1"/"TrkQual_ANN1_v2";rooutil/README.md's example checks"ANN2_v2". Meanwhile the BDT artefact isTrkQual_BDT1_v2.0.ubj—v2.0, notv2. - Impact:
RequireTrkQualVersion(branch, expected)is a literal!=on these strings. An analyst who copies the README's form and runs on standard ntuples gets a spurious throw; one who copies the prolog form gets a spurious throw on reflection ntuples. The provenance feature only works if the strings are conventional. - Suggested fix: pick one form — ideally the model filename stem, so
TrkQual_ANN1_v2andTrkQual_BDT1_v2.0— use it in every fcl and README in this PR, and say infcl/README.mdthat it must match the model file. Settling thev2/v2.0split with MLTrain (the notebook'straining_version = "2.0"is what produces the dotted form) would remove the ambiguity at the source.
- Evidence:
-
🟡 [S2]
fcl/from_mcs-mixed_trkQualCompare.fclpoints at two ONNX files that do not exist.- Evidence: it now sets
onnxFilename : "ArtAnalysis/TrkDiag/data/TrkQual_ANN1_v1.onnx"and".../TrkQual_ANN1_v1.1.onnx".ArtAnalysis/TrkDiag/data/containsTrkQual_ANN1_v1.dat,TrkQual_ANN1_v1.1.dat,TrkQual_ANN1_v2.datandTrkQual_ANN1_v2.onnx— the two v1 models exist only in the retired SOFIE.datformat. - Impact: the example cannot run as shipped. The PR's own comment says the files "must be installed beside the v2 model before running this example", which is honest, and
fcl/README.mdrepeats it — but neither says where to obtain them, and ONNX exports of v1/v1.1 do not appear to exist anywhere. (For context, the file was already broken onmainfor a different reason — it set the retireddatFilenamekey — so this is an improvement that stops one step short.) - Suggested fix: either commit the two ONNX conversions to ArtAnalysis, or point the example at models that exist (e.g. compare
TrkQualAll:ANNagainstTrkQualAll:BDT, which needs no extra files and demonstrates the same feature).
- Evidence: it now sets
-
🟡 [S2]
AGENTS.mdstill documents the removedtrkQualTagskey.- Evidence:
AGENTS.md:90at this head still showstrkQualTags : ["TrkQualDeM"]in its EventNtuple configuration example. Three READMEs were updated in this PR; this one was not. - Impact: it is the file an agent or newcomer reads first for the config shape, and after this PR it documents a key that fails validation.
- Suggested fix: update the snippet to the
trkQualLeavesform used infcl/prolog.fcl.
- Evidence:
-
⚪ [S3] Batch, none gating:
- The metadata round-trips through a formatted string (
"%s: input tag = %s; model version = %s") parsed back withfind. Any field containing": input tag = "or"; model version = "mis-splits, andchar metadataLabel[1024]silently truncates. Both are unlikely in practice; astd::stringconcatenation would at least remove the truncation and the<cstdio>include. - The PR body advertises
util.RequireTrkQualMetadata; the implemented method isRequireTrkQualVersion(the README has it right). trk.fillTrkQualis declared in theConfig(:162) and read nowhere in the module — pre-existing, but it is exactly the switch a user would reach for to work around finding 1, andfcl/from_mcs-reflection.fclandfcl/from_mcs-extracted.fclboth set it expecting an effect. Either wire it up or remove it.- Header-guard styles now differ inside the repo: the new
inc/TrkQualMetadata.hhcorrectly usesEventNtuple_TrkQualMetadata_hh, while the newrooutil/inc/UserBranch.hhusesUserBranch_hh_— which does match its neighbours (Event_hh_,Track_hh_,RooUtil_hh_), so this is a repo-wide inconsistency to settle separately, not something to fix in this PR. EventUserBranch<T>::BranchOutputno-ops whenvalue_is still null, i.e. before the firstGetEntry. Worth a comment on the call-order requirement, since the failure is a silently missing output branch.
- The metadata round-trips through a formatted string (
Verified 🟢 (checked, no action needed)
- 🟢 The cross-file provenance check is correct and is the most valuable part of the feature:
LoadTrkQualMetadatathrows when the same output branch carries a different input tag or model version in different files of a chain, which is exactly the mixed-training accident the metadata exists to prevent. - 🟢 No null dereference of
hTrkQualMetadata. The labelling loop runs only for branches withfill()true insideif (_conf.trk().fill()), and the counting loop is a superset of that, so the pointer is always non-null where it is used andiTrkQualAlgorithmcan never exceed the bin count. (The superset relation is precisely what produces finding 1 — the histogram is over-sized, never under-sized.) - 🟢 The default case is backward compatible:
leafname : ""yieldstrkqual, byte-identical to the oldbranch + "qual"name, so existing analysis code readingtrkqualis unaffected by the schema change. - 🟢 Retiring
trkqual_alt/the hardcodedtrkqual3in favour ofUserBranchis a real simplification — it removes a// TODO: un-hardcode thoseand three special cases fromEvent.hh(bind, per-track update, and the erase path), and the replacement example ships in the same PR. - 🟢
UserBranchis defensively written where it matters:Bindchecks both branch existence and branch status,TrackElementPtrbounds-checks the index,EraseTrackchecks null and range, andRooUtil::SetUserBranchesde-duplicates by name. - 🟢
checkEventNtupleguards onfolder.GetListOfKeys().Contains(...)before reading the histogram, so older files without it print nothing rather than failing. - 🟢 Blast radius of the schema change is contained: no
trkQualTagsreference exists inProductionormu2e-trig-config; all in-repo fcl uses are migrated in this PR (onlyAGENTS.md, finding 6, is left). - 🟢 Storing provenance in
TH1bin labels is not a new hack — it mirrors the existingversionhistogram idiom thatSetVersionNumberalready reads. - 🟢 PR body quality is good: intent, the branch names users will see, and the feature list are all stated, with the superseded plan struck through rather than deleted.
Validation check
- Build/tests run: none — EventNtuple has no CI (no statuses and no check runs at
151e5beb), the PR body carries no build or run evidence, and this review did not compile or run anything. All findings are from reading the code at this head plus the state of ArtAnalysismain. - Config contract check: fail — finding 1 (metadata histogram inconsistent with its own reader) and finding 2 (keys and product instances that do not exist in ArtAnalysis
main). - Cross-repo consistency: needs follow-up — mutually blocking with ArtAnalysis#7 (finding 2);
Productionandmu2e-trig-configare unaffected. - Merge state: the PR reports
mergeable: false— it conflicts withmainand needs a rebase before any of this can be exercised.
Residual risk
- Finding 1 is the sharp edge: it produces files that pass casual inspection and fail hard at analysis time, from configs nobody edits.
- The BDT output itself is unvalidated here and in ArtAnalysis#7 — two quality branches ship with equal standing and no guidance on which analyses should use. A ROC overlay in either PR body would settle it.
RequireTrkQualVersionis only as good as themodelVersionstrings (finding 4); until they are conventional, the safest use isGetTrkQualMetadataplus an explicit comparison in analysis code.
Author follow-ups
- Count
nTrkQualAlgorithmsunder the same_conf.trk().fill()gate that writes the labels, and consider makingLoadTrkQualMetadataskip empty labels rather than throw (finding 1, blocking). - State the merge sequencing with ArtAnalysis#7 in both PR bodies — or de-risk it by keeping the ANN on the unnamed product instance and defaulting
xgbFilenameon the ArtAnalysis side (finding 2). - Reject duplicate
leafnames at construction with a message naming both input tags (finding 3). - Settle one
modelVersionconvention and use it in every fcl and README here (finding 4). - Point
from_mcs-mixed_trkQualCompare.fclat models that exist, or commit the v1/v1.1 ONNX exports (finding 5). - Update the
AGENTS.mdconfig snippet (finding 6), and rebase — the PR currently conflicts withmain. - Optional: the S3 batch in finding 7, especially wiring up or removing
trk.fillTrkQual.
Notes on the merge conflictI ran the merge locally (
Mechanical — no decisions involved
Needs a decision from you
One thing worth knowing before resolving
The Happy to push the three mechanical files and the |
|
Replies to initial AI review. I'll now work on the merge conflict
Good catch. Fixed with code as suggested
I think the AI is making a mountain out of a molehill here. We just need to have them both merged before we tag and release
Added uniqueness check and throw if leafname already found.
Already solved based on work done in response to AI review of Mu2e/ArtAnalysis#7
Will update in the future. Added a TODO to the file
I'm not sure what to do here. Should I remove the AGENTS.md file and we have agents rely on the READMEs?
Fixed a few of these (see commit messages for details) |
oksuzian
left a comment
There was a problem hiding this comment.
PR Review Summary — re-review of "TrkQual BDT" (#381)
Reviewed at head 913baba2 (2026-08-07). Second pass. Prior review: 🔴 at 151e5beb (review 4879365426, 7 findings), plus my merge-conflict notes (comment 5211695423). The delta is 7 commits, 151e5beb..913baba2: six author commits and the main merge 913baba2. Every prior finding is accounted for below, verified in the code at this head rather than taken from the commit messages. The PR is now mergeable: clean, and I checked the merge resolution hunk-by-hunk against main @ 84e7404e (which is the merge's second parent, so git diff upstream/main 913baba2 is exactly this PR's net contribution).
Decision
- 🔴 Request changes. The prior blocker is properly fixed and verified, and four of the seven findings are closed. What replaces it is entirely merge damage: the merge dropped a closing brace and
rooutil/inc/RooUtil.hhno longer compiles, and it rolled the ntuple version number backwards pastmain. Both are mechanical. The good news is the thing I warned was easiest to lose in that merge —main'strkDtDtTag/trkPIDTagswork — came through intact.
Scope understood (delta only)
b4702c05fixes the S0 metadata/trk.fillmismatch;925985bbadds the leafname uniqueness check;22c26c17removes the deadfillTrkQualfhicl key;09492660follows ArtAnalysis#7's model-file rename;c752ee83adds a TODO to the compare example;43aaac09changes an include guard.913baba2mergesmain(45 commits), which had landed the subrun/totals mechanism (#391),trkPIDTagsinstance qualification andtrkDtDtTag(#386), andBranchUtils.hh.
Findings
-
🔴 [S0] The merge lost the closing brace of
LoadTrkQualMetadata—rooutil/inc/RooUtil.hhdoes not compile.- Evidence: at
43aaac09(the pre-merge branch tip) the function closed cleanly —trkqual_metadata[...] = metadata;at:267,}closing the loop at:268,}closing the function at:269. At913baba2the second brace is gone::369assigns,:370closes thefor,:371is a whitespace-only line, and:372isstatic const std::vector<std::string>& TotalNames() {—main's method, now nested insideLoadTrkQualMetadata's body. A brace-depth trace over the file ends at depth 1 at EOF instead of 0;TotalNames,BuildSubRunIndexand every data member from:392on sit at function scope instead of class scope. - Confirmed with a compiler, not by eye. Stripping
#-prefixed lines (so it parses without ROOT) and runningg++ -std=c++20 -fsyntax-only:The stripped-preprocessor trick means the other diagnostics in that run are noise, but these two are pure syntax and independent of the missing headers.error: a function-definition is not allowed here before ‘{’ token <- TotalNames() error: expected ‘}’ at end of input - Impact:
RooUtil.hhis the entry point for every rooutil analysis — all sixrooutil/examples/*.C,validation/test_rooutil_examples.sh, and any user macro. Nothing that includes it builds. It is a pure merge artifact: the pre-merge branch andmainboth compile. - Suggested fix: one
}after:370, and drop the trailing-whitespace line while you are there.
- Evidence: at
-
🟠 [S1] The merge rolled the ntuple version backwards, from
main's 6.12.1 to 6.11.2.- Evidence:
src/EventNtupleMaker_module.cc:638-640at this head stamps major 6, minor 11, patch 2.mainat the merge base stamps 6 / 12 / 1 (:613-615). The net diff againstmainshowsminor 12 → 11andpatch 1 → 2, i.e. the merge took the branch side of a hunk wheremainhad moved on. - Impact: ntuples produced after this merges would advertise a version older than files already produced from
main, andRooUtil::SetVersionNumber(rooutil/inc/RooUtil.hh:63-74) reads those three bins as the file's identity. This PR's ownREADME.mdalso promises the trkqual metadata "for versions after v6.13.0" — a claim 6.11.2 can never satisfy. - Suggested fix: 6 / 13 / 0. This is the release call I flagged in the merge notes rather than resolve myself; the merge resolved it by default instead of by decision.
- Evidence:
-
🟠 [S1]
Track::trkqual_bdtis declared but never bound — a permanently null pointer named exactly like the new branch.- Evidence:
rooutil/inc/Track.hh:206declaresmu2e::MVAResultInfo* trkqual_bdt = nullptr;. The only other occurrence of that identifier anywhere in the repo is the string literal"trkqual_bdt"used as a default argument inrooutil/examples/CompareTrkQualTrainings_UserBranches.C:21. Nothing assigns the member:Event::UpdatecallsUpdateObjectfortrack.trkqual,track.trkpid,track.trkdtdtand then walksuser_branches—trkqual_bdtis not in that list, andEvent.hhhas noCheckForBranchfor it either. - Impact:
prolog.fcl:277-278produces a branch literally calledtrkqual_bdt, and every sibling member of that struct is bound, sotrack.trkqual_bdt->result()is the obvious thing for a user to write — and it dereferences null. This is the residue oftrkqual_alt: it was renamed rather than removed, which also undercuts the PR's own goal of retiring hardcoded quality members in favour ofUserBranch. - Suggested fix: delete the member. The BDT is reached with
GetUserBranch<mu2e::MVAResultInfo>("trkqual_bdt"), which is exactly what the new example demonstrates. - This one is mine to own: my previous review credited this PR with removing
trkqual_altcleanly, and it did not — I read the deletions inEvent.hhand did not checkTrack.hh.
- Evidence:
-
🟡 [S2]
CheckForBranchnow exists twice.- Evidence:
mainmoved it out torooutil/inc/BranchUtils.hh:7as a freeinlineinnamespace rooutil, whereSubRun.hh:10-13uses it. The merge kept the branch's copy as well, sorooutil/inc/Event.hh:108re-declares it as anEventmember with a byte-identical body — whileEvent.hh:51still includesBranchUtils.hh. - Impact: not a compile break. The member hides the namespace-scope function for all 40-odd calls inside
Event, and since the bodies are identical the behaviour is unchanged. The cost is that two copies of the same four lines now have to stay in sync, and the include reads as if the free function were the one in use. - Suggested fix: delete
Event.hh:107-112; the include already provides it. (This is the "drop the now-duplicated member" line from my merge notes.)
- Evidence:
-
🟡 [S2]
modelVersionstill has three conventions — carried over, unaddressed.- You replied that this was "already solved based on work done in response to AI review of Mu2e/ArtAnalysis#7". That work settled the model filenames (
TrkQual_ANN1_v2.0.onnx,TrkQual_BDT1_v2.0.ubj); themodelVersionstrings are separate, and they are whatRequireTrkQualVersioncompares. At this head:fcl/prolog.fcl:206,277,278,294,477→ANN1_v2/BDT1_v2fcl/from_mcs-reflection.fcl:85,92→TrkQual_ANN1_v2fcl/from_mcs-mixed_trkQualCompare.fcl:19-21→TrkQual_ANN1_v1.0/v1.1/v2fcl/README.md:19-20→TrkQual_ANN1_v2/TrkQual_ANN1_v3_rc1
- Impact: an analyst who copies
RequireTrkQualVersion("trkqual", "TrkQual_ANN1_v2")straight out of your ownfcl/README.mdgets a throw on every standard ntuple, becauseprolog.fclstampsANN1_v2. The provenance feature only pays off if the strings are conventional. - Extra:
from_mcs-mixed_trkQualCompare.fcl:21is three spellings of one model on one line — leafname_v2_0,modelVersion : "TrkQual_ANN1_v2", fileTrkQual_ANN1_v2.0.onnx. - Suggested fix: one form everywhere — the filename stem (
TrkQual_ANN1_v2.0,TrkQual_BDT1_v2.0) is the one that cannot drift from the artefact — and a line infcl/README.mdsaying it must match the model file.
- You replied that this was "already solved based on work done in response to AI review of Mu2e/ArtAnalysis#7". That work settled the model filenames (
-
🟡 [S2]
AGENTS.md:90still documents the removedtrkQualTagskey — carried over, with an answer to your question.- You asked whether to delete
AGENTS.mdand let agents rely on the READMEs. I would keep it:AGENTS.mdis the filename agent tooling looks for by convention, and a README is not a substitute for that. But the drift you just hit is the argument against the content it currently has — a duplicated fhicl example that nobody remembers to update. The version that does not rot is a short pointer file: what the repo is, where the build lives, and "for EventNtuple configuration seefcl/README.md", with no inline config snippet at all. - Minimum for this PR is the one line: replace the
trkQualTags : ["TrkQualDeM"]snippet with thetrkQualLeavesform fromprolog.fcl.
- You asked whether to delete
-
🟡 [S2]
from_mcs-mixed_trkQualCompare.fclstill points at ONNX files that do not exist — partially addressed.- The TODO added in
c752ee83is honest and I would not block on it. Worth noting the zero-cost alternative, though: after this PRTrkQualAllproduces both:ANNand:BDTby default, so the example can demonstrate the whole user-branch mechanism by comparingtrkqualagainsttrkqual_bdtwith no extra model files at all, and stop shipping a config that cannot be run as-is.
- The TODO added in
-
⚪ [S3] Batch, none gating:
inc/TrkQualMetadata.hh—43aaac09("Match include guards with files already in repo") changedEventNtuple_TrkQualMetadata_hhtoTrkQualMetadata_hh. That took my note aboutrooutil/inc/UserBranch.hhand applied it to the other file: my review said this header's original guard was the correct one under the Mu2e convention (repo prefix outside Offline). The neighbours it was matched to areTrkInfo_HH,MVAResultInfo_HH,SubRunInfo_HH,TrkDtDtInfo_HH— uppercase_HH— so the new form matches neither the convention nor its own directory. Zero functional impact; revert it or leave it, but the repo-wide cleanup is still the real fix and still belongs in its own PR.- The PR body advertises
util.RequireTrkQualMetadata; the implemented method isRequireTrkQualVersion(the README has it right). - The metadata still round-trips through
snprintfintochar[1024]and is parsed back withfind— unchanged, still not worth blocking on, still cheaper as string concatenation. - The new uniqueness check (
src/EventNtupleMaker_module.cc:571-579) is scoped per fit branch, which is exactly what I asked for. Note that several prolog entries sharebranchname : "trk"(Ext,Off,All,DeCalib,TTMCBranch), so two different fits both configured withleafname : ""would still produce twotrkqualbranches and surface as the misleading "differs between input files" throw. Keying the check on the full output-branch name across all fits closes that too. - Two unrelated blank lines were deleted in
src/EventNtupleMaker_module.cc(both immediately before// Time clusters).
Carry-forward accounting (vs review 4879365426 at 151e5beb)
- 🟢 [was S0]
trk.fill : falseproduced unlabelled metadata bins — FIXED inb4702c05, verified.src/EventNtupleMaker_module.cc:641-647now counts insideif (_conf.trk().fill()), matching the gate that writes the labels, so the count and the labelling are driven by the same condition. Re-checked the null-dereference argument at the new head: the labelling loop is still a subset of the counting loop, sohTrkQualMetadatais non-null wherever it is used.from_dig-calo.fcl,from_rec-crv-kpp.fclandfrom_rec-crv-kpp-MC.fclnow produce no histogram at all rather than a broken one. The reader was not made defensive against empty labels — that half was optional and I am not carrying it. - 🟠 [was S1] Mutual blocking with ArtAnalysis#7 — still open, and the coupling grew. Your reply ("we just need to have them both merged before we tag and release") is a fair answer for the release, and I will not argue the size of the mountain — but it does not cover the window in which someone builds the Analysis musing from the two
mains, which is the failure I was describing. What is new:09492660follows #7's rename toTrkQual_ANN1_v2.0.onnx(fcl/prolog.fcl:17), so this PR is consistent with #7 as it stands today. My re-review of #7 atc0dee801flagged that rename as its own S0 and recommended reverting it (fixing MLTrain'straining_versionto"2"instead), because it breaks EventNtuplemainandfrom_mcs-mixed_trkQualCompare.fcl. Pick the direction once, in #7; if it reverts,prolog.fcl:17here reverts with it. - 🟢 [was S2] Duplicate
leafname— FIXED in925985bb, verified.src/EventNtupleMaker_module.cc:571-579throws acet::exceptionat construction naming the offending output branch. See the scope note in finding 8. - 🟡 [was S2]
modelVersionconventions — UNADDRESSED, now finding 5. - 🟡 [was S2] Nonexistent ONNX files in the compare example — PARTIAL (
c752ee83adds a TODO), now finding 7. - 🟡 [was S2]
AGENTS.md— UNADDRESSED, now finding 6, with an answer to your question. - ⚪ [was S3] Batch — one properly done, one done backwards, the rest open.
trk.fillTrkQualis genuinely removed (22c26c17): the fhicl atom is gone fromConfig, and the two configs that set it (from_mcs-extracted.fcl,from_mcs-reflection.fcl) plus theprolog.fcldefault were all cleaned up in the same commit — the removal of a validated key would otherwise be an S0, so this was the right way to do it. The include-guard change went the wrong direction (finding 8). Thesnprintfround-trip, the PR-body method name and theBranchOutputcall-order comment are unchanged.
Verified 🟢 — no action needed
- 🟢 The merge preserved
main'strkDtDtTag/trkPIDTagswork, which is what I flagged as easiest to lose in these hunks.prolog.fclAllkeepstrkPIDTags : ["TrkPID:MergeKKAll"]andtrkDtDtTag : "TrkDtDt:MergeKKAll"alongside the newtrkQualLeaves;Dekeeps["TrkPID:KKDe"]and itstrkDtDtTag;ENDeCalibkeeps its own;fillTrkDtDt : truesurvives in theEventNtupleMakerdefaults; andTrack::trkdtdt(Track.hh:193) plus all fourEvent.hhsites (:62,:139,:295,:434) are intact. - 🟢 Removing the
fillTrkQualfhicl key is safe outside this repo. Code search over the Mu2e org returnsfillTrkQualonly in EventNtuple — the three files this PR fixes, plus the unrelatedInfoStructHelper::fillTrkQualInfo.Productionandmu2e-trig-configreturn zero hits for bothfillTrkQualandtrkQualTags, so no external config breaks on the schema change. - 🟢
main's subrun/totals mechanism came through unchanged.TotalNames,totals,incomplete_totals,BuildSubRunIndex,SubRun.hh,SelectSubRuns.CandSubRunCounting.Care all present with no edits from this branch — the net diff againstmainin that region is additions only. They are merely mis-scoped by finding 1's missing brace, and correcting that brace restores them to class scope with no further work. - 🟢
bin/checkEventNtupleandREADME.mdmerged correctly — the totals printing frommainand the trkqual-metadata printing from this branch both survive, and the README sentence combines both features rather than replacing one. - 🟢 The provenance design itself is unchanged and still the most valuable part: cross-file
LoadTrkQualMetadatarejects a chain whose files disagree on input tag or model version. - 🟢 The
leafname : ""default still yieldstrkqual, byte-identical to the old name, so existing analysis code is unaffected by the schema change.
Validation check
- Build/tests run: EventNtuple has no CI (no statuses, no check runs at
913baba2) and the PR body carries no build evidence. I did compile-check one file:grep -v '^\s*#' rooutil/inc/RooUtil.hh > /tmp/x.cc && g++ -std=c++20 -fsyntax-only /tmp/x.cc, which is how finding 1 was confirmed rather than inferred. Everything else is static, against this head,main@84e7404e, and ArtAnalysis#7 @c0dee801. - Config contract check: partial — the fhicl schema is coherent and the
fillTrkQualremoval is clean;modelVersion(finding 5) is not. - Cross-repo consistency: needs follow-up — settle the ArtAnalysis#7 model-filename direction, then align
prolog.fcl:17(carry-forward 2).Productionandmu2e-trig-configare unaffected. - Merge quality: verified hunk-by-hunk against
main. Six conflicted files, twelve hunks: ten resolved correctly, one lost a brace (finding 1), one took the stale side of the version bump (finding 2), one carried a duplicate (finding 4).
Residual risk
- Findings 1 and 2 are both merge damage rather than design problems, which is worth saying plainly — the review content of the PR is in better shape than it was last pass. But with no CI, a dropped brace in a header ships to whoever builds next, and a wrong version number is invisible forever after the files are written.
- The BDT is still unvalidated in either PR — two quality branches ship with equal standing and no guidance on which one analyses should use. A ROC overlay in one of the two PR bodies would close it.
RequireTrkQualVersionremains only as good as themodelVersionstrings; until finding 5 is settled,GetTrkQualMetadataplus an explicit comparison is the safer call in analysis code.
Author follow-ups
- Add the missing
}inrooutil/inc/RooUtil.hhafter:370(finding 1, blocking) — and consider a compile-only CI job, since this is precisely the class of break that one would have caught. - Set the version to 6/13/0 (finding 2).
- Delete
Track::trkqual_bdt(finding 3) and the duplicatedEvent::CheckForBranch(finding 4) — both are merge/rename residue. - Settle one
modelVersionconvention and use it in every fcl and README here (finding 5). - Fix the
AGENTS.mdsnippet, or reduce the file to a pointer (finding 6). - Optional: the compare example against
trkqual/trkqual_bdt(finding 7) and the S3 batch (finding 8). - Coordinate the model-filename direction with ArtAnalysis#7 before either merges (carry-forward 2).
Good catch. Fixed
Reverted back to 6.12.1
Removed. As suggested by the AI
Removed |
oksuzian
left a comment
There was a problem hiding this comment.
PR Review Summary — third pass on "TrkQual BDT" (#381)
Reviewed at head 8e45eb7e (2026-08-07). Prior reviews: 🔴 at 151e5beb (4879365426) and 🔴 at 913baba2 (4885041129). The delta is 4 commits, 913baba2..8e45eb7e, +4/−11 across four files — all of it fixes to the merge damage from the last pass. Verified in the code at this head.
Decision
- 🟡 Comment. Every blocker is fixed, and I checked each one rather than taking the commit message for it:
RooUtil.hhnow parses, the version number is right, and the two dangling members are gone. Nothing in this PR's own code is S0 or S1 any more. What is left is the cross-repo sequencing with ArtAnalysis#7 and three 🟡 documentation items carried from the first pass — none of which gate the merge. - Mechanically: a COMMENTED review does not clear an earlier CHANGES_REQUESTED on GitHub, so my block from
913baba2still shows until I approve. I am not re-asserting it. Ping me once themodelVersionstrings are settled (finding 2) and I will approve; I would rather not approve while ArtAnalysis#7 still carries an open S0 that this PR'sprolog.fcl:17depends on.
Findings
-
🟠 [S1] Sequencing with ArtAnalysis#7 — carried over, and the coupling is now on the model filename too.
- Status: ArtAnalysis#7 is unchanged at
c0dee801with my CHANGES_REQUESTED open.fcl/prolog.fcl:17here points atTrkQual_ANN1_v2.0.onnx, which exists only on the #7 branch — ArtAnalysismainstill shipsTrkQual_ANN1_v2.onnx. My review of #7 called that rename its own S0 and recommended reverting it (fixing MLTrain'straining_versionto"2"instead of renaming the deployed artefact). - Impact: this PR is consistent with #7 as it stands today, so the pair is coherent — but if #7 reverts the rename,
prolog.fcl:17andfrom_mcs-mixed_trkQualCompare.fcl:14revert with it. And merging either PR alone still aborts jobs, which your "both merged before we tag and release" plan covers for the release but not for anyone building the Analysis musing from the twomains in between. - Suggested fix: settle the filename direction once, in #7. Nothing to do here until then.
- Status: ArtAnalysis#7 is unchanged at
-
🟡 [S2]
modelVersionstill has four spellings, and both documented examples of the new API throw — carried over, unaddressed.- This is the same finding as last pass; restating it sharply because it is now the main thing between this PR and an approve. For the plain
trkqualbranch ofTrkQualAll:ANN, three files disagree about what is stored there:fcl/prolog.fcl:277— what is actually stamped:ANN1_v2fcl/README.md:19— documents the same leaf asTrkQual_ANN1_v2rooutil/README.md:143—util.RequireTrkQualVersion("trkqual", "ANN2_v2");
- Impact:
RequireTrkQualVersionis a literal!=, so both README examples throw on a standard ntuple. The rooutil one is also the only usage example the feature ships with, and itsANN2looks like a typo forANN1on top of the convention mismatch. Elsewhere:from_mcs-reflection.fcl:85,92useTrkQual_ANN1_v2, andfrom_mcs-mixed_trkQualCompare.fcl:21manages three spellings of one model on a single line — leafname_v2_0,modelVersion : "TrkQual_ANN1_v2", fileTrkQual_ANN1_v2.0.onnx. - Suggested fix: one form everywhere — the model filename stem (
TrkQual_ANN1_v2.0,TrkQual_BDT1_v2.0) is the only one that cannot drift from the artefact — plus a line infcl/README.mdsaying it must match the model file, and the same string in both README examples.
- This is the same finding as last pass; restating it sharply because it is now the main thing between this PR and an approve. For the plain
-
🟡 [S2]
AGENTS.md:90still documents the removedtrkQualTagskey — carried over.- Answering the question you raised: I would keep the file.
AGENTS.mdis the name agent tooling looks for by convention and a README is not a substitute for it. But the drift you just hit is the argument against its current contents — a duplicated fhicl example nobody remembers to update. The version that does not rot is a short pointer: what the repo is, where the build lives, and "for EventNtuple configuration seefcl/README.md", with no inline config snippet at all. - Minimum for this PR is the one line: replace
trkQualTags : ["TrkQualDeM"]with thetrkQualLeavesform.
- Answering the question you raised: I would keep the file.
-
🟡 [S2]
from_mcs-mixed_trkQualCompare.fclstill points at ONNX files that do not exist — partially addressed.- The TODO from
c752ee83is honest and I would not block on it. Restating the zero-cost alternative: after this PRTrkQualAllproduces both:ANNand:BDTby default, so the example can demonstrate the whole user-branch mechanism by comparingtrkqualagainsttrkqual_bdtwith no extra model files, and stop shipping a config that cannot be run as written.
- The TODO from
-
⚪ [S3] Batch, none gating, all carried:
- The PR body still advertises
util.RequireTrkQualMetadata; the implemented method isRequireTrkQualVersion. inc/TrkQualMetadata.hhstill guards onTrkQualMetadata_hh. As noted last pass,43aaac09applied myUserBranch.hhremark to the wrong file — and theinc/neighbours it was matched to areTrkInfo_HH,MVAResultInfo_HH,SubRunInfo_HH,TrkDtDtInfo_HH, uppercase. Zero functional impact; the repo-wide cleanup belongs in its own PR either way.- The metadata still round-trips through
snprintfintochar[1024]and is parsed back withfind. - The uniqueness check is scoped per fit branch. Several prolog entries share
branchname : "trk"(Ext,Off,All,DeCalib,TTMCBranch), so two different fits both configured withleafname : ""would still collide and surface as the misleading "differs between input files" throw. Keying on the full output-branch name across all fits closes that too.
- The PR body still advertises
Carry-forward accounting (vs review 4885041129 at 913baba2)
- 🟢 [was S0] Missing closing brace in
LoadTrkQualMetadata— FIXED in7dba084, verified.rooutil/inc/RooUtil.hh:371now closes the function beforeTotalNames(). Re-ran the same check that found it —grep -v '^\s*#' rooutil/inc/RooUtil.hh > /tmp/x.cc && g++ -std=c++20 -fsyntax-only /tmp/x.cc— and both structural errors are gone; brace depth returns to 0 at EOF, soTotalNames,BuildSubRunIndexand the data members are back at class scope. - 🟢 [was S1] Version number — FIXED in
541dc62, verified, and my suggested value was wrong.src/EventNtupleMaker_module.cc:638-640is back to 6 / 12 / 1. I had asked for 6/13/0; matchingmainis the better answer and I should have checked the convention before prescribing one. This repo bumps the version in a dedicated release commit (cda78eb, 2026-07-06, "Update version number"), the stamped value tracks the latest tag (v06_12_01), andmain's own README already forward-references v6.13.0 for the totals histograms while stamping 6.12.1. So the README claim in this PR is the same forward reference, and the release commit will cover both features at once. Reverting tomain's value was exactly right. - 🟢 [was S1]
Track::trkqual_bdt— FIXED in5cf51fd, verified. The member is gone fromrooutil/inc/Track.hh; the only remaining occurrence of the identifier in the repo is the string literal inCompareTrkQualTrainings_UserBranches.C:21, which is the branch name passed toGetUserBranch— i.e. the mechanism this PR was built to use. - 🟢 [was S2] Duplicated
CheckForBranch— FIXED in8e45eb7, verified. The member is removed fromrooutil/inc/Event.hh;Event.hh:51still includesBranchUtils.hh, so all ~40 call sites now resolve to the single freeinlineatBranchUtils.hh:7thatSubRun.hhalso uses. - 🟡 [was S2]
modelVersionconventions — UNADDRESSED, now finding 2. - 🟡 [was S2]
AGENTS.md— UNADDRESSED, now finding 3. - 🟡 [was S2] Nonexistent ONNX files in the compare example — PARTIAL, now finding 4.
- ⚪ [was S3] Batch — unchanged, now finding 5.
- 🟠 [was S1] ArtAnalysis#7 sequencing — carried, now finding 1.
Verified 🟢 — no action needed
- 🟢 No other structural damage survived the merge. Brace-balance check over every header this PR touches —
Event.hh,Track.hh,UserBranch.hh,RooUtil.hh,inc/TrkQualMetadata.hh— returns 0 for all five. The lost brace was the only one. - 🟢 The rest of the merge is still intact after these four commits. The net contribution against
mainis unchanged in the twelve files this delta did not touch, andmain's work that these hunks sit on top of —trkDtDtTag, the instance-qualifiedtrkPIDTags, the subrun/totals mechanism,SubRun.hh,BranchUtils.hh— is all present and unmodified. - 🟢 The prior S0 fix from pass two still holds at this head:
nTrkQualAlgorithmsis counted under the same_conf.trk().fill()gate that writes the bin labels, and the labelling loop remains a subset of the counting loop, sohTrkQualMetadatais never dereferenced null. - 🟢 The leafname uniqueness check, the
fillTrkQualschema removal (verified clean across the Mu2e org last pass), and theleafname : ""backward compatibility are all unchanged and still correct.
Validation check
- Build/tests run: EventNtuple has no CI (no statuses, no check runs at
8e45eb7e). I ran the preprocessor-strippedg++ -fsyntax-onlyparse check onrooutil/inc/RooUtil.hh— that is a syntax check, not a build, and nothing here has been compiled against ROOT or Offline. Everything else is static, against this head,main@84e7404e, and ArtAnalysis#7 @c0dee801. - Config contract check: partial — the fhicl schema is coherent;
modelVersion(finding 2) is not. - Cross-repo consistency: needs follow-up — the ArtAnalysis#7 model-filename direction (finding 1).
Productionandmu2e-trig-configare unaffected. - Merge quality: the three defects found in
913baba2are all corrected; the remaining resolution was verified hunk-by-hunk againstmainlast pass and is unchanged.
Residual risk
- No CI still means the next dropped brace ships to whoever builds first. This PR is the second time in two passes that a header-only merge artifact was caught by reading rather than by a machine — a compile-only job over
rooutil/examples/would pay for itself. - The BDT remains unvalidated in either PR: two quality branches ship with equal standing and no guidance on which analyses should use. A ROC overlay in one of the two PR bodies would close it.
- Until finding 2 is settled,
GetTrkQualMetadataplus an explicit comparison is the safer call in analysis code thanRequireTrkQualVersion.
Author follow-ups
- Settle one
modelVersionconvention and use it inprolog.fcl, both READMEs and the two example configs (finding 2) — this is the one I would like before approving. - Fix the
AGENTS.mdsnippet, or reduce the file to a pointer (finding 3). - Coordinate the model-filename direction with ArtAnalysis#7 (finding 1); nothing to change here until that is decided.
- Optional: retarget the compare example at
trkqual/trkqual_bdt(finding 4), and the S3 batch (finding 5).
I think this is a non-issue. Both PRs will go in and be validated before we re-run
Fixed
Fixed
Not fixing now. Will fix later
Not fixing these |
oksuzian
left a comment
There was a problem hiding this comment.
PR Review Summary — fourth pass on "TrkQual BDT" (#381)
Reviewed at head 03f9290c (2026-08-07). Prior reviews: 🔴 at 151e5beb (4879365426), 🔴 at 913baba2 (4885041129), 🟡 at 8e45eb7e (4885210239). The delta is 2 commits, 8e45eb7e..03f9290c, +8/−8 across four files — modelVersion strings and the AGENTS.md snippet. No C++ changed since the last pass.
Decision
- 🟢 Approve. Every blocker raised across three passes is fixed and verified in the code, not taken from the commit messages. What remains is one 🟡 that is half-done and two items you have explicitly declined or deferred — none of which gate this. Approving also clears my two standing CHANGES_REQUESTED, which is the right state for the PR now.
- One condition that is yours to hold, not mine: this still has to land in the same musing build as ArtAnalysis#7, because
fcl/prolog.fcl:17names a model file that only exists on that branch. You have said twice that the plan covers it, and I am taking that as settled — I am not raising it again.
Scope understood (delta only)
63fd21fmovesprolog.fcltoANN1_v2.0/BDT1_v2.0and corrects therooutil/README.mdexample fromANN2_v2toANN1_v2.0;fcl/README.mdgoes toTrkQual_ANN1_v2.0.03f9290replaces the staletrkQualTagssnippet inAGENTS.mdwith thetrkQualLeavesform.
Findings
-
🟡 [S2]
modelVersion— partially fixed. Thev2→v2.0half is done; theTrkQual_prefix half is not, and one of the two new commits is already stale against the other.- The important half landed:
rooutil/README.md:143is nowRequireTrkQualVersion("trkqual", "ANN1_v2.0"), which is exactly whatfcl/prolog.fcl:277stamps fortrkqual. The one documented call of the new API works on a standard ntuple. That was the sharp edge and it is gone. - What is left is a prefix split — three of five places still carry
TrkQual_:fcl/prolog.fcl:206,277,278,294,477→ANN1_v2.0/BDT1_v2.0fcl/README.md:19→TrkQual_ANN1_v2.0for the sameTrkQualAll:ANNleaffcl/from_mcs-reflection.fcl:85,92→TrkQual_ANN1_v2(untouched)fcl/from_mcs-mixed_trkQualCompare.fcl:21→TrkQual_ANN1_v2for a leaf named_v2_0whose model file isTrkQual_ANN1_v2.0.onnxAGENTS.md:90→ANN1_v2
- Impact, concretely:
TrkQualReflecte/TrkQualReflectmuare@table::TrkQual(from_mcs-reflection.fcl:25-32), so they load the sameTrkQual_ANN1_v2.0.onnxasTrkQualAll:ANN— but record it asTrkQual_ANN1_v2instead ofANN1_v2.0. One model, two provenance strings, decided by which shipped fcl produced the file.RequireTrkQualVersion("trkqual", "ANN1_v2.0")copied from the newly-corrected README throws on reflection ntuples. - Worth noting for its own sake:
03f9290("Fix AGENTS.md") writesmodelVersion : "ANN1_v2", which63fd21f— the commit immediately before it — had already superseded withANN1_v2.0. A duplicated config example went stale inside the same PR that fixed it, roughly forty minutes apart. That is the argument for reducingAGENTS.mdto a pointer atfcl/README.mdrather than keeping a second copy of the config in it. - Suggested fix: four one-line edits —
AGENTS.md:90,fcl/README.md:19,from_mcs-reflection.fcl:85,92,from_mcs-mixed_trkQualCompare.fcl:21— all to whichever form you want as canonical. Not blocking; the reflection one is the only one with a runtime consequence.
- The important half landed:
-
⚪ [S3] Accepted as your call, recorded so the thread is complete: the compare-example ONNX files (deferred — "will fix later", TODO in the file), and the S3 batch from the last pass (declined —
snprintfround-trip,TrkQualMetadata_hhguard, PR body'sRequireTrkQualMetadatavsRequireTrkQualVersion, per-fit scope of the uniqueness check). No action requested.
Carry-forward accounting — full history across four passes
From review 4885210239 at 8e45eb7e:
- 🟡 [was S2]
modelVersion— PARTIAL, finding 1 above. - 🟢 [was S2]
AGENTS.mdtrkQualTags— FIXED in03f9290, verified.AGENTS.md:90now shows thetrkQualLeavesform, so the file no longer documents a key that fails fhicl validation. (ItsmodelVersionvalue is stale — finding 1 — but that is a string, not a schema error.) - 🟡 [was S2] Compare-example ONNX files — deferred by author, no longer carried as open.
- ⚪ [was S3] Batch — declined by author, no longer carried as open.
- 🟠 [was S1] ArtAnalysis#7 sequencing — closed as author's decision. You have stated the plan twice; it is your merge to sequence and I am not re-litigating it. Recording the fact rather than the disagreement:
prolog.fcl:17andfrom_mcs-mixed_trkQualCompare.fcl:14nameTrkQual_ANN1_v2.0.onnx, which exists on the ArtAnalysis#7 branch and not on ArtAnalysismain.
From review 4885041129 at 913baba2 — all fixed and verified last pass, re-checked at this head:
6. 🟢 [was S0] Missing brace in LoadTrkQualMetadata — fixed in 7dba084. Re-ran the parse check at this head: clean.
7. 🟢 [was S1] Version number rolled backwards — fixed in 541dc62; 6/12/1, matching main and the v06_12_01 tag.
8. 🟢 [was S1] Track::trkqual_bdt dangling member — removed in 5cf51fd.
9. 🟢 [was S2] Duplicated CheckForBranch — removed in 8e45eb7.
From review 4879365426 at 151e5beb:
10. 🟢 [was S0] trk.fill : false producing unlabelled metadata bins — fixed in b4702c05, verified, and still correct at this head.
11. 🟢 [was S2] Duplicate leafname values went unvalidated — fixed in 925985bb; construction now throws naming the offending output branch.
12. 🟢 [was S3] Dead trk.fillTrkQual fhicl key — removed in 22c26c17, with all three configs that set it cleaned up in the same commit and no external consumer anywhere in the Mu2e org.
Verified 🟢 — no action needed
- 🟢 No C++ changed since the last pass.
git diff --name-only 8e45eb7e 03f9290creturns onlyAGENTS.md,fcl/README.md,fcl/prolog.fcl,rooutil/README.md, so everything verified at8e45eb7estands unchanged. Re-ran theRooUtil.hhparse check anyway: clean. - 🟢 The feature is coherent end to end.
trkQualLeavesnames the branches,trkqual_metadatarecords provenance per branch,checkEventNtupleprints it,LoadTrkQualMetadatareads it back and rejects a chain whose files disagree, andRequireTrkQualVersionasserts on it — with the README example now matching what the default config actually stamps. - 🟢 Backward compatibility holds.
leafname : ""still yieldstrkqual, byte-identical to the old name, so existing analysis code is untouched by the schema change;checkEventNtupleandLoadTrkQualMetadataboth no-op on files without the histogram. - 🟢 The merge with
mainis clean in substance, verified hunk-by-hunk two passes ago and unchanged since:trkDtDtTag, the instance-qualifiedtrkPIDTags, the subrun/totals mechanism,SubRun.hhandBranchUtils.hhare all present and unmodified by this branch. - 🟢 Retiring
trkqual_alt/trkqual3forUserBranchis a genuine simplification and now actually complete — three special cases gone fromEvent.hh, the hardcoded member gone fromTrack.hh, and a working example shipped in the same PR. This resolves #306.
Validation check
- Build/tests run: EventNtuple has no CI (no statuses, no check runs at
03f9290c). Across the four passes the only machine check I ran was a preprocessor-strippedg++ -fsyntax-onlyparse ofrooutil/inc/RooUtil.hh— a syntax check, not a build. Nothing here has been compiled against ROOT or Offline, and no ntuple has been produced or read. Everything else is static, against this head,main@84e7404e, and ArtAnalysis#7 @c0dee801. - Config contract check: pass — the fhicl schema is coherent, the
fillTrkQualremoval is clean org-wide, and the metadata histogram is consistent with its own reader. The residualmodelVersioninconsistency is a value, not a contract. - Cross-repo consistency: by plan — consistent with ArtAnalysis#7 as it stands; merge sequencing is the author's stated plan.
Productionandmu2e-trig-configare unaffected.
Residual risk
- Reflection ntuples will record a different provenance string for the same model than everything else (finding 1). Cheap to fix now, awkward to fix once files exist.
- The BDT itself is still unvalidated in either PR — two quality branches ship with equal standing and no guidance on which analyses should use. Not a review finding, but it is the open physics question behind the whole pair; a ROC overlay in one PR body would close it.
- No CI means the next header-only breakage ships to whoever builds first. Two of the four passes on this PR found defects a compile-only job over
rooutil/examples/would have caught in seconds. That is a repo-level gap, not this PR's problem, but this PR is decent evidence for it.
Author follow-ups (none blocking)
- The four
modelVersionone-liners,from_mcs-reflection.fcl:85,92first (finding 1). - Consider reducing
AGENTS.mdto a pointer atfcl/README.md— the stale value it shipped with today is a good argument for not keeping a second copy of the config. - Fix the PR body's
util.RequireTrkQualMetadata→RequireTrkQualVersionbefore merge, since the body becomes the squash-commit message.
Four passes in a day is a fast loop, and worth saying: every blocker I raised was real and every fix for one landed correctly. The two places I was wrong — prescribing 6/13/0 for the version, and the include-guard remark that sent you at the wrong file — cost you edits you should not have had to make.
This PR goes with Mu2e/ArtAnalysis#7. Once this PR is merged, there will be two trkqual branches:trkqualcontains the ANN resulttrkqual2contains the BDT resultUPDATE 2026-07-24:
This PR goes with Mu2e/ArtAnalysis#7. Once this PR is merged, there will be two trkqual branches:
trkqualcontains the ANN resulttrkqual_bdtcontains the BDT resultThe following features have been added:
checkEventNtuplescriptutil.RequireTrkQualMetadata(see rooutil/examples/CompareTrkQualTrainings_UserBranches.C)UserBranch(should resolve RooUtil can only handle two trkqual branches and one mcsteps branch #306)