Give constant names one owner: definitions, lexical resolution, and reference sites #1718

Open
opened 2026-11-12 20:29:18 +00:00 by jared · 7 comments
Owner

Problem

Knowledge of the codebase's constant names is built again in at least seven places in os-sdlc, and the copies give different answers for the same code. On the live plugins/os-sdlc/lib tree (1,181 files), the pieces differ at 1,169 of 4,724 shared reference sites. The cause is that each piece builds its own definition table: three pieces know 1,822, 1,201, and 965 names for the same tree. Two lint cops each fail to resolve about 1,160 names that exist in lib, and a cop cannot flag a name that it cannot resolve.

Observed

A read-only comparison on 2026-11-12 ran SystemModel::Extractor, Lint::ConceptCycleCheck::References, Detector::ConstructorGraph::Visitor, Detector::ErrorLineage::Definitions, Crc::Definitions, ExpectedConstants, and the cops InwardDependency::ConstantReference and AdapterBuiltOutsideEntry::AdapterConstant on the same tree. The reference was a static resolver that applies Ruby's lookup rule; it is not loaded Ruby.
ConceptCycleCheck::References resolved 4,692 of 4,717 sites correctly with 0 wrong names. The extractor missed 328 internal names. The constructor graph missed 64 and gave 1 wrong name. The two cops missed 1,169 and 1,156.
At lib/os_sdlc/crc/filing.rb:28, Result.new is the Struct at line 10. The detector and both cops resolve it to OsSdlc::Crc::Result.
AdapterConstant matches file names without case. The constant WORD resolves to census/word.rb (7 sites of this kind).
ExpectedConstants gave 974 names in a fresh process and 1,187 after another loader was loaded.
A responsibility audit of 81 files on the same day found nine copies of the lexical-nesting rule with three behaviors (compact module A::B, the root anchor in ::Y, the superclass scope). Examples: system_model/extractor/definition.rb:15, lint/concept_cycle_check/references.rb:69, detector/constructor_graph/visitor.rb:61, cops/structural/inward_dependency/constant_reference.rb:23. On small fixtures the copies give wrong targets for ::Y and for a name under compact module A::B; on the live tree these rule defects cause 14 of the differing sites.

Reproduce

n/a: a design finding; the comparison script was a scratch file of the session and is not in the repository.

Approach

Not decided. The owner must rule on these points before a code ticket exists:

  • The domain name: OsSdlc::Constants, or another name such as ConstantNames.
  • Whether system-model collaborator edges may change where the present rule is wrong. Element ids must stay byte-identical (ADR-0300).
  • Whether the six os-sdlc-only detector sources (inward, effects, params, calls, flay, reek) must measure the target project.
  • The order and the start time.

The audit proposes OsSdlc::Constants with three parts. Constants::Nesting holds Ruby's lookup rule on plain strings, so that the RuboCop cops can share the rule and stay on the RuboCop tree (the memo docs/crc/os-sdlc/detector-cop-memo.md advised against a move of the cops onto Prism). Constants::Definitions holds each definition site with name, kind, path, and line, and every file of a reopened class. Constants::References holds each reference site with written name, nesting, path, line, and kind. ExpectedConstants stays the owner of the Zeitwerk expected-file table. Each caller keeps its own policy for a name that does not resolve.

Required work: none beyond the acceptance criteria.

Origin

  • Trigger: an inventory of AST code in os-sdlc, then a resolver comparison and a responsibility audit, on 2026-11-12, for the House Explore work.
  • Improvised this session: none
  • Chain: pieces give different answers for one name ← each piece builds its own definition table and its own copy of the nesting rule ← no object has "constant names" as its job, so each new detector writes a copy (the latest is detector/parameter_types/argument_type.rb:39) ← DESIGN (docs/system-design-format/README.md:242 records that the system model was built new because the detector was bound to os-sdlc source sets)
  • Root candidate: none found; searched constant names, nesting, lexical resolution, ExpectedConstants, ConstantReference, constant resolution, and AST domain
  • Where: plugins/os-sdlc/lib/os_sdlc/system_model/extractor/, lint/concept_cycle_check/references.rb, detector/constructor_graph/visitor.rb, detector/error_lineage/, crc/definitions.rb, cops/structural/inward_dependency/constant_reference.rb
  • Session: 5aee9255-7fc4-41bc-8166-9473b8d19d71
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/5aee9255-7fc4-41bc-8166-9473b8d19d71.jsonl

VERDICT: CREATE
REASON: This is an observed failure that the session did not cause. The comparison showed 1,169 of 4,724 shared reference sites resolved differently, with wrong names such as Result.new at crc/filing.rb:28. The Origin chain ends in DESIGN, not IMPROVISATION. No open ticket has the same mechanism. #1118 and #1087 are different faults.

