Type parameter values and group name collisions by class and namespace home #1118
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#1118
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
The
name_collisiondetector finds the wrong collisions. It splits the uses of a parameter name by call shape (pathuses versusnodeuses), not by the class of the value. In run 20261018T172139Z it reportsnode(OsSdlc::Map::NodeandPrism::Node, two separate domains), and it does not reportrun. Therunname is a real collision: sixOsSdlc::Crc::*::Runsites receive aHashthatOsSdlc::Boot::Inputsbuilds from argv, and twelveOsSdlc::CollaboratorPasssites receive anOsSdlc::CollaboratorPass::RunDirectory. The prescriber saw this only by accident, in the rationale ofrecipes/collaborator_pass.ymlof that run.Approach
Type the values at measure time and group uses by type and home.
1. Value types (
ParameterTypes, new). At measure time,CollaboratorPasshas the constructor edges with argument text (ConstructorEdge#args,#value_of).ParameterTypesgives a type to a parameternameof a site from the argument text of each edge into that site. The rules, in this order:X.new(...)orX.for(...): the type isX. ResolveXlexically from the caller namespace to a measured constant, the same wayConstructorGraph::Visitor#resolvedoes, soFactsandBrief::Factsbecome one constant. When no measured constant matches, the type is the constant as written (for examplePrism::Node). The value is born at the edge line.{...}: the type isHash, born at the edge line.@x: the caller assigns@x = <rule 1 or 2>or@x ||= <rule 1 or 2>. Barex: the caller assigns the localxthat way, or defines a methodxwhose last expression is such a value (a modifierifand@x ||=are allowed). The value is born at that assignment line. A barexthat is a parameter of the caller'sinitializeuses rule 4, not this rule.xor@xthat is the caller's own constructor parameterx(or its alias@x = x) gives the callee every type the caller has forx, with the caller's birth. Repeat until no type changes.OsSdlc::Boot::Commandswhose argument is a parameter of the script lambda that holds the call, the second lambda parameter is theInput#valueof theInputsmethod that the entry names (EntryPoints::InputHopsalready finds that method). Its type is thevalue:expression ofInput.newin that method, by rules 1 to 3, plus one more rule: amergeorcompactcall on aHashvalue isHash. The value is born at theInput.newline. This givesHashto the sixCrc::*::Runsites.ParameterTypes#signalsemits one signal per site, name, and type:Signal(source: "params", kind: "type", path: <birth path>, line: <birth line>, constant: <site>, names: [name], group: <type>, weight: 1).CollaboratorPass#parameter_signalsadds these signals, so they go tosignals.tsvwith no new run file. A site that gets two types for one name has two type signals.A use is untyped when its site has no type signal for its name. An untyped use stays out of the grouping. The finding evidence counts the untyped sites. A scratch measure of the current code with rules 1 to 4 typed 87 of 993
usesignals (76 of 846 site and name pairs); rule 5 adds the sixCrcsites. So about 90% of the uses stay untyped. In the current codenodehas no typed use, so it leaves the findings for that reason; the namespace rule below is what keeps it out when both types are known.2. Detector (
NameCollisions). DeleteSIDESand the path and node split. For each name, join itsusesignals to thetypesignals of the same site and name. Group the typed sites by type. The home of a type is:OsSdlcfor a constant underOsSdlc::;OsSdlcfor a top-level constant with no::(Ruby core and standard library classes such asHash,String,Pathname). Such a class has no domain of its own, so it competes with the OsSdlc type for the same name;Prism,RuboCop,Reek).Two types are related when they have the same home. A name is a finding when one home holds two or more of its types. The finding sites are the sorted sites of all types in each such home. Types in other homes are not part of the finding. The evidence is
<type> takers <n>for each type in the finding, thenuntyped <n>, joined with;. Example forrun:Hash takers 6; OsSdlc::CollaboratorPass::RunDirectory takers 12; untyped 2.UseGroupand thegroupcolumn ofusesignals stay as they are; nothing in this ticket reads them.Shared contract with ticket B. The type signal row has exactly this form:
Signal(source: "params", kind: "type", path: <birth path>, line: <birth line>, constant: <site>, names: [name], group: <type>, weight: 1). Ticket B tests against fixture rows of this form, so ticket A and ticket B can be implemented in parallel; neither changes the other's files.Required work:
OsSdlc::CollaboratorPass::ParameterTypes, which emitstypesignals by the five rules — check: presentkind: "type"inplugins/os-sdlc/lib/os_sdlc/collaborator_pass/parameter_types.rbParameterTypes.newinplugins/os-sdlc/lib/os_sdlc/collaborator_pass.rbNameCollisions— check: absentSIDESinplugins/os-sdlc/lib/os_sdlc/collaborator_pass/name_collisions.rbRejected: keep the path and node split and add a type split beside it — two splits give two answers for one name, and the call shape does not tell the class of the value.
Rejected: give a type to an untyped use from the messages sent to it (duck typing) — core types such as
Hashanswer[]andfetchlike many other classes, so the guess is not reliable.Acceptance criteria
runhas typeHashat three sites and typeOsSdlc::CollaboratorPass::RunDirectoryat three other sites, when name collisions are found, then there is onename_collisionfinding with subjectrun, all six sites, and evidenceHash takers 3; OsSdlc::CollaboratorPass::RunDirectory takers 3; untyped 0.nodehas typeOsSdlc::Map::Nodeat some sites and typePrism::Nodeat the other sites, when name collisions are found, then there is no finding fornode.nodehas typeOsSdlc::Map::Nodeat some sites and typeOsSdlc::Brief::Nodeat the other sites, when name collisions are found, then there is a finding fornode.runcollision as in criterion 1 plus one more site that usesrunwith no type signal, when name collisions are found, then that site is not a finding site and the evidence ends withuntyped 1.@run = RunDirectory.new(dir)and passesrun: @runto a class A, and A passesrun: @runto a class B, when parameter types are measured, then A and B each have aruntype signal forRunDirectoryborn at the assignment line.runto a class, and whoseInputsmethod buildsvalue:withmergeon a Hash literal, when parameter types are measured, then that class has aruntype signal forHashborn at theInput.newline.Prism::Node.new, when parameter types are measured, then the callee has a type signal forPrism::Node.bin/collaborator-pass measureruns, thenfindings.ymlhas aname_collisionfinding with subjectrunand noname_collisionfinding with subjectnode.Owner sketch
Non-goals
runtoargumentsin theCrcsites. The loop prescribes it after this ticket ships.RunPathbuilt in the initializer).UseGroupand of the path and node classification ofusesignals.initialize.Retired tests
plugins/os-sdlc/tests/collaborator_pass/name_collisions_test.rb
Origin
nodereported andrunnot reported (docs/crc/os-sdlc/collaborator-runs/20261018T172139Z/findings.yml,name_collisionnode, evidencepath takers 2; node takers 3) ←NameCollisionscompares thepathandnodesides only (SIDESinplugins/os-sdlc/lib/os_sdlc/collaborator_pass/name_collisions.rb) ←UseGroupclassifies a use by call shape, not by the class of the value (plugins/os-sdlc/lib/os_sdlc/collaborator_pass/parameter_uses/use_group.rb) ← DESIGN (plugins/os-sdlc/lib/os_sdlc/collaborator_pass/name_collisions.rb). Therunmeanings were seen by the prescriber indocs/crc/os-sdlc/collaborator-runs/20261018T172139Z/recipes/collaborator_pass.yml(rationale), and Jared ruled the detector and sharpening design on 2026-10-18 in this session.name_collision,name collision,rename recipe,sharpenin open issuesplugins/os-sdlc/lib/os_sdlc/collaborator_pass/name_collisions.rb,plugins/os-sdlc/lib/os_sdlc/collaborator_pass/parameter_uses/use_group.rbSibling: #1119 (ticket A gathers type signals; ticket B sharpens). Both ship before the collaborator-pass loop returns to the run name collision. They may be implemented in parallel.
Work started via /os-sdlc:implement on branch ticket-1118 (worktree .claude/worktrees/ticket-1118).
Resolution
Done: ParameterTypes (Argument, ArgumentType, AssignedValue, Birth, EntrySeeds) emits type signals by the five rules; CollaboratorPass#parameter_signals adds them; NameCollisions groups typed uses by type and Home, SIDES removed, evidence ends with untyped count. Retired name_collisions_test.rb deleted.
Evidence: Branch ticket-1118 commits
f84888e7and70fb7a5e; implementation 68 suite-check pass (dispatch 640), behavior-verifier pass (639), contract-auditor pass (641)Follow-ups: none
Needs review: Read the diff d98dbd19..70fb7a5e on branch ticket-1118 before /os:worktree finish. Confirm acceptance criterion 8 by hand: no gate ran the real bin/collaborator-pass measure.