stemedb/docs/app-concepts/consumer-health.md
jordan 1ce4004807 feat: Complete Phase 2 (The Cortex) - query, lens, and API layers
This commit adds the read path (Cortex) to complement the write path (Spine):

## Crates
- stemedb-api: HTTP API with axum + utoipa OpenAPI
  - /v1/assert, /v1/query, /v1/epoch, /v1/skeptic, /v1/trace, /v1/audit
  - Metered endpoints with quota enforcement
  - Ed25519 signature verification
- stemedb-lens: Truth resolution lenses
  - RecencyLens, ConsensusLens, ConfidenceLens
  - VoteAwareConsensusLens (Ballot Box pattern)
  - TrustAwareAuthorityLens (The Hive pattern)
  - SkepticLens (conflict analysis)
  - EpochAwareLens (paradigm-safe queries)
- stemedb-query: Query engine with materialized views

## Storage Extensions
- VoteStore: Vote aggregation with cached counts
- TrustRankStore: Agent reputation with decay
- AuditStore: Query audit trail
- IndexStore: SP/P/S index structures
- SupersessionStore: Epoch supersession chains

## SDKs
- sdk/go/steme: Go HTTP client with Ed25519 signing
- sdk/go/adk: ADK-Go tools for AI agents

## Documentation
- Updated CLAUDE.md, architecture.md, roadmap.md
- New ai-lookup entries for all services
- Use case docs for consumer health intelligence
- Arena roadmap for simulation advancement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 13:22:44 -07:00

779 lines
32 KiB
Markdown

