UnusedPrivateMethod cop flags private hook methods that only a superclass calls #1091
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#1091
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
Sdlc/Structural/UnusedPrivateMethodflags a private method of a superclass when only its subclasses call it. Example:plugin_keyis private in cc-architectOperations::Base(plugins/cc-architect/scripts/plugin_config/operations/base.rb:23), anddisable.rb:15,enable.rb:12andaudit.rb:15(all< Base) call it. The cop reports it. This is the last Sdlc offense that blocks the cc-architect worklist, apart from RequireRelativeDensity (#1462).The other direction already works: a private hook in a subclass that the superclass calls is not flagged (
tests/cops/unused_private_method_test.rb:164,:168). The original repro (CommandControllerand thecrc_*/command.rbhooks) no longer exists.The lookup also needs a root.
SuperclassCallsfinds the root with[%r{\A.*/lib(?=/|\z)}]. A file with nolib/ancestor, such as cc-architectscripts/, gets no root, so no related file is read.Observed
Reproduce
Approach
Extend the existing related-class lookup in
cops/structural/unused_private_method/so it answers in both directions.UnusedDefs#unused_private_defskeeps its one@superclass_calls.call?(def_node)reject.parent_classshort name equals the short name of the def's owner class is a subclass. ItsCallSitesnames count as callers of the superclass's private methods. UseCallSitesunchanged, the same as the superclass direction.< <OwnerShortName>. Do not parse every file under the root.libancestor, as today. With nolibancestor, the root is the file's own directory, and the glob is not recursive (<dir>/*.rb). Both directions use the same root.Name matching stays by short name, as in the superclass direction today. Qualified-constant matching is out of scope.
Required work:
its superclass or its subclassesinplugins/os-sdlc/lib/os_sdlc/cops/structural/unused_private_method.rbreturn false unless @lib_rootinplugins/os-sdlc/lib/os_sdlc/cops/structural/unused_private_method/superclass_calls.rbAcceptance criteria
lib/with a private method, and a subclass file in the same root that calls that method, when the cop runs on the superclass file, then it reports nothing.lib/ancestor and a subclass in the same directory that calls the superclass's private method, when the cop runs on the superclass file, then it reports nothing.lib/ancestor and a subclass in a subdirectory of it that calls the superclass's private method, when the cop runs on the superclass file, then it reports the method (the fallback root is not recursive).plugins/cc-architect/scripts/plugin_config/operations/base.rb, when the cop runs, thenplugin_keyis not reported.Owner sketch
Retired tests
plugins/os-sdlc/tests/cops/unused_private_method_test.rb#test_flags_private_method_never_called_anywhere_in_file
Non-goals
Basecollisions).sendcalls (#1091 comment 1, item 2).Origin
/os-backlog:ticket-decompose(session 6ee3ad76)plugin_keyflagged in cc-architect ← cop finds only superclass callers, and only under alib/root ← DESIGN (plugins/os-sdlc/lib/os_sdlc/cops/structural/unused_private_method/superclass_calls.rb)VERDICT: CREATE
REASON: Existing design defect in the cop giving a false positive on template-method hooks; #1090's protected is only a workaround.
VERDICT: CREATE (2026-10-28 re-check) — the fault is live at cc-architect operations/base.rb:23; no other open ticket has this mechanism.
Occurrence: Sdlc/Structural/UnusedPrivateMethod: count subclass and string-named calls across the project
Seen: plugins/os-sdlc/lib/os_sdlc/cops/unused_private_method.rb (found by the #1325 zero-false-positive audit)
Run: Session eb98cdcd-6783-46a0-b7df-cf18a8fe2a56
Adds: (1) parent lookup searches only under the nearest lib dir, so hooks called by a parent outside lib/ fire: operations/validate.rb:45,49 via schema_checks.rb:19,28; test/test_list.rb:36 via test/support/plugin_config_test_case.rb:96. (2) send(var) over a string list: plugins/os-adr/lib/adr/record/command_line.rb:8,30-34; fix: count str names as call sites. (3) Parent private helpers called by subclasses: backlog/cli/commands.rb:14-36, cc-architect operations/base.rb:23,62,71.
Occurrence: UnusedPrivateMethod flags a base-class private helper that only subclasses call
Seen: plugins/cc-architect/scripts/plugin_config/operations/base.rb:27 Base#plugin_key (callers: operations/audit.rb, disable.rb, enable.rb)
Run: fix-lints on target cc-architect, 2026-10-27, session 9f415790-a6bd-4850-8d70-3dd45004ce2b (main
f138ee07)Adds: a second plugin with the superclass-helper direction; this is the last Sdlc offense besides RequireRelativeDensity that keeps the cc-architect worklist from clear. Left in place, no disable.
Work started 2026-10-28 via /os-sdlc:implement on branch ticket-1091 (session 6ee3ad76). Body re-scoped by /os-backlog:ticket-decompose first: original repro is gone; scope is the subclass-calls-superclass-private direction plus a non-recursive fallback root.
Resolution
Done: UnusedPrivateMethod now counts receiverless calls in subclasses as callers of a superclass private method (SubclassDefinition, text pre-filter before parse); with no lib ancestor the lookup root is the file's own directory, non-recursive; message names its superclass or its subclasses. Implementation 212, commit
bfe8955fon ticket-1091.Evidence: Pipeline: 10 dispatches, all gates pass, suite-check green (44.9 s suite), contract-auditor pass. rubocop --only Sdlc/Structural/UnusedPrivateMethod on plugins/cc-architect/scripts/plugin_config/operations/base.rb: no offenses (was base.rb:23 plugin_key).
Follow-ups: none; qualified-constant matching and string-named send calls are recorded as Non-goals in the body; the cc-architect RequireRelativeDensity offense is #1462.