lint rule: cross-file structural duplication via flay #200

Closed
opened 2026-08-01 14:16:49 +00:00 by jared · 2 comments
Owner

Migrated from jared/os-sdlc#12 (repo retired).

lint rule: cross-file structural duplication via flay

Problem

RuboCop cops (including Sdlc/Dry/DuplicatedStatementSequence) operate on one
file's AST at a time (RuboCop::Cop::Base#on_new_investigation runs per source
file) -- they structurally cannot catch duplicate logic that lives in two
different files. The original example (identical overlaps? in
ac_coverage_lint.rb and a since-removed lint/spec_ticket_coverage.rb) no
longer exists verbatim -- that specific pair was consolidated -- but the gap
it illustrated is still real and still generalizable: no current lint step
catches near-identical methods/blocks duplicated across separate files.

Detection

  • Inputs: the Ruby source tree under lib/.
  • Algorithm: run flay (already a project dependency, gem list flay ->
    2.14.4) over lib/; fail if any reported duplicate-mass pair exceeds a
    chosen threshold. This is a standalone flay-based check, not a rubocop cop
    (out of scope for rubocop's single-file model) -- wire it as its own
    script/rake task alongside the existing custom lint/ tools
    (ac_coverage_lint.rb is the precedent for a standalone advisory lint in
    this plugin).
  • Failure message: flay's own duplicate-mass report, at minimum quoting
    the two file:method locations and the shared statement text.

Correction

Extract the shared logic into one module/method both call.

Pass/fail examples

  • Must fail: any lib/ tree where flay reports duplicate mass over the
    chosen threshold between two distinct files.
  • Must pass: flay mass under threshold repo-wide.

Scope note

The original namespace/layout complaint (OsSdlc::AcCoverageLint file location
vs OsSdlc::Lint::SpecTicketCoverage) is dropped from this ticket -- it was
tied to a file pairing that no longer exists, and was a one-off convention
gripe rather than a generalizable rule. If a *Lint naming/location
convention is wanted, file that separately once there are enough live
examples to generalize from.

Provenance

Fable finding 2, severity should-fix. Council review of run #5 (ticket #5,
artifact .sdlc/5.md), 2026-07-20. Reassessed and narrowed 2026-08-01 during
os-sdlc sandbox retirement triage.

Implementation plan

Threshold value and wiring (rake task vs CI step vs pipeline lint stage) are
open implementation decisions -- not fully specified.

Migrated from jared/os-sdlc#12 (repo retired). # lint rule: cross-file structural duplication via flay ## Problem RuboCop cops (including Sdlc/Dry/DuplicatedStatementSequence) operate on one file's AST at a time (RuboCop::Cop::Base#on_new_investigation runs per source file) -- they structurally cannot catch duplicate logic that lives in two different files. The original example (identical `overlaps?` in ac_coverage_lint.rb and a since-removed lint/spec_ticket_coverage.rb) no longer exists verbatim -- that specific pair was consolidated -- but the gap it illustrated is still real and still generalizable: no current lint step catches near-identical methods/blocks duplicated across separate files. ## Detection * **Inputs:** the Ruby source tree under lib/\. * **Algorithm:** run `flay` (already a project dependency, `gem list flay` -\> 2.14.4) over lib/; fail if any reported duplicate-mass pair exceeds a chosen threshold. This is a standalone flay-based check, not a rubocop cop (out of scope for rubocop's single-file model) -- wire it as its own script/rake task alongside the existing custom lint/ tools (ac_coverage_lint.rb is the precedent for a standalone advisory lint in this plugin). * **Failure message:** flay's own duplicate-mass report, at minimum quoting the two file:method locations and the shared statement text. ## Correction Extract the shared logic into one module/method both call. ## Pass/fail examples * **Must fail:** any lib/ tree where flay reports duplicate mass over the chosen threshold between two distinct files. * **Must pass:** flay mass under threshold repo-wide. ## Scope note The original namespace/layout complaint (OsSdlc::AcCoverageLint file location vs OsSdlc::Lint::SpecTicketCoverage) is dropped from this ticket -- it was tied to a file pairing that no longer exists, and was a one-off convention gripe rather than a generalizable rule. If a `*Lint` naming/location convention is wanted, file that separately once there are enough live examples to generalize from. ## Provenance Fable finding 2, severity should-fix. Council review of run #5 (ticket #5, artifact .sdlc/5.md), 2026-07-20. Reassessed and narrowed 2026-08-01 during os-sdlc sandbox retirement triage. ## Implementation plan Threshold value and wiring (rake task vs CI step vs pipeline lint stage) are open implementation decisions -- not fully specified.
Author
Owner

This was generated by AI during triage.

Recommendation for maintainer: flay defaults to reporting all pairs; add a --minimum (mass) threshold rather than "any duplicate mass". Flay convention treats mass ~20 as noise, ~40+ as worth extracting. Recommend a conservative default of --minimum 40, tunable later via .flay.yml or a rake arg. Wire as rake lint:flay (a lint/flay_lint.rb script alongside ac_coverage_lint.rb) run in the same lint stage as rubocop, not folded into RuboCop itself (cross-file, out of its per-file model). This fully specifies detection + wiring; no open maintainer decision remains.

> *This was generated by AI during triage.* Recommendation for maintainer: flay defaults to reporting all pairs; add a --minimum (mass) threshold rather than "any duplicate mass". Flay convention treats mass ~20 as noise, ~40+ as worth extracting. Recommend a conservative default of --minimum 40, tunable later via .flay.yml or a rake arg. Wire as rake lint:flay (a lint/flay_lint.rb script alongside ac_coverage_lint.rb) run in the same lint stage as rubocop, not folded into RuboCop itself (cross-file, out of its per-file model). This fully specifies detection + wiring; no open maintainer decision remains.
Author
Owner

This was generated by AI during triage.

Implemented via os-sdlc pipeline (2 review rounds, APPROVE). OsSdlc::FlayCheck, threshold 40, findings quote shared source. Merged to main in 4e32fbc. Note: overlaps with the older human-invoked bin/dup-sweep; consolidation left for human review.

> *This was generated by AI during triage.* Implemented via os-sdlc pipeline (2 review rounds, APPROVE). OsSdlc::FlayCheck, threshold 40, findings quote shared source. Merged to main in 4e32fbc. Note: overlaps with the older human-invoked bin/dup-sweep; consolidation left for human review.
jared 2026-08-01 20:06:29 +00:00
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#200
No description provided.