Repository navigation
refactor(output): sort rule violations once, in the pipeline (#147) - #195
Conversation
`sortViolationsBySeverity` was called by three renderers — printRules, buildRulesSection for --summary-file, and printJson — each re-sorting the same list at render time. `aggregated.ruleViolations` itself stayed in detection order, so "the order of ruleViolations" was three separate facts that agreed only by convention, and a fourth consumer added later would silently reintroduce #87's bug for its own surface. Sort once, at the single point the report is assembled (#84 made there be one), and let every renderer read the field directly. Plugin findings are appended before the sort rather than after it, so they interleave by severity like any other violation: a plugin's error is not ranked below hermex's info merely for having been produced last. That is a decision, not a side effect, and it is now pinned by a test. No output change: all 30 output-review cases match main. The new e2e guard was confirmed to fail without the fix — its fixture makes detection order and severity order disagree, which the existing plugin fixture could not do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Output Review0 of 30 case(s) changed · 0 invariant breach(es) · all 30 cases Reference: Output is unchanged. Nothing to review. |
Coverage Report
File Coverage
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Opened #196 to get a second opinion on the plugin-interleaving decision in this PR, rather than leaving it in a code comment and one test. Two things it records that reviewers of this PR should see:
Neither changes what this PR does; both are things I'd rather have on the record than assumed. |
Closes #147. Follow-up to #194.
What
sortViolationsBySeveritywas called by three renderers, each re-sorting the same list at render time:printRules(src/utils/print-rules.ts)buildRulesSectionfor--summary-file(src/utils/write-summary-file.ts)printJson(src/utils/print-json.ts)aggregated.ruleViolationsitself stayed in detection order, so "the order ofruleViolations" wasn't one fact about the report — it was three that agreed by convention. A fourth consumer added later would silently reintroduce #87's bug for its own surface.Now it's sorted once, at the single point the report is assembled, and every renderer reads the field directly.
#147 hedged on where the sort should go — "in
aggregateReports… or inrunPipeline" — because at the time there was no single assembly point. #194 created one, so this lands atrunPipeline's singleAggregatedReportconstruction and the hedge resolves itself.The one real decision
Plugin findings are appended before the sort rather than after it, so they interleave by severity like any other violation — a plugin's
erroris not ranked below hermex'sinfomerely for having been produced last.That's a change to what
aggregated.ruleViolationsmeans, even though no current output moves (every renderer already sorted the combined list). It's a decision rather than a side effect of where the sort landed, so it's stated in a comment at the call site and pinned by a test.Verification
pnpm run test:output: all 30 cases matchmain.warn → info → errorand the test catches it.The guard needed a new fixture. The existing plugin fixture adds its findings in
error → warn → infoorder, so detection order and severity order already agreed there and the assertion would have passed against unsorted code.tests/e2e/hermex-plugin-ordering.config.tsmakes them disagree on purpose: hermex contributeswarntheninfo, the plugin contributes the onlyerror, last.Consumers that read the list raw
Checked before changing the order underneath them, since this is where an upstream sort could have changed behaviour silently:
printPackages→collectPackageFlags.find()the list, butno-packagesbreaks after the first match per package andno-deprecated-packagesemits one per package, so there is nothing to pick betweenbuildRulesSection(--summary-file)infoout, and filter-then-sort agrees with sort-then-filter for a stable sortcomputeCompliance→groupBySeverityPluginInventoryView.violations)runPluginPhasereceives the pre-assembly list, so plugins still see detection ordersortViolationsBySeveritystays exported for a caller assembling its own list; its doc comment previously asserted the opposite invariant ("render-time only") and has been corrected.🤖 Generated with Claude Code