## Problem Knowledge of the codebase's constant names is built again in at least seven places in os-sdlc, and the copies give different answers for the same code. On the live `plugins/os-sdlc/lib` tree (1,181 files), the pieces differ at 1,169 of 4,724 shared reference sites. The cause is that each piece builds its own definition table: three pieces know 1,822, 1,201, and 965 names for the same tree. Two lint cops each fail to resolve about 1,160 names that exist in `lib`, and a cop cannot flag a name that it cannot resolve. ## Observed A read-only comparison on 2026-11-12 ran `SystemModel::Extractor`, `Lint::ConceptCycleCheck::References`, `Detector::ConstructorGraph::Visitor`, `Detector::ErrorLineage::Definitions`, `Crc::Definitions`, `ExpectedConstants`, and the cops `InwardDependency::ConstantReference` and `AdapterBuiltOutsideEntry::AdapterConstant` on the same tree. The reference was a static resolver that applies Ruby's lookup rule; it is not loaded Ruby. `ConceptCycleCheck::References` resolved 4,692 of 4,717 sites correctly with 0 wrong names. The extractor missed 328 internal names. The constructor graph missed 64 and gave 1 wrong name. The two cops missed 1,169 and 1,156. At `lib/os_sdlc/crc/filing.rb:28`, `Result.new` is the Struct at line 10. The detector and both cops resolve it to `OsSdlc::Crc::Result`. `AdapterConstant` matches file names without case. The constant `WORD` resolves to `census/word.rb` (7 sites of this kind). `ExpectedConstants` gave 974 names in a fresh process and 1,187 after another loader was loaded. A responsibility audit of 81 files on the same day found nine copies of the lexical-nesting rule with three behaviors (compact `module A::B`, the root anchor in `::Y`, the superclass scope). Examples: `system_model/extractor/definition.rb:15`, `lint/concept_cycle_check/references.rb:69`, `detector/constructor_graph/visitor.rb:61`, `cops/structural/inward_dependency/constant_reference.rb:23`. On small fixtures the copies give wrong targets for `::Y` and for a name under compact `module A::B`; on the live tree these rule defects cause 14 of the differing sites. ## Reproduce n/a: a design finding; the comparison script was a scratch file of the session and is not in the repository. ## Approach Not decided. The owner must rule on these points before a code ticket exists: - [ ] The domain name: `OsSdlc::Constants`, or another name such as `ConstantNames`. - [ ] Whether system-model collaborator edges may change where the present rule is wrong. Element ids must stay byte-identical (ADR-0300). - [ ] Whether the six os-sdlc-only detector sources (`inward`, `effects`, `params`, `calls`, `flay`, `reek`) must measure the target project. - [ ] The order and the start time. The audit proposes `OsSdlc::Constants` with three parts. `Constants::Nesting` holds Ruby's lookup rule on plain strings, so that the RuboCop cops can share the rule and stay on the RuboCop tree (the memo `docs/crc/os-sdlc/detector-cop-memo.md` advised against a move of the cops onto Prism). `Constants::Definitions` holds each definition site with name, kind, path, and line, and every file of a reopened class. `Constants::References` holds each reference site with written name, nesting, path, line, and kind. `ExpectedConstants` stays the owner of the Zeitwerk expected-file table. Each caller keeps its own policy for a name that does not resolve. Required work: none beyond the acceptance criteria. ## Origin - Trigger: an inventory of AST code in os-sdlc, then a resolver comparison and a responsibility audit, on 2026-11-12, for the House Explore work. - Improvised this session: none - Chain: pieces give different answers for one name ← each piece builds its own definition table and its own copy of the nesting rule ← no object has "constant names" as its job, so each new detector writes a copy (the latest is `detector/parameter_types/argument_type.rb:39`) ← DESIGN (`docs/system-design-format/README.md:242` records that the system model was built new because the detector was bound to os-sdlc source sets) - Root candidate: none found; searched constant names, nesting, lexical resolution, ExpectedConstants, ConstantReference, constant resolution, and AST domain - Where: `plugins/os-sdlc/lib/os_sdlc/system_model/extractor/`, `lint/concept_cycle_check/references.rb`, `detector/constructor_graph/visitor.rb`, `detector/error_lineage/`, `crc/definitions.rb`, `cops/structural/inward_dependency/constant_reference.rb` - Session: 5aee9255-7fc4-41bc-8166-9473b8d19d71 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/5aee9255-7fc4-41bc-8166-9473b8d19d71.jsonl VERDICT: CREATE REASON: This is an observed failure that the session did not cause. The comparison showed 1,169 of 4,724 shared reference sites resolved differently, with wrong names such as `Result.new` at `crc/filing.rb:28`. The Origin chain ends in DESIGN, not IMPROVISATION. No open ticket has the same mechanism. #1118 and #1087 are different faults.
Author
Owner

