@lucern/sdk 0.2.0-alpha.3 → 0.2.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +584 -112
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,19 +2,21 @@
2
2
 
3
3
  The reasoning operating system for AI-native applications.
4
4
 
5
- Your AI agent reads 50 documents. It generates insights. A week later, it reads 50 more and has no memory of what it learned before. Every session starts from zero.
5
+ Your AI agent reads 50 documents, generates insights, and forgets everything by tomorrow. Every session starts from zero. The same questions get re-investigated. The same dead ends get explored. Nothing compounds.
6
6
 
7
- Lucern fixes this. It gives your application a knowledge graph that accumulates understanding over time. Beliefs strengthen or weaken as evidence arrives. Contradictions are detected, not hidden. Your agents get smarter with every interaction because the graph remembers what was learned, when, and why.
8
-
9
- ## Install
7
+ Lucern gives your agents a reasoning graph that accumulates understanding across sessions, across agents, and across time. Beliefs have confidence scores that change as evidence arrives. Contradictions are detected and tracked. Investigations are structured with hypotheses, evidence, and outcomes. Every mutation is traceable, every decision auditable.
10
8
 
11
9
  ```bash
12
10
  npm install @lucern/sdk
13
11
  ```
14
12
 
15
- ## Quickstart Build an Agent That Remembers
13
+ ## The Full Developer Journey
14
+
15
+ This walkthrough mirrors what a developer building an AI-powered code review system would experience in a real coding session. Every API call is something you would actually use.
16
+
17
+ ### 1. Set Up Your Reasoning Scope
16
18
 
17
- Your agent processes customer support tickets and builds an evolving understanding of product issues:
19
+ Topics are how you organize knowledge. They nest hierarchically, like folders that think.
18
20
 
