Compute the ClassLength backstop from unfiltered offenses #1391

Open
opened 2026-10-25 21:43:41 +00:00 by jared · 0 comments
Owner

Problem

The backstop check in the gate reply can report a false "backstop hit". The gate lint step shows only offenses that are new against the base branch. The backstop check reads that filtered set. When a family cop offense already exists on base, the filter removes it. The check then sees Metrics/ClassLength with no family cop and says no family cop explains the file, but one does.

Observed

Gate::Reply#lint_output passes the lint step output to LintOutput.backstop_paths (plugins/os-sdlc/lib/os_sdlc/gate/reply.rb:30-37).
The lint step command runs bin/os-sdlc-lint --base <base> (plugins/os-sdlc/lib/os_sdlc/gate/commands.rb:84-88).
NewOffenseReport#print writes only added_offenses as the JSON (plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_report.rb:22-23).
NewOffenseCheck#added_offenses drops every head offense that matches a base offense by key (plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_check.rb:17-39).
The key is [path, cop, message] (plugins/os-sdlc/lib/os_sdlc/fast_gate/offense.rb:12).
LintOutput.backstop? flags a file when it has Metrics/ClassLength and no cop of the family (plugins/os-sdlc/lib/os_sdlc/gate/lint_output.rb:29-32).
Case: a class grows past the limit, so Metrics/ClassLength is new. A family cop offense in the same file exists on base with the same message, so the step filters it out. backstop_paths returns that file.

Reproduce

n/a: found by reading the code in the orchestrator review of commit cb4674b6; no run was made.

Approach

Compute the family check from the unfiltered offenses of the flagged files. The ticket decides the exact design.

Required work:

  1. Make the backstop decision use the unfiltered offenses of the files that carry a new Metrics/ClassLength offense.

Acceptance criteria

  1. Given a file with a new Metrics/ClassLength offense and a family cop offense that already exists on base, when the gate renders the lint failure, then the reply shows no backstop hit for that file.
  2. Given a file with a new Metrics/ClassLength offense and no family cop offense on head, when the gate renders the lint failure, then the reply shows a backstop hit for that file.

Owner sketch

class OsSdlc::Gate::LintOutput
  def self.backstop_paths(output, family)
end

Origin

  • Trigger: orchestrator review of the #1324 diff (commit cb4674b6)
  • Improvised this session: none
  • Chain: false backstop hit ← backstop reads the base-filtered lint JSON ← DESIGN (plugins/os-sdlc/lib/os_sdlc/gate/reply.rb:30-37, plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_check.rb:17-39)
  • Skeptic: VERDICT: CREATE
  • Root candidate: none found; searched backstop, unfiltered, ClassLength, LintOutput, NewOffenseCheck
  • Where: plugins/os-sdlc/lib/os_sdlc/gate/lint_output.rb
  • Session: eb98cdcd-6783-46a0-b7df-cf18a8fe2a56
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-ticket-1325/eb98cdcd-6783-46a0-b7df-cf18a8fe2a56.jsonl
## Problem The backstop check in the gate reply can report a false "backstop hit". The gate lint step shows only offenses that are new against the base branch. The backstop check reads that filtered set. When a family cop offense already exists on base, the filter removes it. The check then sees `Metrics/ClassLength` with no family cop and says no family cop explains the file, but one does. ## Observed `Gate::Reply#lint_output` passes the lint step output to `LintOutput.backstop_paths` (`plugins/os-sdlc/lib/os_sdlc/gate/reply.rb:30-37`). The lint step command runs `bin/os-sdlc-lint --base <base>` (`plugins/os-sdlc/lib/os_sdlc/gate/commands.rb:84-88`). `NewOffenseReport#print` writes only `added_offenses` as the JSON (`plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_report.rb:22-23`). `NewOffenseCheck#added_offenses` drops every head offense that matches a base offense by key (`plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_check.rb:17-39`). The key is `[path, cop, message]` (`plugins/os-sdlc/lib/os_sdlc/fast_gate/offense.rb:12`). `LintOutput.backstop?` flags a file when it has `Metrics/ClassLength` and no cop of the family (`plugins/os-sdlc/lib/os_sdlc/gate/lint_output.rb:29-32`). Case: a class grows past the limit, so `Metrics/ClassLength` is new. A family cop offense in the same file exists on base with the same message, so the step filters it out. `backstop_paths` returns that file. ## Reproduce n/a: found by reading the code in the orchestrator review of commit cb4674b6; no run was made. ## Approach Compute the family check from the unfiltered offenses of the flagged files. The ticket decides the exact design. Required work: 1. Make the backstop decision use the unfiltered offenses of the files that carry a new `Metrics/ClassLength` offense. ## Acceptance criteria 1. Given a file with a new `Metrics/ClassLength` offense and a family cop offense that already exists on base, when the gate renders the lint failure, then the reply shows no backstop hit for that file. 2. Given a file with a new `Metrics/ClassLength` offense and no family cop offense on head, when the gate renders the lint failure, then the reply shows a backstop hit for that file. ## Owner sketch ```ruby class OsSdlc::Gate::LintOutput def self.backstop_paths(output, family) end ``` ## Origin - Trigger: orchestrator review of the #1324 diff (commit cb4674b6) - Improvised this session: none - Chain: false backstop hit ← backstop reads the base-filtered lint JSON ← DESIGN (`plugins/os-sdlc/lib/os_sdlc/gate/reply.rb:30-37`, `plugins/os-sdlc/lib/os_sdlc/fast_gate/new_offense_check.rb:17-39`) - Skeptic: VERDICT: CREATE - Root candidate: none found; searched backstop, unfiltered, ClassLength, LintOutput, NewOffenseCheck - Where: plugins/os-sdlc/lib/os_sdlc/gate/lint_output.rb - Session: eb98cdcd-6783-46a0-b7df-cf18a8fe2a56 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-ticket-1325/eb98cdcd-6783-46a0-b7df-cf18a8fe2a56.jsonl
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1391
No description provided.