{"id":50529904,"url":"https://github.com/elchemista/spectre_mnemonic","last_synced_at":"2026-06-03T12:02:56.557Z","repository":{"id":354240453,"uuid":"1213849226","full_name":"elchemista/spectre_mnemonic","owner":"elchemista","description":"Memory  layer for Spectre Agent Framework","archived":false,"fork":false,"pushed_at":"2026-05-30T16:35:34.000Z","size":317,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T18:13:32.580Z","etag":null,"topics":["agents","ai-agents","elixir","rag"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elchemista.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-17T20:36:07.000Z","updated_at":"2026-05-30T16:35:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/elchemista/spectre_mnemonic","commit_stats":null,"previous_names":["elchemista/spectre_mnemonic"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/elchemista/spectre_mnemonic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elchemista%2Fspectre_mnemonic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elchemista%2Fspectre_mnemonic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elchemista%2Fspectre_mnemonic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elchemista%2Fspectre_mnemonic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elchemista","download_url":"https://codeload.github.com/elchemista/spectre_mnemonic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elchemista%2Fspectre_mnemonic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33863264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai-agents","elixir","rag"],"created_at":"2026-06-03T12:02:55.715Z","updated_at":"2026-06-03T12:02:56.550Z","avatar_url":"https://github.com/elchemista.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SpectreMnemonic\n\nSpectreMnemonic is an Elixir memory engine for live applications and agentic\nsystems. It keeps hot working memory in ETS, links moments into a graph,\npersists durable memory through append-only stores, and recalls useful context\nwith deterministic text matching, graph expansion, optional embeddings, and\ndurable hybrid search.\n\nIt is not a replacement for your application database. It is the memory layer\nbeside your framework: record what happened, recall nearby context, promote\nimportant moments, track stale or contradicted facts, and keep compact knowledge\navailable without hydrating every old event.\n\n```elixir\n{:ok, memory} =\n  SpectreMnemonic.remember(\"Alice email is alice@example.com\",\n    stream: :chat,\n    kind: :personal_fact,\n    persist?: true\n  )\n\n{:ok, packet} = SpectreMnemonic.recall(\"Alice email\")\n{:ok, results} = SpectreMnemonic.search(\"Alice email\")\n{:ok, durable} = SpectreMnemonic.consolidate()\n```\n\n## What It Gives You\n\n- `remember/2` for high-level intake: text, maps, parsed documents, chat,\n  tasks, research notes, code notes, and tool events.\n- `signal/2` for low-level event recording when the caller already knows the\n  stream, kind, task, and metadata.\n- Active ETS memory for recent moments, task status, graph associations,\n  artifacts, secrets, and action recipes.\n- Deterministic local recall through keywords, entities, fingerprints, and graph\n  expansion, even with no model configured.\n- Optional embedding recall through an adapter or local Model2Vec provider.\n- Built-in durable hybrid search over persisted records using BM25-style text\n  scoring plus vector/signature reranking when embeddings exist.\n- Scoped memory with optional `scope`, temporal validity fields, token-budget\n  recall, and budgeted retrieval depth.\n- Evidence-grounded observations and curated mental models for recall and\n  reflection over memory.\n- Governance state records: `:candidate`, `:short_term`, `:promoted`,\n  `:pinned`, `:stale`, `:contradicted`, and `:forgotten`.\n- Structured fact freshness and contradiction tracking for facts such as email,\n  phone, age, status, birthday, deadline, and owner.\n- Compact progressive knowledge in `knowledge.smem`.\n- Encrypted secret memories with authorization-aware reveal.\n- Plugs, adapters, and storage backends for framework-specific behavior.\n\n## Installation\n\nAdd the dependency:\n\n```elixir\ndef deps do\n  [\n    {:spectre_mnemonic, github: \"elchemista/spectre_mnemonic\"}\n  ]\nend\n```\n\nStart it as an OTP application or under your supervision tree. The default\napplication starts ETS ownership, persistence, the durable index, stream routing,\nactive focus, recall, consolidation, and the opt-in consolidation scheduler.\n\n## Quick Start\n\nUse `remember/2` for normal application memory:\n\n```elixir\n{:ok, packet} =\n  SpectreMnemonic.remember(\"TODO: implement durable graph search\",\n    title: \"Planner note\",\n    stream: :planning,\n    task_id: \"alpha\",\n    scope: {:project, \"alpha\"},\n    occurred_at: ~U[2026-05-30 10:00:00Z],\n    metadata: %{source: :agent},\n    persist?: true\n  )\n\npacket.root\npacket.chunks\npacket.summaries\npacket.categories\npacket.associations\n```\n\nUse `recall/2` for active context:\n\n```elixir\n{:ok, packet} =\n  SpectreMnemonic.recall(\"how is alpha going?\",\n    scope: {:project, \"alpha\"},\n    max_tokens: 2_000,\n    budget: :mid\n  )\n\npacket.moments\npacket.observations\npacket.mental_models\npacket.active_status\npacket.associations\npacket.knowledge\n```\n\n`max_tokens` is a best-effort packet budget. Recall may include one oversized\nprimary evidence item when excluding it would make the packet empty.\n\nUse `search/2` when you want active recall plus durable persisted memory:\n\n```elixir\n{:ok, results} = SpectreMnemonic.search(\"durable graph search\", limit: 10)\n\nEnum.map(results, \u0026{\u00261.source, \u00261.family, \u00261.id, \u00261.score})\n```\n\nUse `consolidate/1` to promote active memory into durable families:\n\n```elixir\n{:ok, knowledge} = SpectreMnemonic.consolidate(min_attention: 1.0)\n```\n\nUse `forget/2` to remove active memories and write tombstones:\n\n```elixir\nSpectreMnemonic.forget({:task, \"alpha\"})\nSpectreMnemonic.forget(\"mom_123\")\n```\n\n## Remember Plug Pipeline\n\n`remember/2` can run a composable plug pipeline before normal intake. This is\nthe first extension point to reach for when SpectreMnemonic is embedded inside a\nlarger framework.\n\nUse plugs for framework-specific routing, classification, metadata,\nsummarization, filtering, compression, secret detection, or replacing intake\nwith a final custom packet.\n\nConfigure global plugs:\n\n```elixir\nconfig :spectre_mnemonic,\n  plugs: [\n    MyApp.Memory.ProjectPlug,\n    {MyApp.Memory.SecretRouterPlug, providers: [:github, :stripe]}\n  ]\n```\n\nAdd per-call plugs:\n\n```elixir\nSpectreMnemonic.remember(\"sk_live_...\",\n  task_id: \"chat-123\",\n  plugs: [MyApp.Memory.SessionPlug],\n  secret_key: secret_key_32_bytes\n)\n```\n\nImplement a plug:\n\n```elixir\ndefmodule MyApp.Memory.ProjectPlug do\n  @behaviour SpectreMnemonic.Intake.Plug\n\n  @impl true\n  def call(memory, _opts) do\n    %{\n      memory\n      | metadata: Map.put(memory.metadata, :project, :billing),\n        tags: [:billing | memory.tags]\n    }\n  end\nend\n```\n\nPlugs may continue, halt, or return a final packet, moment, secret, or signal.\nLow-level `signal/2` does not run remember plugs.\n\n## Secret Memory\n\nSecret memory is first-class because agents and live apps often see tokens,\nkeys, passwords, credentials, or private notes while doing real work.\n\nSecrets are stored as encrypted `%SpectreMnemonic.Memory.Secret{}` structs. The\nindexed text is redacted, and plaintext is encrypted before it enters active ETS\nor durable persistence.\n\nRecommended flow:\n\n1. A remember plug detects the secret.\n2. The plug sets `memory.secret? = true` and `memory.label`.\n3. SpectreMnemonic encrypts the original text.\n4. Recall finds the redacted secret by label and metadata.\n5. Reveal requires application authorization.\n\nLow-level explicit secret storage:\n\n```elixir\n{:ok, %{moment: secret}} =\n  SpectreMnemonic.signal(\"github_pat_...\",\n    secret?: true,\n    label: \"GitHub token\",\n    secret_key: secret_key_32_bytes\n  )\n\nsecret.text\n#=\u003e \"secret: GitHub token\"\n```\n\nConfigure key access:\n\n```elixir\nconfig :spectre_mnemonic,\n  secret_key_fun: fn -\u003e MyApp.Keys.memory_secret_key() end\n```\n\nConfigure authorization:\n\n```elixir\nconfig :spectre_mnemonic,\n  secret_authorization_adapter: MyApp.SecretAuthorization\n```\n\nReveal:\n\n```elixir\n{:ok, revealed} =\n  SpectreMnemonic.reveal(secret,\n    secret_key: secret_key_32_bytes,\n    authorization_adapter: MyApp.SecretAuthorization,\n    authorization_context: %{user_id: current_user.id}\n  )\n```\n\nIf authorization is denied or missing, recall still succeeds and returns the\nlocked redacted secret.\n\n## Core Concepts\n\n### Active Memory\n\nActive memory is the hot working set in ETS. It stores signals, moments,\nassociations, artifacts, action recipes, attention, and task status.\n\n`signal/2` writes one moment directly:\n\n```elixir\n{:ok, %{moment: moment}} =\n  SpectreMnemonic.signal(\"implemented disk replay checksum\",\n    stream: :task_execution,\n    task_id: \"alpha\",\n    kind: :task_execution,\n    persist?: true,\n    metadata: %{source: :agent}\n  )\n```\n\n### Intake Memory\n\n`remember/2` is the higher-level intake path. It normalizes input, creates a\nroot moment, chunks long text, creates summaries and categories, extracts an\nentity timeline graph, and links the graph with typed associations.\n\nThe deterministic extractor handles names, ISO/month dates, simple events,\nemails, ages, numbers, and phone-like values. Phone-like values are redacted by\ndefault. Use:\n\n```elixir\nSpectreMnemonic.remember(text, sensitive_numbers: :raw)\nSpectreMnemonic.remember(text, sensitive_numbers: :skip)\nSpectreMnemonic.remember(text, extract_entities?: false)\n```\n\nMemory can be scoped without changing the existing stream/task model. A scope is\ncaller-owned data, such as a user, agent, tenant, or project tuple. Scoped recall\nonly searches matching memory; unscoped recall stays broad for backward\ncompatibility.\n\n```elixir\nSpectreMnemonic.remember(\"Payment retry policy is stable\",\n  scope: {:tenant, \"acme\"},\n  mission: :code_agent,\n  extraction_mode: :concise,\n  occurred_at: ~U[2026-05-01 12:00:00Z],\n  valid_from: ~U[2026-05-01 00:00:00Z],\n  persist?: true\n)\n\nSpectreMnemonic.recall(\"payment retry\",\n  scope: {:tenant, \"acme\"},\n  valid_at: ~U[2026-05-30 00:00:00Z]\n)\n```\n\n`mission:` is metadata by default. To let a mission affect intake retention,\nadd the opt-in mission policy plug:\n\n```elixir\nSpectreMnemonic.remember(\"TODO fix API retry contract\",\n  mission: :code_agent,\n  plugs: [SpectreMnemonic.Intake.MissionPolicy]\n)\n```\n\nThe built-in `:code_agent` policy drops low-value conversational filler and\nprioritizes technical decisions, bugs, API contracts, constraints, TODOs, user\npreferences, and project state.\n\nTemporal fields separate when something happened from when SpectreMnemonic\nlearned it:\n\n- `:occurred_at` - when the event or fact happened.\n- `:observed_at` - when memory observed or learned it.\n- `:last_verified_at` - when evidence was last verified.\n- `:valid_from` and `:valid_until` - when a fact/model should be treated as\n  true.\n\nFor richer extraction, configure an adapter:\n\n```elixir\nconfig :spectre_mnemonic,\n  entity_extraction_adapter: MyApp.MemoryExtractor\n```\n\nAdapters implement `SpectreMnemonic.Intake.Extraction.Adapter` and return graph\nfragments with `entities`, `events`, `times`, `values`, and `relations`.\n\n### Graph Associations\n\nMemories can be linked manually:\n\n```elixir\nSpectreMnemonic.link(source_id, :supported_by, target_id, weight: 0.8)\n```\n\nRecall expands through graph associations, so a task can bring in related\nresearch, code notes, artifacts, and action recipes.\n\n## Durable Persistence And Search\n\nThe default durable backend is an append-only local file store. Configure it\nexplicitly when you want a custom data root:\n\n```elixir\nconfig :spectre_mnemonic,\n  persistent_memory: [\n    write_mode: :all,\n    read_mode: :smart,\n    failure_mode: :best_effort,\n    stores: [\n      [\n        id: :local_file,\n        adapter: SpectreMnemonic.Persistence.Store.File,\n        role: :primary,\n        duplicate: true,\n        opts: [data_root: \"mnemonic_data\"]\n      ]\n    ]\n  ]\n```\n\nPersistent records are backend-neutral envelopes in families such as:\n\n- `:signals`\n- `:moments`\n- `:summaries`\n- `:categories`\n- `:embeddings`\n- `:associations`\n- `:knowledge`\n- `:observations`\n- `:mental_models`\n- `:memory_states`\n- `:consolidation_jobs`\n- `:semantic_compaction_jobs`\n- `:artifacts`\n- `:action_recipes`\n- `:tombstones`\n\n`SpectreMnemonic.Persistence.Manager.replay/1` replays durable envelopes and\napplies tombstones.\n\n### Built-in Durable Hybrid Search\n\nSpectreMnemonic keeps a rebuildable local durable index derived from replayed\npersistent records. The append-only store remains the source of truth.\n\nThe durable index scores with:\n\n- BM25-style full-text scoring\n- exact term overlap\n- entity overlap\n- vector cosine and binary-signature similarity when embeddings exist\n- lifecycle boosts and demotions from `:memory_states`\n\nDefault visibility:\n\n- `:forgotten` and `:contradicted` are hidden\n- `:stale` is demoted\n- `:promoted` is boosted\n- `:pinned` is strongly boosted\n\nThe public entrypoint stays simple:\n\n```elixir\n{:ok, results} = SpectreMnemonic.search(\"payment retry decision\", limit: 10)\n```\n\nRebuild the derived durable index if you manually changed durable storage:\n\n```elixir\nSpectreMnemonic.Durable.Index.rebuild()\n```\n\n### Observations, Mental Models, And Reflection\n\nObservations are consolidated beliefs built from existing moments. Fact\nobservations still come from governance facts, while deterministic V1 extraction\nalso recognizes preferences, decisions, patterns, and project state. Observation\ntype is stored in metadata as `:observation_type` so old `%Observation{}`\nrecords continue to work.\n\n```elixir\n{:ok, observations} =\n  SpectreMnemonic.consolidate_observations(scope: {:project, \"alpha\"})\n\n{:ok, matches} =\n  SpectreMnemonic.search_observations(\"payment retry\",\n    scope: {:project, \"alpha\"}\n  )\n\n{:ok, verified} =\n  SpectreMnemonic.verify_observation(hd(observations),\n    source_id: \"mom_123\",\n    relation: :supports\n  )\n```\n\nMental models are curated stable answers for recurring queries. They are stored\nthrough the same persistence and durable search machinery as other memory.\n\n```elixir\n{:ok, model} =\n  SpectreMnemonic.put_mental_model(%{\n    title: \"Payment Retry Policy\",\n    query: \"payment retry\",\n    answer: \"Use bounded retries with idempotency keys.\",\n    scope: {:project, \"alpha\"},\n    source_ids: [\"mom_123\"]\n  })\n\n{:ok, models} =\n  SpectreMnemonic.search_mental_models(\"payment retry\",\n    scope: {:project, \"alpha\"}\n  )\n```\n\n`reflect/2` gathers mental models first, ranked observations second, then raw\nrecall evidence. Observation evidence is ranked as decisions, preferences,\nproject state, patterns, then facts. Without an adapter it returns a structured\npacket. With an adapter it normalizes the adapter output into `packet.response`.\n`max_tokens` is forwarded to recall as a best-effort packet budget and may\ninclude one oversized primary evidence item when excluding it would make the\npacket empty.\n\n```elixir\n{:ok, packet} =\n  SpectreMnemonic.reflect(\"What is the payment retry policy?\",\n    scope: {:project, \"alpha\"},\n    max_tokens: 4_096\n  )\n\npacket.mental_models\npacket.observations\npacket.raw_memories\npacket.citations\npacket.response\n```\n\n### Compaction\n\nPhysical compaction writes snapshots for append-only local files:\n\n```elixir\nSpectreMnemonic.Persistence.Manager.compact(mode: :physical)\n```\n\nSemantic compaction asks a store or adapter to create compact records and\ntombstones:\n\n```elixir\nSpectreMnemonic.Persistence.Manager.compact(mode: :semantic)\nSpectreMnemonic.Persistence.Manager.compact(mode: :all)\n```\n\nConfigure a semantic compaction adapter when your application wants custom,\nLLM-backed, or database-native compaction:\n\n```elixir\nconfig :spectre_mnemonic,\n  persistent_memory: [\n    semantic_compact_adapter: MyApp.PersistentCompactAdapter,\n    semantic_compact_families: [\n      :moments,\n      :knowledge,\n      :summaries,\n      :categories,\n      :associations,\n      :memory_states\n    ],\n    semantic_compact_limit: 1_000\n  ]\n```\n\n## Governance, Freshness, And Contradictions\n\nGovernance is stored as append-only `:memory_states` records so existing memory\nstructs remain backward compatible.\n\nLifecycle states:\n\n```elixir\n[:candidate, :short_term, :promoted, :pinned, :stale, :contradicted, :forgotten]\n```\n\nWhen a persisted moment is observed, SpectreMnemonic writes a lifecycle state.\nConsolidation promotes selected moments. Forgetting writes `:forgotten`.\n\nPin important memories:\n\n```elixir\nSpectreMnemonic.signal(\"Payment retry policy is stable\",\n  persist?: true,\n  memory_state: :pinned\n)\n```\n\nInspect state:\n\n```elixir\nSpectreMnemonic.Governance.state_for(\"mom_123\")\n```\n\n### Structured Fact Upserts\n\nSpectreMnemonic detects simple entity facts such as:\n\n```text\nAlice email is alice@example.com\nDeploy deadline is 2026-06-01\nTask42 status is blocked\n```\n\nThe upsert key is `{normalized_subject, attribute}`. A newer conflicting value\nmarks the older fact `:contradicted` and promotes the newer fact.\n\n```elixir\n{:ok, %{moment: old}} =\n  SpectreMnemonic.signal(\"Alice email is old@example.com\", persist?: true)\n\n{:ok, %{moment: new}} =\n  SpectreMnemonic.signal(\"Alice email is new@example.com\", persist?: true)\n\nSpectreMnemonic.Governance.state_for(old.id)\n#=\u003e :contradicted\n\nSpectreMnemonic.Governance.state_for(new.id)\n#=\u003e :promoted\n```\n\nPinned facts are not replaced automatically.\n\n### Provenance\n\nGenerated and persisted records carry provenance in `metadata.provenance`:\n\n```elixir\n%{\n  source_ids: [\"mom_123\"],\n  source_span: nil,\n  provider: :consolidator,\n  confidence: 1.0,\n  occurred_at: ~U[...],\n  observed_at: ~U[...],\n  last_verified_at: ~U[...],\n  valid_from: ~U[...],\n  valid_until: ~U[...]\n}\n```\n\nUse provenance to explain why a recalled fact exists and whether it was\ngenerated, extracted, verified, or compacted.\n\n## Background Consolidation Scheduler\n\nThe scheduler is supervised but disabled by default. Enable it through config:\n\n```elixir\nconfig :spectre_mnemonic,\n  consolidation_scheduler: [\n    enabled: true,\n    interval_ms: 300_000,\n    mode: :all,\n    min_attention: 1.0,\n    stale_after_ms: 30 * 24 * 60 * 60 * 1_000\n  ]\n```\n\nEach tick can:\n\n- run consolidation\n- run freshness decay\n- mark old unverified facts `:stale`\n- compact persistent memory\n- rebuild the durable search index\n\nCheck status:\n\n```elixir\nSpectreMnemonic.ConsolidationScheduler.status()\n```\n\n## Progressive Knowledge\n\n`knowledge.smem` is a compact append-only knowledge log stored at\n`data_root/knowledge/knowledge.smem`. It is separate from active ETS memory and\nfrom the durable persistent-memory families.\n\nSupported event types:\n\n- `:summary`\n- `:skill`\n- `:latest_ingestion`\n- `:fact`\n- `:procedure`\n- `:compaction_marker`\n\nAppend compact events:\n\n```elixir\nSpectreMnemonic.Knowledge.Base.append(%{\n  type: :skill,\n  name: \"Replay durable storage\",\n  text: \"Use SpectreMnemonic.Persistence.Manager.replay/1 to inspect records.\",\n  metadata: %{attention: 2.0}\n})\n```\n\nTeach a reusable skill:\n\n```elixir\n{:ok, learned} =\n  SpectreMnemonic.learn(\"\"\"\n  Debug local replay\n  - inspect active.smem\n  - check tombstones\n  - compare replayed ids\n  \"\"\")\n\nlearned.event.name\nlearned.event.steps\n```\n\nSearch compact knowledge without loading the whole packet:\n\n```elixir\n{:ok, matches} = SpectreMnemonic.search_knowledge(\"replay storage\", limit: 5)\n```\n\nLoad a budgeted packet:\n\n```elixir\n{:ok, knowledge} =\n  SpectreMnemonic.load_knowledge(\n    max_loaded_bytes: 8_000,\n    max_skills: 10,\n    max_latest_ingestions: 10\n  )\n```\n\nCompact progressive knowledge:\n\n```elixir\nSpectreMnemonic.compact_knowledge()\n```\n\nConfigure a custom compact adapter:\n\n```elixir\nconfig :spectre_mnemonic,\n  compact_adapter: MyApp.KnowledgeCompactAdapter\n```\n\n## Embeddings\n\nEmbeddings are optional. Without an adapter, recall and search still work\nthrough text, fingerprints, graph associations, and durable BM25-style scoring.\n\nConfigure a custom adapter:\n\n```elixir\nconfig :spectre_mnemonic,\n  embedding_adapter: MyApp.EmbeddingAdapter\n```\n\nAdapters implement `SpectreMnemonic.Embedding.Adapter.embed/2` and return\n`{:ok, vector}`, `{:ok, embedding_map}`, or `{:error, reason}`.\n\nEnable the local Model2Vec provider:\n\n```elixir\nconfig :spectre_mnemonic,\n  embedding: [\n    fast: [\n      enabled: true,\n      model_id: \"minishlab/potion-base-8M\",\n      download: true\n    ]\n  ]\n```\n\nDownloads are opt-in. For production, pre-populate the cache or pass\n`:model_dir`.\n\nConsolidation does not re-embed text. It copies the `vector`,\n`binary_signature`, and `embedding` already stored on each moment.\n\n## Action Recipes\n\nMemories and artifacts can carry inert Action Language recipes. SpectreMnemonic\nstores and recalls these recipes as data only. It does not execute them.\n\n```elixir\n{:ok, %{moment: moment, action_recipe: recipe}} =\n  SpectreMnemonic.signal(\"cached weather JSON for Rome\",\n    action_recipe: \"When Kinetic asks, refresh JSON from the weather endpoint\",\n    action_intent: \"refresh cached JSON\",\n    ttl_ms: 60_000,\n    refresh_on_recall?: true,\n    source_url: \"https://api.example.test/weather\",\n    tags: [:weather, :json]\n  )\n\n{:ok, packet} = SpectreMnemonic.recall(\"weather JSON Rome\")\nEnum.map(packet.action_recipes, \u0026 \u00261.text)\n```\n\nExecution is delegated only when you configure an adapter:\n\n```elixir\nconfig :spectre_mnemonic,\n  action_runtime_adapter: MyApp.KineticRuntime\n```\n\n## Runnable Example\n\nThe `example/` folder contains a local demo:\n\n- parses `test.txt`, `tasks.txt`, and `chat.txt`\n- writes a tiny local Model2Vec fixture\n- remembers fixture events in parallel\n- creates chunks, summaries, categories, extraction nodes, and graph edges\n- persists active and consolidated records to append-only local storage\n- demonstrates governed facts, contradiction, pinned/stale states, and durable\n  hybrid search\n- writes compact progressive knowledge\n- registers an artifact\n- runs replay, search, compaction, scheduler status, and evaluation output\n\nRun it:\n\n```bash\nmix run example/demo.exs\n```\n\nExpected output includes lines like:\n\n```text\nmodel        Smoke test vector_dims=4 signature_bytes=1\nremembered   ... chunks=1 summaries=2 categories=... edges=...\ngovernance   old=.../contradicted new=.../promoted pinned=.../pinned stale=.../stale\nhybrid       source=persistent family=moments state=promoted score=...\nknowledge    search \"durable replay storage\" -\u003e ... compact matches\nreplay       Loaded ... records from .../example/mnemonic_data/segments/active.smem\ncompact      example_file snapshot=.../example/mnemonic_data/snapshots/snapshot-...\neval         size=6 recall_accuracy=... exact_fact_recall=... latency_ms=...\n```\n\nGenerated runtime data goes under `example/mnemonic_data/`.\n\n## Evaluation And Development\n\nRun the deterministic evaluation harness from IEx or your own test code:\n\n```elixir\nSpectreMnemonic.Evaluation.run(size: 100)\n```\n\nIt reports:\n\n- recall accuracy\n- exact fact recall\n- latency in milliseconds\n\nFor development:\n\n```bash\nmix format\nmix credo --strict\nmix dialyzer\nmix test\n```\n\n## Project Layout\n\n- `lib/spectre_mnemonic.ex` is the public facade.\n- `lib/spectre_mnemonic/active/*` owns hot ETS focus, routing, and stream\n  workers.\n- `lib/spectre_mnemonic/durable/*` owns derived durable search indexes.\n- `lib/spectre_mnemonic/governance.ex` owns lifecycle states, provenance, and\n  structured fact contradiction logic.\n- `lib/spectre_mnemonic/observations.ex` and\n  `lib/spectre_mnemonic/mental_models.ex` own evidence-grounded observations\n  and curated mental models.\n- `lib/spectre_mnemonic/reflection*` builds reflection packets and delegates to\n  optional reflection adapters.\n- `lib/spectre_mnemonic/consolidation_scheduler.ex` owns opt-in background\n  consolidation and freshness decay.\n- `lib/spectre_mnemonic/intake*` powers `remember/2`, plugs, extraction, and\n  intake packets.\n- `lib/spectre_mnemonic/recall/*` builds recall packets, cues, fingerprints,\n  and active embedding indexes.\n- `lib/spectre_mnemonic/knowledge/*` loads `knowledge.smem`, compacts\n  progressive knowledge, and consolidates active graph memory into durable\n  families.\n- `lib/spectre_mnemonic/persistence/*` coordinates durable stores, records,\n  codecs, compaction, and storage behaviours.\n- `lib/spectre_mnemonic/embedding/*` contains embedding adapters, vector math,\n  binary quantization, and Model2Vec helpers.\n- `lib/spectre_mnemonic/actions/*` delegates optional Action Language analysis\n  and execution to an explicitly configured runtime adapter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felchemista%2Fspectre_mnemonic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felchemista%2Fspectre_mnemonic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felchemista%2Fspectre_mnemonic/lists"}