19
21
  ```typescript
20
22
  import { createLucernClient } from "@lucern/sdk";
@@ -24,184 +26,654 @@ const lucern = createLucernClient({
24
26
  baseUrl: "https://api.lucern.ai",
25
27
  });
26
28
 
27
- // Create a reasoning scope
28
- const topic = await lucern.topics.create({
29
- name: "Product Issue Tracker",
29
+ // Create a parent topic for your domain
30
+ const codeQuality = await lucern.topics.create({
31
+ name: "Code Quality Intelligence",
30
32
  type: "domain",
31
33
  });
32
- const topicId = topic.data.topicId;
33
34
 
34
- // Your agent processes a batch of support tickets and forms a belief
35
- const belief = await lucern.beliefs.create({
36
- topicId,
37
- canonicalText: "Authentication timeout errors are caused by the session refresh race condition, not the auth provider",
35
+ // Create child topics for specific areas
36
+ const securityTopic = await lucern.topics.create({
37
+ name: "Security Patterns",
38
+ type: "theme",
39
+ parentTopicId: codeQuality.data.topicId,
38
40
  });
39
41
 
40
- // Attach the evidence that led to this belief
41
- await lucern.evidence.create({
42
+ const performanceTopic = await lucern.topics.create({
43
+ name: "Performance Patterns",
44
+ type: "theme",
45
+ parentTopicId: codeQuality.data.topicId,
46
+ });
47
+ ```
48
+
49
+ ### 2. Populate the Graph With What You Know
50
+
51
+ Beliefs represent understanding at different epistemic levels: facts (verified), beliefs (confident), hypotheses (testing), and assumptions (untested).
52
+
53
+ ```typescript
54
+ const topicId = securityTopic.data.topicId;
55
+
56
+ // A fact — verified and anchored
57
+ const sqlFact = await lucern.beliefs.create({
42
58
  topicId,
43
- text: "12 of 15 timeout tickets occur within 200ms of token refresh. Stack traces show concurrent refresh calls.",
44
- targetId: belief.data.nodeId,
45
- weight: 0.85,
59
+ canonicalText: "Parameterized queries prevent SQL injection in all major database drivers",
60
+ beliefType: "fact",
61
+ });
62
+ await lucern.beliefs.modulateConfidence(sqlFact.data.nodeId, {
63
+ confidence: 0.95,
64
+ trigger: "evidence_added",
65
+ rationale: "OWASP verified, industry standard for 20+ years",
46
66
  });
47
67
 
48
- // Score confidence based on evidence strength
49
- await lucern.beliefs.modulateConfidence(belief.data.nodeId, {
50
- confidence: 0.75,
68
+ // A belief confident from observation
69
+ const patternBelief = await lucern.beliefs.create({
70
+ topicId,
71
+ canonicalText: "Most security vulnerabilities in our codebase come from unvalidated user input at API boundaries, not from cryptographic weaknesses",
72
+ });
73
+ await lucern.beliefs.modulateConfidence(patternBelief.data.nodeId, {
74
+ confidence: 0.82,
51
75
  trigger: "evidence_added",
52
- rationale: "Strong correlation in timing data, but haven't reproduced in staging yet",
76
+ rationale: "Last 6 months of security audits: 14 input validation issues, 1 crypto issue",
53
77
  });
54
78
 
55
- // Flag what would change this understanding
56
- await lucern.questions.create({
79
+ // A hypothesis under active testing
80
+ const hypothesis = await lucern.beliefs.create({
57
81
  topicId,
58
- text: "Does disabling concurrent refresh eliminate the timeout pattern?",
59
- priority: "high",
60
- linkedBeliefId: belief.data.nodeId,
82
+ canonicalText: "Automated static analysis catches fewer than 30% of the input validation vulnerabilities that human reviewers find",
83
+ beliefType: "hypothesis",
84
+ });
85
+ await lucern.beliefs.modulateConfidence(hypothesis.data.nodeId, {
86
+ confidence: 0.6,
87
+ trigger: "manual",
88
+ rationale: "Anecdotal — need to run a proper comparison study",
89
+ });
90
+
91
+ // An assumption — untested but used as a basis for decisions
92
+ const assumption = await lucern.beliefs.create({
93
+ topicId,
94
+ canonicalText: "Our CI pipeline runs all static analysis rules on every PR",
95
+ beliefType: "assumption",
96
+ });
97
+ ```
98
+
99
+ ### 3. Connect Beliefs With Edges
100
+
101
+ Edges express the reasoning relationships between beliefs.
102
+
103
+ ```typescript
104
+ // The pattern belief depends on the SQL injection fact
105
+ await lucern.graph.createEdge({
106
+ topicId,
107
+ sourceId: patternBelief.data.nodeId,
108
+ targetId: sqlFact.data.nodeId,
109
+ edgeType: "depends_on",
110
+ reasoning: "Input validation pattern includes SQL injection as a subclass",
111
+ reasoningMethod: "deductive",
112
+ });
113
+
114
+ // The hypothesis is derived from the pattern belief
115
+ await lucern.graph.createEdge({
116
+ topicId,
117
+ sourceId: hypothesis.data.nodeId,
118
+ targetId: patternBelief.data.nodeId,
119
+ edgeType: "derived_from",
120
+ reasoning: "If most vulns are input validation, and static analysis misses them, the tooling gap is at the input boundary",
121
+ reasoningMethod: "inductive",
122
+ });
123
+
124
+ // The hypothesis depends on an untested assumption
125
+ await lucern.graph.createEdge({
126
+ topicId,
127
+ sourceId: hypothesis.data.nodeId,
128
+ targetId: assumption.data.nodeId,
129
+ edgeType: "depends_on",
130
+ reasoning: "If the CI pipeline is not running all rules, the comparison would be invalid",
131
+ reasoningMethod: "deductive",
61
132
  });
62
133
  ```
63
134
 
64
- Next week, your agent processes more tickets:
135
+ ### 4. Open an Investigation With a Worktree
136
+
137
+ Worktrees are focused investigations — like a feature branch for knowledge. You test beliefs, gather evidence, and merge the findings.
65
138
 
