system-model: Edge asks PrecedenceInEdge whether a class holds a domain rule #1517
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#1517
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
ClassFacts#leaks_io?decides whether a class holds a domain rule beside I/O.It uses
ClassFacts::IoReach, a call-reach heuristic over self-sends.Commit
1814bfef(2026-11-01, Run 4) added it with no ticket.Its one consumer is
Catalog::Edge(!pure? && !value? && !leaks_io?), which feeds Domain, Repository and Hex.The cop
PrecedenceInEdgeanswers the same question with better precision.Jared ruled option D: Edge asks
PrecedenceInEdgewhether the class holds a domain rule.Observed
Measured over os-sdlc, os-backlog and os-me, and scored against
docs/system-design-format/proof/2026-11-02-detector-a19/findings.tsv:The 6 TRUE classes under D:
LayerLedger::Ledger,Crc::ProgressTable,Project::Config::Document,Project::ConfigCommand,Tracker::IssueSource::TrackerLookup,bin/wakeup-poll.The in-process cop run cost about +11 s on a 2 s extractor run (os-sdlc).
Reproduce
n/a: design change. The measurement script is described under Approach.
Approach
Mechanism (prototype, scratch only, not permanent): run the cop in-process, map each offense to the class whose def span covers it.
CopSites(plugins/os-sdlc/lib/os_sdlc/detector/cop_sites.rb) gives the same hits as the rubocop CLI.The scratch prototypes were
variants.rbandinproc.rb(session scratchpad); this body carries the essential part.Design:
ClassFacts#leaks_io?becomes true whenPrecedenceInEdgereports an offense inside one of the class's def spans, or in the script file for a script.ExtractorandScripts(2-3 lines).class_facts.rbgets one field and a one-lineleaks_io?.[PrecedenceInEdge]). Cops O and E (#1510, #1511, in progress in another session) may join later by a one-line change. Do not add them here.system_model_pattern_match_hand_counts_test.rb) have no defs. The implementer states in the PR how those fixtures get cop sites (a real fixture file on disk, or a stub offense list) and why.Delete what only
IoReachuses. Verify each has no other caller before deleting (grep at ticket time found none):plugins/os-sdlc/lib/os_sdlc/system_model/class_facts/io_reach.rbDef#io_after?andsend_lines(class_facts/def.rb)Decision#sends(class_facts/decision.rb) andBranching#sends(extractor/branching.rb)send_lines:argument andSelfSends#first_lines(extractor/frame.rb:34,extractor/self_sends.rb:15)Required work:
IoReachinplugins/os-sdlc/lib/os_sdlc/**/*.rbsend_linesinplugins/os-sdlc/lib/os_sdlc/**/*.rbfirst_linesinplugins/os-sdlc/lib/os_sdlc/**/*.rbPrecedenceInEdgeinplugins/os-sdlc/lib/os_sdlc/system_model/**/*.rbleaks_iotests inplugins/os-sdlc/tests/system_model_class_facts_edge_test.rb, 6 insystem_model_catalog_test.rb, 7 insystem_model_pattern_match_hand_counts_test.rb(counts from Jared, re-count at start). Add 1-2 tests with a fixture file.Orchestrator work: regenerate
plugins/os-sdlc/design/generated; measure the os-sdlc system-model run time before and after; re-measure the run-4 agreement figure (17/18 at 2026-11-01) and record it on this ticket.Rejected: keep
IoReachand tune it — 22 FALSE against findings.tsv; the cop already encodes the rule.Acceptance criteria
bin/system-modelregeneration, when it finishes, thenplugins/os-sdlc/design/generatedis current.leaks_io?true and is not an Edge.Owner sketch
ClassFactsholds the flag;leaks_io?returns it.Non-goals
Cops O and E (#1510, #1511). Class.new constants (#1513).
Blocks
Blocks: #1513 (both change the system_model extractor wiring; implement this first).
Origin
ClassFacts#leaks_io?; Jared ruled option D after the variant measurement.leaks_io?usesIoReachcall-reach heuristic ← DESIGN (plugins/os-sdlc/lib/os_sdlc/system_model/class_facts.rb:24, commit1814bfef, no ticket)VERDICT: CREATE
Work started on branch
ticket-1517through /os-sdlc:implement.Resolution
Done: Catalog::Edge asks PrecedenceInEdge (run in-process by Extractor::CopOffenses) whether a class holds a domain rule; ClassFacts::IoReach deleted. Measured 2026-11-03 (snapshot, not target): Edge 179, Domain 492, Repository 136, Hex 10, 6 TRUE / 0 FALSE vs findings.tsv. AC1 said Domain 487: the tree gained six DomainOperandInEdge classes and lost IoReach since the prototype; Jared accepted that counts move as code evolves. AC4: extractor 1.5 s -> 2.8 s on os-sdlc (+1.3 s; estimate was +11 s). AC5: run-4 agreement 13/18 (was 17/18); the five disagreements are cop misses (LabelFile, RouteComparison, Scorer, ReplyShape::EvalRun, Census::SourceRow), no false hits.
Evidence: main
24d18dfe(commit5ff021b3). Implementation 223 escalated on a test defect and was hand-finished, then abandoned. Suite 3305 runs 0 failures on the merged branch; rubocop 0 offenses; POODR design audit findings (cop-only leaks_io?, decides? reverted, in-memory catalog fixture) fixed; design/generated rebuilt with drift 0.Follow-ups: #1526 add DomainOperandInEdge and DomainEffectInEdge to the Edge cop list (held for Jared's map review).