Class: Goal

Inherits:
ApplicationRecord show all
Defined in:
app/models/goal.rb

Overview

A persistent objective tracked by the analytical brain during a session. Goals form a two-level hierarchy: root goals represent high-level objectives (semantic episodes), while sub-goals are TODO-style steps rendered as checklist items in the agent’s system prompt.

The analytical brain creates and completes goals; the main agent sees them in its context window but never manages them directly.

Constant Summary collapse

STATUSES =
%w[active completed].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.evictableActiveRecord::Relation

Completed goals pending eviction — visible to the brain for age-based review.

Returns:

  • (ActiveRecord::Relation)


36
# File 'app/models/goal.rb', line 36

scope :evictable, -> { completed.where(evicted_at: nil) }

.not_evictedActiveRecord::Relation

Goals still visible in context (not yet evicted by the analytical brain).

Returns:

  • (ActiveRecord::Relation)


31
# File 'app/models/goal.rb', line 31

scope :not_evicted, -> { where(evicted_at: nil) }

Instance Method Details

#active?Boolean

Returns true if this goal is still active.

Returns:

  • (Boolean)

    true if this goal is still active



45
# File 'app/models/goal.rb', line 45

def active? = status == "active"

#as_summaryHash{String => Object}

Serializes this goal for ActionCable broadcast and TUI display. Includes nested sub-goals for root goals.

Returns:

  • (Hash{String => Object})

    with keys “id”, “description”, “status”, and “sub_goals” (Array of Hash with “id”, “description”, “status”)



84
85
86
87
88
89
90
91
92
93
# File 'app/models/goal.rb', line 84

def as_summary
  {
    "id" => id,
    "description" => description,
    "status" => status,
    "sub_goals" => sub_goals.map { |sub|
      {"id" => sub.id, "description" => sub.description, "status" => sub.status}
    }
  }
end

#cascade_completion!void

This method returns an undefined value.

Cascades completion to all active sub-goals. Called when a root goal is finished — remaining sub-items are implicitly resolved because the semantic episode that spawned them has ended.

Uses update_all to avoid N per-record after_commit broadcasts; the caller (AnalyticalBrain::Tools::FinishGoal) wraps the whole operation in a transaction so the root goal’s single broadcast includes the cascaded state.



63
64
65
66
# File 'app/models/goal.rb', line 63

def cascade_completion!
  now = Time.current
  sub_goals.active.update_all(status: "completed", completed_at: now, updated_at: now)
end

#completed?Boolean

Returns true if this goal has been completed.

Returns:

  • (Boolean)

    true if this goal has been completed



42
# File 'app/models/goal.rb', line 42

def completed? = status == "completed"

#evicted?Boolean

Returns true if this goal has been evicted from display.

Returns:

  • (Boolean)

    true if this goal has been evicted from display



51
# File 'app/models/goal.rb', line 51

def evicted? = evicted_at.present?

#release_orphaned_pins!Integer

Releases pinned messages that have no remaining active Goal references anywhere in the session. Called after goal (and cascade) completion —the orphaned scope checks all Goals, so pins shared with other active Goals survive automatically via reference counting.

Returns:

  • (Integer)

    number of released pins



74
75
76
77
# File 'app/models/goal.rb', line 74

def release_orphaned_pins!
  orphaned = session.pinned_messages.orphaned
  orphaned.destroy_all.size
end

#root?Boolean

Returns true if this is a root goal (no parent).

Returns:

  • (Boolean)

    true if this is a root goal (no parent)



48
# File 'app/models/goal.rb', line 48

def root? = !parent_goal_id