66
139
  ```typescript
67
- // New evidence arrives that contradicts the original belief
140
+ const investigation = await lucern.worktrees.create({
141
+ topicId,
142
+ title: "Static analysis vs human review comparison",
143
+ hypothesis: "Running the same 50 PRs through both static analysis and manual review will show static analysis catches less than 30% of what humans find",
144
+ beliefIds: [hypothesis.data.nodeId],
145
+ });
146
+
147
+ // Create tasks to drive the investigation
148
+ await lucern.tasks.create({
149
+ topicId,
150
+ title: "Run Semgrep + CodeQL on the last 50 merged PRs and log findings",
151
+ linkedWorktreeId: investigation.data.worktreeId,
152
+ linkedQuestionId: comparisonQuestion.data.questionId,
153
+ taskType: "data_collection",
154
+ });
155
+
156
+ await lucern.tasks.create({
157
+ topicId,
158
+ title: "Have two senior engineers independently review the same 50 PRs",
159
+ linkedWorktreeId: investigation.data.worktreeId,
160
+ taskType: "research",
161
+ });
162
+ ```
163
+
164
+ ### 5. Add Evidence as You Work
165
+
166
+ Your agent processes the results and commits evidence to the graph.
167
+
168
+ ```typescript
169
+ // Static analysis results
170
+ await lucern.evidence.create({
171
+ topicId,
172
+ text: "Semgrep + CodeQL found 23 issues across 50 PRs. 18 were true positives. Categories: 12 input validation, 4 auth bypass patterns, 2 information disclosure.",
173
+ sourceUrl: "ci://semgrep-run/batch-50pr-comparison",
174
+ targetId: hypothesis.data.nodeId,
175
+ weight: 0.6, // partially supports
176
+ });
177
+
178
+ // Human review results
68
179
  await lucern.evidence.create({
69
180
  topicId,
70
- text: "Timeouts persist after disabling concurrent refresh. Network traces show upstream latency spikes from the auth provider during peak hours.",
71
- targetId: belief.data.nodeId,
72
- weight: -0.7, // contradicts
181
+ text: "Two senior engineers found 41 issues across the same 50 PRs (34 unique after dedup). Categories: 22 input validation, 7 business logic flaws, 3 auth, 2 race conditions.",
182
+ sourceUrl: "review://manual-audit/batch-50pr",
183
+ targetId: hypothesis.data.nodeId,
184
+ weight: 0.9, // strongly supports — 18/34 = 53%, even worse than hypothesized
185
+ });
186
+
187
+ // Answer the question with data
188
+ const comparisonQuestion = await lucern.questions.create({
189
+ topicId,
190
+ text: "What percentage of vulnerabilities does static analysis catch compared to human reviewers?",
191
+ priority: "high",
192
+ linkedBeliefId: hypothesis.data.nodeId,
73
193
  });
74
194
 
75
- // Confidence drops — the graph tracks the evolution
76
- await lucern.beliefs.modulateConfidence(belief.data.nodeId, {
77
- confidence: 0.3,
195
+ await lucern.questions.answer(comparisonQuestion.data.questionId, {
196
+ text: "Static analysis caught 18 of 34 unique issues (53%). However, it missed all 7 business logic flaws and both race conditions — categories where it found 0%.",
197
+ confidence: "strong",
198
+ evidenceIds: [staticEvidence.data.nodeId, humanEvidence.data.nodeId],
199
+ });
200
+ ```
201
+
202
+ ### 6. Fork a Belief When Understanding Evolves
203
+
204
+ The original hypothesis said "less than 30%." The data shows 53% — but the story is more nuanced. Fork the belief to capture the evolved understanding.
205
+
206
+ ```typescript
207
+ // Drop confidence on the original (it was too pessimistic about overall catch rate)
208
+ await lucern.beliefs.modulateConfidence(hypothesis.data.nodeId, {
209
+ confidence: 0.2,
78
210
  trigger: "evidence_added",
79
- rationale: "Fix attempt failed. Evidence now points to upstream provider, not our code.",
211
+ rationale: "Static analysis catches 53% overall, not <30%. But the category breakdown reveals something more important.",
80
212
  });
81
213
 
82
- // Fork the belief understanding has evolved
83
- const revised = await lucern.beliefs.fork(belief.data.nodeId, {
84
- newFormulation: "Authentication timeouts are caused by upstream provider latency during peak hours, compounded by our lack of request queuing",
85
- forkReason: "contradiction_response",
214
+ // Fork into a more precise belief
215
+ const evolved = await lucern.beliefs.fork(hypothesis.data.nodeId, {
216
+ newFormulation: "Static analysis catches 53% of vulnerabilities overall, but has a complete blind spot for business logic flaws and race conditions — the hardest categories to detect",
217
+ forkReason: "refinement",
86
218
  });
87
219
 
88
- await lucern.beliefs.modulateConfidence(revised.data.nodeId, {
89
- confidence: 0.82,
220
+ await lucern.beliefs.modulateConfidence(evolved.data.nodeId, {
221
+ confidence: 0.88,
90
222
  trigger: "evidence_added",
91
- rationale: "Network traces confirm provider latency. Queuing fix in PR #847 eliminated 90% of timeouts in staging.",
223
+ rationale: "Direct comparison data: 0/7 business logic flaws caught, 0/2 race conditions caught, while input validation catch rate was 12/22 (55%)",
224
+ });
225
+
226
+ // This creates a new actionable belief
227
+ const actionable = await lucern.beliefs.create({
228
+ topicId,
229
+ canonicalText: "Human code review should focus on business logic and concurrency — the categories where static analysis provides zero coverage",
230
+ beliefType: "belief",
92
231
  });
93
- ```
94
232
 
