Design the cop-message formula so lint rules teach instead of obstruct #450

Open
opened 2026-08-20 16:49:45 +00:00 by jared · 1 comment
Owner

Context

On 2026-08-20, while building #439, rubocop's stock Metrics/ParameterLists flagged a
six-keyword-argument constructor with a threshold-only message: "Avoid parameter lists
longer than 4 parameters. [6/4]". The main-loop assistant reasoned from that message to
the wrong conclusion — it proposed configuring the cop away (CountKeywordArgs: false)
with an invented rationale about positional-argument safety, at 80% confidence. The user
corrected it: the rule is a Sandi Metz idea, and a long parameter list means the object
holds a responsibility nobody has named yet. The right fix was extracting a NodeSpec
value object (decision D8, commit 329a90a).

The repo's own Sdlc/Structural/* cops do not cause this failure. Their messages name
the offender, name the smell, and prescribe the extraction with a candidate class name.
Agents obey them without argument. Nothing enforces that shape — plugins/os-sdlc/lib/os_sdlc/cops/
currently holds 30 cop files, and only 2 (parameter_list_signals_missing_object.rb,
hand_rolled_fetch.rb) define an explicit MSG constant; skills/mine-lints/references/implement-cop.md
documents mechanics only, nothing about message content.

Full background

Read docs/handoffs/2026-08-20-lint-message-guidance.md before starting. It records what
happened, why the message caused it, what was already built, and the open questions. Do
not re-derive the mechanism findings — they are settled there.

What to do

  1. Decide the required parts of a cop message. Candidate: evidence, smell, prescription.
  2. Settle whether a prescription must suggest a class name or only may. Both NodeSpec
    (good) and PathHolder (weak, describes mechanism not concept) are mechanically
    generated today.
  3. Settle how a message should handle the case where the rule genuinely does not apply,
    so a prescription is never confidently wrong.
  4. Decide whether the convention bans naming config as a remedy.
  5. Decide whether message enrichment (computed parameter names, suggested class names)
    is worth its cost, and whether it should be a shared helper.
  6. Write the convention into plugins/os-sdlc/skills/mine-lints/references/implement-cop.md.
  7. lib/os_sdlc/cops/parameter_list_signals_missing_object.rb already carries a
    design-pointed MSG (evidence + smell + prescription), committed as ae0f188 under this
    ticket's own number — confirm it satisfies the agreed formula once settled, rather than
    treating it as an untouched placeholder.
  8. Audit the other 29 cop messages against the formula; fix the ones that only flag.

Notes

  • This settles a standing convention, so run /os-adr:find first and record an ADR.
  • One finding worth generalizing: deleting the stock cop's self.max = count block
    removed --auto-gen-config's ability to raise the limit. Removing an automated escape
    hatch proved more effective than arguing against it in prose.

Origin

  • Trigger: live session 2026-08-20 building #439; user-directed follow-up
  • Improvised this session: none
  • Chain: threshold-only lint message led to a wrong remediation proposal <- stock cop message design
  • Root candidate: this ticket
## Context On 2026-08-20, while building #439, rubocop's stock Metrics/ParameterLists flagged a six-keyword-argument constructor with a threshold-only message: "Avoid parameter lists longer than 4 parameters. [6/4]". The main-loop assistant reasoned from that message to the wrong conclusion — it proposed configuring the cop away (CountKeywordArgs: false) with an invented rationale about positional-argument safety, at 80% confidence. The user corrected it: the rule is a Sandi Metz idea, and a long parameter list means the object holds a responsibility nobody has named yet. The right fix was extracting a NodeSpec value object (decision D8, commit 329a90a). The repo's own Sdlc/Structural/* cops do not cause this failure. Their messages name the offender, name the smell, and prescribe the extraction with a candidate class name. Agents obey them without argument. Nothing enforces that shape — plugins/os-sdlc/lib/os_sdlc/cops/ currently holds 30 cop files, and only 2 (parameter_list_signals_missing_object.rb, hand_rolled_fetch.rb) define an explicit MSG constant; skills/mine-lints/references/implement-cop.md documents mechanics only, nothing about message content. ## Full background Read docs/handoffs/2026-08-20-lint-message-guidance.md before starting. It records what happened, why the message caused it, what was already built, and the open questions. Do not re-derive the mechanism findings — they are settled there. ## What to do 1. Decide the required parts of a cop message. Candidate: evidence, smell, prescription. 2. Settle whether a prescription must suggest a class name or only may. Both NodeSpec (good) and PathHolder (weak, describes mechanism not concept) are mechanically generated today. 3. Settle how a message should handle the case where the rule genuinely does not apply, so a prescription is never confidently wrong. 4. Decide whether the convention bans naming config as a remedy. 5. Decide whether message enrichment (computed parameter names, suggested class names) is worth its cost, and whether it should be a shared helper. 6. Write the convention into plugins/os-sdlc/skills/mine-lints/references/implement-cop.md. 7. lib/os_sdlc/cops/parameter_list_signals_missing_object.rb already carries a design-pointed MSG (evidence + smell + prescription), committed as ae0f188 under this ticket's own number — confirm it satisfies the agreed formula once settled, rather than treating it as an untouched placeholder. 8. Audit the other 29 cop messages against the formula; fix the ones that only flag. ## Notes - This settles a standing convention, so run /os-adr:find first and record an ADR. - One finding worth generalizing: deleting the stock cop's self.max = count block removed --auto-gen-config's ability to raise the limit. Removing an automated escape hatch proved more effective than arguing against it in prose. ## Origin - Trigger: live session 2026-08-20 building #439; user-directed follow-up - Improvised this session: none - Chain: threshold-only lint message led to a wrong remediation proposal <- stock cop message design - Root candidate: this ticket
Author
Owner

Body corrected 2026-08-23 from code-probe verification (agent a7132caa5ab303d6b, claude-sonnet-5).

Change log:

  • Corrected cop count: "all 28 cops hardcode their own message string" -> plugins/os-sdlc/lib/os_sdlc/cops/ holds 30 cop files; only 2 (parameter_list_signals_missing_object.rb, hand_rolled_fetch.rb) define an explicit MSG constant. Step 8's audit scope changed from 27 to 29 remaining cops accordingly.
  • Corrected step 7: parameter_list_signals_missing_object.rb already carries a design-pointed MSG (evidence + smell + prescription), shipped in commit ae0f188 under this ticket's own number on the day it was filed. It is not an untouched placeholder — step 7 is now "confirm it matches the agreed formula," not "replace it."

Reason: steps 1-6 and the corrected step 8 audit remain open work; nothing here closes the ticket, but the scope and step-7 status were stale relative to code already shipped.

Body corrected 2026-08-23 from code-probe verification (agent a7132caa5ab303d6b, claude-sonnet-5). Change log: - Corrected cop count: "all 28 cops hardcode their own message string" -> plugins/os-sdlc/lib/os_sdlc/cops/ holds 30 cop files; only 2 (parameter_list_signals_missing_object.rb, hand_rolled_fetch.rb) define an explicit MSG constant. Step 8's audit scope changed from 27 to 29 remaining cops accordingly. - Corrected step 7: parameter_list_signals_missing_object.rb already carries a design-pointed MSG (evidence + smell + prescription), shipped in commit ae0f188 under this ticket's own number on the day it was filed. It is not an untouched placeholder — step 7 is now "confirm it matches the agreed formula," not "replace it." Reason: steps 1-6 and the corrected step 8 audit remain open work; nothing here closes the ticket, but the scope and step-7 status were stale relative to code already shipped.
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#450
No description provided.