reading.go raw

   1  // Package oracle implements an I Ching oracle state machine that steers
   2  // the organism's data ingestion strategy. Hexagram readings chain from one
   3  // to the next — the resulting hexagram becomes the next primary, with 2 bits
   4  // of PRNG entropy XOR'd onto each line to evolve the state.
   5  //
   6  // The oracle steers all data sources: self-ingest, web search, local repos,
   7  // Nostr relays, market feeds. The inner trigram determines what kind of
   8  // understanding is needed; the outer trigram determines what kind of source
   9  // to emphasize; the ADSR phase determines engagement depth; changing lines
  10  // identify which dimensions are in flux.
  11  package oracle
  12  
  13  import (
  14  	"git.mleku.dev/mleku/dendrite/pkg/ratio"
  15  	"git.mleku.dev/mleku/dendrite/pkg/state"
  16  )
  17  
  18  // LineState is the 2-bit state of a single hexagram line.
  19  // Encoding is chosen so XOR gives clean transitions:
  20  //
  21  //	state ^ 00 = same state     (no change)
  22  //	state ^ 01 = flip polarity  (yin↔yang, keep stability)
  23  //	state ^ 10 = flip stability (young↔old, keep polarity)
  24  //	state ^ 11 = flip both      (full inversion)
  25  type LineState uint8
  26  
  27  const (
  28  	YoungYin  LineState = 0b00 // fixed broken   — stable yin
  29  	YoungYang LineState = 0b01 // fixed solid     — stable yang
  30  	OldYin    LineState = 0b10 // changing broken — yin becoming yang
  31  	OldYang   LineState = 0b11 // changing solid  — yang becoming yin
  32  )
  33  
  34  // IsChanging returns true if the line is old (transitioning).
  35  func (ls LineState) IsChanging() bool { return ls&0b10 != 0 }
  36  
  37  // IsYang returns true if the current polarity is yang (solid).
  38  func (ls LineState) IsYang() bool { return ls&0b01 != 0 }
  39  
  40  // Stabilize returns the young (fixed) version of this line state,
  41  // preserving the post-change polarity. For a changing line, this is
  42  // the state after the change has been applied (old→young, polarity
  43  // already flipped in the resulting hexagram).
  44  func (ls LineState) Stabilize() LineState { return ls &^ 0b10 }
  45  
  46  // Reading is a single I Ching casting with full line detail.
  47  type Reading struct {
  48  	// Primary is the 6-bit hexagram (inner=low3, outer=high3).
  49  	Primary state.Hexagram `json:"primary"`
  50  
  51  	// Lines holds the state of each of the 6 lines (index 0 = bottom).
  52  	// Indices 0-2 are the inner trigram, 3-5 are the outer trigram.
  53  	Lines [6]LineState `json:"lines"`
  54  
  55  	// Resulting is the hexagram after all changing lines have been applied.
  56  	// If no lines are changing, Resulting == Primary.
  57  	Resulting state.Hexagram `json:"resulting"`
  58  
  59  	// Generation is the dendrite generation when this reading was cast.
  60  	Generation uint32 `json:"generation"`
  61  
  62  	// Sequence is the ordinal position in the reading chain (0 = abiogenesis).
  63  	Sequence uint32 `json:"sequence"`
  64  
  65  	// Source identifies what data source prompted this reading
  66  	// (e.g., "self", "forage", "nostr", "market").
  67  	Source string `json:"source"`
  68  
  69  	// Directives are the strategy instructions derived from this reading.
  70  	Directives []Directive `json:"directives"`
  71  
  72  	// Absorbed is true when the EWMA stability signal fires, indicating
  73  	// the organism has finished absorbing this reading's content.
  74  	Absorbed bool `json:"absorbed"`
  75  
  76  	// AbsorbedAtGen records the generation when stability was detected.
  77  	AbsorbedAtGen uint32 `json:"absorbed_at_gen,omitempty"`
  78  
  79  	// Metrics captures learning effectiveness for this reading.
  80  	Metrics LearningMetrics `json:"metrics"`
  81  }
  82  
  83  // ChangingLines returns the indices (0-5) of all changing lines.
  84  func (r *Reading) ChangingLines() []int {
  85  	var out []int
  86  	for i, ls := range r.Lines {
  87  		if ls.IsChanging() {
  88  			out = append(out, i)
  89  		}
  90  	}
  91  	return out
  92  }
  93  
  94  // InnerTrigram returns the primary reading's inner trigram.
  95  func (r *Reading) InnerTrigram() state.Trigram { return r.Primary.Inner() }
  96  
  97  // OuterTrigram returns the primary reading's outer trigram.
  98  func (r *Reading) OuterTrigram() state.Trigram { return r.Primary.Outer() }
  99  
 100  // LearningMetrics tracks how well the organism absorbed a reading.
 101  type LearningMetrics struct {
 102  	ElementsFetched int64       `json:"elements_fetched"`
 103  	ElementsBonded  int64       `json:"elements_bonded"`
 104  	BondRate        ratio.Ratio `json:"bond_rate"`
 105  	EWMAAtCast      ratio.Ratio `json:"ewma_at_cast"`
 106  	EWMAAtAbsorb    ratio.Ratio `json:"ewma_at_absorb"`
 107  	DirectivesRun   int         `json:"directives_run"`
 108  }
 109  
 110  // DirectiveType classifies the kind of strategy instruction.
 111  type DirectiveType uint8
 112  
 113  const (
 114  	DirectiveSearch     DirectiveType = iota // web search query
 115  	DirectiveWalkWeight                      // file prioritization weights
 116  	DirectiveEnzyme                          // enzyme selection/params
 117  	DirectiveSubscribe                       // Nostr subscription filter
 118  	DirectiveFocus                           // general focus directive
 119  )
 120  
 121  // Directive is a strategy instruction derived from the reading.
 122  type Directive struct {
 123  	Type        DirectiveType     `json:"type"`
 124  	Query       string            `json:"query,omitempty"`
 125  	Domain      DomainType        `json:"domain"`
 126  	Intention   IntentionType     `json:"intention"`
 127  	Style       EngagementStyle   `json:"style"`
 128  	ChangingBit int               `json:"changing_bit"` // -1 = primary, 0-5 = changing line
 129  	Params      map[string]string `json:"params,omitempty"`
 130  	Completed   bool              `json:"completed"`
 131  }
 132