# Consumer Health Intelligence: Application Layer Guide
> **Vertical:** Consumer-facing health information
> **Use Case:** [consumer-health-intelligence.md](../../use-cases/consumer-health-intelligence.md)
> **Status:** Design spec — implementation not started
This guide describes the **application layer components** needed to build a Consumer Health Intelligence platform on Episteme. It covers what you build, not what Episteme provides.
---
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONSUMER HEALTH APP │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PubMed │ │ Reddit │ │ FAERS │ │ FDA │ │
│ │ Crawler │ │ Crawler │ │ Crawler │ │ Crawler │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │ │
│ └────────────┬────┴────────┬────────┴─────────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ NLP Extraction Pipeline │ │
│ │ (claim identification, confidence scoring) │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Source-Class Classifier │ │
│ │ (tier assignment: 0=regulatory → 6=media) │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Metadata Enrichment Service │ │
│ │ (DOI lookup, journal info, engagement stats) │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Agent Wallet │ │
│ │ (key management, Ed25519 signing) │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
└──────────────────────────────────────┼───────────────────────────────────────┘
│ POST /assert
┌─────────────────────────────────────────────────────────────────────────────┐
│ EPISTEME DATABASE │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ WAL │ │ KV │ │ Indexes │ │ Lenses │ │ MV │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│ GET /query
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONSUMER HEALTH APP │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Background Gardener │ │
│ │ (cluster detection, escalation assertions) │ │
│ └───────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ LLM Summary Generator │ │
│ │ (plain-language synthesis of query results) │ │
│ └───────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Disagreement Dashboard │ │
│ │ (web UI for consumers) │ │
│ └───────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Component 1: Ingestion Pipeline
### 1.1 Data Sources
| Source | API/Method | Volume Estimate | Source Class |
|--------|------------|-----------------|--------------|
| **PubMed/biorxiv** | NCBI E-utilities API | ~5,000 papers for GLP-1 | Tier 1-2 |
| **ClinicalTrials.gov** | ClinicalTrials.gov API | ~800 registered trials | Tier 1-2 |
| **FDA Labels** | DailyMed API / EDGAR scraping | ~50 documents | Tier 0 |
| **FAERS** | openFDA API | ~50,000 reports for semaglutide | Tier 3 |
| **Reddit** | Reddit API (r/Ozempic, r/loseit) | ~500,000 posts/comments | Tier 5 |
| **Patient Forums** | Web scraping | ~100,000 posts | Tier 5 |
| **News** | News API | ~10,000 articles | Tier 6 |
| **Social Media** | Platform APIs / Firehose | ~1,000,000+ mentions | Tier 6 |
### 1.2 Crawler Implementation
Each source needs a dedicated crawler. Here's the pattern:
```go
// crawler/pubmed/crawler.go
type PubMedCrawler struct {
apiKey string
baseURL string
rateLimit time.Duration
}
func (c *PubMedCrawler) FetchArticles(query string, since time.Time) ([]RawArticle, error) {
// Call NCBI E-utilities API
// Respect rate limits (3 req/sec with API key)
// Return raw article data
}
type RawArticle struct {
PMID string
Title string
Abstract string
Authors []string
Journal string
DOI string
PubDate time.Time
MeSHTerms []string
StudyDesign string // RCT, observational, meta-analysis, etc.
}
```
### 1.3 NLP Extraction Pipeline
Transforms raw documents into structured claims.
```go
// extraction/pipeline.go
type ClaimExtractor struct {
llm LLMClient // Claude, GPT-4, or local model
promptStore PromptStore // Domain-specific extraction prompts
}
type ExtractedClaim struct {
Subject string // e.g., "semaglutide/adverse-effects/gastroparesis"
Predicate string // e.g., "risk_level"
Object string // e.g., "No statistically significant increase"
Confidence float32 // Extraction confidence, not source authority
Evidence string // Quote from source supporting the claim
}
func (e *ClaimExtractor) Extract(doc RawDocument) ([]ExtractedClaim, error) {
// Use LLM to identify claims in the document
// Map claims to subject/predicate ontology
// Assign extraction confidence based on clarity
// Return structured claims
}
```
**Prompt Engineering:** The extraction prompt is critical. It must:
- Define the claim ontology (what subjects/predicates are valid)
- Distinguish claims from context
- Extract supporting evidence for provenance
- Rate extraction confidence (not source authority)
**Example prompt structure:**
```
You are extracting health claims from a medical document.
Ontology:
- Subjects: {drug}/adverse-effects/{condition}, {drug}/efficacy/{outcome}
- Predicates: risk_level, incidence_rate, relative_risk, clinical_significance
For each claim found, return:
- subject: The specific topic (e.g., "semaglutide/adverse-effects/gastroparesis")
- predicate: What aspect is being claimed
- object: The claim value (use exact quotes where possible)
- confidence: Your confidence in the extraction accuracy (0.0-1.0)
- evidence: The exact text supporting this claim
Document:
{document_text}
```
### 1.4 Source-Class Classifier
Assigns tier based on source metadata.
```go
// classification/source_class.go
type SourceClassifier struct {
rules []ClassificationRule
}
type ClassificationRule struct {
Name string
Predicate func(SourceMetadata) bool
Tier uint8
}
func (c *SourceClassifier) Classify(meta SourceMetadata) uint8 {
for _, rule := range c.rules {
if rule.Predicate(meta) {
return rule.Tier
}
}
return 6 // Default to lowest tier
}
// Default pharma rules
var PharmaRules = []ClassificationRule{
{
Name: "FDA Label",
Predicate: func(m SourceMetadata) bool {
return m.Source == "dailymed" || m.Source == "fda.gov"
},
Tier: 0,
},
{
Name: "Peer-Reviewed RCT",
Predicate: func(m SourceMetadata) bool {
return m.DOI != "" && m.StudyDesign == "RCT" && m.PeerReviewed
},
Tier: 1,
},
{
Name: "Meta-Analysis",
Predicate: func(m SourceMetadata) bool {
return m.StudyDesign == "meta-analysis" && m.PeerReviewed
},
Tier: 1,
},
{
Name: "Observational Study",
Predicate: func(m SourceMetadata) bool {
return m.DOI != "" && (m.StudyDesign == "cohort" || m.StudyDesign == "case-control")
},
Tier: 2,
},
{
Name: "FAERS Report",
Predicate: func(m SourceMetadata) bool {
return m.Source == "openfda" || m.Source == "faers"
},
Tier: 3,
},
{
Name: "Case Report",
Predicate: func(m SourceMetadata) bool {
return m.StudyDesign == "case-report"
},
Tier: 4,
},
{
Name: "Reddit",
Predicate: func(m SourceMetadata) bool {
return strings.Contains(m.URL, "reddit.com")
},
Tier: 5,
},
{
Name: "Patient Forum",
Predicate: func(m SourceMetadata) bool {
return m.Source == "patient-forum"
},
Tier: 5,
},
{
Name: "News",
Predicate: func(m SourceMetadata) bool {
return m.Source == "news-api"
},
Tier: 6,
},
{
Name: "Social Media",
Predicate: func(m SourceMetadata) bool {
return m.Source == "tiktok" || m.Source == "instagram" || m.Source == "twitter"
},
Tier: 6,
},
}
```
### 1.5 Metadata Enrichment
Adds structured metadata before submission.
```go
// enrichment/service.go
type EnrichmentService struct {
crossref CrossRefClient
pubmed PubMedClient
reddit RedditClient
}
type EnrichedMetadata struct {
// Academic sources
Journal string `json:"journal,omitempty"`
DOI string `json:"doi,omitempty"`
SampleSize int `json:"sample_size,omitempty"`
StudyDesign string `json:"study_design,omitempty"`
// Social sources
Platform string `json:"platform,omitempty"`
Subreddit string `json:"subreddit,omitempty"`
Upvotes int `json:"upvotes,omitempty"`
Replies int `json:"replies,omitempty"`
// Sentiment (computed during extraction)
Sentiment string `json:"sentiment,omitempty"`
SentimentPolarity float32 `json:"sentiment_polarity,omitempty"`
}
func (e *EnrichmentService) Enrich(source SourceMetadata) (*EnrichedMetadata, error) {
meta := &EnrichedMetadata{}
if source.DOI != "" {
// CrossRef lookup for journal, citations
crossrefData, _ := e.crossref.GetByDOI(source.DOI)
meta.Journal = crossrefData.ContainerTitle
}
if strings.Contains(source.URL, "reddit.com") {
// Reddit API for engagement metrics
redditData, _ := e.reddit.GetPost(source.URL)
meta.Upvotes = redditData.Score
meta.Replies = redditData.NumComments
meta.Subreddit = redditData.Subreddit
}
return meta, nil
}
```
### 1.6 Assembly & Submission
Combines all components into a signed assertion.
```go
// ingestion/submit.go
type AssertionSubmitter struct {
episteme EpistemeClient
wallet AgentWallet
classifier SourceClassifier
enricher EnrichmentService
}
func (s *AssertionSubmitter) Submit(claim ExtractedClaim, source SourceMetadata) error {
// 1. Classify source tier
tier := s.classifier.Classify(source)
// 2. Enrich metadata
meta, _ := s.enricher.Enrich(source)
metaJSON, _ := json.Marshal(meta)
// 3. Compute source hash
sourceHash := blake3.Sum256([]byte(source.URL + source.Content))
// 4. Build assertion
assertion := &CreateAssertionRequest{
Subject: claim.Subject,
Predicate: claim.Predicate,
Object: ObjectText(claim.Object),
Confidence: claim.Confidence,
SourceClass: &tier,
SourceHash: hex.EncodeToString(sourceHash[:]),
SourceMetadata: string(metaJSON),
Lifecycle: "Proposed",
}
// 5. Sign with agent key
signature, _ := s.wallet.Sign(assertion)
assertion.Signatures = []SignatureDTO{signature}
// 6. Submit to Episteme
return s.episteme.Assert(assertion)
}
```
---
## Component 2: Background Gardener
Monitors the knowledge graph for emerging signals.
### 2.1 Cluster Detection
```go
// gardener/cluster_detector.go
type ClusterDetector struct {
episteme EpistemeClient
thresholds ClusterThresholds
}
type ClusterThresholds struct {
MinAssertions int // Minimum assertions to be considered a cluster
TimeWindow time.Duration // Window for growth rate calculation
GrowthRateHigh float64 // Assertions per month considered "high"
TierGapTrigger bool // Trigger if Tier 5+ exists but Tier 1-2 doesn't
}
type DetectedCluster struct {
Subject string
Predicate string
Tier5Count int
Tier1Count int
GrowthRate float64 // Assertions per month
HasClinicalGap bool // Tier 5+ exists, no Tier 1-2
EarliestReport time.Time
}
func (d *ClusterDetector) Scan() ([]DetectedCluster, error) {
// Query Episteme for all subject+predicate pairs
// For each pair, count assertions by tier
// Calculate growth rate over time window
// Flag clusters that meet thresholds
}
```
### 2.2 Escalation Assertion Generation
```go
// gardener/escalation.go
func (g *Gardener) GenerateEscalation(cluster DetectedCluster) *CreateAssertionRequest {
meta := map[string]interface{}{
"trigger": "cluster_threshold",
"tier_5_count": cluster.Tier5Count,
"tier_1_count": cluster.Tier1Count,
"growth_rate": fmt.Sprintf("%.0f/month", cluster.GrowthRate),
"clinical_gap": cluster.HasClinicalGap,
"earliest_report": cluster.EarliestReport.Format(time.RFC3339),
}
metaJSON, _ := json.Marshal(meta)
return &CreateAssertionRequest{
Subject: cluster.Subject,
Predicate: "escalation_signal",
Object: ObjectText("Anecdotal cluster detected"),
Confidence: 0.6, // Moderate confidence in the signal
SourceClass: ptr(uint8(255)), // Meta-tier for system-generated
SourceMetadata: string(metaJSON),
Lifecycle: "UnderReview",
}
}
```
### 2.3 Scheduled Tasks
```go
// gardener/scheduler.go
func (g *Gardener) RunSchedule(ctx context.Context) {
// Every hour: Scan for new clusters
go g.runEvery(ctx, 1*time.Hour, g.ScanClusters)
// Every day: Decay trust ranks
go g.runEvery(ctx, 24*time.Hour, g.DecayTrustRanks)
// Every week: Generate summary reports
go g.runEvery(ctx, 7*24*time.Hour, g.GenerateReports)
}
func (g *Gardener) DecayTrustRanks(ctx context.Context) error {
return g.episteme.Post("/v1/admin/decay-trust-ranks", nil)
}
```
---
## Component 3: Disagreement Dashboard
### 3.1 Query Patterns
The dashboard needs these Episteme queries:
```go
// dashboard/queries.go
// Get layered consensus for a topic
func (d *Dashboard) GetLayeredConsensus(subject string) (*LayeredResponse, error) {
return d.episteme.Query(&QueryParams{
Subject: subject,
Lens: "LayeredConsensus",
})
}
// Get conflict map using Skeptic lens
func (d *Dashboard) GetConflictMap(subject string) (*SkepticResponse, error) {
return d.episteme.Query(&QueryParams{
Subject: subject,
Lens: "Skeptic",
})
}
// Get historical state at a point in time
func (d *Dashboard) GetHistoricalState(subject string, asOf time.Time) (*QueryResponse, error) {
return d.episteme.Query(&QueryParams{
Subject: subject,
Lens: "LayeredConsensus",
AsOf: asOf.Unix(),
})
}
// Get changes since last visit
func (d *Dashboard) GetChangesSince(subject string, since time.Time) (*QueryResponse, error) {
return d.episteme.Query(&QueryParams{
Subject: subject,
Lens: "LayeredConsensus",
Since: since.Unix(),
})
}
```
### 3.2 Response Transformation
```go
// dashboard/transform.go
type ConsumerView struct {
Topic string `json:"topic"`
Summary string `json:"summary"` // LLM-generated
ConflictScore float32 `json:"conflict_score"`
TierPositions []TierPosition `json:"tier_positions"`
ResolvedTopics []ResolvedTopic `json:"resolved"`
ActiveDisputes []ActiveDispute `json:"active_disputes"`
EmergingSignals []EmergingSignal `json:"emerging_signals"`
LastUpdated time.Time `json:"last_updated"`
}
type TierPosition struct {
TierName string `json:"tier_name"` // "Clinical Evidence", "Patient Community"
TierNumber uint8 `json:"tier_number"`
Position string `json:"position"`
Confidence float32 `json:"confidence"`
AssertionCount int `json:"assertion_count"`
}
func TransformForConsumer(layered *LayeredResponse, skeptic *SkepticResponse) *ConsumerView {
view := &ConsumerView{
ConflictScore: layered.OverallConflictScore,
}
for _, tier := range layered.Tiers {
view.TierPositions = append(view.TierPositions, TierPosition{
TierName: tierToName(tier.Tier),
TierNumber: tier.Tier,
Position: summarizePosition(tier.Winner),
Confidence: tier.Winner.Confidence,
AssertionCount: tier.CandidatesCount,
})
}
// Categorize by conflict level
for _, topic := range skeptic.Topics {
if topic.ConflictScore < 0.2 {
view.ResolvedTopics = append(view.ResolvedTopics, ...)
} else if topic.ConflictScore < 0.7 {
view.ActiveDisputes = append(view.ActiveDisputes, ...)
} else {
view.EmergingSignals = append(view.EmergingSignals, ...)
}
}
return view
}
func tierToName(tier uint8) string {
names := map[uint8]string{
0: "Regulatory",
1: "Clinical Evidence",
2: "Real-World Evidence",
3: "Pharmacovigilance",
4: "Clinical Anecdote",
5: "Patient Community",
6: "Media",
}
return names[tier]
}
```
### 3.3 LLM Summary Generation
```go
// dashboard/summary.go
type SummaryGenerator struct {
llm LLMClient
}
func (g *SummaryGenerator) GenerateSummary(view *ConsumerView) (string, error) {
prompt := fmt.Sprintf(`
Summarize the following health topic for a consumer in 2-3 sentences.
Be factual. Acknowledge uncertainty where it exists. Do not give medical advice.
Topic: %s
Conflict Score: %.2f (0 = agreement, 1 = max disagreement)
Tier Positions:
%s
Output a concise, balanced summary.
`, view.Topic, view.ConflictScore, formatTierPositions(view.TierPositions))
return g.llm.Complete(prompt)
}
```
**Example output:**
> "Clinical trials show low incidence of gastroparesis with semaglutide; post-marketing reports and patient communities report higher rates. The FDA added gastroparesis to the label in January 2024. There is moderate disagreement between clinical evidence and real-world reports."
---
## Component 4: Agent Wallet
Manages signing keys for the ingestion pipeline.
### 4.1 Key Storage
```go
// wallet/wallet.go
type AgentWallet struct {
keyPath string
privateKey ed25519.PrivateKey
publicKey ed25519.PublicKey
}
func NewAgentWallet(keyPath string) (*AgentWallet, error) {
// Load or generate Ed25519 keypair
// Store private key securely (file permissions, encryption at rest)
}
func (w *AgentWallet) Sign(assertion *CreateAssertionRequest) (*SignatureDTO, error) {
// Serialize assertion for signing
message := fmt.Sprintf("%s:%s", assertion.Subject, assertion.Predicate)
sig := ed25519.Sign(w.privateKey, []byte(message))
return &SignatureDTO{
AgentID: hex.EncodeToString(w.publicKey),
Signature: hex.EncodeToString(sig),
Timestamp: time.Now().Unix(),
}, nil
}
```
### 4.2 Multi-Agent Setup
For different source types, use different agent identities:
```go
// wallet/multi.go
type MultiAgentWallet struct {
agents map[string]*AgentWallet
}
func (m *MultiAgentWallet) SignAs(agentName string, assertion *CreateAssertionRequest) (*SignatureDTO, error) {
agent, ok := m.agents[agentName]
if !ok {
return nil, fmt.Errorf("unknown agent: %s", agentName)
}
return agent.Sign(assertion)
}
// Usage:
// - "pubmed-crawler" agent for academic sources
// - "reddit-crawler" agent for social sources
// - "gardener" agent for escalation assertions
```
---
## Deployment Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PubMed │ │ Reddit │ │ FAERS │ Crawlers │
│ │ Crawler │ │ Crawler │ │ Crawler │ (CronJob) │
│ │ Pod │ │ Pod │ │ Pod │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Extraction Service │ │
│ │ (Deployment, 3 replicas) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Episteme API │ │
│ │ (Deployment, 3 replicas) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Dashboard API │ │
│ │ (Deployment, 2 replicas) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Dashboard UI │ │
│ │ (Static, CDN) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Background Gardener │ │
│ │ (CronJob, hourly) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## Checklist: What You Need to Build
### Crawlers (one per source)
- [ ] PubMed/biorxiv crawler (NCBI E-utilities)
- [ ] ClinicalTrials.gov crawler
- [ ] FDA DailyMed scraper
- [ ] openFDA FAERS consumer
- [ ] Reddit API consumer
- [ ] Patient forum scrapers
- [ ] News API consumer
- [ ] Social media sampler
### Extraction Pipeline
- [ ] LLM-based claim extractor
- [ ] Subject/predicate ontology for pharma domain
- [ ] Extraction prompt library
- [ ] Confidence calibration
### Classification & Enrichment
- [ ] Source-class classifier with pharma rules
- [ ] CrossRef integration for DOI lookup
- [ ] PubMed integration for study metadata
- [ ] Reddit API for engagement metrics
- [ ] Sentiment analysis model
### Infrastructure
- [ ] Agent wallet with secure key storage
- [ ] Multi-agent identity management
- [ ] Rate limiting for external APIs
- [ ] Retry logic and error handling
### Gardener
- [ ] Cluster detection algorithm
- [ ] Escalation assertion generator
- [ ] TrustRank decay scheduler
- [ ] Alert/notification system
### Dashboard
- [ ] Query orchestration layer
- [ ] Response transformation
- [ ] LLM summary generator
- [ ] React/Vue frontend
- [ ] Time-travel UI
- [ ] Change notification system
---
## See Also
- [Use Case: Consumer Health Intelligence](../../use-cases/consumer-health-intelligence.md) — Full scenario walkthrough
- [App Concepts Index](./index.md) — General application layer patterns
- [Roadmap: Phase 3](../../roadmap.md) — Database features needed
- [ADK-Go Integration](../../.claude/guides/integrations/adk-go-episteme.md) — Agent framework integration