Widen routes_to and trace wakeup-poll from its script so scan facts clear 21 near misses #1562

Closed
opened 2026-11-05 13:10:12 +00:00 by jared · 4 comments
Owner

Problem

The scan reports 29 os-backlog near misses, and 21 of them come from missing facts, not from the code. routes? at plugins/os-sdlc/lib/os_sdlc/system_model/extractor/fact_ledger.rb:72 fires only on a receiver name that ends in Command inside a Thor subclass, so Backlog::CLI gets no routes_to edge. This gives 18 "CMD has no Router" rows. The wakeup-poll journey stops at the empty-index check at plugins/os-backlog/bin/wakeup-poll:57, so it gives 3 more rows (LAY, P&F, TXS). pattern-landscape.md F12 names the routes_to widening.

Design

/home/jared/dev/cc-os/docs/takedown/1562-20261105-104950/final-design.json

Approach

FactLedger (owner of routes_to) records an edge for every project class a Thor class calls, deleting the "Command" name-suffix predicate.

Required work:

  1. Write plugins/os-sdlc/lib/os_sdlc/system_model/extractor/fact_ledger.rb
  2. Write test plugins/os-sdlc/tests/system_model_thor_routing_test.rb

Acceptance criteria

  • Thor class calls a known project class (any name): includes it
  • Thor class calls an unknown constant (File, Thor): dropped by KnownNames
  • Non-Thor class calls a class: empty
  • Thor class with no constant calls: empty

Owner sketch

Responsibility Owner
Record which classes are Thor classes FactLedger (@thor)
Record the routes_to edge for a Thor class FactLedger#call
Drop unknown constants from routes_to KnownNames (unchanged)
Read routes_to for the Router and Front roles catalog/command_router.rb, front_controller.rb (unchanged)
Attribute script-body calls in journeys Recorder::Trace and Journey (separate ticket)

plugins/os-sdlc/tests/system_model_thor_routing_test.rb

# frozen_string_literal: true

require_relative "test_helper"

module OsSdlc
  class SystemModelThorRoutingTest < TmpdirTestCase
    THOR_CLI = <<~RUBY
      module Tool
        class CLI < Thor
          def run_item
            item_commands.go
            Helper.new.help
            File.read("x")
          end

          private

          def item_commands = ItemCommands.new
        end
      end
    RUBY

    ITEM_COMMANDS = <<~RUBY
      module Tool
        class ItemCommands
          def go = 1
        end
      end
    RUBY

    HELPER = <<~RUBY
      module Tool
        class Helper
          def help = 1
        end
      end
    RUBY

    def setup
      super
      write("lib/tool/cli.rb", THOR_CLI)
      write("lib/tool/item_commands.rb", ITEM_COMMANDS)
      write("lib/tool/helper.rb", HELPER)
    end

    def test_a_thor_cli_routes_to_a_plural_commands_class
      assert_includes facts.fetch("Tool::CLI").routes_to, "Tool::ItemCommands"
    end

    def test_a_thor_cli_routes_to_any_project_class_it_calls
      assert_includes facts.fetch("Tool::CLI").routes_to, "Tool::Helper"
    end

    def test_a_thor_cli_routes_only_to_the_project_classes_it_calls
      assert_equal %w[Tool::Helper Tool::ItemCommands], facts.fetch("Tool::CLI").routes_to.sort
    end

    private

    def write(relative, source)
      path = File.join(@dir, relative)
      FileUtils.mkdir_p(File.dirname(path))
      File.write(path, source)
    end

    def facts
      aliases = SystemModel::Aliases.new({})
      SystemModel::Extractor.new(root: @dir, aliases: aliases).call(SystemModel::Model.new(cards: [])).facts
    end
  end
