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},
  "reference" => {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,
  "reference" => :references,
  "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.

Emits a deprecation warning via ‘ClaudeMemory::Deprecations` when an actual synonym is hit, since the predicate vocabulary is part of the public API contract (`docs/api_stability.md` §6) and silent canonicalization makes the legacy form indistinguishable from the current one. Removal of the SYNONYMS entries is scheduled for `1.0.0`.



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 72

def self.canonicalize(predicate)
  return predicate if predicate.nil?
  canonical = SYNONYMS.fetch(predicate, predicate)
  if canonical != predicate
    ClaudeMemory::Deprecations.warn(
      name: "predicate=#{predicate}",
      replacement: "predicate=#{canonical}",
      removed_in: "1.0.0",
      message: "PredicatePolicy::SYNONYMS will be removed; emit canonical predicate names directly."
    )
  end
  canonical
end

.exclusive?(predicate) ⇒ Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 103

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

.known_predicatesObject



59
60
61
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 59

def self.known_predicates
  POLICIES.keys
end

.policy_for(predicate) ⇒ Object



95
96
97
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 95

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.



89
90
91
92
93
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 89

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)


99
100
101
# File 'lib/claude_memory/resolve/predicate_policy.rb', line 99

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