Split Sdlc/Structural/InwardDependency into one cop per offense kind #1153

Closed
opened 2026-10-19 11:58:31 +00:00 by jared · 2 comments
Owner

Problem

Sdlc/Structural/InwardDependency reports two different offense kinds under one cop name. on_const reports an inward edge (%{constant} (layer %{outer}) is referenced from layer %{inner}. ...). on_new_investigation reports an unmapped ledger file (%{constant} has no ledger row with a layer role; add it to %{ledger}). One is design debt in the code. The other is a gap in the CRC ledger. A reader cannot count, enable, or route one kind without the other, and the cop name describes only the first kind.

Reproduce

n/a: design ticket; both kinds are in plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb (MESSAGE and UNMAPPED_MESSAGE).

Approach

Sdlc/Structural/InwardDependency keeps the inward-edge kind only. A new cop Sdlc/Structural/UnlayeredConstant reports the unmapped-ledger-file kind with the same message. Both cops read the same LedgerPath parameter and the same layer map; the shared ledger reading (ROLE_LAYER, layer_map, ledger_path, Ledger, LayerMap) moves to one owner that both cops use, so neither cop depends on the other. OsSdlc::CollaboratorPass::InwardEdges reads the layer map from that owner.

Required work:

  1. Add the Sdlc/Structural/UnlayeredConstant cop with the unmapped message — check: present UnlayeredConstant in plugins/os-sdlc/lib/os_sdlc/cops/unlayered_constant.rb
  2. Remove the unmapped-ledger offense from InwardDependency — check: absent UNMAPPED_MESSAGE in plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb
  3. Enable both cops, each with LedgerPath, in the sweep tier — check: present Sdlc/Structural/UnlayeredConstant in plugins/os-sdlc/.rubocop-sweep.yml
  4. Add a lint lesson for the new cop, next to lint-lessons/sdlc-structural-inward-dependency.yml — check: present Sdlc/Structural/UnlayeredConstant in plugins/os-sdlc/lint-lessons/*.yml
  5. Name both cops where skill text names the sweep cops or the inward cop's unmapped offense (skills/fix-lints/SKILL.md, skills/crc-refactor/references/*.md) — check: present UnlayeredConstant in plugins/os-sdlc/skills/fix-lints/SKILL.md

Rejected: keep one cop with a per-kind config switch — two kinds under one name is the problem.

Acceptance criteria

  1. Given a lib file whose constant has no ledger row with a layer role, when the sweep runs, then Sdlc/Structural/UnlayeredConstant reports it with the unmapped message and Sdlc/Structural/InwardDependency reports nothing for it.
  2. Given a constant of an outer layer referenced from an inner layer, when the sweep runs, then Sdlc/Structural/InwardDependency reports the edge and Sdlc/Structural/UnlayeredConstant reports nothing for it.
  3. Given the os-sdlc plugin on this branch, when bin/lint-sweep runs from plugins/os-sdlc/, then the InwardDependency offense set equals the set before the split.
  4. Given bin/collaborator-pass measure, when it reads inward edges, then it reports the same edges as before the split.

Owner sketch

module RuboCop::Cop::Sdlc::Structural
  class LayerLedger            # ROLE_LAYER, .layer_map(path), #layers, ledger path resolution; Ledger, LayerMap
  class InwardDependency < Base   # on_const: inward edges only
  class UnlayeredConstant < Base  # on_new_investigation: unmapped ledger file only
end
OsSdlc::CollaboratorPass::InwardEdges  # reads LayerLedger.layer_map

Retired tests

plugins/os-sdlc/tests/cops/inward_dependency_test.rb#test_ledger_lacking_own_constant_reports_unmapped_offense_on_first_constant

Origin

  • Trigger: CRC refactor run 2 close-out review, docs/crc/os-sdlc/HANDOFF.md Review item 3 (Q32); requested by the user on 2026-10-19
  • Improvised this session: none
  • Chain: two offense kinds under one cop name ← one cop class owns both investigations ← DESIGN: plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb
  • Root candidate: none found; searched InwardDependency, Sdlc/Structural, one cop per offense, split cop, LedgerPath, ledger.tsv
  • Where: plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb
  • Session: 22b441ac-a301-4637-8de9-a9e0f0970fae
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/22b441ac-a301-4637-8de9-a9e0f0970fae.jsonl

VERDICT: CREATE (backlog search only; user-requested 2026-10-19)

## Problem `Sdlc/Structural/InwardDependency` reports two different offense kinds under one cop name. `on_const` reports an inward edge (`%{constant} (layer %{outer}) is referenced from layer %{inner}. ...`). `on_new_investigation` reports an unmapped ledger file (`%{constant} has no ledger row with a layer role; add it to %{ledger}`). One is design debt in the code. The other is a gap in the CRC ledger. A reader cannot count, enable, or route one kind without the other, and the cop name describes only the first kind. ## Reproduce n/a: design ticket; both kinds are in plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb (`MESSAGE` and `UNMAPPED_MESSAGE`). ## Approach `Sdlc/Structural/InwardDependency` keeps the inward-edge kind only. A new cop `Sdlc/Structural/UnlayeredConstant` reports the unmapped-ledger-file kind with the same message. Both cops read the same `LedgerPath` parameter and the same layer map; the shared ledger reading (`ROLE_LAYER`, `layer_map`, `ledger_path`, `Ledger`, `LayerMap`) moves to one owner that both cops use, so neither cop depends on the other. `OsSdlc::CollaboratorPass::InwardEdges` reads the layer map from that owner. Required work: 1. Add the `Sdlc/Structural/UnlayeredConstant` cop with the unmapped message — check: present `UnlayeredConstant` in `plugins/os-sdlc/lib/os_sdlc/cops/unlayered_constant.rb` 2. Remove the unmapped-ledger offense from `InwardDependency` — check: absent `UNMAPPED_MESSAGE` in `plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb` 3. Enable both cops, each with `LedgerPath`, in the sweep tier — check: present `Sdlc/Structural/UnlayeredConstant` in `plugins/os-sdlc/.rubocop-sweep.yml` 4. Add a lint lesson for the new cop, next to `lint-lessons/sdlc-structural-inward-dependency.yml` — check: present `Sdlc/Structural/UnlayeredConstant` in `plugins/os-sdlc/lint-lessons/*.yml` 5. Name both cops where skill text names the sweep cops or the inward cop's unmapped offense (`skills/fix-lints/SKILL.md`, `skills/crc-refactor/references/*.md`) — check: present `UnlayeredConstant` in `plugins/os-sdlc/skills/fix-lints/SKILL.md` Rejected: keep one cop with a per-kind config switch — two kinds under one name is the problem. ## Acceptance criteria 1. Given a lib file whose constant has no ledger row with a layer role, when the sweep runs, then `Sdlc/Structural/UnlayeredConstant` reports it with the unmapped message and `Sdlc/Structural/InwardDependency` reports nothing for it. 2. Given a constant of an outer layer referenced from an inner layer, when the sweep runs, then `Sdlc/Structural/InwardDependency` reports the edge and `Sdlc/Structural/UnlayeredConstant` reports nothing for it. 3. Given the os-sdlc plugin on this branch, when `bin/lint-sweep` runs from plugins/os-sdlc/, then the `InwardDependency` offense set equals the set before the split. 4. Given `bin/collaborator-pass measure`, when it reads inward edges, then it reports the same edges as before the split. ## Owner sketch ```ruby module RuboCop::Cop::Sdlc::Structural class LayerLedger # ROLE_LAYER, .layer_map(path), #layers, ledger path resolution; Ledger, LayerMap class InwardDependency < Base # on_const: inward edges only class UnlayeredConstant < Base # on_new_investigation: unmapped ledger file only end OsSdlc::CollaboratorPass::InwardEdges # reads LayerLedger.layer_map ``` ## Retired tests plugins/os-sdlc/tests/cops/inward_dependency_test.rb#test_ledger_lacking_own_constant_reports_unmapped_offense_on_first_constant ## Origin - Trigger: CRC refactor run 2 close-out review, docs/crc/os-sdlc/HANDOFF.md Review item 3 (Q32); requested by the user on 2026-10-19 - Improvised this session: none - Chain: two offense kinds under one cop name ← one cop class owns both investigations ← DESIGN: plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb - Root candidate: none found; searched InwardDependency, Sdlc/Structural, one cop per offense, split cop, LedgerPath, ledger.tsv - Where: plugins/os-sdlc/lib/os_sdlc/cops/inward_dependency.rb - Session: 22b441ac-a301-4637-8de9-a9e0f0970fae - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/22b441ac-a301-4637-8de9-a9e0f0970fae.jsonl VERDICT: CREATE (backlog search only; user-requested 2026-10-19)
Author
Owner

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

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

Resolution

Done: Sdlc/Structural/InwardDependency now reports inward edges only; new cop Sdlc/Structural/UnlayeredConstant reports a lib constant with no ledger row with a layer role. Shared ledger reading moved to RuboCop::Cop::Sdlc::Structural::LayerLedger (Ledger, LayerMap), used by both cops and by CollaboratorPass::InwardEdges. Both cops in .rubocop-sweep.yml with LedgerPath; lint lesson sdlc-structural-unlayered-constant.yml; fix-lints and crc-refactor skill text name both cops.

Evidence: Merged to main (3f9e2dd5; implementation 75 commit plus d3e0d4d3 for the moved files' deletions). Suite 2115 runs, 0 failures. bin/lint-sweep from plugins/os-sdlc/ and repo root: InwardDependency 175 offenses (124 unique edges) each, identical, equal to the pre-split set on the same main (the 176->175 drop is FlayCheck deleted by #1141); UnlayeredConstant 0; 0 crash lines from either cop.

Follow-ups: Runner commit omitted the moved files' deletions: occurrence posted on #916 (skeptic OCCURRENCE). No other follow-ups.

## Resolution **Done:** Sdlc/Structural/InwardDependency now reports inward edges only; new cop Sdlc/Structural/UnlayeredConstant reports a lib constant with no ledger row with a layer role. Shared ledger reading moved to RuboCop::Cop::Sdlc::Structural::LayerLedger (Ledger, LayerMap), used by both cops and by CollaboratorPass::InwardEdges. Both cops in .rubocop-sweep.yml with LedgerPath; lint lesson sdlc-structural-unlayered-constant.yml; fix-lints and crc-refactor skill text name both cops. **Evidence:** Merged to main (3f9e2dd5; implementation 75 commit plus d3e0d4d3 for the moved files' deletions). Suite 2115 runs, 0 failures. bin/lint-sweep from plugins/os-sdlc/ and repo root: InwardDependency 175 offenses (124 unique edges) each, identical, equal to the pre-split set on the same main (the 176->175 drop is FlayCheck deleted by #1141); UnlayeredConstant 0; 0 crash lines from either cop. **Follow-ups:** Runner commit omitted the moved files' deletions: occurrence posted on #916 (skeptic OCCURRENCE). No other follow-ups.
jared closed this issue 2026-10-19 12:34:31 +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#1153
No description provided.