{"_id":"@alexshrestha/marble","_rev":"2-91c119345fc6304ed71575820f7a1a2b","name":"@alexshrestha/marble","dist-tags":{"latest":"0.4.0"},"versions":{"0.3.0":{"name":"@alexshrestha/marble","version":"0.3.0","keywords":["simulation","personalization","person-synthesis","knowledge-graph","user-modeling","ai","recommendations","vector-search","link-prediction"],"author":{"name":"Alex Shrestha"},"license":"MIT","_id":"@alexshrestha/marble@0.3.0","maintainers":[{"name":"alekshresth","email":"alex.shresthax@gmail.com"}],"homepage":"https://github.com/AlexShrestha/marble#readme","bugs":{"url":"https://github.com/AlexShrestha/marble/issues"},"bin":{"marble":"bin/marble.mjs"},"dist":{"shasum":"7e19951f2fbf41f39e8d35b66aad1c4e1e9f62c9","tarball":"https://registry.npmjs.org/@alexshrestha/marble/-/marble-0.3.0.tgz","fileCount":67,"integrity":"sha512-78S4x8cRNIeJ1ZJH5icMg1GDhl2RqYg0iXgyPwwAmY4pQ84h7Qsg6J4AebDDjIjCvv80GVN116ggKWGNewQ03A==","signatures":[{"sig":"MEUCIQCoLRR78EctgTNL6ptT7LWfAmb4QcgfeYfhcKqYJK0QkgIgMGIa7DH4qG9qWT3gdZeQEvJxr2TTcCUCXZggR989JQY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1275200},"main":"core/index.js","type":"module","engines":{"node":">=20.0.0"},"gitHead":"0ba85bc72f98c0b4324ab60346053b1c3b0e4e91","scripts":{"web":"node web/index.js","test":"node --test test/*.test.js","start":"node api/server.js","web:reader":"node web/reader.js","web:tracker":"node web/tracker.js","web:dashboard":"node web/dashboard.js"},"_npmUser":{"name":"alekshresth","email":"alex.shresthax@gmail.com"},"repository":{"url":"git+https://github.com/AlexShrestha/marble.git","type":"git"},"_npmVersion":"10.8.2","description":"Hyper-personalized content curation through person synthesis and clone-population simulation.","directories":{},"_nodeVersion":"20.18.3","dependencies":{"cors":"^2.8.5","openai":"^6.33.0","express":"^4.18.2","node-fetch":"^3.3.2","rss-parser":"^3.13.0","@anthropic-ai/sdk":"^0.39.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/marble_0.3.0_1779446020106_0.6901479511271724","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@alexshrestha/marble","version":"0.4.0","description":"Hyper-personalized content curation through person synthesis and clone-population simulation.","type":"module","main":"core/index.js","exports":{".":"./core/index.js","./worldsim":"./worldsim/index.js","./mcp-tools":"./core/mcp-tools.js","./cli":"./bin/marble.mjs","./core/*":"./core/*","./package.json":"./package.json"},"bin":{"marble":"bin/marble.mjs","marble-mcp":"bin/marble-mcp.mjs"},"scripts":{"test":"node --test test/*.test.js test/*.test.mjs","test:manual":"echo \"Legacy standalone scripts in test/manual/ are NOT part of CI. Run individually, e.g. node test/manual/test-acceptance.js — see test/manual/README.md\""},"keywords":["simulation","personalization","person-synthesis","knowledge-graph","user-modeling","ai","recommendations","vector-search","link-prediction"],"author":{"name":"Alex Shrestha"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/AlexShrestha/marble.git"},"homepage":"https://github.com/AlexShrestha/marble#readme","bugs":{"url":"https://github.com/AlexShrestha/marble/issues"},"engines":{"node":">=20.0.0"},"publishConfig":{"access":"public"},"dependencies":{"@anthropic-ai/sdk":"^0.39.0","openai":"^6.33.0"},"peerDependencies":{"@modelcontextprotocol/sdk":"^1.0.0"},"peerDependenciesMeta":{"@modelcontextprotocol/sdk":{"optional":true}},"_id":"@alexshrestha/marble@0.4.0","gitHead":"174e377e4b6bc4facf2fe43154c2c00553b00a6f","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-9PUXb+/POtUmQwplqLSvChW/d8HoBmHdLLFvexz3dSRRpZ9x6Dw0fjVyTImE8W/yEkk7qPea6hmDj0kheNt2ug==","shasum":"508d4e7a14c44962a6118d104b9a2cb2c735f4c1","tarball":"https://registry.npmjs.org/@alexshrestha/marble/-/marble-0.4.0.tgz","fileCount":67,"unpackedSize":1313639,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC4t4di1DMMlRmbzHPniYxTKzbhRm1DjlRZiL96LddJBwIhALMpdQ5QqYv7QJqu2XXkxwek0nv/i02iAcRcUKQdOBQe"}]},"_npmUser":{"name":"alekshresth","email":"alex.shresthax@gmail.com"},"directories":{},"maintainers":[{"name":"alekshresth","email":"alex.shresthax@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/marble_0.4.0_1784039694547_0.31165129478365095"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-22T10:33:40.005Z","modified":"2026-07-14T14:34:54.818Z","0.3.0":"2026-05-22T10:33:40.266Z","0.4.0":"2026-07-14T14:34:54.690Z"},"bugs":{"url":"https://github.com/AlexShrestha/marble/issues"},"author":{"name":"Alex Shrestha"},"license":"MIT","homepage":"https://github.com/AlexShrestha/marble#readme","keywords":["simulation","personalization","person-synthesis","knowledge-graph","user-modeling","ai","recommendations","vector-search","link-prediction"],"repository":{"type":"git","url":"git+https://github.com/AlexShrestha/marble.git"},"description":"Hyper-personalized content curation through person synthesis and clone-population simulation.","maintainers":[{"name":"alekshresth","email":"alex.shresthax@gmail.com"}],"readme":"# Marble\n\n[![CI](https://github.com/AlexShrestha/marble/workflows/CI/badge.svg)](https://github.com/AlexShrestha/marble/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/badge/npm-0.4.0-blue)](https://www.npmjs.com/package/@alexshrestha/marble)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node.js 20+](https://img.shields.io/badge/node-20+-brightgreen)](https://nodejs.org/)\n\n**Hyper-personalized content curation through person synthesis and simulation.**\n\n```javascript\nimport { Marble } from '@alexshrestha/marble';\n\nconst marble = new Marble({\n  storage: './user-kg.json',\n  llm: async (prompt) => callYourLLM(prompt),\n});\nawait marble.init();\n\n// Rank items — second arg is ephemeral context, not user data.\n// User data is built up through react() / feedbackBatch() / learn().\nconst top10 = await marble.select(items, {\n  calendar: ['investor call 14:00'],\n  active_projects: ['launch prep'],\n});\n```\n\nMarble creates multiple simulated versions of a user, tests them against real-world signals, and learns which version predicts their actual behavior. No thumbs-up buttons needed.\n\n## Why Marble?\n\n### Works from User One\nCreates multiple synthetic versions of a user, tests them against real signals, and evolves the best-predicting clone daily — no large user base required.\n\n### Predicts from Day Zero\nCreates personalized recommendations within hours of signup—from just 3 interactions, Marble synthesizes preferences across timing, context, and novelty without waiting for behavioral patterns.\n\n### Explains the WHY\nHypothesis-driven insights with confidence scores — \"Why this matters to your specific goals right now.\"\n\n### Synthesizes Missing Intelligence\nGenerates insights about relationships, timing, and stakeholder concerns you never explicitly provided.\n\n### Models Your Network\nUnderstands the people who influence your decisions and tailors recommendations for multi-party dynamics.\n\n**The result:** Content that feels like *\"How did it know I needed to see this today?\"*\n\n## Marble vs. The Competition\n\n## What Marble Does\n\n| Capability | How |\n|------------|-----|\n| **Day-one intelligence** | Synthetic clones work immediately — no cold start |\n| **Temporal awareness** | Considers calendar, deadlines, project phases |\n| **Relationship modeling** | Understands stakeholder concerns and decision dynamics |\n| **Business outcome focus** | Optimizes for your KPIs, not generic engagement |\n| **Predictive reasoning** | \"Why this will help your meeting\" — not just similarity scores |\n| **Privacy-first** | Runs locally, no data upload required |\n\n### Real-World Scenarios\n\n**Day-Zero Personalization**\nNew user signs up, visits 3 stories on AI safety and startup hiring. Marble generates a clone, tests it against the stories they engaged with, surfaces \"Engineering culture during scaling\" before they ask for it.\n\n**Implicit Preference Learning**\nNo rating buttons. Marble reads dwell time (45 sec on AI policy piece), skip pattern (2 sec on crypto), and detects emerging interests—then predicts what's relevant tomorrow based on what resonates today.\n\n**Temporal & Context Evolution**\nContinuously refines predictions by moment—morning check: growth strategies, afternoon: customer success challenges, as context shifts.\n\n**Zero Data Cold Start**\nImmediate personalization through synthetic clone evolution — no waiting for behavior data.\n\n## Technical Architecture\n```javascript\n// Marble approach\nconst contextGraph = {\n  interests: { ai: 0.8, startups: 0.6 },\n  calendar: [{ event: \"investor_pitch\", time: \"today 2pm\" }],\n  relationships: {\n    skeptical_cto: { concerns: [\"security\"], influence: 0.9 }\n  },\n  activeProjects: [{ name: \"product_launch\", deadline: \"2026-04-15\" }]\n};\n// Predict based on business context + psychology\n```\n\n### 7-Dimensional Scoring vs. Similarity Matching\n```javascript\n// CF: Single similarity score\nscore = cosineSimilarity(userPrefs, itemFeatures);\n\n// Marble: Multi-dimensional business intelligence\nmagic_score = interest(0.25) + temporal(0.30) + novelty(0.20)\n            + actionability(0.15) + source_trust(0.10)\n            × freshness_decay × stakeholder_alignment;\n```\n\n**Why competitors can't easily copy this:** Requires rebuilding recommendation infrastructure from scratch—context graphs, business metric optimization, relationship modeling, and temporal intelligence. Not a feature add to existing systems.\n\n## Quick Start\n\n```bash\nnpm install @alexshrestha/marble\n```\n\n```javascript\nimport { Marble } from '@alexshrestha/marble';\n\nconst marble = new Marble({\n  storage: './user-kg.json',\n  llm: async (prompt) => callYourLLM(prompt), // enables L1.5-L3 pipeline\n});\nawait marble.init();\n\n// (Optional) Ingest existing user data\nawait marble.ingestConversations('./chatgpt-export.json');\n\n// Rank items. Second arg is ephemeral context — calendar/projects/mood,\n// not user profile data.\nconst ranked = await marble.select(items, {\n  calendar: ['investor call 14:00'],\n  active_projects: ['launch prep'],\n});\n\nranked.forEach((item, i) => {\n  console.log(`${i+1}. [${item.relevance_score.toFixed(3)}] ${item.title}`);\n});\n\n// Record a reaction — pass the full item, not just the id\nawait marble.react(items[0], 'up');\n\n// Or process an entire batch at once (contrastive learning: Day 2 > Day 1)\nawait marble.feedbackBatch([\n  { item: items[0], reaction: 'up' },\n  { item: items[1], reaction: 'skip' },\n  { item: items[2], reaction: 'share' },\n]);\n\n// Run the canonical learning pipeline. `learn()` is the one entry point\n// you need to call — it runs every layer in order:\n//   seedClones → L1.5 insight swarm → L2 inference → L3 clone evolution\n//   → refreshClones → rebuildVectorIndex → cluster → predictLinks\n//   → hypothesisTesting\n// Each post-evolution stage is idempotent and graceful — they skip\n// cleanly when their inputs aren't ready (no embeddings provider, no\n// vector index, etc.). No separate orchestrator call needed.\nconst stats = await marble.learn();\n// { insights: 7, candidates: 4, clones: 12 }\n\n// (Optional) L2 trait synthesis — derives structured traits with replication,\n// contradiction, and emergent-fusion origins. Persists to kg.user.syntheses[].\nawait marble.synthesize();\n\n// (Optional) Churn scan + salience diagnostic. Detects \"serial pivoter\"-style\n// traits that live in the time series of belief invalidations, and reports\n// how many of the KG's nodes are stale-active one-offs.\nconst { churnSyntheses, distribution } = await marble.rebuild();\n```\n\n> **`learn()` is required for the \"Day 2 > Day 1\" progressive improvement claim.**\n> `react()` and `feedbackBatch()` record signals into the KG, but the clone\n> population, inference engine, and insight swarm only update when you call\n> `learn()`. A typical integration calls `learn()` after every N reactions\n> (e.g. N=10) or on a daily schedule. Without it, ranking relies on interest\n> aggregation alone and will not show clone-driven improvements over time.\n>\n> **Caveat — clones add the most value on warmer profiles.** With ≥ ~20\n> signals and items that carry real category labels, clone consensus\n> reranks correctly and warm > cold (validated on Last.fm — see\n> [Validation](#validation)). On very thin cold-start data (3–10 history\n> items, no real category metadata), the LLM-driven `seedClones` step can\n> introduce variance large enough to hurt top-K. Lower `cloneBoostWeight`\n> or gate clone consensus until the KG has enough signal if you see this.\n>\n> **`synthesize()` and `rebuild()` are optional** and run on their own schedule —\n> `synthesize()` is LLM-heavy (per-node trait extraction) so daily/weekly is\n> typical; `rebuild()` is cheap and deterministic, safe to run on every\n> `learn()` or on a cron.\n\n**Run tests:**\n\n```bash\ngit clone https://github.com/AlexShrestha/marble.git\ncd marble && npm install && npm test\n```\n\n## Features\n\n- **Pluggable providers** — Anthropic, OpenAI, DeepSeek, or any OpenAI-compatible host (Moonshot, Together, Fireworks, Groq, OpenRouter, Azure, vLLM) for LLM; OpenAI or DeepSeek for embeddings\n- **Privacy-first** — All user state stays on your machine; only per-item scoring/enrichment calls go out to the provider you configure\n- **Three modes** — Score (fast), Swarm (rich), WorldSim (B2B PMF)\n- **Implicit learning** — Learns from dwell time, scroll depth, forwards, silence\n- **Insight-driven KG** — Reasons about WHY, not just WHAT (see [docs/insight-kg.md](docs/insight-kg.md))\n- **Trait synthesis** — Structured cross-domain traits with five origin types (`single_node`, `trait_replication`, `contradiction`, `emergent_fusion`, `churn_pattern`) — downstream tools match against `traits` / `affinities` / `aversions` as predicates, not prose labels\n- **Salience-aware** — `getTopSalient()` filters for important nodes before any pairwise pass; stale one-off facts fade automatically; churn scan surfaces \"serial pivoter\" traits that live in the time series of invalidations\n- **Relationship-aware** — Models the people in a user's life to improve recommendations\n- **Narrative arc** — Stories sequenced for flow, not just ranked by score\n\n## How It Works\n\n```\n┌──────────────────────────────────────────────────┐\n│  1. GATHER                                        │\n│  RSS, HN, NewsAPI + World Signals (trends,        │\n│  search volume, social velocity)  ~100 stories    │\n├──────────────────────────────────────────────────┤\n│  2. SCORE / SWARM                                 │\n│  Score: magic_score formula (embeddings-based)     │\n│  Swarm: 5 agents evaluate through different lenses │\n├──────────────────────────────────────────────────┤\n│  3. ARC REORDER                                   │\n│  Sequence into narrative flow (opener → closer)    │\n├──────────────────────────────────────────────────┤\n│  4. DELIVER                                       │\n│  Telegram, Email, JSON API, Webhook, Video         │\n├──────────────────────────────────────────────────┤\n│  5. LEARN                                         │\n│  L1.5 Insight Swarm (7 psychological lenses)       │\n│  → L2 Inference + Temporal Patterns                │\n│  → L3 Clone Evolution (kill bottom 20%, mutate)    │\n├──────────────────────────────────────────────────┤\n│  6. SYNTHESIZE (optional, LLM-heavy)              │\n│  Trait extraction → Replication grouping           │\n│  → Contradiction detection → K-way fusion          │\n│  → kg.user.syntheses[] (5 origin types)            │\n├──────────────────────────────────────────────────┤\n│  7. REBUILD (optional, deterministic)             │\n│  Churn scan (slots reassigned ≥3× in 180d)         │\n│  + Salience distribution diagnostic                │\n└──────────────────────────────────────────────────┘\n```\n\n### Three Modes\n\n| Mode | What it does | Use case |\n|------|-------------|----------|\n| **Score** (v1) | Deterministic scoring against user KG | Fast, predictable, no API calls |\n| **Swarm** (v2) | Multi-agent evaluation with 6 specialized lenses (injectable — see `new Swarm(kg, { lenses })`) | Richer selection, catches what scoring misses |\n| **Debate** (v2+) | Swarm + a second LLM round on items where agents disagree (variance > 0.04) | When divergent agent opinions should be reconciled, not just averaged |\n| **WorldSim** (v3) | Population-level simulation for product-market fit | B2B — \"which users for this product?\" |\n\n### The Magic Score\n\n```\nmagic_score = interest(0.25) + temporal(0.30) + novelty(0.20)\n            + actionability(0.15) + source_trust(0.10)\n            × freshness_decay\n```\n\n- **Interest match (25%)** — Semantic similarity via local ONNX embeddings\n- **Temporal relevance (30%)** — Is this relevant TODAY? (calendar, projects, deadlines)\n- **Novelty (20%)** — Surprise factor (inverse topic frequency)\n- **Actionability (15%)** — Can the user act on this?\n- **Source trust (10%)** — Learned per-source credibility\n\n### Swarm Agents (default lens set)\n\nSix agents, each asking a different question. The set is the default when `new Swarm(kg)` is constructed without a `lenses` option; callers can inject their own lens array for tailored/per-user curation.\n\n| Agent | Weight | Question |\n|-------|--------|----------|\n| Career | 25% | \"Will this help their business?\" |\n| Timing | 25% | \"Does this matter TODAY specifically?\" |\n| Serendipity | 20% | \"Would this delight them unexpectedly?\" |\n| Growth | 15% | \"Will this stretch their thinking?\" |\n| Contrarian | 15% | \"What is everyone else missing?\" |\n| Social Proof | 10% | \"How well-received is this among the broader population and similar users?\" |\n\n> **Note on \"swarm\" naming.** Marble has three distinct systems all called \"swarm\" — this one (narrative curation with static-by-default lenses), `generateAgentFleet` (programmatic per-story scoring, always dynamic), and `runInsightSwarm` (L1.5 psychological probing, always dynamic). They don't share wiring. See [docs/architecture.md](docs/architecture.md#swarm-systems--three-parallel-purpose-built-layers) for the distinction.\n\n## Architecture\n\n```\nmarble/\n├── core/                   # The engine (standalone)\n│   ├── index.js           # Main Marble class — select/react/learn/synthesize/rebuild\n│   ├── kg.js              # Insight-driven knowledge graph (v2)\n│   ├── scorer.js          # magic_score computation\n│   ├── swarm.js           # Multi-agent curation (5 lenses)\n│   ├── insight-swarm.js   # L1.5 psychological probe committee (7 lenses)\n│   ├── inference-engine.js# L2 inference: L1.5 passthrough + temporal patterns\n│   ├── trait-synthesis.js # L2 trait synthesis (4 origins) — per-node extraction,\n│   │                      # replication, contradiction, K-way fusion\n│   ├── salience.js        # Salience scoring + churn scan (5th origin:\n│   │                      # churn_pattern) + getTopSalient/salienceDistribution\n│   ├── clone.js           # Digital twin — user snapshot for simulation\n│   ├── evolution.js       # Clone population evolution\n│   ├── arc.js             # Narrative arc reranking (10 slots)\n│   ├── decay.js           # Exponential decay (14-day half-life)\n│   ├── embeddings.js      # Local ONNX embeddings (384-dim)\n│   └── types.js           # Type definitions, weights\n│\n├── worldsim/            # World Clone — B2B product-market fit\n│   ├── archetypes.js    # Synthetic user population\n│   ├── pmf.js           # PMF analysis engine\n│   └── index.js         # WorldSim class\n│\n├── test/                # Test harness (30 stories)\n├── examples/            # Integration examples\n└── docs/                # Detailed documentation\n    ├── architecture.md\n    ├── api-reference.md\n    ├── insight-kg.md\n    ├── archetypes-relationships.md\n    └── contributing.md\n```\n\n## Core Concepts\n\n### Knowledge Graph (Insight-Driven)\n\nNot a flat interest tracker. Marble's KG generates hypotheses about WHY a user cares about something, then tests those hypotheses with content.\n\n```\n         YOU (root)\n        / | \\   \\\n projects interests people calendar\n    |        |        |       |\n\"side-app\" \"AI/ML\" \"co-founder\" \"call 14:00\"\n    |        |        |       |\n[stories connecting to these nodes score higher]\n```\n\nEvery signal triggers hypothesis generation, not just a weight increment. See [docs/insight-kg.md](docs/insight-kg.md) for the full deep-dive.\n\n### Digital Twin (Clone)\n\nA synthetic snapshot of the user for simulation. Captures weighted interests, behavioral patterns, today's context, and source trust. The evolution engine spawns N variants and kills the bottom 20% per cycle — survivors converge on real preferences over repeated `learn()` cycles as the KG accumulates signal.\n\n### Narrative Arc\n\nTop 10 stories aren't just ranked — they're sequenced:\n\n| Position | Role | Purpose |\n|----------|------|---------|\n| 1 | Opener | High energy, attention-grabbing |\n| 2 | Bridge | Transition to substance |\n| 3-4 | Deep dives | Core insights |\n| 5 | Pivot | Change of pace, surprise |\n| 6 | Deep dive | Third substantive piece |\n| 7 | Practical | Actionable, how-to |\n| 8 | Horizon | Future-looking |\n| 9 | Personal | Close to home |\n| 10 | Closer | Warm, human, memorable |\n\n### Signal Layers\n\nNo thumbs-up/down needed. Three layers of implicit feedback:\n\n| Layer | Weight | Signals | User effort |\n|-------|--------|---------|-------------|\n| **World** | ~80% | Trends, search volume, social velocity | Zero |\n| **Sector** | ~15% | Industry forums, competitor activity | Zero |\n| **Personal** | ~5% | Dwell time, forwards, replies, silence | Passive |\n\n### Reactions are multi-signal\n\nA reaction is one record of user engagement with an item. Marble accepts a structured shape so a single physical action can decompose into multiple signals:\n\n```js\n// One click on a recommended item produces three reactions:\nkg.recordReaction({ item_id, signal: 'title_attention',  validity: 0.40, polarity: +1 })\nkg.recordReaction({ item_id, signal: 'click_through',    validity: 0.20, polarity: +1 })\nkg.recordReaction({ item_id, signal: 'destination_bounce', validity: 0.55, polarity: -1, meta: { reason: 'paywall' } })\n```\n\nClones learn three different things: title patterns work, source has friction, recommend similar titles but route around the source. `ClonePopulation.evolve` multiplies fitness deltas by `validity × polarity` per reaction.\n\n**Validity calibration** — caller-tunable, no enforcement:\n\n| Signal cost | Example | Validity range |\n|---|---|---|\n| Real-world action | purchase, calendar booking, workout completed | ~1.0 |\n| Sustained engagement | read-to-end, full-track-play | 0.65–0.85 |\n| Click | tap, follow-link | 0.2–0.5 |\n| Hover, visible-impression | mouse-over, scrolled-past | 0.1 |\n\nFriction signals get `polarity: -1` with validity proportional to confidence (quick bounce ≈ 0.4, explicit \"this was bad\" ≈ 0.85).\n\n**Marble does NOT ship a signal taxonomy.** What signals exist and how strong each is are consumer decisions — a music app cares about \"song-full-play\", a fitness app about \"workout-completed\", a news app about \"read-to-end\". Marble accepts whatever `recordReaction` calls come in and multiplies. The legacy `marble.react(item, 'up')` surface still works as a convenience for the simple thumbs-up case.\n\n### Curator (fact verification, the keystone)\n\nMarble's pipeline is one-shot at install: `init → ingest → learn → investigate → synthesize`. After it runs, evolution requires reactions — but until reactions are wired, the system is structurally inert past cold-start. The curator is the missing piece that turns this into a continuous learning loop.\n\n```bash\n# Run periodically (cron / launchd) — picks 15 suspect facts, classifies each\n*/30 * * * * cd /path/to/project && marble curate --limit 15 >> ~/.marble/curator.log 2>&1\n```\n\nThe curator never deletes (`valid_to` retirement is mechanical reconciliation's job). It applies one of four decisions per fact:\n\n| Decision | Action |\n|---|---|\n| `confirm` | bump strength + evidence_count |\n| `unclear` | keep fact, lower strength, flag `_meta.challenge_candidate: true` (surfaced via `select({ includeChallenges: N })`) |\n| `ambiguous` | lower strength + write `gap:<topic>` belief (unblocks `seedClones()` 50-archetype path) |\n| `skip` | leave alone |\n\nRun undo with `marble curate --revert <run_id>` if a curator pass made wrong calls — every decision has a per-fact `_meta.history` entry the revert walks.\n\n**Don't run `marble learn` and `marble curate` concurrently** — both write KG state. Schedule them sequentially in cron.\n\n**Want to see the KG and the curator running live?** There's a separate\n[companion 3D visualization](docs/graph-visualization.md) — Hono server +\n3d-force-graph front-end — that renders your graph, streams the\nautonomous curator loop with animated decisions, and lets you chat with\nthe KG. Not bundled with marble core (marble ships as a library); see\n[docs/graph-visualization.md](docs/graph-visualization.md) for setup\nincluding launchd / systemd auto-start.\n\n## Integration Modes\n\n### 1. Local-First (Recommended)\n\n```javascript\nconst marble = new Marble({ mode: 'local' });\nconst results = await marble.select(stories, userContext);\n```\n\n### 2. Enhanced (Optional LLM)\n\n```javascript\nconst marble = new Marble({\n  mode: 'enhanced',\n  llm: async (prompt) => await yourLLMProvider(prompt)\n});\n```\n\nAny `(prompt) => string` works. Or skip writing an adapter entirely:\nmarble has six built-in providers driven by `LLM_PROVIDER` —\n`anthropic`, `openai`, `deepseek`, `openai-compatible` (Moonshot /\nTogether / Groq / OpenRouter / vLLM / Ollama / LM Studio), **`opencode`**\n(free, local CLI, no API key), and **`claude-cli`** (uses your Claude\nCode subscription). See [docs/llm-providers.md](docs/llm-providers.md)\nfor the env vars, defaults, and budget controls.\n\n### 3. World Clone (B2B PMF)\n\n```javascript\nimport { WorldSim } from '@alexshrestha/marble/worldsim';\nconst worldsim = new WorldSim();\nconst pmf = await worldsim.simulate(yourProduct);\nconsole.log(`PMF Score: ${pmf.pmf_score}/1.0`);\n// pmf.best_fit_archetypes → which buyer segments fit best\n```\n\n## Connect over MCP\n\nMarble ships a generic [Model Context Protocol](https://modelcontextprotocol.io)\nserver so any MCP host (Claude Desktop, IDEs, agents) can query a KG — no local\ncheckout required, just the published package + a KG path.\n\n```bash\nnpm i @alexshrestha/marble @modelcontextprotocol/sdk   # SDK is an optional peer dep\nMARBLE_KG_PATH=./user-kg.json marble-mcp               # serves over stdio\n```\n\nRegister it with an MCP host (e.g. Claude Desktop `mcpServers`):\n\n```json\n{\n  \"marble\": {\n    \"command\": \"marble-mcp\",\n    \"env\": { \"MARBLE_KG_PATH\": \"/abs/path/to/user-kg.json\" }\n  }\n}\n```\n\nTools exposed: `kg_status`, `diagnose`, `select` (rank items — no LLM needed),\nand `learn` (full pipeline — needs an `LLM_PROVIDER`). Build your own server\nfrom the same dispatch via `import { MCP_TOOL_DEFS, runMcpTool } from\n'@alexshrestha/marble/mcp-tools'`.\n\n## Documentation\n\n| Doc | What it covers |\n|-----|---------------|\n| [Installation & Setup](docs/installation.md) | Full setup guide, configuration, adapters, troubleshooting |\n| [How It Works](docs/how-it-works.md) | The data synthesis process explained simply |\n| [Architecture](docs/architecture.md) | Full system design, data flow, component interactions |\n| [API Reference](docs/api-reference.md) | Every endpoint and function with examples |\n| [Usage Examples](docs/usage-examples.md) | Real code showing how to integrate Marble |\n| [LLM Providers](docs/llm-providers.md) | Wiring Anthropic / OpenAI / DeepSeek / opencode / Claude CLI / Ollama into the `llm` option, including free no-API-key paths |\n| [Graph Visualization](docs/graph-visualization.md) | Companion 3D viz project, autonomous curator loop, launchd/systemd auto-start, building your own renderer |\n| [Glossary](docs/glossary.md) | Disambiguates overloaded terms (swarm / clone / curate) — read this if you're confused which \"curate\" or \"swarm\" a piece of code means |\n| [Competitive Positioning](docs/competitive-positioning.md) | Why Marble isn't just \"better collaborative filtering\" |\n| [Insight-Driven KG](docs/insight-kg.md) | How Marble reasons about WHY, not just WHAT |\n| [Archetypes & Relationships](docs/archetypes-relationships.md) | Relationship simulation, archetype generation |\n| [Contributing](docs/contributing.md) | How to contribute to Marble |\n\n## Validation\n\nMarble's ranking claims have been validated against public datasets via\nthe `marble-bench` benchmark suite. All runs use seeded PRNG and are\nreproducible. The harness covers six datasets across news, e-commerce,\nand music, with both cold-start and post-`learn()` measurements, run\nagainst three LLM providers (OpenAI gpt-4o-mini, Anthropic Claude\nHaiku 4.5, Moonshot Kimi K2.5).\n\n| Dataset | Metric | Result |\n|---|---|---|\n| MIND-small (news, ~70K test impressions, 100 users) | nDCG@10 cold-start | **+45–46% over popularity baseline**; MRR ~2×, P@5 ~1.7× |\n| Amazon Reviews 5-core — Gift_Cards | nDCG@10 (K=20) | **0.944 vs 0.629 popularity (+50%)** |\n| Amazon Reviews 5-core — Video_Games | nDCG@10 (K=20) | **0.928 vs 0.564 popularity (+65%)** |\n| Last.fm-1K (year-1 → year-2 drift, 5 users, post-`learn()`) | warm nDCG@10 | **+28% on user with room to improve** (1/5 users; 4/5 already at ceiling 1.000) |\n\n**Honest caveats:**\n\n- AUC sits near 0.5 on topic-thin MIND data — top-K ranking is strong\n  (nDCG/MRR/P@5 all beat baselines), but the tied tail is what AUC\n  penalizes. The OOTB-pass-3 `#interestMatch` embedding fallback addresses\n  the structural cause; remaining ties are dataset artefacts (HuggingFace\n  `mteb/mind_small` strips category metadata).\n- `learn()` clone consensus can hurt top-K on cold-start MIND profiles\n  (3–10 history items, no real category labels). The variance comes from\n  LLM non-determinism in `seedClones` — same seed, two runs, different\n  archetype hypotheses. Two follow-ups suggested: down-weight\n  `cloneBoostWeight` (currently 0.3) when seedClones ran in the cold-start\n  branch, or gate clone consensus behind a KG-size minimum (e.g. ≥ 20\n  signals).\n- Provider behaviour matters. Kimi K2.5 emits 1–2K tokens of chain-of-thought\n  prose before the JSON array on the seedClones prompt, hits the 4096\n  `max_tokens` cap mid-output, and returns `LLM_UNPARSEABLE`. This is now\n  surfaced in `learn()` `failures` rather than silently degrading; raise\n  `max_tokens` or use a model that respects \"respond with only JSON\"\n  instructions.\n\nThe benchmark suite also caught 15 real Marble issues during build, 14\nof which are merged across the OOTB integration passes (`743091d`,\n`9c69de5`, `016eaa2`, `6fe7cf6`, `f8cd52a`).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}