Add cop Sdlc/Sequel/RubyFilterAfterAll #910

Closed
opened 2026-10-02 16:57:06 +00:00 by jared · 2 comments
Owner

Problem

Rows are loaded, then filtered in Ruby.
Signal: rows are loaded, then filtered in Ruby; the filter is a query that belongs to the owning model.

Approach

Add the cop Sdlc/Sequel/RubyFilterAfterAll under plugins/os-sdlc/lib/os_sdlc/cops/.
Templates: registry_outside_entry_point.rb, row_index_on_model.rb, and the shared sequel_allowed_paths.rb.
Authoring guide: plugins/os-sdlc/skills/mine-lints/references/implement-cop.md.
RuboCop runs with DisabledByDefault: true, so every enabled cop blocks at the gate and every hit must always need a correction.
Do not widen the match lists below.
Receiver test: the chain starts from a *_dataset call.
Direct model queries (Model.where) are out of scope.

Flagged: .all or .to_a on a *_dataset chain followed by find detect select filter find_all reject filter_map partition, or by the block forms of count any? none? all?. The materializer is required, because Sequel's own select on a dataset picks columns.

Not flagged: entries; direct each/map on a dataset; grep sort_by group_by min_by max_by uniq sum first(n) take.

Known hits: model/implementation.rb:78, model/implementation.rb:97-98 (.all.select(&:green?)), model/implementation/progress_witness.rb:29 (all under plugins/os-sdlc/lib/os_sdlc/).
Correct the existing hits in plugins/os-sdlc/lib/ in the same change.
Intended correction: move the query or decision into a named dataset_module method on the model that owns the rows, and delegate from the caller.

Lesson (ADR-0265): the lesson file is a required part of the cop.
Path: plugins/os-sdlc/lint-lessons/<kebab-cop-name>.md in the plugin (ADR-0266).
Sections in order: The signal; What it is a sign of; Why it matters; Your decision; Correction; Example.
Lesson text uses generic names (for example orders_dataset, Order), never names from this codebase.
Lesson example: before orders_dataset.all.select(&:shipped?), after orders_dataset.shipped.
Depends on #908 (Lesson concept move; Lesson::Library reads the plugin lesson directory).

