Write lessons for the ClassLength family cops #1323

Closed
opened 2026-10-25 16:44:42 +00:00 by jared · 3 comments
Owner

Problem

Lessons guide the agent when a cop fires. The ClassLength family has cops with no lesson (ModuleOwnsInstanceState, DuplicatedStatementSequence) and cops not yet built (the repeated-method-skeleton cop Sdlc/Dry/RepeatedRescueClause, #1321, and the repeated-collaborator-construction cop Sdlc/Dry/RepeatedCollaboratorConstruction, #1322). plugins/os-sdlc/lint-lessons/metrics-class-length.yml (commit a830d451) covers only one cause: the same work written many times. Its correction contradicts the shipped split into job-named classes (IssueCommands, ProjectCommands).

Reproduce

n/a: missing prose artifacts.

Approach

Write lessons in plugins/os-sdlc/lint-lessons/*.yml. A lesson uses no repo-specific names.
Rewrite metrics-class-length.yml by cause:

  • Repeated work: move it into a wrapper or a shared collaborator.
  • Several jobs: extract job classes that own their own state.
  • Methods that use another object's data: move the methods to that object.
  • One long algorithm: use a method object.
  • Declarations or tables: move the data out, or file a false-positive ticket.
  • Test classes: split by the behavior under test.
    Add one lesson each for Sdlc/Dry/RepeatedRescueClause (#1321), Sdlc/Dry/RepeatedCollaboratorConstruction (#1322), Sdlc/Structural/ModuleOwnsInstanceState, and Sdlc/Dry/DuplicatedStatementSequence.
    This ticket is blocked by #1321 and #1322.
    The two new-cop lessons take their content from the smell analyses in docs/plans/class-length-family/: smell-repeated-method-skeleton.md sections 5, 10, 13 (wrapper fix, forms A and B) and smell-repeated-collaborator-construction.md sections 5, 7, 10 (the build_<class> fix, its name check, the three memoization checks as a note in decision, and the false-positive ticket as the escape path).
    The #1325 audit (docs/plans/class-length-family/sdlc-cop-audit.md) found false-positive shapes for Sdlc/Dry/DuplicatedStatementSequence (#1345: windows of local assignments the caller reads later; windows in two different classes) and Sdlc/Structural/ModuleOwnsInstanceState (#1372: a module with extend self or module_function, whose ivars are state on the module object). Their lessons name those shapes in decision as cases to report through a false-positive ticket, not to fix.
    Each lesson file has the fields that OsSdlc::Lesson.from requires: key, signal, sign_of, why, decision, correction, example (before, after). Lesson::Catalog finds a lesson by a file name made from its key, for example Sdlc/Dry/RepeatedRescueClause -> sdlc-dry-repeated-rescue-clause.yml.

Required work:

  1. Rewrite metrics-class-length.yml by cause — check: present Several jobs in plugins/os-sdlc/lint-lessons/metrics-class-length.yml
  2. Add a lesson for the repeated-method-skeleton cop — check: present key: Sdlc/Dry/RepeatedRescueClause in plugins/os-sdlc/lint-lessons/sdlc-dry-repeated-rescue-clause.yml
  3. Add a lesson for the repeated-collaborator-construction cop — check: present key: Sdlc/Dry/RepeatedCollaboratorConstruction in plugins/os-sdlc/lint-lessons/sdlc-dry-repeated-collaborator-construction.yml
  4. Add a lesson for Sdlc/Structural/ModuleOwnsInstanceState — check: present key: Sdlc/Structural/ModuleOwnsInstanceState in plugins/os-sdlc/lint-lessons/sdlc-structural-module-owns-instance-state.yml
  5. Add a lesson for Sdlc/Dry/DuplicatedStatementSequence — check: present key: Sdlc/Dry/DuplicatedStatementSequence in plugins/os-sdlc/lint-lessons/sdlc-dry-duplicated-statement-sequence.yml

Acceptance criteria

  1. Given the ClassLength lesson, when it is read, then it lists all six causes with one correction each.
  2. Given each of the four cops (two new, two existing), when it fires, then Lesson::Relay renders a lesson for its key.
  3. Given the five lesson files, when they are searched for repo-specific names (OsSdlc, Backlog, IssueCommands, ProjectCommands, CliCommandWiring, TrackerCommandFailed, os-backlog, os-sdlc), then none match.

Owner sketch

Prose only; no Ruby object changes. Files written in plugins/os-sdlc/lint-lessons/:

  • metrics-class-length.yml (rewritten by cause)
  • sdlc-dry-repeated-rescue-clause.yml
  • sdlc-dry-repeated-collaborator-construction.yml
  • sdlc-structural-module-owns-instance-state.yml
  • sdlc-dry-duplicated-statement-sequence.yml
    The readers stay as they are: OsSdlc::Lesson.from(path, data) validates the fields and OsSdlc::Lesson::Catalog#lesson(key) loads the file; OsSdlc::Lesson::Relay#render(keys) renders it. Test for criterion 2: one Relay render per key with Lesson::Catalog.default.

Origin

  • Trigger: A ClassLength offense whose lesson gave a correction that contradicts the shipped split.
  • Improvised this session: none
  • Chain: Wrong correction guidance ← one-cause lesson ← DESIGN (plugins/os-sdlc/lint-lessons/metrics-class-length.yml)
  • Root candidate: none found; searched ClassLength, lesson, ModuleOwnsInstanceState, DuplicatedStatementSequence, skeleton, collaborator construction, metrics-class-length.yml
    VERDICT: CREATE
  • Where: plugins/os-sdlc/lint-lessons/metrics-class-length.yml
  • Session: f4187368-5e7e-459d-80c6-ddf7620389cd
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f4187368-5e7e-459d-80c6-ddf7620389cd.jsonl
## Problem Lessons guide the agent when a cop fires. The ClassLength family has cops with no lesson (ModuleOwnsInstanceState, DuplicatedStatementSequence) and cops not yet built (the repeated-method-skeleton cop `Sdlc/Dry/RepeatedRescueClause`, #1321, and the repeated-collaborator-construction cop `Sdlc/Dry/RepeatedCollaboratorConstruction`, #1322). plugins/os-sdlc/lint-lessons/metrics-class-length.yml (commit a830d451) covers only one cause: the same work written many times. Its correction contradicts the shipped split into job-named classes (IssueCommands, ProjectCommands). ## Reproduce n/a: missing prose artifacts. ## Approach Write lessons in plugins/os-sdlc/lint-lessons/*.yml. A lesson uses no repo-specific names. Rewrite metrics-class-length.yml by cause: - Repeated work: move it into a wrapper or a shared collaborator. - Several jobs: extract job classes that own their own state. - Methods that use another object's data: move the methods to that object. - One long algorithm: use a method object. - Declarations or tables: move the data out, or file a false-positive ticket. - Test classes: split by the behavior under test. Add one lesson each for Sdlc/Dry/RepeatedRescueClause (#1321), Sdlc/Dry/RepeatedCollaboratorConstruction (#1322), Sdlc/Structural/ModuleOwnsInstanceState, and Sdlc/Dry/DuplicatedStatementSequence. This ticket is blocked by #1321 and #1322. The two new-cop lessons take their content from the smell analyses in docs/plans/class-length-family/: smell-repeated-method-skeleton.md sections 5, 10, 13 (wrapper fix, forms A and B) and smell-repeated-collaborator-construction.md sections 5, 7, 10 (the `build_<class>` fix, its name check, the three memoization checks as a note in `decision`, and the false-positive ticket as the escape path). The #1325 audit (docs/plans/class-length-family/sdlc-cop-audit.md) found false-positive shapes for Sdlc/Dry/DuplicatedStatementSequence (#1345: windows of local assignments the caller reads later; windows in two different classes) and Sdlc/Structural/ModuleOwnsInstanceState (#1372: a module with `extend self` or `module_function`, whose ivars are state on the module object). Their lessons name those shapes in `decision` as cases to report through a false-positive ticket, not to fix. Each lesson file has the fields that `OsSdlc::Lesson.from` requires: key, signal, sign_of, why, decision, correction, example (before, after). `Lesson::Catalog` finds a lesson by a file name made from its key, for example `Sdlc/Dry/RepeatedRescueClause` -> sdlc-dry-repeated-rescue-clause.yml. Required work: 1. Rewrite metrics-class-length.yml by cause — check: present `Several jobs` in `plugins/os-sdlc/lint-lessons/metrics-class-length.yml` 2. Add a lesson for the repeated-method-skeleton cop — check: present `key: Sdlc/Dry/RepeatedRescueClause` in `plugins/os-sdlc/lint-lessons/sdlc-dry-repeated-rescue-clause.yml` 3. Add a lesson for the repeated-collaborator-construction cop — check: present `key: Sdlc/Dry/RepeatedCollaboratorConstruction` in `plugins/os-sdlc/lint-lessons/sdlc-dry-repeated-collaborator-construction.yml` 4. Add a lesson for Sdlc/Structural/ModuleOwnsInstanceState — check: present `key: Sdlc/Structural/ModuleOwnsInstanceState` in `plugins/os-sdlc/lint-lessons/sdlc-structural-module-owns-instance-state.yml` 5. Add a lesson for Sdlc/Dry/DuplicatedStatementSequence — check: present `key: Sdlc/Dry/DuplicatedStatementSequence` in `plugins/os-sdlc/lint-lessons/sdlc-dry-duplicated-statement-sequence.yml` ## Acceptance criteria 1. Given the ClassLength lesson, when it is read, then it lists all six causes with one correction each. 2. Given each of the four cops (two new, two existing), when it fires, then Lesson::Relay renders a lesson for its key. 3. Given the five lesson files, when they are searched for repo-specific names (OsSdlc, Backlog, IssueCommands, ProjectCommands, CliCommandWiring, TrackerCommandFailed, os-backlog, os-sdlc), then none match. ## Owner sketch Prose only; no Ruby object changes. Files written in plugins/os-sdlc/lint-lessons/: - metrics-class-length.yml (rewritten by cause) - sdlc-dry-repeated-rescue-clause.yml - sdlc-dry-repeated-collaborator-construction.yml - sdlc-structural-module-owns-instance-state.yml - sdlc-dry-duplicated-statement-sequence.yml The readers stay as they are: `OsSdlc::Lesson.from(path, data)` validates the fields and `OsSdlc::Lesson::Catalog#lesson(key)` loads the file; `OsSdlc::Lesson::Relay#render(keys)` renders it. Test for criterion 2: one Relay render per key with `Lesson::Catalog.default`. ## Origin - Trigger: A ClassLength offense whose lesson gave a correction that contradicts the shipped split. - Improvised this session: none - Chain: Wrong correction guidance ← one-cause lesson ← DESIGN (plugins/os-sdlc/lint-lessons/metrics-class-length.yml) - Root candidate: none found; searched ClassLength, lesson, ModuleOwnsInstanceState, DuplicatedStatementSequence, skeleton, collaborator construction, metrics-class-length.yml VERDICT: CREATE - Where: plugins/os-sdlc/lint-lessons/metrics-class-length.yml - Session: f4187368-5e7e-459d-80c6-ddf7620389cd - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f4187368-5e7e-459d-80c6-ddf7620389cd.jsonl
Author
Owner

Blocked by #1321 and #1322: the lessons for the new cops need the cops to exist.

Blocked by #1321 and #1322: the lessons for the new cops need the cops to exist.
Author
Owner

Work started on branch ticket-1323 through /os-sdlc:implement.

Work started on branch ticket-1323 through /os-sdlc:implement.
Author
Owner

Resolution

Done: Rewrote lint-lessons/metrics-class-length.yml by six causes with one correction each, and added lessons for Sdlc/Dry/RepeatedRescueClause, Sdlc/Dry/RepeatedCollaboratorConstruction, Sdlc/Structural/ModuleOwnsInstanceState and Sdlc/Dry/DuplicatedStatementSequence. The last two name the #1325 false-positive shapes as cases to report.

Evidence: Commit 3a159229. New test tests/lesson/class_length_family_lessons_test.rb was red before the lesson files existed (all four keys false) and is green now. Full os-sdlc suite: 2648 runs, 0 failures, 0 errors. Rubocop on the new test: no offenses. Criterion 3 grep over the five files: no match. One design audit (opus reviewer): verdict pass; its three items were fixed (bare-rescue group, private_class_method case, assert-window skip).

Follow-ups: Occurrence on #812: implementation-open refuses category/prose-artifact tickets, so this ticket ran outside the pipeline. Suite wall time 61.3 s over the 60 s budget is already #1341. Dropped: one-word scope note (blocks, sclass) in the RescueClause signal; the lesson's class-or-module wording covers the usual case.

## Resolution **Done:** Rewrote lint-lessons/metrics-class-length.yml by six causes with one correction each, and added lessons for Sdlc/Dry/RepeatedRescueClause, Sdlc/Dry/RepeatedCollaboratorConstruction, Sdlc/Structural/ModuleOwnsInstanceState and Sdlc/Dry/DuplicatedStatementSequence. The last two name the #1325 false-positive shapes as cases to report. **Evidence:** Commit 3a159229. New test tests/lesson/class_length_family_lessons_test.rb was red before the lesson files existed (all four keys false) and is green now. Full os-sdlc suite: 2648 runs, 0 failures, 0 errors. Rubocop on the new test: no offenses. Criterion 3 grep over the five files: no match. One design audit (opus reviewer): verdict pass; its three items were fixed (bare-rescue group, private_class_method case, assert-window skip). **Follow-ups:** Occurrence on #812: implementation-open refuses category/prose-artifact tickets, so this ticket ran outside the pipeline. Suite wall time 61.3 s over the 60 s budget is already #1341. Dropped: one-word scope note (blocks, sclass) in the RescueClause signal; the lesson's class-or-module wording covers the usual case.
jared closed this issue 2026-10-25 21:33:07 +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#1323
No description provided.