Close the A18 gaps in the EffectInModel cop #1507

Open
opened 2026-11-02 16:01:02 +00:00 by jared · 0 comments
Owner

Problem

The Sdlc/Structural/EffectInModel cop finds a value only through the CRC ledger role, not by syntax. The cop is disabled. Its effect list omits most of the outside world that principle 9 names. A18 says that parsing from a string and to_<format> stay in the value.

Reproduce

n/a: design ticket. The probe in Approach shows the gap.

Approach

Gap 1, role lookup. The cop reads the role from the ledger in role at plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb:33-38. A Data.define or Struct.new value that the ledger does not list as value or model is silent. A probe with Data.define and File.read under role=adapter did not fire.

Gap 2, disabled. plugins/os-sdlc/.rubocop.yml:366-367 sets Sdlc/Structural/EffectInModel: to Enabled: false. The cop needs LedgerPath.

Gap 3, effect list. plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model/effect_call.rb:11 is DEFAULT_CONSTANTS = %w[File Dir IO YAML JSON Open3 Process Kernel].freeze. The list omits ENV, Net::HTTP, $stdout and puts, Time.now, and sleep. Principle 9 at plugins/os-sdlc/skills/lens-principles/SKILL.md:63 names files, directories, git, subprocesses, ENV, the network, the terminal, and the clock as the outside world.

Related: #1351 edits the same effect list (effect_in_model/effect_call.rb) for a different fault; sequence the two to avoid a merge conflict.

Gap 4, tests. plugins/os-sdlc/tests/cops/effect_in_model_test.rb covers only plain classes.

A18 NOT: parsing from a string and to_<format> stay in the value. The cop must not flag them.

Cop criteria. All criteria are required.

  • The cop scopes to the whole issue.
  • Each known case is a must-fire fixture.
  • The cop catches unseen cases.
  • The cop has zero false positives. Each false hit is made exact by value. The cop has no exclude list.

Required work:

  1. Detect a Data.define or Struct.new value by syntax — check: present Data in plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb
  2. Extend the effect list with the omitted effects — check: present Net in plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model/effect_call.rb
  3. Add must-fire and must-not-fire fixtures for value classes — check: present Data.define in plugins/os-sdlc/tests/cops/effect_in_model_test.rb

Orchestrator work: Rule on whether to enable the cop in plugins/os-sdlc/.rubocop.yml.

Acceptance criteria

  1. Given a Data.define value that calls File.read and has role adapter in the ledger, when the cop runs, then it reports an offense.
  2. Given a Struct.new value that calls File.read, when the cop runs, then it reports an offense.
  3. Given a value that calls ENV.fetch, Net::HTTP, puts, Time.now, or sleep, when the cop runs, then it reports an offense for each call.
  4. Given a value that parses from a string or defines to_<format>, when the cop runs, then it reports no offense.
  5. Given the cop with the repository configuration, when rubocop runs, then the cop is enabled or the ticket records the ruling that keeps it disabled.

Owner sketch

class RuboCop::Cop::Sdlc::Structural::EffectInModel < RecordingCop
  def role
  end
end

Origin

  • Session: f1340f6a-3a9c-4161-984d-16e6863438c7
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f1340f6a-3a9c-4161-984d-16e6863438c7.jsonl
  • Where: plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb
  • Chain: EffectInModel misses A18 cases ← DESIGN (plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb)
  • Root candidate: none found; searched EffectInModel, effect_in_model, effect_call, A18, principle 9
    VERDICT: CREATE
## Problem The `Sdlc/Structural/EffectInModel` cop finds a value only through the CRC ledger role, not by syntax. The cop is disabled. Its effect list omits most of the outside world that principle 9 names. A18 says that parsing from a string and `to_<format>` stay in the value. ## Reproduce n/a: design ticket. The probe in Approach shows the gap. ## Approach Gap 1, role lookup. The cop reads the role from the ledger in `role` at `plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb:33-38`. A `Data.define` or `Struct.new` value that the ledger does not list as value or model is silent. A probe with `Data.define` and `File.read` under `role=adapter` did not fire. Gap 2, disabled. `plugins/os-sdlc/.rubocop.yml:366-367` sets `Sdlc/Structural/EffectInModel:` to `Enabled: false`. The cop needs `LedgerPath`. Gap 3, effect list. `plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model/effect_call.rb:11` is `DEFAULT_CONSTANTS = %w[File Dir IO YAML JSON Open3 Process Kernel].freeze`. The list omits ENV, `Net::HTTP`, `$stdout` and `puts`, `Time.now`, and `sleep`. Principle 9 at `plugins/os-sdlc/skills/lens-principles/SKILL.md:63` names files, directories, git, subprocesses, ENV, the network, the terminal, and the clock as the outside world. Related: #1351 edits the same effect list (`effect_in_model/effect_call.rb`) for a different fault; sequence the two to avoid a merge conflict. Gap 4, tests. `plugins/os-sdlc/tests/cops/effect_in_model_test.rb` covers only plain classes. A18 NOT: parsing from a string and `to_<format>` stay in the value. The cop must not flag them. Cop criteria. All criteria are required. - The cop scopes to the whole issue. - Each known case is a must-fire fixture. - The cop catches unseen cases. - The cop has zero false positives. Each false hit is made exact by value. The cop has no exclude list. Required work: 1. Detect a `Data.define` or `Struct.new` value by syntax — check: present `Data` in `plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb` 2. Extend the effect list with the omitted effects — check: present `Net` in `plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model/effect_call.rb` 3. Add must-fire and must-not-fire fixtures for value classes — check: present `Data.define` in `plugins/os-sdlc/tests/cops/effect_in_model_test.rb` Orchestrator work: Rule on whether to enable the cop in `plugins/os-sdlc/.rubocop.yml`. ## Acceptance criteria 1. Given a `Data.define` value that calls `File.read` and has role adapter in the ledger, when the cop runs, then it reports an offense. 2. Given a `Struct.new` value that calls `File.read`, when the cop runs, then it reports an offense. 3. Given a value that calls `ENV.fetch`, `Net::HTTP`, `puts`, `Time.now`, or `sleep`, when the cop runs, then it reports an offense for each call. 4. Given a value that parses from a string or defines `to_<format>`, when the cop runs, then it reports no offense. 5. Given the cop with the repository configuration, when rubocop runs, then the cop is enabled or the ticket records the ruling that keeps it disabled. ## Owner sketch ```ruby class RuboCop::Cop::Sdlc::Structural::EffectInModel < RecordingCop def role end end ``` ## Origin - Session: f1340f6a-3a9c-4161-984d-16e6863438c7 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f1340f6a-3a9c-4161-984d-16e6863438c7.jsonl - Where: plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb - Chain: EffectInModel misses A18 cases ← DESIGN (plugins/os-sdlc/lib/os_sdlc/cops/structural/effect_in_model.rb) - Root candidate: none found; searched EffectInModel, effect_in_model, effect_call, A18, principle 9 VERDICT: CREATE
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#1507
No description provided.