Skip to content

fix(ci-versioning): do not gate on inferred attribution - #16

Merged
sakanni merged 3 commits into
developfrom
fix/ci-versioning-no-gate-on-inferred-attribution
Sep 4, 2026
Merged

fix(ci-versioning): do not gate on inferred attribution#16
sakanni merged 3 commits into
developfrom
fix/ci-versioning-no-gate-on-inferred-attribution

Conversation

@sakanni

@sakanni sakanni commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What

A finding whose ownership was inferred from a namespace prefix no longer fails the check. It is reported as unverified instead: still in the log, still in the artefact, CountedAsReal: false, exit 0.

Findings carrying a declaring assembly are untouched and still gate.

Why

AttributeToSubject reads the declaring assembly the dataset record names, and falls back to a namespace prefix when there is none. Object records never carry one, because FromJsonDatasets builds a failure's Information from CurrentEvents() and only method entries produce a Method ... from ... event. So for every object finding the basis is a guess.

The runner already knew this. It recorded AttributedBy: NamespaceFallback per row and warned in plain English that "these specific findings may not be its own", then counted them towards a failing verdict anyway. Recording the uncertainty and discarding it is the defect, not the fallback, which is a reasonable response to having no better evidence.

Precision is not the fix and should not be attempted. BH.oM.Structure.Elements is declared by both BHoM/BHoM and BuroHappoldEngineering/StructuralEngineering_Toolkit. No string rule can say which of two repositories sharing a namespace owns a type.

Evidence

BHoM/BHoM run 33849699768, commit d6db0a1, a PR adding one file and removing nothing:

  • 145 findings, 145 by namespace fallback, 0 by declaring assembly
  • ownership resolved by hand: 81 StructuralEngineering_Toolkit, 47 BHoM/BHoM, 9 Clustering_Toolkit, 6 the two IES toolkits, 2 unplaced
  • BHoMBot's versioning check passed the same commit, running the same Verify.FromJsonDatasets over the same single dataset version, differing only in that CloneInstaller builds the whole installer set

Restricting the 9.2 dataset to the 963 object records the prefix filter would attribute here, and crossing "references a type outside the closure" against "produced a finding":

finding no finding
has ≥1 foreign type reference 145 14
fully resolvable in closure 0 804

Zero of the 804 cleanly-resolvable records failed. All 145 findings carry a foreign reference.

Re-running the current runner against a fuller closure, same subject set of 30, same dataset version:

CI run full-closure re-run
assemblies loaded 75 186
findings 145 98
by declaring assembly 0 0
by namespace fallback 145 98

47 cleared, none appeared, and the basis stayed at 100% inferred. Of the 98 survivors, the only 4 owned by BHoM/BHoM are BH.oM.Data.Requests.{Batch,LogicalAnd,LogicalNot,LogicalOr}Request, and every one embeds BH.oM.Adapters.Sharepoint.Requests.*, absent from that closure too. So closure quality changes the volume and never the basis. Closure work is still worth doing, and is tracked separately; it does not address gating.

Verification

Against the 186-assembly closure with this branch built and run:

Classification: unverified/NoMethodEvent=98
Attribution basis: 0 by declaring assembly, 98 by namespace fallback
Status:   Warning
Failures: 0
Coverage: 1190 subject type(s) across 30 subject assembl(ies); 186 assembl(ies) loaded
exit 0

All 98 diagnostics retained, CountedAsReal: false on every one, coverage unchanged. Suite: 123 passed, 0 failed.

action.yml needs no change: it branches only on exit_code != '0', and Warning was already exit 0.

Second change in here

The attribution basis line is now printed whenever there are diagnostics, not only when a count is non-zero. Its own comment said a number printed only when non-zero cannot be distinguished from one nobody measured, and the guard below it produced exactly that state. Establishing that BHoM/BHoM is the only affected repository needed a log sweep of all 62 repositories carrying ci-beta.yml, where a repository with no fallback attributions was indistinguishable from one that had never run the check. Of those 62, only 11 have ever produced a real ci-versioning run.