95
- The graph now tells the complete story: your agent initially believed the issue was a race condition (75% confidence), new evidence contradicted that (dropped to 30%), and the understanding evolved to identify the real cause (82% confidence on the revised belief). Every step is traceable.
233
+ await lucern.beliefs.modulateConfidence(actionable.data.nodeId, {
234
+ confidence: 0.85,
235
+ trigger: "worktree_outcome",
236
+ rationale: "Direct implication of the comparison study findings",
237
+ });
96
238
 
97
- ## Why This Matters
239
+ // Link them
240
+ await lucern.graph.createEdge({
241
+ topicId,
242
+ sourceId: actionable.data.nodeId,
243
+ targetId: evolved.data.nodeId,
244
+ edgeType: "derived_from",
245
+ reasoning: "The review focus recommendation follows directly from the coverage gap data",
246
+ reasoningMethod: "deductive",
247
+ });
248
+ ```
98
249
 
99
- Without Lucern, your agent would process the second batch of tickets with no memory of the first. It would re-discover the race condition theory, waste time investigating it again, and maybe reach the right answer — or maybe not.
250
+ ### 7. Compile Context for Your Next Agent Session
100
251
 
101
- With Lucern, the agent reads its prior beliefs before processing new tickets. It knows what it already tried. It knows what questions are still open. It starts from understanding, not from zero.
252
+ Before your agent starts its next session, it reads the graph:
102
253
 
103
254
  ```typescript
104
- // Before processing new data, the agent reads its current understanding
105
255
  const context = await lucern.context.compile(topicId, {
106
- query: "authentication timeout root cause",
256
+ query: "code review strategy and static analysis coverage",
107
257
  ranking: "weighted_v1",
108
258
  tokenBudget: 2000,
109
259
  });
110
260
 
111
- // The context pack contains:
112
- // - Active beliefs ranked by relevance and confidence
113
- // - Open questions that still need answers
114
- // - Unresolved contradictions the agent should address
115
- // - Recent evidence for grounding
116
-
117
- // Feed this to your LLM as working memory
118
- const prompt = `
119
- Current understanding:
120
- ${context.data.activeBeliefs.map(b =>
121
- `[${b.confidence}] ${b.canonicalText}`
122
- ).join("\n ")}
123
-
124
- Open questions:
125
- ${context.data.openQuestions.map(q => q.text).join("\n ")}
126
-
127
- Given this foundation, analyze the following new tickets...
128
- `;
261
+ // context.data contains:
262
+ // - activeBeliefs: ranked by confidence and relevance
263
+ // - openQuestions: what still needs investigation
264
+ // - contradictions: tensions the agent should address
265
+ // - recentEvidence: latest findings for grounding
266
+
267
+ // Your agent starts from understanding, not from zero
268
+ console.log(`
269
+ ${context.data.summary.totalBeliefs} beliefs in scope
270
+ ${context.data.summary.openQuestions} open questions
271
+ ${context.data.summary.contradictions} unresolved contradictions
272
+ `);
129
273
  ```
130
274
 
131
- This is the compound intelligence loop. Read from the graph, reason, write back. Every cycle makes the next one smarter.
275
+ This is the compound intelligence loop. Every session reads the graph, reasons against it, and writes findings back. The graph gets smarter every cycle.
276
+
277
+ ---
132
278
 
133
279
  ## Core Concepts
134
280
 
135
281
  | Concept | What It Does |
136
282
  |---------|-------------|
137
- | **Topic** | Scopes reasoning to a domain (like a database for understanding) |
138
- | **Belief** | A statement held to be true, with a confidence score that changes over time |
139
- | **Evidence** | A fact that supports or contradicts a belief, with weight and source |
140
- | **Question** | An open investigation that drives evidence collection |
141
- | **Contradiction** | An explicit tension between beliefs that must be addressed |
142
- | **Worktree** | A focused investigation (branch, test hypotheses, merge findings) |
143
- | **Confidence** | A 0-to-1 score on every belief, with append-only history showing how it changed |
283
+ | **Topic** | Scopes reasoning into a domain. Topics nest hierarchically. |
284
+ | **Belief** | A statement with a confidence score. Types: fact, belief, hypothesis, assumption. |
285
+ | **Evidence** | A weighted fact linked to beliefs. Positive weight supports, negative contradicts. |
286
+ | **Question** | An open investigation. Linked to the belief it tests. |
287
+ | **Contradiction** | An explicit tension between two beliefs. May remain permanently unresolved. |
288
+ | **Worktree** | A focused investigation. Create hypotheses, gather evidence, merge findings. |
289
+ | **Edge** | A typed relationship: supports, informs, depends_on, derived_from, contains, tests. |
290
+ | **Confidence** | 0 to 1 score with append-only history. Every change has a trigger and rationale. |
291
+ | **Task** | Execution work linked to a question or worktree. |
292
+ | **Context Pack** | The graph compiled into ranked, budgeted context for LLM injection. |
144
293
 
145
294
  ## SDK Surface
146
295
 
147
296
  ```typescript
