Add cop LibraryOutsideOwner for an external library named outside its owner namespace #1744
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#1744
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
Agents that do the same job independently call the same external library. Each agent writes its own copy of the job, and the copies are spelled differently, so a clone detector does not find them. The library name is the one sign that the copies share. No cop reports a file that names a library outside the namespace that owns it, so each new copy passes the gate.
Observed
Prism, and 26 test files parse their input with it. All 84 files were read and are true offenses. The final rule had 0 false hits.Prism.parseandPrism.parse_filehave 20 call sites in 19 files. The copies do not agree: one copy handles a leading::and another copy does not.docs/decisions/rulings.tsv) names one owner for this work:OsSdlc::CodeShape.Sdlc/Dry/OwnedMessagecannot express the rule. It matches a send by its message name. It cannot match a constant, a superclass, a type check, a pattern, or arequire.File,Set,JSON). Thus the cop reads a configured map of owner to libraries. A library that is not in the map is not checked.docs/research/lint-lessons/2026-11-12-domain-boundary-cops/smell-3-library-owner.md.Reproduce
n/a: feature ticket.
Approach
Add the cop
Sdlc/Dry/LibraryOutsideOwnerand its lint lesson. Model the configuration and the "inside the owner" test onSdlc/Dry/OwnedMessage(lib/os_sdlc/cops/dry/owned_message.rb:9,Ownersread at:32-36,inside?at:38-42).Configuration:
The value rule:
Consequences of the rule, with no special case in the code:
File,Set,JSON) is not checked.RuboCop::Cop) is out of scope. The cop is silent there.Constraints:
Ownersmap changes only when an ownership ruling changes. A new caller never adds an entry to clear a report. Use noExcludelist.Offense message:
Lesson file
lint-lessons/sdlc-dry-library-outside-owner.yml(fields as inlib/os_sdlc/lesson.rb:4; modellint-lessons/sdlc-dry-owned-message.yml):Enable the cop in the sweep tier only (
.rubocop-sweep.yml, with noExclude). The tree has 84 true offenses, andOsSdlc::CodeShapedoes not hold the moved code yet, so the cop does not go in.rubocop.ymlin this ticket. The sweep count is the measure of the migration toOsSdlc::CodeShape.Required work:
Sdlc/Dry/LibraryOutsideOwnerinplugins/os-sdlc/lint-lessons/sdlc-dry-library-outside-owner.ymlOsSdlc::CodeShapeinplugins/os-sdlc/.rubocop-sweep.ymlOrchestrator work: none.
Rejected: new
Ownersentries inSdlc/Dry/OwnedMessage— it catches 19 of 58 files, and it is a gate cop, so the entries would fail each gate run now. Rejected: today's concepts as configured owners — that is an allowlist with a different name. Rejected: a rule with no map — core classes such asFileandSetwould be false hits.Acceptance criteria
Ownersthat mapsLibtoRoot::Owner, and a file whose class isRoot::Other::Thingand namesLib::Node, when the cop runs, then it reports one offense on the reference, and the message namesLib::Node,Lib, andRoot::Owner.Owners, and a file whose class isRoot::Owner::Thingand namesLib::Node, when the cop runs, then it reports no offense.Owners, and a file whose class isRoot::Other::Thingand hasrequire "lib", when the cop runs, then it reports one offense on therequire.Owners, and a file whose class isRoot::Other::Thing, withLib::Visitoras its superclass,is_a?(Lib::Node), anin Lib::Nodepattern, andrescue Lib::Error, when the cop runs, then it reports one offense for each of the four references.Owners, and a file whose class isRoot::Other::Thingand names::Lib::Node, when the cop runs, then it reports one offense.Owners, and a file whose class isRoot::Other::Thingand namesOtherLib::Node, when the cop runs, then it reports no offense.Owners, and a file whose class isRoot::Other::Thingand contains the nameLibonly in a string, a symbol, and a heredoc, when the cop runs, then it reports no offense.Owners, and a file whose class isElsewhere::Thingand namesLib::Node, when the cop runs, then it reports no offense.Owners, and a file that definesmodule Root; module Other; module Lib; class Node; end; end; end; endand namesLib::Nodeinside that body, when the cop runs, then it reports no offense.Ownersin the configuration, when the cop runs on a file that namesLib::Node, then it reports no offense.Owners, and a file that hasrequire "lib"at file level, beforemodule Root; module Other; class Thing; end; end; end, and no other reference toLib, when the cop runs, then it reports one offense on therequire.Owners, and a file that hasrequire "lib"at file level, beforemodule Elsewhere; class Thing; end; end, when the cop runs, then it reports no offense.Owners, and a file that hasrequire "lib"at file level, beforemodule Root; module Owner; class Thing; end; end; end, when the cop runs, then it reports no offense.Owner sketch
Non-goals
OsSdlc::CodeShape. That work belongs to #1718 and its child tickets..rubocop.yml.YAMLandOpen3each need their own ruling.Origin
docs/decisions/rulings.tsvnames the owner; no cop reads it)VERDICT: CREATE (ticket-skeptic, 2026-11-12, backlog search only; Jared asked for this ticket): no open ticket adds a cop for a library named outside its owner; the #1718 children migrate callers and add no cop.
Work started on branch ticket-1744 through the os-sdlc implement pipeline.
Check of the cop on the real tree (branch ticket-1744): 224 offenses in 80 files. The cop missed 4 library files that have only a file-level require of the library (crc/census/requirers.rb, detector/constructor_graph.rb, system_model/extractor/body.rb, system_model/extractor/process_exit.rb). ticket-skeptic verdict on a follow-up ticket: DROP, Better-seam test: this is a defect in the #1744 deliverable; fix it inside #1744. Acceptance criteria 11 to 13 and one scope line are added to the body. A second implementation run on the same branch builds them.
Resolution
Done: Cop Sdlc/Dry/LibraryOutsideOwner, its lint lesson (lint-lessons/sdlc-dry-library-outside-owner.yml), and its sweep configuration with the owner map OsSdlc::CodeShape: [Prism]. The cop is in the sweep tier only. Acceptance criteria 1 to 13 are built in two runs (implementations 287 and 288).
Evidence: Branch ticket-1744, commits
16558bafeand the run 288 commit. Both runs ended implementation_complete with suite and survivor gates green. Check on the real tree: the sweep reports 84 files (58 lib, 26 test), the same set as the smell analysis probe; 0 files under lib/os_sdlc/cops; the gate configuration (.rubocop.yml) reports no offense.Follow-ups: 1. File-level require miss: ticket-skeptic verdict DROP (Better-seam test); built inside #1744 as acceptance criteria 11 to 13. 2. Fifth copy of the enclosing-namespace rule (contract-auditor LENS A12): ticket-skeptic verdict OCCURRENCE 1734; recorded as a comment on #1734. 3. The move of the cop to .rubocop.yml after the sweep count is 0: dropped as a ticket now; it depends on the #1718 migration and is listed in the session handoff.