Sdlc/Structural/SimulatedPolymorphism: carve out dispatch on core classes #1384
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1384
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Rewritten 2026-10-27 from docs/lint-lessons/analyses/sdlc-structural-simulated-polymorphism.md.
Sdlc/Structural/SimulatedPolymorphismflags type switches where every type is a core or gem class (for exampleis_a?(Hash)/is_a?(Array)in a recursive walk). The fix it prescribes needs a monkey-patch. The cop also missescase R when ConstA ... when ConstB, so a real switch on owned types (implementation/initiation.rb:19) goes unreported.Approach
Fix by shape, in the matcher. Use no list of core class names. Rule:
docs/lint-lessons/analyses/sdlc-structural-simulated-polymorphism.mdstep 6.Required work:
Module#const_source_locationpoints inside the project root. Report an offense only when at least one type is owned.case Rwhen itswhenclauses hold two or more constants.implementation/initiation.rbopen_error_labelso that each error class answerslabel, so the gate stays green.Acceptance criteria
Encoder, when the cop runs, then it reports no offense.case value when Hash ... when String ..., when the cop runs, then it reports no offense.case n when Prism::DefNode ... when Prism::CallNode ..., when the cop runs, then it reports no offense.is_a?(Circle)/is_a?(Square)chain with both types undefined in the lint run, when the cop runs, then it reports an offense.case error when OwnedErrorA ... when OwnedErrorB ..., when the cop runs, then it reports an offense.plugins/, when the run ends, then it reports no offenses.case error when OwnedErrorA ... when OwnedErrorB ...where both types are registered for autoload (Module#autoload, as Zeitwerk does) from files inside the project root and not yet loaded, when the cop runs, then it reports an offense (same result as when they are loaded).def self.label(error)), when the cop runs, then it reports an offense (the real siteimplementation/initiation.rbopen_error_labelis adef self.method).Owner sketch
Origin
VERDICT: CREATE
Body rewritten 2026-10-27 from the false-positive process; analysis and lint lesson merged to main (docs/lint-lessons/analyses/, plugins/os-sdlc/lint-lessons/).
Work started on branch ticket-1384 (os-sdlc implement pipeline).
Known limit, not filed (skeptic DROP: no observed failure): OwnedType counts an unresolved constant as owned, so a switch over two types from a gem the rubocop process does not load would fire. If such a false positive appears, file a ticket then.
Lens challenge (implementation 203): owned_type.rb:9 reads Gem.path in a constant inside library code (P2 break; the cop is the injection point, as unlayered_constant.rb does). Skeptic DROP as a separate ticket (depth-1): fix by passing the roots from the cop into OwnedType.among. Not done in this run because the run was complete; listed as a suggested follow-up for Jared.
Resolution
Done: SimulatedPolymorphism now reports only when at least one type is owned (unresolved, autoload path or source file outside gem/Ruby library roots, not C-defined core). Reads case R when ConstA/ConstB and def self. methods. Message names only owned types. initiation.rb open_error_label replaced by each error class answering label. Lesson YAML updated. AC7/AC8 (autoload-pending constants, def self.) added mid-run after a probe.
Evidence: Implementation 203 merge-ready; commit
1ba3ee0f. os-sdlc suite 2850 runs 0 failures (31.6s). Cop over plugins/: 0 offenses before and after (2754 files). Probe: main's initiation.rb open_error_label now fires; Hash/Array and Prism:: switches silent; OsSdlc:: autoload-pending types fire.Follow-ups: none filed: (1) Gem.path read in OwnedType constant (P2 break) - skeptic DROP, fix-in-place suggested, see comment; (2) unloaded gem constants count as owned - skeptic DROP, known limit, see comment