Redmine 6.x regression sweep — baseline-drift over a release line
Date: 2026-05-21. Rigor: v0.1.9 (master).
Target: redmine/redmine, 13 tags 6.0.0 → 6.1.2.
Second run of the rigor-regression-sweep
procedure, after the Mastodon v4.5.x sweep
(20260521-mastodon-v4.5-regression-sweep.md).
Broadens the empirical corpus and — unlike Mastodon’s flat-zero
result — produces a real baseline-breach event to dissect.
- Blobless clone of
redmine/redmine; 13 tags in release order:6.0.0 … 6.0.9(a 10-patch series) plus6.1.0 6.1.1 6.1.2— so the range spans both a patch-stabilisation window and the6.0 → 6.1minor-feature bump (per the SKILL’s Phase 1 guidance: prefer a feature-spanning range over a pure patch series). - Frozen config, identical to the Mastodon sweep for
cross-project comparability:
paths: [app, lib],exclude: [vendor, tmp],severity_profile: lenient,signature_paths:→ therigor-activesupport-core-extsig/bundle (absolute path). - Baseline at
6.0.0: 145 diagnostics / 59 buckets. app + libscope: 322.rbfiles at6.0.0, 331 at6.1.2; 112.rbfiles changed across the range.
Result — a flat 22, then a +6 breach at 6.1.2
Section titled “Result — a flat 22, then a +6 breach at 6.1.2”| Tag | raw | silenced | surfaced |
|---|---|---|---|
| 6.0.0 … 6.1.1 | 144–145 | 122–123 | 22 |
| 6.1.2 | 145 | 117 | 28 |
Two things to explain: the constant 22, and the +6 at 6.1.2.
The constant 22 — parse errors, un-baselineable
Section titled “The constant 22 — parse errors, un-baselineable”The 22 diagnostics surfaced at every tag — including the baseline
tag 6.0.0 itself — are parse errors (unexpected '<', ignoring it, unterminated string meets end of file, …), and all 22 come
from one file: lib/generators/redmine_plugin_model/templates/migration.rb.
That file is a Rails generator template — a .rb-extension file
that is actually an ERB template (<%= … %>), the survey-§8a
pattern from 20260519-oss-library-survey.md.
They surface at the baseline tag because parse errors carry no
rule, and Baseline.from_diagnostics only buckets diagnostics
that have both a qualified_rule and a path — so a rule-less
diagnostic is never recorded in the baseline and Baseline#filter
always passes it through (unkeyable set). This is a methodology
finding: the baseline mechanism has a blind spot for parse /
internal-analyzer errors. The fix is config-side — exclude: the
generator-template directory — and the rigor-regression-sweep and
rigor-project-init SKILLs should say so.
The +6 at 6.1.2 — false positives from FP-pattern churn
Section titled “The +6 at 6.1.2 — false positives from FP-pattern churn”6.1.1 → 6.1.2 surfaced 6 new call.undefined-method
diagnostics, all in app/models/issue.rb:
issue.rb:1763 undefined method `user' for nilissue.rb:1799 undefined method `user' for nilissue.rb:2008 undefined method `user' for nilissue.rb:2008 undefined method `notes' for nilissue.rb:2009 undefined method `private_notes' for nilissue.rb:2019 undefined method `notes_and_details_empty?' for nilEvery one is @current_journal.METHOD inside an if @current_journal (or @current_journal && …) guard:
if @current_journal @copied_from.init_journal(@current_journal.user) # flaggedendThese are false positives. The code explicitly guards the
instance variable; Rigor does not narrow an instance variable to
non-nil through a truthy if @ivar guard, so @current_journal
stays Journal | nil inside the consequent and every method call on
it reports … for nil. This is the same family as the cluster-4
G2 ivar-narrowing gap
(20260521-mastodon-cluster4-flow-folding-triage.md).
They surfaced at 6.1.2 — not earlier — because Redmine’s 6.1
development added more guarded-@current_journal call sites to
issue.rb, pushing the (app/models/issue.rb, call.undefined-method)
bucket over its 6.0.0 baseline count; WD4 ALL-or-NOTHING then
surfaces the whole bucket.
Verdict
Section titled “Verdict”- The sweep detected a real baseline-drift event — unlike
Mastodon’s flat zero. The drift mechanism works: a project that
adopted Rigor at
6.0.0would have seen CI go from clean to “6 new diagnostics in issue.rb” at6.1.2. - But the breach is false positives, not bugs. The experiment
shows baseline drift catches FP-pattern accumulation too: as
normal development adds more code matching a known false-positive
shape (guarded ivar access), the bucket breaches even though no
real defect was introduced. This is an argument for the
severity_profile: lenientdefault (the 6 areerroronly becausecall.undefined-methodstayserrorunderlenient; they would still surface) and for the ivar-narrowing engine fix below. - Engine gap surfaced — ivar narrowing through a truthy guard.
if @ivar; @ivar.methoddoes not narrow@ivarto non-nil. A concrete, recurring false-positive source; queued (see below). - Parse errors are a baseline blind spot. Rule-less
diagnostics bypass the baseline and surface forever. Generator
.rbtemplates are the common source;exclude:them.
Triage-heuristic cross-check (ADR-23)
Section titled “Triage-heuristic cross-check (ADR-23)”rigor triage on 6.0.0 without the AS bundle (320 diagnostics)
— to check the heuristic catalogue on a second project:
- H1
activesupport-core-ext— 155 diagnostics,html_safe×94 wrap×25 deep_dup×5 …; action correctly namesplugins:activation (the ADR-25 H1 fix landed this cycle). Correct. - H4
activerecord-relation-misinference— 4, namesrigor-activerecord. Correct. - H5
systemic-file-cluster— pinpoints the 22-parse-error filelib/generators/redmine_plugin_model/templates/migration.rbexactly. Correct. - H6
genuine-bugs— 3call.argument-type-mismatch. Plausible.
The heuristic catalogue holds up on a second, structurally different project.
Follow-ups
Section titled “Follow-ups”- Engine — ivar narrowing through
if @ivar/@ivar && …truthy guards. Recurring false positive; same surface as cluster-4 G2 (control-flow-analysis.md§ “mutation effects” / narrowing). Queued. - SKILL —
rigor-regression-sweep(andrigor-project-init) should note that generator.rbtemplates produce un-baselineable parse errors and belong inexclude:.
Reproduction
Section titled “Reproduction”~/repo/ruby/rigor-survey/_redmine-sweep/ holds sweep.sh,
tabulate.rb, rigor-no-as.yml, the frozen baseline.yml, and
reports/<tag>.json.
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.