148
297
  const lucern = createLucernClient({ apiKey, baseUrl });
149
298
 
150
- lucern.beliefs // Create, fork, archive, score beliefs
151
- lucern.evidence // Add evidence, link to beliefs with weight
152
- lucern.questions // Track what needs investigation
153
- lucern.contradictions // Detect and manage tensions
154
- lucern.topics // Organize reasoning into domains
155
- lucern.worktrees // Run focused investigations
156
- lucern.context // Compile the graph into agent-ready context
157
- lucern.graph // Traverse, analyze structure, detect bias
158
- lucern.ontologies // Define domain vocabulary
159
- lucern.tasks // Track execution work
299
+ lucern.beliefs // Create, fork, score, archive
300
+ lucern.evidence // Add weighted evidence, link to beliefs
301
+ lucern.questions // Ask, answer, track status
302
+ lucern.contradictions // Flag tensions between beliefs
303
+ lucern.topics // Nested topic hierarchy
304
+ lucern.worktrees // Focused investigations
305
+ lucern.graph // Edges, traversal, structural analysis
306
+ lucern.context // Compile graph into agent-ready context
307
+ lucern.ontologies // Domain vocabulary
308
+ lucern.tasks // Execution tracking
160
309
  lucern.audit // Full mutation history
161
- lucern.identity // Manage API keys and sessions
310
+ lucern.identity // API keys and sessions
311
+ ```
312
+
313
+ ## Graph Intelligence
314
+
315
+ The graph doesn't just store knowledge — it analyzes itself.
316
+
317
+ ### Structural Analysis
318
+
319
+ ```typescript
320
+ // Run the full analysis suite: PageRank, Louvain clustering,
321
+ // Tarjan SCC, spectral analysis, confirmation bias detection
322
+ const analysis = await lucern.graph.analyze({ topicId });
323
+
324
+ // Which beliefs are most central to your reasoning?
325
+ analysis.data.pageRank.topNodes.forEach(node => {
326
+ console.log(`${node.canonicalText} — centrality: ${node.score}`);
327
+ });
328
+
329
+ // Are there isolated belief clusters that should be connected?
330
+ analysis.data.communities.forEach(cluster => {
331
+ console.log(`Cluster "${cluster.label}": ${cluster.nodeCount} beliefs`);
332
+ });
333
+
334
+ // Any circular dependencies in your reasoning?
335
+ if (analysis.data.cycles.length > 0) {
336
+ console.warn(`Found ${analysis.data.cycles.length} circular reasoning chains`);
337
+ }
338
+ ```
339
+
340
+ ### Confirmation Bias Detection
341
+
342
+ ```typescript
343
+ // Are you only collecting evidence that supports your beliefs?
344
+ const bias = await lucern.graph.detectBias({ topicId });
345
+
346
+ bias.data.beliefs.forEach(b => {
347
+ if (b.riskLevel === "critical") {
348
+ console.warn(
349
+ `"${b.canonicalText}" has ${b.supportingEvidence} supporting ` +
350
+ `and ${b.contradictingEvidence} contradicting evidence — ` +
351
+ `one-sided evidence base`
352
+ );
353
+ }
354
+ });
355
+ ```
356
+
357
+ ### Find Gaps in Your Reasoning
358
+
359
+ ```typescript
360
+ // What beliefs have no evidence? What questions have no answers?
361
+ const gaps = await lucern.graph.findGaps({ topicId });
362
+
363
+ console.log(`Beliefs without evidence: ${gaps.data.unsupportedBeliefs.length}`);
364
+ console.log(`Beliefs without questions: ${gaps.data.untestedBeliefs.length}`);
365
+ console.log(`Orphan evidence: ${gaps.data.unlinkedEvidence.length}`);
366
+ ```
367
+
368
+ ### Falsification — What Would Disprove Your Beliefs?
369
+
370
+ ```typescript
371
+ // Generate the questions most likely to disprove your strongest beliefs
372
+ const questions = await lucern.graph.falsify({ topicId });
373
+
374
+ questions.data.forEach(q => {
375
+ console.log(`To disprove "${q.beliefText}", ask: "${q.question}"`);
376
+ });
377
+ ```
378
+
379
+ ### Traverse the Graph
380
+
381
+ ```typescript
382
+ // Walk the reasoning chain from a belief down to its evidence
383
+ const traversal = await lucern.graph.traverse({
384
+ startNodeId: belief.data.nodeId,
385
+ direction: "down", // L3 belief -> L2 evidence -> L1 sources
386
+ maxDepth: 3,
387
+ });
388
+
389
+ traversal.data.nodes.forEach(node => {
390
+ console.log(`[${node.epistemicLayer}] ${node.canonicalText}`);
391
+ });
162
392
  ```
163
393
 
164
- ## Edge Types
394
+ ## LLM Integration Patterns
395
+
396
+ ### Inject Reasoning Context Into Any LLM Call
397
+
398
+ ```typescript
399
+ const context = await lucern.context.compile(topicId, {
400
+ query: "security review priorities",
401
+ ranking: "weighted_v1",
402
+ tokenBudget: 3000,
403
+ includeEntities: true,
404
+ });
405
+
406
+ // The context pack is structured for direct prompt injection
407
+ const systemPrompt = `
408
+ You are a security-focused code reviewer. Your reasoning state:
409
+
410
+ FACTS (high confidence, treat as ground truth):
411
+ ${context.data.invariants
412
+ .map(b => `- [${b.confidence}] ${b.canonicalText}`)
413
+ .join("\n")}
414
+
415
+ ACTIVE BELIEFS (current understanding, may evolve):
416
+ ${context.data.activeBeliefs
417
+ .map(b => `- [${b.confidence}] ${b.canonicalText}`)
418
+ .join("\n")}
419
+
420
+ OPEN QUESTIONS (unresolved — investigate these):
421
+ ${context.data.openQuestions
422
+ .map(q => `- [${q.priority}] ${q.text}`)
423
+ .join("\n")}
424
+
425
+ CONTRADICTIONS (tensions you must address):
426
+ ${context.data.contradictions
427
+ .map(c => `- [${c.severity}] ${c.description}`)
428
+ .join("\n")}
429
+
430
+ RULES:
431
+ - If your analysis contradicts an active belief, flag it explicitly
432
+ - If you discover evidence for an open question, answer it
433
+ - Never silently override a high-confidence belief without evidence
434
+ - When uncertain, create a new question rather than stating a conclusion
435
+ `;
436
+ ```
165
437
 
166
- Six canonical reasoning relationships:
438
+ ### Let the LLM Write Back to the Graph
167
439
 
168
- | Edge | Meaning |
169
- |------|---------|
170
- | `supports` | Evidence reinforces a belief |
171
- | `informs` | Evidence bears on a belief (directional, weighted) |
172
- | `depends_on` | Belief B requires belief A to hold |
173
- | `derived_from` | Belief was derived from another (lineage) |
174
- | `contains` | Structural containment |
175
- | `tests` | Question tests a belief (falsification link) |
440
+ ```typescript
441
+ // After your LLM generates analysis, parse its output
442
+ // and write structured findings back to the graph
443
+
444
+ const findings = await llm.generate({
445
+ system: systemPrompt,
446
+ prompt: `Review this PR diff and identify security concerns:\n${prDiff}`,
447
+ tools: [{
448
+ name: "record_finding",
449
+ description: "Record a security finding as evidence in the reasoning graph",
450
+ parameters: {
451
+ finding: { type: "string" },
452
+ severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
453
+ relatedBeliefId: { type: "string", description: "ID of the belief this relates to" },
454
+ supportsOrContradicts: { type: "number", description: "-1 to 1" },
455
+ },
456
+ }],
457
+ });
176
458
 