Required work:

  1. Add the cop file — check: present RubyFilterAfterAll in plugins/os-sdlc/lib/os_sdlc/cops/*.rb
  2. Add the lesson file — check: present The signal in plugins/os-sdlc/lint-lessons/sdlc-sequel-ruby-filter-after-all.md

Acceptance criteria

  1. Given a call in the flagged list on a *_dataset chain, when the cop runs, then it flags it.
  2. Given a call in the not-flagged list, when the cop runs, then it does not flag it.
  3. Given the existing hits in plugins/os-sdlc/lib, when the gate runs, then none remain.
  4. Given a gate reply for a flagged offense, when the gate renders, then the lesson file text appears.

Owner sketch

class RuboCop::Cop::Sdlc::Sequel::RubyFilterAfterAll < RuboCop::Cop::Base
  def on_send(node)
end

Origin

  • Trigger: user request this session, from a review of Sequel dataset use.
  • Improvised this session: none
  • Chain: query or decision outside owning model ← no cop covers it (DESIGN: ADR-0265)
  • Root candidate: none
  • Where: plugins/os-sdlc/lib/os_sdlc/cops/
  • Session: 9ee7805f-a4e4-470e-af50-5020e5c524a4
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/9ee7805f-a4e4-470e-af50-5020e5c524a4.jsonl
## Problem Rows are loaded, then filtered in Ruby. Signal: rows are loaded, then filtered in Ruby; the filter is a query that belongs to the owning model. ## Approach Add the cop `Sdlc/Sequel/RubyFilterAfterAll` under `plugins/os-sdlc/lib/os_sdlc/cops/`. Templates: `registry_outside_entry_point.rb`, `row_index_on_model.rb`, and the shared `sequel_allowed_paths.rb`. Authoring guide: `plugins/os-sdlc/skills/mine-lints/references/implement-cop.md`. RuboCop runs with `DisabledByDefault: true`, so every enabled cop blocks at the gate and every hit must always need a correction. Do not widen the match lists below. Receiver test: the chain starts from a `*_dataset` call. Direct model queries (`Model.where`) are out of scope. Flagged: `.all` or `.to_a` on a `*_dataset` chain followed by `find detect select filter find_all reject filter_map partition`, or by the block forms of `count any? none? all?`. The materializer is required, because Sequel's own `select` on a dataset picks columns. Not flagged: `entries`; direct `each`/`map` on a dataset; `grep sort_by group_by min_by max_by uniq sum first(n) take`. Known hits: `model/implementation.rb:78`, `model/implementation.rb:97-98` (`.all.select(&:green?)`), `model/implementation/progress_witness.rb:29` (all under `plugins/os-sdlc/lib/os_sdlc/`). Correct the existing hits in `plugins/os-sdlc/lib/` in the same change. Intended correction: move the query or decision into a named `dataset_module` method on the model that owns the rows, and delegate from the caller. Lesson (ADR-0265): the lesson file is a required part of the cop. Path: `plugins/os-sdlc/lint-lessons/<kebab-cop-name>.md` in the plugin (ADR-0266). Sections in order: The signal; What it is a sign of; Why it matters; Your decision; Correction; Example. Lesson text uses generic names (for example `orders_dataset`, `Order`), never names from this codebase. Lesson example: before `orders_dataset.all.select(&:shipped?)`, after `orders_dataset.shipped`. Depends on #908 (Lesson concept move; `Lesson::Library` reads the plugin lesson directory). Required work: 1. Add the cop file — check: present `RubyFilterAfterAll` in `plugins/os-sdlc/lib/os_sdlc/cops/*.rb` 2. Add the lesson file — check: present `The signal` in `plugins/os-sdlc/lint-lessons/sdlc-sequel-ruby-filter-after-all.md` ## Acceptance criteria 1. Given a call in the flagged list on a `*_dataset` chain, when the cop runs, then it flags it. 2. Given a call in the not-flagged list, when the cop runs, then it does not flag it. 3. Given the existing hits in `plugins/os-sdlc/lib`, when the gate runs, then none remain. 4. Given a gate reply for a flagged offense, when the gate renders, then the lesson file text appears. ## Owner sketch ```ruby class RuboCop::Cop::Sdlc::Sequel::RubyFilterAfterAll < RuboCop::Cop::Base def on_send(node) end ``` ## Origin - Trigger: user request this session, from a review of Sequel dataset use. - Improvised this session: none - Chain: query or decision outside owning model ← no cop covers it (DESIGN: ADR-0265) - Root candidate: none - Where: plugins/os-sdlc/lib/os_sdlc/cops/ - Session: 9ee7805f-a4e4-470e-af50-5020e5c524a4 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/9ee7805f-a4e4-470e-af50-5020e5c524a4.jsonl
Author
Owner

Work started on branch ticket-910 through the os-sdlc pipeline.

Work started on branch ticket-910 through the os-sdlc pipeline.
Author
Owner

Resolution

Done: Cop Sdlc/Sequel/RubyFilterAfterAll added and enabled, with its lesson file plugins/os-sdlc/lint-lessons/sdlc-sequel-ruby-filter-after-all.md. Three existing hits corrected by delegation to Reroute and StepResult dataset_module methods.

Evidence: Branch ticket-910; os-sdlc implementation 16: 11 dispatches, 1 retry (suite-check failed once, programmer-repair, suite green on the second check), required-work gate green, contract-auditor pass. Implementation 14 was abandoned after Map::ChangedError; its state is kept on branch ticket-910-run14-wip.

Follow-ups: Lesson converts to YAML under the Lesson YAML ticket (to be captured). Capture a workflow ticket: a map change merged by another session stops an in-flight run with Map::ChangedError. History row added after merge.

## Resolution **Done:** Cop Sdlc/Sequel/RubyFilterAfterAll added and enabled, with its lesson file plugins/os-sdlc/lint-lessons/sdlc-sequel-ruby-filter-after-all.md. Three existing hits corrected by delegation to Reroute and StepResult dataset_module methods. **Evidence:** Branch ticket-910; os-sdlc implementation 16: 11 dispatches, 1 retry (suite-check failed once, programmer-repair, suite green on the second check), required-work gate green, contract-auditor pass. Implementation 14 was abandoned after Map::ChangedError; its state is kept on branch ticket-910-run14-wip. **Follow-ups:** Lesson converts to YAML under the Lesson YAML ticket (to be captured). Capture a workflow ticket: a map change merged by another session stops an in-flight run with Map::ChangedError. History row added after merge.
jared closed this issue 2026-10-02 17:37:03 +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#910
No description provided.