Class: Woods::SessionTracer::SessionFlowAssembler

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/session_tracer/session_flow_assembler.rb

Overview

Assembles a context tree from captured session requests against the extracted index.

Does NOT require Rails — reads from a store + on-disk extracted index.

Algorithm:

  1. Load requests from store for session_id

  2. For each request, resolve “Controller#action” via IndexReader

  3. Expand dependencies via DependencyGraph — filter :job/:mailer as async side effects

  4. Deduplicate units across steps (include source once, reference by identifier)

  5. Token budget allocation with priority-based truncation

  6. Build SessionFlowDocument

rubocop:disable Metrics/ClassLength

Examples:

assembler = SessionFlowAssembler.new(store: store, reader: reader)
doc = assembler.assemble("abc123", budget: 8000, depth: 1)
puts doc.to_context

Constant Summary collapse

ASYNC_TYPES =
%w[job mailer].to_set.freeze

Instance Method Summary collapse

Constructor Details

#initialize(store:, reader:) ⇒ SessionFlowAssembler

Returns a new instance of SessionFlowAssembler.

Parameters:



33
34
35
36
# File 'lib/woods/session_tracer/session_flow_assembler.rb', line 33

def initialize(store:, reader:)
  @store = store
  @reader = reader
end

Instance Method Details

#assemble(session_id, budget: 8000, depth: 1) ⇒ SessionFlowDocument

Assemble a context tree for a session.

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength

Parameters:

  • session_id (String)

    The session to assemble

  • budget (Integer) (defaults to: 8000)

    Maximum token budget (default: 8000)

  • depth (Integer) (defaults to: 1)

    Expansion depth (0=metadata only, 1=direct deps, 2+=full flow)

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/woods/session_tracer/session_flow_assembler.rb', line 45

def assemble(session_id, budget: 8000, depth: 1)
  requests = @store.read(session_id)
  return empty_document(session_id) if requests.empty?

  steps = []
  context_pool = {}
  side_effects = []
  dependency_map = {}
  seen_units = Set.new

  requests.each_with_index do |req, idx|
    step = build_step(req, idx)
    steps << step

    next if depth.zero?

    controller_id = req['controller']
    next unless controller_id

    # Resolve controller unit
    unit = @reader.find_unit(controller_id)
    if unit && !seen_units.include?(controller_id)
      seen_units.add(controller_id)
      context_pool[controller_id] = unit_summary(unit)
    end
    step[:unit_refs] = [controller_id].compact

    # Expand dependencies
    next unless unit

    deps = resolve_dependencies(controller_id, seen_units, context_pool,
                                side_effects, step, dependency_map, depth)
    step[:unit_refs].concat(deps)
  end

  # Apply token budget
  token_count = apply_budget(context_pool, budget)

  SessionFlowDocument.new(
    session_id: session_id,
    steps: steps,
    context_pool: context_pool,
    side_effects: side_effects,
    dependency_map: dependency_map,
    token_count: token_count
  )
end