177
- ## Custom Tools
459
+ // Each tool call becomes evidence in the graph
460
+ for (const call of findings.toolCalls) {
461
+ await lucern.evidence.create({
462
+ topicId,
463
+ text: call.args.finding,
464
+ sourceUrl: `pr://review/${prNumber}`,
465
+ targetId: call.args.relatedBeliefId,
466
+ weight: call.args.supportsOrContradicts,
467
+ });
468
+ }
469
+ ```
178
470
 
179
- Extend the graph with domain operations any agent can invoke:
471
+ ### Search the Graph Semantically
180
472
 
181
473
  ```typescript
182
- import { z } from "zod";
474
+ // Find beliefs related to a natural language query
475
+ const results = await lucern.search.beliefs({
476
+ topicId,
477
+ query: "race conditions in authentication",
478
+ minConfidence: 0.5,
479
+ limit: 10,
480
+ });
481
+
482
+ results.data.forEach(b => {
483
+ console.log(`[${b.confidence}] ${b.canonicalText}`);
484
+ });
485
+
486
+ // Search evidence
487
+ const evidence = await lucern.search.evidence({
488
+ topicId,
489
+ query: "token refresh timing data",
490
+ limit: 5,
491
+ });
492
+ ```
493
+
494
+ ## Agent Configuration
495
+
496
+ ### CLAUDE.md / AGENTS.md Integration
497
+
498
+ Add Lucern context to your coding agent's system instructions:
499
+
500
+ ```markdown
501
+ <!-- In your CLAUDE.md or AGENTS.md -->
502
+
503
+ ## Reasoning Graph
504
+
505
+ This project uses Lucern for knowledge management. Before making
506
+ architectural decisions, check the reasoning graph:
507
+
508
+ - Read current beliefs: `lucern.beliefs.list({ topicId: "..." })`
509
+ - Check for contradictions: `lucern.contradictions.list({ topicId: "..." })`
510
+ - Compile context before analysis: `lucern.context.compile(topicId, { query: "..." })`
511
+
512
+ After making decisions, write them back:
513
+ - Create beliefs for architectural decisions
514
+ - Add evidence linking to the code/PR that validates them
515
+ - Flag contradictions when new evidence conflicts with existing beliefs
516
+ - Answer open questions when your work resolves them
517
+
518
+ Confidence scale:
519
+ - 0.90+: Verified in production
520
+ - 0.80-0.90: Strong evidence, working implementation
521
+ - 0.70-0.80: Directionally right, needs validation
522
+ - Below 0.70: Hypothesis, needs investigation
523
+ ```
524
+
525
+ ### Claude Code Skill
526
+
527
+ Create a Lucern skill for your coding agents at `~/.claude/skills/lucern-sdk/SKILL.md`:
183
528
 
529
+ ```markdown
530
+ # Lucern SDK Skill
531
+
532
+ Use this skill when building features that read or write to the
533
+ reasoning graph, or when the user asks about beliefs, evidence,
534
+ contradictions, or knowledge state.
535
+
536
+ ## Before Starting Work
537
+
538
+ Compile the current context to understand what the graph knows:
539
+
540
+ \`\`\`typescript
541
+ const context = await lucern.context.compile(topicId, {
542
+ query: "<what you're working on>",
543
+ tokenBudget: 2000,
544
+ });
545
+ \`\`\`
546
+
547
+ Check for open questions related to your task. If one exists, your work
548
+ should aim to answer it.
549
+
550
+ ## After Completing Work
551
+
552
+ Record what you learned:
553
+
554
+ \`\`\`typescript
555
+ // Create a belief for decisions made
556
+ await lucern.beliefs.create({
557
+ topicId,
558
+ canonicalText: "<what you now believe to be true>",
559
+ });
560
+
561
+ // Add evidence from your implementation
562
+ await lucern.evidence.create({
563
+ topicId,
564
+ text: "<what you observed or built>",
565
+ sourceUrl: "commit://<sha>",
566
+ targetId: beliefId,
567
+ weight: 0.8,
568
+ });
569
+
570
+ // Answer questions your work resolved
571
+ await lucern.questions.answer(questionId, {
572
+ text: "<the answer>",
573
+ confidence: "strong",
574
+ });
575
+ \`\`\`
576
+ ```
577
+
578
+ ### Custom Tool Registration for Agents
579
+
580
+ Give your agents domain-specific capabilities that interact with the graph:
581
+
582
+ ```typescript
184
583
  lucern.tools.register({
185
- namespace: "support",
186
- name: "classify_ticket",
187
- description: "Classify a support ticket and link it to relevant beliefs",
584
+ namespace: "codebase",
585
+ name: "trace_dependency",
586
+ description: "Trace how a code change would affect beliefs in the reasoning graph",
188
587
  inputSchema: z.object({
189
- ticketId: z.string(),
190
- content: z.string(),
588
+ filePath: z.string(),
589
+ changeDescription: z.string(),
191
590
  topicId: z.string(),
192
591
  }),
193
592
  outputSchema: z.object({
194
- category: z.string(),
195
- linkedBeliefs: z.array(z.string()),
196
- suggestedQuestions: z.array(z.string()),
593
+ affectedBeliefs: z.array(z.object({
594
+ beliefId: z.string(),
595
+ text: z.string(),
596
+ currentConfidence: z.number(),
597
+ impact: z.enum(["strengthens", "weakens", "invalidates"]),
598
+ })),
197
599
  }),
198
- handler: async ({ ticketId, content, topicId }) => {
199
- // Your classification logic
200
- return classifyAndLink(ticketId, content, topicId);
600
+ handler: async ({ filePath, changeDescription, topicId }) => {
601
+ // Search for beliefs related to this file/area
602
+ const related = await lucern.search.beliefs({
603
+ topicId,
604
+ query: `${filePath} ${changeDescription}`,
605
+ });
606
+ // Analyze impact...
607
+ return { affectedBeliefs: analyzeImpact(related, changeDescription) };
201
608
  },
202
609
  });
203
610
  ```