end
## Problem The scan reports 29 os-backlog near misses, and 21 of them come from missing facts, not from the code. `routes?` at `plugins/os-sdlc/lib/os_sdlc/system_model/extractor/fact_ledger.rb:72` fires only on a receiver name that ends in `Command` inside a Thor subclass, so `Backlog::CLI` gets no `routes_to` edge. This gives 18 "CMD has no Router" rows. The `wakeup-poll` journey stops at the empty-index check at `plugins/os-backlog/bin/wakeup-poll:57`, so it gives 3 more rows (LAY, P&F, TXS). `pattern-landscape.md` F12 names the `routes_to` widening. ## Design /home/jared/dev/cc-os/docs/takedown/1562-20261105-104950/final-design.json ## Approach FactLedger (owner of routes_to) records an edge for every project class a Thor class calls, deleting the "Command" name-suffix predicate. Required work: 1. Write `plugins/os-sdlc/lib/os_sdlc/system_model/extractor/fact_ledger.rb` 2. Write test `plugins/os-sdlc/tests/system_model_thor_routing_test.rb` ## Acceptance criteria - Thor class calls a known project class (any name): includes it - Thor class calls an unknown constant (File, Thor): dropped by KnownNames - Non-Thor class calls a class: empty - Thor class with no constant calls: empty ## Owner sketch | Responsibility | Owner | |---|---| | Record which classes are Thor classes | FactLedger (`@thor`) | | Record the routes_to edge for a Thor class | FactLedger#call | | Drop unknown constants from routes_to | KnownNames (unchanged) | | Read routes_to for the Router and Front roles | catalog/command_router.rb, front_controller.rb (unchanged) | | Attribute script-body calls in journeys | Recorder::Trace and Journey (separate ticket) | `plugins/os-sdlc/tests/system_model_thor_routing_test.rb` ```ruby # frozen_string_literal: true require_relative "test_helper" module OsSdlc class SystemModelThorRoutingTest < TmpdirTestCase THOR_CLI = <<~RUBY module Tool class CLI < Thor def run_item item_commands.go Helper.new.help File.read("x") end private def item_commands = ItemCommands.new end end RUBY ITEM_COMMANDS = <<~RUBY module Tool class ItemCommands def go = 1 end end RUBY HELPER = <<~RUBY module Tool class Helper def help = 1 end end RUBY def setup super write("lib/tool/cli.rb", THOR_CLI) write("lib/tool/item_commands.rb", ITEM_COMMANDS) write("lib/tool/helper.rb", HELPER) end def test_a_thor_cli_routes_to_a_plural_commands_class assert_includes facts.fetch("Tool::CLI").routes_to, "Tool::ItemCommands" end def test_a_thor_cli_routes_to_any_project_class_it_calls assert_includes facts.fetch("Tool::CLI").routes_to, "Tool::Helper" end def test_a_thor_cli_routes_only_to_the_project_classes_it_calls assert_equal %w[Tool::Helper Tool::ItemCommands], facts.fetch("Tool::CLI").routes_to.sort end private def write(relative, source) path = File.join(@dir, relative) FileUtils.mkdir_p(File.dirname(path)) File.write(path, source) end def facts aliases = SystemModel::Aliases.new({}) SystemModel::Extractor.new(root: @dir, aliases: aliases).call(SystemModel::Model.new(cards: [])).facts end end end ```
Author
Owner

Routed to /os-sdlc:ticket-takedown by the ticket-decompose rule: a new object and more than one sub-system.

a. One change: part A, routes_to also counts constructs inside a def that a forward names, when they have at least 2 distinct targets. Backlog::CLI has no receiver named *Command; def project_commands = ProjectCommands.new(...) builds it. Part B, Trace names a top-level script frame by its relative path, so bin/wakeup-poll owns its messages.
b. Owner: part A, a new Extractor::ForwardedRoutes, secret: which constructs a class's forwards reach. Part B, Recorder::Trace.

Evidence read, not run: the parts are independent (extractor and recorder), with separate gauge deltas (18 rows and 3 rows). tests/wakeup_poll_script_test.rb:36 has a non-empty index, but the project never opts in, so the script stops at bin/wakeup-poll:75, not at line 57.
Predicted side effect: OsSdlc::Gate::Commands gains routes_to to Suite::DiffPlan and Project::Changes, so it would stop being a CMD Command and gain the Router role. Rule on this in the design.

Routed to /os-sdlc:ticket-takedown by the ticket-decompose rule: a new object and more than one sub-system. a. One change: part A, `routes_to` also counts constructs inside a def that a forward names, when they have at least 2 distinct targets. `Backlog::CLI` has no receiver named `*Command`; `def project_commands = ProjectCommands.new(...)` builds it. Part B, `Trace` names a top-level script frame by its relative path, so `bin/wakeup-poll` owns its messages. b. Owner: part A, a new `Extractor::ForwardedRoutes`, secret: which constructs a class's forwards reach. Part B, `Recorder::Trace`. Evidence read, not run: the parts are independent (extractor and recorder), with separate gauge deltas (18 rows and 3 rows). `tests/wakeup_poll_script_test.rb:36` has a non-empty index, but the project never opts in, so the script stops at `bin/wakeup-poll:75`, not at line 57. Predicted side effect: `OsSdlc::Gate::Commands` gains `routes_to` to `Suite::DiffPlan` and `Project::Changes`, so it would stop being a CMD Command and gain the Router role. Rule on this in the design.
Author
Owner

Work started on branch ticket-1562, map takedown-implement.

Work started on branch ticket-1562, map takedown-implement.
Author
Owner

Limit: routes_to fires only in a Thor subclass. The skeptic gate dropped the non-Thor follow-up because no scanned codebase has a non-Thor router. File a ticket when a non-Thor codebase shows a false CMD-has-no-Router row.

Limit: routes_to fires only in a Thor subclass. The skeptic gate dropped the non-Thor follow-up because no scanned codebase has a non-Thor router. File a ticket when a non-Thor codebase shows a false CMD-has-no-Router row.
Author
Owner

Resolution

Done: routes? no longer needs a Command suffix, so a Thor class records a routes_to edge for each project class it calls and Backlog::CLI gets the Router role. Part B moved to #1575.

Evidence: Implementation 239, takedown run docs/takedown/1562-20261105-104950, all gates passed, suite green

Follow-ups: #1575 captured with VERDICT: CREATE; non-Thor routers dropped by the skeptic gate, reason on this ticket

## Resolution **Done:** routes? no longer needs a Command suffix, so a Thor class records a routes_to edge for each project class it calls and Backlog::CLI gets the Router role. Part B moved to #1575. **Evidence:** Implementation 239, takedown run docs/takedown/1562-20261105-104950, all gates passed, suite green **Follow-ups:** #1575 captured with VERDICT: CREATE; non-Thor routers dropped by the skeptic gate, reason on this ticket
jared closed this issue 2026-11-05 16:06:21 +00:00
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#1562
No description provided.