What this does not do

Coverage on BHoM/BHoM is effectively zero for object records after this, because 100% of its findings are on the fallback path. That is a truthful zero replacing a false red, but it is still zero, and it must not be the state of things when versioning becomes this system's to emit at BHoMBot sunset. Tracked as a sunset blocker.

Refs BHoM/internal-tickets#31

AttributeToSubject falls back to a namespace prefix when the dataset record
names no declaring assembly. Findings on that path were counted towards a
failing verdict while the run simultaneously warned that they "may not be its
own". Recording the uncertainty and then discarding it is the defect.

Route fallback-attributed findings to the existing unverified bucket instead.
DeriveStatus yields Warning and ExitCodeFor yields 0, so they stay in the log
and the artefact and no longer fail the check. Findings carrying a declaring
assembly are unaffected and still gate.

Precision is not the fix and should not be attempted: BH.oM.Structure.Elements
is declared by both BHoM/BHoM and StructuralEngineering_Toolkit, so no string
rule can say which of two repos sharing a namespace owns a type.

Measured on BHoM/BHoM (run 33849699768, commit d6db0a1): 145 of 145 findings
arrived on the fallback path, 0 by declaring assembly, and all 145 proved to be
another repository's or an artefact of an incomplete closure. Re-running the
same subject against a closure with 186 assemblies loaded instead of 75 cut the
count to 98 and left the basis at 100% inferred, so closure quality changes the
volume and never the basis. Verified against that closure: Status Warning,
Failures 0, exit 0, all 98 diagnostics retained with CountedAsReal false.

Also print the attribution basis whenever there are diagnostics rather than only
when a count is non-zero. The comment already said a number printed only when
non-zero cannot be distinguished from one nobody measured; the guard below it
produced exactly that. A sweep of all 62 repos carrying ci-beta.yml could not
tell a repo with no fallback attributions from one that had never run.

Refs BHoM/internal-tickets#31
…ean one

The routing in the previous commit stops inferred attribution failing a build.
It also made the job summary read "Versioning passed" over 98 findings that
could not be attributed, because $failed derives from exit_code and Warning and
Pass are both exit 0 by design. That is the vacuous green this summary exists to
prevent, reintroduced one level up.

Read the runner's own Status, which the summary already parses out of
versioning-result.json, and branch the heading three ways. Warning now reads
"Versioning inconclusive: N finding(s) reported, none verified". The findings
table's empty branch no longer says only "no regressions", which is true and
misleading on its own when nothing was checked that could have produced one.

The check conclusion stays success. A GHA job cannot emit neutral, so the
summary heading and the coverage table are the only places the difference can
show; the two aggregate warning annotations remain the other signal.

Verified against the real artefacts: Status Warning with 98 unverified renders
the inconclusive heading, Status Error with 145 real renders the failure
heading, Status Pass renders the pass heading. No Pester assertions covered the
heading, so none needed updating.

Refs BHoM/internal-tickets#31
A missing subject assembly list is a precondition failure, not a finding, and
the "do not gate on inferred attribution" policy must not reach it.

It does not today. ReadSubjectAssemblyList returns null when the list is absent,
attribution falls back to the whole loaded closure, and that path records
AttributionBasis.NotRecorded while the policy keys on NamespaceFallback, so the
over-reporting run still fails. But that safety is incidental rather than
designed: both paths match a description against a set of namespaces, so
unifying them looks like a harmless refactor and would silently turn a
whole-closure red into a green tick.

Two cases pin it. Verified by mutation: widening the policy to
attributedBy != DeclaringAssembly fails
WholeClosureFindingWithNoMethodEvent_StillCountsAsRealAndStillGates, so the
test is not vacuous.

Prompted by BHoM/Versioning_Toolkit PR #348, run 33619426914, where the
action's own guard caught the missing list and exited 1 before the runner
started. This is defence in depth behind that guard, not a replacement for it.
No production code changed.

Refs BHoM/internal-tickets#31
@sakanni
sakanni merged commit 3c48dd5 into develop Sep 4, 2026
5 checks passed
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