Evidence files, copied from the session scratchpad on 2026-11-12: /home/jared/dev/house-explore-replay/pilot10/constant-names-2026-11-12/. resolvers/report.txt is the full comparison output; resolvers/run.rb, truth.rb, and analyze.rb make it again. audit-probes/ holds the fixtures and probe scripts of the responsibility audit. The directory is outside this repository and has no version control.

Evidence files, copied from the session scratchpad on 2026-11-12: `/home/jared/dev/house-explore-replay/pilot10/constant-names-2026-11-12/`. `resolvers/report.txt` is the full comparison output; `resolvers/run.rb`, `truth.rb`, and `analyze.rb` make it again. `audit-probes/` holds the fixtures and probe scripts of the responsibility audit. The directory is outside this repository and has no version control.
Author
Owner

Ruling by Jared, 2026-11-12, on the first checkbox (domain name): the name is OsSdlc::SymbolIndex, not OsSdlc::Constants. His words: "Constants feels like an incredibly generic and useless name. If showed this codebase to another developer, would they immediately know the purpose and function of Constants?" and then "SymbolIndex is much better". Read each Constants::... name in the body as follows: Constants::Definitions is SymbolIndex::Definitions; Constants::References is SymbolIndex::References; Constants::Nesting is SymbolIndex::LexicalScope (this third name is the session's proposal; Jared ruled on the parent name only). Intended use: SymbolIndex.of(root).definitions_of(name), references_to(name), resolve(written, at: site). The other three checkboxes stay open.

Ruling by Jared, 2026-11-12, on the first checkbox (domain name): the name is `OsSdlc::SymbolIndex`, not `OsSdlc::Constants`. His words: "Constants feels like an incredibly generic and useless name. If showed this codebase to another developer, would they immediately know the purpose and function of Constants?" and then "SymbolIndex is much better". Read each `Constants::...` name in the body as follows: `Constants::Definitions` is `SymbolIndex::Definitions`; `Constants::References` is `SymbolIndex::References`; `Constants::Nesting` is `SymbolIndex::LexicalScope` (this third name is the session's proposal; Jared ruled on the parent name only). Intended use: `SymbolIndex.of(root).definitions_of(name)`, `references_to(name)`, `resolve(written, at: site)`. The other three checkboxes stay open.
Author
Owner

Rulings recorded 2026-11-12 in docs/decisions/rulings.tsv, rows 141-144. Row 141 (Jared): the name is OsSdlc::SymbolIndex. Rows 142-144 are predictions by /os-me:predict at confidence 74, applied by the session and open to correction by Jared. Row 142: system-model collaborator edges may change where the present rule is wrong; element ids stay byte-identical; record the before and after edge diff. Row 143: the scope of the six os-sdlc-only detector sources is not part of this ticket and gets its own ticket. Row 144: start now in a worktree; SymbolIndex::Definitions is the first increment, because the tables cause 1,169 of the differing sites and the rule defects cause 14; build the lexical-scope rule only as far as Definitions needs it.

Rulings recorded 2026-11-12 in `docs/decisions/rulings.tsv`, rows 141-144. Row 141 (Jared): the name is `OsSdlc::SymbolIndex`. Rows 142-144 are predictions by /os-me:predict at confidence 74, applied by the session and open to correction by Jared. Row 142: system-model collaborator edges may change where the present rule is wrong; element ids stay byte-identical; record the before and after edge diff. Row 143: the scope of the six os-sdlc-only detector sources is not part of this ticket and gets its own ticket. Row 144: start now in a worktree; `SymbolIndex::Definitions` is the first increment, because the tables cause 1,169 of the differing sites and the rule defects cause 14; build the lexical-scope rule only as far as `Definitions` needs it.
Author
Owner

Follow-up not filed (ticket-skeptic VERDICT: DROP): "Rule whether the six os-sdlc-only detector sources measure the target project". REASON: The need is speculative. Nobody has run the detector on a second project, and docs/system-design-format/README.md:242 already records that the detector is bound to os-sdlc source sets. The parent #1718 is an AI-created follow-up, so the depth-1 limit applies. Ruling 143 is only a predicted ruling, not a user request, and no security or data-loss floor applies. If a second target ever appears, file the ticket then.

Follow-up not filed (ticket-skeptic VERDICT: DROP): "Rule whether the six os-sdlc-only detector sources measure the target project". REASON: The need is speculative. Nobody has run the detector on a second project, and `docs/system-design-format/README.md:242` already records that the detector is bound to os-sdlc source sets. The parent #1718 is an AI-created follow-up, so the depth-1 limit applies. Ruling 143 is only a predicted ruling, not a user request, and no security or data-loss floor applies. If a second target ever appears, file the ticket then.
Author
Owner

Decompose, phase Decide (2026-11-12). a. One change: one table of definition sites, OsSdlc::SymbolIndex::Definitions, built once from the parsed files of a root; each caller reads it and stops building its own. It removes the cause of 1,169 of the 4,724 differing reference sites. b. Owner: no existing object owns this data. Lint::ConceptCycleCheck::References has the most complete name set (1,822 names) but keeps no file or line; Crc::Definitions keeps every file but reads one file at a time and resolves nothing; SystemModel::Extractor keeps only the first file of a reopened class. New object: SymbolIndex::Definitions; its secret is how a definition site is found and named (class, module, constant write, Data.define, Struct.new, reopened class, root anchor). c. Routing: step b names a new object and the ticket touches more than one sub-system (system model, lint, detector, crc), so this ticket goes to /os-sdlc:ticket-takedown 1718. Takedown writes the Owner sketch. No new investigation agents ran in this phase: the evidence is the resolver comparison and the responsibility audit of the same day (/home/jared/dev/house-explore-replay/pilot10/constant-names-2026-11-12/). Constraints for takedown, from rulings 141-144 (commit 50545d495): name OsSdlc::SymbolIndex; Definitions is the first increment, with the lexical-scope rule only as far as it needs; first caller to move is Lint::ConceptCycleCheck::References, then one caller per ticket; system-model element ids stay byte-identical (ADR-0300) and the edge diff is recorded; work in a worktree; detector source scope is out.

Decompose, phase Decide (2026-11-12). a. One change: one table of definition sites, `OsSdlc::SymbolIndex::Definitions`, built once from the parsed files of a root; each caller reads it and stops building its own. It removes the cause of 1,169 of the 4,724 differing reference sites. b. Owner: no existing object owns this data. `Lint::ConceptCycleCheck::References` has the most complete name set (1,822 names) but keeps no file or line; `Crc::Definitions` keeps every file but reads one file at a time and resolves nothing; `SystemModel::Extractor` keeps only the first file of a reopened class. New object: `SymbolIndex::Definitions`; its secret is how a definition site is found and named (class, module, constant write, `Data.define`, `Struct.new`, reopened class, root anchor). c. Routing: step b names a new object and the ticket touches more than one sub-system (system model, lint, detector, crc), so this ticket goes to `/os-sdlc:ticket-takedown 1718`. Takedown writes the Owner sketch. No new investigation agents ran in this phase: the evidence is the resolver comparison and the responsibility audit of the same day (`/home/jared/dev/house-explore-replay/pilot10/constant-names-2026-11-12/`). Constraints for takedown, from rulings 141-144 (commit 50545d495): name `OsSdlc::SymbolIndex`; `Definitions` is the first increment, with the lexical-scope rule only as far as it needs; first caller to move is `Lint::ConceptCycleCheck::References`, then one caller per ticket; system-model element ids stay byte-identical (ADR-0300) and the edge diff is recorded; work in a worktree; detector source scope is out.
Author
Owner

Split by Jared, 2026-11-12 (rulings 145 and 146). The owner is now OsSdlc::CodeShape (replaces SymbolIndex, row 141), because it will also own other duplicated Prism/AST code. Takedown run 1718-20261112-160038 failed green-suite: each design moved one caller and the grader wanted all callers moved. Children: #1729 CodeShape core + ConceptCycleCheck::References (blocks the rest except #1737); constant-name callers #1730 ErrorLineage, #1731 SystemModel::Extractor, #1732 ConstructorGraph/Holders + in-file constants, #1733 Crc/ArgumentType, #1734 cops lexical scope + AdapterConstant file-name match, #1735 ExpectedConstants load order, #1736 constant-node-to-name; other AST jobs #1737 tree flatten walks, #1738 method-definition collection, #1739 call-site collection, #1740 parse once per process.

Split by Jared, 2026-11-12 (rulings 145 and 146). The owner is now `OsSdlc::CodeShape` (replaces `SymbolIndex`, row 141), because it will also own other duplicated Prism/AST code. Takedown run `1718-20261112-160038` failed green-suite: each design moved one caller and the grader wanted all callers moved. Children: #1729 CodeShape core + ConceptCycleCheck::References (blocks the rest except #1737); constant-name callers #1730 ErrorLineage, #1731 SystemModel::Extractor, #1732 ConstructorGraph/Holders + in-file constants, #1733 Crc/ArgumentType, #1734 cops lexical scope + AdapterConstant file-name match, #1735 ExpectedConstants load order, #1736 constant-node-to-name; other AST jobs #1737 tree flatten walks, #1738 method-definition collection, #1739 call-site collection, #1740 parse once per process.
Author
Owner

Added child #1741: CodeShape::Definitions. The #1729 design built only CodeShape::LexicalScope; #1730-#1733 also wait for #1741.

Added child #1741: CodeShape::Definitions. The #1729 design built only CodeShape::LexicalScope; #1730-#1733 also wait for #1741.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1718
No description provided.