204
611
 
612
+ ## Worktree Patterns
613
+
614
+ ### List and Resume Investigations
615
+
616
+ ```typescript
617
+ // See all active investigations
618
+ const worktrees = await lucern.worktrees.list({
619
+ topicId,
620
+ status: "active",
621
+ });
622
+
623
+ worktrees.data.forEach(wt => {
624
+ console.log(`${wt.title} — phase: ${wt.phase}, beliefs: ${wt.beliefCount}`);
625
+ });
626
+
627
+ // Get the full state of an investigation
628
+ const investigation = await lucern.worktrees.get(worktreeId);
629
+ console.log(`Hypothesis: ${investigation.data.hypothesis}`);
630
+ console.log(`Questions: ${investigation.data.questionCount} open`);
631
+ console.log(`Evidence: ${investigation.data.evidenceCount} collected`);
632
+ ```
633
+
634
+ ### Merge an Investigation
635
+
636
+ When the investigation is complete, merge findings into the main graph:
637
+
638
+ ```typescript
639
+ await lucern.worktrees.merge(worktreeId, {
640
+ summary: "Static analysis catches 53% of vulns overall but has zero coverage for business logic flaws",
641
+ outcomes: [
642
+ {
643
+ beliefId: hypothesis.data.nodeId,
644
+ confidence: 0.2,
645
+ rationale: "Overall catch rate was 53%, not <30% — but the category breakdown is more important",
646
+ },
647
+ {
648
+ beliefId: evolved.data.nodeId,
649
+ confidence: 0.88,
650
+ rationale: "Direct comparison data confirms the coverage gap hypothesis at category level",
651
+ },
652
+ ],
653
+ });
654
+ ```
655
+
656
+ ## Confidence History
657
+
658
+ Every confidence change is recorded with full provenance:
659
+
660
+ ```typescript
661
+ const history = await lucern.beliefs.confidenceHistory(beliefId);
662
+
663
+ history.data.forEach(entry => {
664
+ console.log(
665
+ `${new Date(entry.timestamp).toISOString()} ` +
666
+ `${entry.previousConfidence} -> ${entry.confidence} ` +
667
+ `[${entry.trigger}] ${entry.rationale}`
668
+ );
669
+ });
670
+
671
+ // Example output:
672
+ // 2026-04-10T14:00:00Z null -> 0.60 [manual] Anecdotal observation
673
+ // 2026-04-11T09:30:00Z 0.60 -> 0.75 [evidence_added] Semgrep data supports
674
+ // 2026-04-11T16:00:00Z 0.75 -> 0.20 [evidence_added] Comparison study contradicted
675
+ ```
676
+
205
677
  ## Authentication
206
678
 
207
679
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucern/sdk",
3
- "version": "0.2.0-alpha.3",
3
+ "version": "0.2.0-alpha.4",
4
4
  "description": "Lucern reasoning platform SDK. Install this one package, provide your API key, and start building in 10 minutes.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",