stemedb/.claude/skills/stemedb-lens/SKILL.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

90 lines
3.1 KiB
Markdown

---
name: stemedb-lens
description: Guidelines for implementing Lenses (Resolution Logic). Use when working on query resolution or ranking.
---
# StemeDB Lens Implementation
## Identity
You are building the **Cortex** of Episteme. Lenses allow us to view a contradictory reality and get a deterministic answer.
## The Lens Traits
```rust
// Synchronous lens — for pure logic (Recency, Consensus, Authority)
pub trait Lens {
fn resolve(&self, candidates: &[Assertion]) -> Resolution;
fn name(&self) -> &'static str;
}
// Async lens — for I/O-backed resolution (VoteAwareConsensus, TrustAwareAuthority)
pub trait AsyncLens {
async fn resolve_async(&self, candidates: &[Assertion]) -> Resolution;
fn name(&self) -> &'static str;
}
```
## Current Implementations
| Lens | Trait | Strategy | File |
|------|-------|----------|------|
| RecencyLens | Lens | Highest timestamp wins | `recency.rs` |
| ConsensusLens | Lens | Most common object value wins | `consensus.rs` |
| ConfidenceLens | Lens | Highest confidence field wins | `confidence.rs` |
| VoteAwareConsensusLens | AsyncLens | VoteStore vote counts | `vote_aware_consensus.rs` |
| TrustAwareAuthorityLens | AsyncLens | confidence * TrustRank | `trust_aware_authority.rs` |
**API Mapping:** In the API, `LensDto::Authority` and `LensDto::TrustAwareAuthority` both route to `TrustAwareAuthorityLens`. Use `LensDto::Confidence` to get the simple confidence-field selector.
## Principles
* **Stateless**: Sync lenses must be stateless (logic only).
* **Deterministic**: Same input + Same context = Same output.
* **Fast**: This runs on every read. Avoid allocations.
* **Instrumented**: Every `resolve`/`resolve_async` impl MUST have `#[instrument]`.
## Tracing Pattern (Required)
Every lens resolve method must be instrumented:
```rust
use tracing::instrument;
impl Lens for MyLens {
#[instrument(skip(self, candidates), fields(candidates_count = candidates.len(), lens = "MyLens"))]
fn resolve(&self, candidates: &[Assertion]) -> Resolution {
// ...
}
}
```
## Common Strategies
### 1. Recency (LWW)
Sort by `timestamp` descending. Return first. Tiebreaker: `source_hash`.
### 2. Consensus (Voting)
Map `object` -> `count`. Return object with `max(count)`. From largest group, pick most recent.
### 3. Confidence
Return assertion with highest `confidence` field. Tiebreaker: most recent timestamp.
Uses the assertion's self-declared confidence, NOT agent reputation.
### 4. Vote-Aware Consensus (Phase 2+)
Query VoteStore for real vote counts per assertion. Tiebreaker: confidence then timestamp.
### 5. Trust-Aware Authority (Phase 2+)
weighted_score = confidence * TrustRank. Query TrustRankStore for agent reputation.
## Do
* Handle empty candidate sets gracefully (return `Resolution::empty()`).
* Implement `Default` for standard lenses.
* Document the ranking logic clearly.
* Add `#[instrument]` with `candidates_count` and `lens` fields.
* Use `Resolution::with_winner()` for consistent output.
## Do Not
* Perform I/O inside a sync `Lens` (use `AsyncLens` if DB lookups are needed).
* Use `unwrap()` or `expect()` — workspace lints deny these.