Class: ClaudeMemory::Resolve::PredicatePolicy

Inherits:
Object
  • Object
show all
Defined in:
lib/claude_memory/resolve/predicate_policy.rb

Constant Summary collapse

POLICIES =

Canonical predicate vocabulary. Curated after a multi-project survey of real memory databases under ~/src — predicates with zero facts across every database were pruned; predicates observed in the wild but missing from the policy (architecture, uses_language) were added.

  • convention / decision: workhorse multi-value predicates

  • uses_framework / uses_language: multi-value (projects use multiple)

  • uses_database / deployment_platform / auth_method: single-value, correctly 1:1 per project in observed data

  • architecture: multi-value structural knowledge (was implicit)

{
  "convention" => {cardinality: :multi, exclusive: false},
  "decision" => {cardinality: :multi, exclusive: false},
  "architecture" => {cardinality: :multi, exclusive: false},
  "uses_framework" => {cardinality: :multi, exclusive: false},
  "uses_language" => {cardinality: :multi, exclusive: false},
  "uses_database" => {cardinality: :single, exclusive: true},
  "deployment_platform" => {cardinality: :single, exclusive: true},
  "auth_method" => {cardinality: :single, exclusive: true}
}.freeze
DEFAULT_POLICY =
{cardinality: :multi, exclusive: false}.freeze
SYNONYMS =

Drift canonicalization. Maps predicate names the distiller has organically coined onto the canonical form in POLICIES. Consulted at insert time by the Resolver so synonym drift never fragments the knowledge graph.

Entries observed in real project DBs:

  • has_convention (chaos_to_the_rescue): prefix-drift of convention

  • primary_language (prior policy entry): supplanted by uses_language which the distiller emits naturally and has multi-value semantics

{
  "has_convention" => "convention",
  "primary_language" => "uses_language"
}.freeze
SECTION_MAP =

Section classification for the published snapshot. Keeps Publish from hard-coding predicate names; adding a new predicate to the policy and the section map in one place updates everything.

{
  "decision" => :decisions,
  "convention" => :conventions,
  "uses_database" => :constraints,
  "uses_framework" => :constraints,
  "uses_language" => :constraints,
  "deployment_platform" => :constraints,
  "auth_method" => :constraints
  # architecture intentionally falls through to :additional for now
}.freeze

Class Method Summary collapse

Class Method Details

.canonicalize(predicate) ⇒ Object

Return the canonical form of a predicate name, applying known synonym mappings. Leaves unmapped predicates unchanged.



63
64
65
66
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 63

def self.canonicalize(predicate)
  return predicate if predicate.nil?
  SYNONYMS.fetch(predicate, predicate)
end

.exclusive?(predicate) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 85

def self.exclusive?(predicate)
  policy_for(predicate)[:exclusive]
end

.known_predicatesObject



57
58
59
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 57

def self.known_predicates
  POLICIES.keys
end

.policy_for(predicate) ⇒ Object



77
78
79
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 77

def self.policy_for(predicate)
  POLICIES.fetch(predicate, DEFAULT_POLICY)
end

.section_for(predicate) ⇒ Object

Return the snapshot section a predicate belongs to. Respects legacy prefix/suffix patterns (decided_*, *_convention) that pre-date the policy.



71
72
73
74
75
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 71

def self.section_for(predicate)
  return :decisions if predicate&.start_with?("decided_")
  return :conventions if predicate&.include?("_convention")
  SECTION_MAP.fetch(predicate, :additional)
end

.single?(predicate) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 81

def self.single?(predicate)
  policy_for(predicate)[:cardinality] == :single
end