One substrate, three states. Schema deployed = DDL exists in prod D1; many of these are 0 rows. Writes live = a code path is filling rows. Reads live + externally usable = serves traffic on api.kaeva.app. The diagram below is the same regardless of which lens you pick — the prose tells you which boxes are which today.
For an agent: UNBLOCK is the substrate you read from on cold-start and write to whenever you decide something load-bearing. Every conversation lands in the corpus verbatim; remember marks decisions; extract pulls structured signals out; share + list + purchase let you delegate or trade access. Everything is signed, scoped, and survives the agent that minted it. Single-tenant hardened on api.kaeva.app; multi-tenant FK isolation lands in Phase 4.
For an investor — what's actually here today:
/v1/query. The substrate is real, the user count is single-tenant honest, and we're using it on ourselves daily.UNBLOCK, BlockIP, Marketplace, StakeRegistry). Indexer Worker reads events; relay Worker signs and submits. No mainnet, no production traffic, no settled purchases yet.Defensibility thesis (still a thesis, not a metric): the same cap-token primitive that gates a private team-share also clears a paid public sale; the marketplace is a column on listings, not a separate stack. Whether reputation-as-ranker actually compounds is a Phase 4 question — the writers aren't wired yet.
The hand-coded SVG above was regenerated 2026-05-05 with the Cocoon-AI/architecture-diagram-generator skill. Status colors mirror section [03]'s integrated ERD: green = on main HEAD, amber = proposed / ADR / Phase 4 wiring, purple = evolving (today: working_memory + rules typed memory tables, migration 030, ADR-037, just landed). The interactive Mermaid flowchart below remains the canonical source-of-truth — color and layout match.
Verbs are the stable surface. Storage tier and chain layer are swappable per ADR. The diagram shows the shape; the schema legend in section [03] tells you which boxes are wired today.
Auth bridge gap (honest): EIP-1271 wallet signatures authenticate users; cap-tokens are Ed25519. The bridging ceremony — how an EIP-1271 wallet sig mints an Ed25519 cap-token, and how revocation gossip propagates to verifiers — is specified at the schema level (see cap_tokens, revoked_cap_tokens) but the verifier and revocation propagation are Phase 4. The dedicated api_keys + revocation_table for first-class API-key revocation is migration 031, proposed (TRACK-F).
Today's deltas (annotated above): (a) working_memory + rules typed memory landed via migration 030 (ADR-037, commit 0bf0841) — borrowed from Mem0 + Memanto + Hindsight schemas; (b) OTel observability via @microlabs/otel-cf-workers (TRACK-A landing tonight); (c) packages/unblock-hooks client-side runner (TRACK-D bootstrapping); (d) api_keys + revocation dedicated table (TRACK-F migration 031 proposed). Items (b) (c) (d) are not on main HEAD as of this regen.
Deployed (prod D1 unblock-catalog-v01, schema v30 · just bumped 2026-05-05). 45 tables across four tiers (substrate · economic · cognition + ops · typed memory). blocks carries the load (~890k rows); 10 tables are wired into request paths; the rest are schema-only or background-fill. Per ADR-008, conversation + utterance are tier-1 block types — passive capture lives in the same table as everything else. The 13 block_type values: note, snippet, doc, code, trace, decision, anti-pattern, dataset, exploit, kg, conversation, utterance, other (per migrations/001_init.sql CHECK). Migration 030 (today, ADR-037) added 4 typed-memory tables: working_memory (per-user 2000-cap hot tier with composite-score eviction), rules (typed memory: rule/pattern/anti-pattern/preference/constraint/exception/convention with forward-pointer supersession), rule_scopes (multi-scope applicability), and rule_violations (observability for confidence decay). Plus 2 FTS5 virtuals. Schema borrows from Mem0 (working-memory shape), Memanto (typed-memory schema, 89.8% LongMemEval with one typed table), and Hindsight (confidence/bank separation). No KG layer — explicitly skipped per ADR-037.
Planned (codegen at migrations/000_dev_db.sql, not deployed). 26 tables generated by scripts/schema/def.py per ADR-034 + ADR-036 (per-user D1 + shared marketplace D1). Two tables (users, extraction_jobs) appear in both deployed and planned — counted once. So the integrated ERD shows 69 distinct tables: 45 deployed (43 unique post mig 030) + 26 planned (24 unique) + 2 evolving = 69 nodes. The "Full ERD" tab below renders all of them, color-coded.
| Table | What it holds | Key columns |
|---|---|---|
| blocks | Every signed unit of knowledge. 13 block_types: note, snippet, doc, code, trace, decision, anti-pattern, dataset, exploit, kg, conversation, utterance, other. |
block_id · user_id · parent_block · content_hash · scope · embedding_id |
| users | Tenant identity. Privy ID · wallet address · API-key hash collapse here. | user_id · privy_id · wallet_addr · created_at |
| grants | Read/write delegation between users on specific blocks. Issued by share; revocable. |
grant_id · block_id · grantor · grantee · permissions · expires_at |
| listings | Marketplace state — block + price + tier + seller. Off-chain mirror; on-chain settlement via relay. |
listing_id · block_id · seller · price_unblock · tier · status |
| purchases | Settled buys. tx_hash links to Base Sepolia. | purchase_id · listing_id · buyer · tx_hash · paid_at |
| cap_tokens | Ed25519-signed delegation tokens · 3 classes (read-only · scoped-write · admin-handoff). DDL deployed; verifier middleware on /v1/query; revocation gossip and EIP-1271-wallet → Ed25519-cap minting bridge are Phase 4 (see revoked_cap_tokens). |
cap_token_id · block_id · issuer · recipient · permissions · revoked_at |
| outcome_traces (0 rows · writers Phase 4) |
Did the buyer's task succeed using this block? Schema deployed. Writers unwired — no settlement path is producing traces yet. Reputation rollup endpoint exists in OpenAPI but reads against an empty table. | trace_id · block_id · agent_id · verdict · evidence_hash |
| escalations (0 rows · endpoint live) |
When an agent can't resolve a query — pop up to a higher-tier validator or human reviewer. POST endpoint live; verifier wiring is Phase 4. | escalation_id · query · originating_agent · resolver · resolved_at |
parent_block, so a decision can cite the exact utterance that minted it. Even rejected_alternatives stay linked — the platinum signal is the full deliberation, not just the picked outcome (ADR-008).
"Implemented" in this doc means schema deployed in prod D1. It does not mean writers are firing or reads are externally usable. The honest split-by-axis is in the table below; the diagram color reduces to three visual states for legibility.
Per-table maturity (the 7-axis split, collapsed to a quick reference):
| Tier | DDL deployed | Writes live | Reads live (external) | Prod traffic | Net / mainnet |
|---|---|---|---|---|---|
Substrate (blocks, extracted_facts, FTS shadows) | Yes (8/8) | Yes (8/8) | Yes — /v1/remember, /v1/query, /v1/extract | Single-tenant dogfood | — |
Economic (listings, purchases, grants, cap_tokens, attestations, verifications, request_costs) | Yes (11/11) | Partial · request_costs 2,420 rows; 10/11 are 0 rows | Endpoints live; reads return empty for 10/11 | None | Base Sepolia testnet |
Cognition (belief_states, block_reconsolidations, 12 others) | Yes (14/14) | Partial · 3/14 firing (block_reconsolidations 7,442 · belief_states 1,081 · precision_state 15) | Only block_reconsolidations reads back into /v1/query | None | — |
Ops (workspace_state, workspace_audit, escalations, consumer_tasks, webhooks, webhook_deliveries) | Yes (6/6) | No (0/6) | POST endpoints live for escalations, webhooks | None | — |
Typed memory · ADR-037 (working_memory, rules, rule_scopes, rule_violations) [mig 030, today] | Yes (4/4 · v30) | No (0/4) | No — wiring planned next sprint into /v1/remember + /v1/query | None | — |
Test coverage and revenue-bearing axes are tracked separately in docs/handoff/COVERAGE-AUDIT-20260428.md; today there is no revenue and no mainnet.
Solid arrows = hard FK (CASCADE / SET NULL / RESTRICT). Dashed = soft / denorm / cross-tier. Six tables are EVOLVING: users (current adds display_name / persona_id / tz / working_style; planned splits external_accounts JSON into user_external_accounts); extraction_jobs (current is conversation-scoped per ADR-033; planned generalizes to target_kind + job_kind covering granite-triple / kimi-reason / classify / ner / topic-segment / pattern-scan / embed / wiki-sync); plus the four typed-memory tables that just landed via migration 030 (working_memory, rules, rule_scopes, rule_violations) — DDL on prod D1 v30, writers wiring next sprint. Live counts (prod): blocks 890k · extracted_facts 63k · block_reconsolidations 7.4k · request_costs 2.4k · synthesis_cache 1.5k · belief_states 1.1k · users 28 · working_memory 0 · rules 0 · everything else 0.
Implemented · 45 tables
Prod D1 unblock-catalog-v01 · schema_version 30 · last migration 030_working_memory_and_rules.sql (today, 2026-05-05) · ~966k total rows · 0 rows in the 4 new typed-memory tables.
Planned · 26 tables
ADR-034 codegen · migrations/000_dev_db.sql · ADR-036 per-user D1 + shared marketplace D1 · 0 rows (not deployed). Migration 031 (api_keys + revocation, TRACK-F) proposed but not on main HEAD.
Evolving · 6 tables
users + extraction_jobs (ADR-036 splits); working_memory + rules + rule_scopes + rule_violations (ADR-037 mig 030, schema deployed today, writers next sprint).
blocks (12 semantic shapes, 890k rows) splits into conversations + utterances + source_provenance + conversation_participants + conversation_annotations + utterance_attributions + eval_qa_pairs + eval_qa_evidence. The 14 columns currently NULL on atomic rows (agent_id, workspace_path, git_branch, affect_*, etc.) get proper homes.extracted_facts (63k rows, single triple-store) splits into granite_facts (per-utterance synchronous, IBM Granite extraction) + kimi_facts (per-conversation async, K2.5 reasoning with ts_start/ts_end EVENT-time anchors). Plus kimi_fact_evidence + pattern_evidence as proper join tables instead of JSON arrays.utterance_classifications (8-emotion Plutchik + 6-axis toxicity + 9 boolean signals), entity_mentions (sparse NER), topic_segments (per-conversation boundaries).canonical_entities (de-duped KG nodes per user with FTS5) + patterns (temporal/thematic/linguistic/behavioral/co-occurrence/instinct rules; instinct rules are imported from operator memory at user creation).user_wiki tier — 19 section_kinds (identity / location / profession / projects / relationships / preferences / beliefs / goals / skills / health / timeline / communication-style / working-style / technical-stack / finances / education / hobbies / moods / general). Auto-marked needs_refresh=1 on every utterance ingest; cron synthesizes content_md from referenced facts/patterns/entities.user_id string keys.embeddings_pending_delete queue + DELETE/UPDATE/soft-delete triggers on every embedding-bearing source table. GDPR forget actually propagates to FTS shadow + Vectorize.scripts/schema/def.py (declarative tables) → scripts/schema/gen.py (mixin-emitted SQL) → migrations/000_dev_db.sql (artifact). Lint at scripts/schema/lint.py enforces invariants on every regeneration.▾ Detailed table-level ERDs (drill-down)
Per-tier erDiagram blocks below show full column lists, types, and relationship cardinalities. Section borders match status colors above.
▸ Implemented · prod D1 v29 — table-by-table
blocks is overloaded — 12 semantic shapes; conv/utt = 99.998% of rows. No FK from any user_id column → users.user_id (multi-tenant isolation is app-code only). FTS5 hand-synced — drift-prone. Source of truth for migrations is schema_version (29 rows); wrangler's d1_migrations is empty/bypassed.
Soft cross-tier FKs to blocks, users, cap_tokens (no hard CASCADE — economic tier is logical-only). Marketplace + grants + attestations + verifications all gated on Phase 4 wiring. purchases.payment_method CHECK still allows 'privy' — stale per feedback_no_privy.md; tighten to wallet|relay. user_costs_rollup cron isn't running (0 rows despite 2,420 live request_costs).
Production status: 14 tables, 3 of them have rows. block_reconsolidations (7,442) is the only one wired into a request path — the sensing-gate + reconsolidation hook on /v1/query writes here. belief_states (1,081) and precision_state (15) are written by background standalone scripts; nothing reads them back into a user-facing flow. outcome_traces, plans, simulations, self_model, policy_decisions, metacog_judgments, executions, abstractions, replay_runs, replay_sequences, block_outcome_rollup = 0 rows. The schema is the bet on what these surfaces will look like when Phase 4 wires writers; treat the prose accordingly. Foot-gun: outcome_traces.task_id CASCADE from consumer_tasks would nuke settlement evidence — switch to SET NULL or RESTRICT before Phase 4 turns writers on. Only hard FK leaving this tier today: block_reconsolidations.block_id → blocks.
workspace_state + workspace_audit implement the global-workspace "ignition" primitive (chunks compete for K-slot capacity). Escalations queue is the human-in-the-loop surface for cap-token-blocked or low-confidence agent decisions. All 0 rows — Phase 4 wires writers.
▸ Planned · ADR-036 dev codegen — table-by-table (not deployed)
scripts/schema/def.py — declarative tables: tier, columns, mixins (tenant, lifecycle, audit, FTS5, embedding, soft-delete).scripts/schema/gen.py — emits the migration (26 tables, 6 FTS5 virtuals, triggers, indexes).scripts/schema/lint.py — invariant lint for tenancy, FK shape, GDPR-cascade.Structural fixes folded into codegen (review by Codex + Gemini Pro 3.1; details in repo, not here):
kimi_fact_evidence, pattern_evidence, user_wiki_evidence) — cross-tenant / cross-tier writes impossible at FK layer.conversations, utterances, granite_facts, kimi_facts, canonical_entities, patterns, user_wiki) — composite FKs above have a key to point at.embeddings_pending_delete._au_del + _au_ins with WHEN NEW.is_forgotten = 0 guard; soft-delete actually removes from FTS shadow.uw_mark_refresh debounces.conversations → utterances; eval_qa_evidence promoted to a real join table.Migration artifact: migrations/000_dev_db.sql. Adding a table = declare it in def.py with the right mixins; lint enforces compliance.
Dev DB · 2026-05-03 · 26 base tables across 6 + 1 tiers + 6 FTS5 virtuals + auto-sync triggers. Includes user_wiki + user_wiki_evidence for per-user living knowledge synthesized from kimi_facts + patterns + canonical_entities; auto-marked needs_refresh=1 on every new utterance via trigger. Full DDL in the repo: migrations/000_dev_db.sql.
Solid arrows = hard FK CASCADE. Dashed = soft / denorm / cascade-target. New post-Codex: 7 normalization tables (split-from-JSON + cascade GC + eval). New user_wiki layer (gold): per-user living knowledge across 19 section_kinds (identity / location / profession / projects / relationships / preferences / beliefs / goals / skills / health / timeline / communication-style / working-style / technical-stack / finances / education / hobbies / moods / general). Auto-marked needs_refresh=1 on every utterance ingest; cron synthesizes via job_kind='wiki-sync'.
Personas encode CEO/CTO/engineer (CLAUDE.md alpha-as-CTO model) and OSS roles (maintainer/contributor/reviewer). Same persona pool used by users and agents. Operating rules + quality bar + escalation policy travel with the persona.
UNIQUE constraint (SQL only): (user_id, source_kind, source_id) — idempotent re-ingest of LoCoMo by sample_id.
Captures OSS authorship + license + commit/repo + Git-style attribution. Per sop_oss_block_mining.md: accept Apache-2.0 / MIT / BSD-3 / BSD-2 / ISC / CC0; reject GPL / LGPL / AGPL.
UNIQUE constraints (SQL only): (conversation_id, turn_index) · (conversation_id, source_msg_id). Companion FTS5 virtual: utterances_fts(text, speaker_label).
One row per utterance per classifier_run. Powers "find toxic / sarcastic / praising / questioning / hedged" filters. UNIQUE on (utterance_id, classifier_model, classifier_version).
0..N rows per utterance for each named entity surface form. Linked to canonical_entities for cross-conversation dedup. Char offsets preserved for highlighting.
Companion FTS5: granite_facts_fts(subject, predicate, object, raw_text). Vectorize embedding ID per fact.
Key innovation vs granite: ts_start/ts_end are EVENT time, not utterance-mention time. The cat-3 (temporal) fix surface lives here. Plus same memory-framing (memory_kind / why_text / how_to_apply_text) as granite — both fact tables isomorphic for downstream "give me memories" retrieval. Async cron only — K2.5 subrequest can exceed 3-min Workers AI cap.
Deduplicated entities per user. UNIQUE on (user_id, entity_type, canonical_name). Companion FTS5 over canonical_name + aliases + description.
Absorbs instincts.md (trigger→action→confidence) via pattern_kind='instinct'. CHECK constraint: instinct rows MUST have trigger_text + action_text. UNIQUE (user_id, pattern_signature).
Single orchestration table for ALL extraction surfaces (granite per-utt, kimi per-conv, classify, ner, topic-segment, pattern-scan, embed). Per ADR-033 generalized: cron drainer reads pending jobs, processes chunks, persists progress between cron ticks. UNIQUE (dedupe_key, status IN pending/running) prevents duplicate work.
'usr_[0-9a-f]*'-style CHECKs were decorative — * is wildcard, not "repeat previous class." Replaced all 14 ID-prefix CHECKs (and agent_kind) with length() = N AND substr(...) NOT GLOB '*[^0-9a-f]*'. Bad IDs now actually rejected at write.conversations, utterances) expose composite UNIQUE on (, user_id, tier) ; every extraction child references the composite key. Cross-user / cross-tier writes are now impossible at the FK layer (no triggers needed).conversations(user_id, tier, source_kind, source_id), canonical_entities(user_id, tier, entity_type, canonical_name), patterns(user_id, tier, pattern_signature) — prod/eval can no longer collide.conversation_annotations PK was collapsing LoCoMo session data. Redesigned with annotation_id PK + (conversation_id, kind, session_index, ordinal) UNIQUE. Multiple observation / session_summary rows per conversation now coexist properly.embeddings_pending_delete table + per-source DELETE triggers. is_forgotten=1 now actually propagates to all search side-channels.patterns.evidence_*_ids and kimi_facts.evidence_utterance_ids into pattern_evidence and kimi_fact_evidence join tables with real FK CASCADE.is_superseded=0 OR superseded_by IS NOT NULL; is_stale=0 OR length(trim(stale_reason))>0; is_forgotten=0 OR forgotten_at IS NOT NULL. superseded_by ON DELETE switched to RESTRICT + no self-supersession.is_active; live = is_forgotten=0 AND is_superseded=0 AND is_stale=0.users.external_accounts → user_external_accounts. conversations.participants → conversation_participants. utterances.{signed_off_by, reviewed_by, co_authored_by} → utterance_attributions.eval_qa_pairs table for LoCoMo's 1,986 QA pairs (was being shoved into conversation_annotations.payload).cost_usd REAL → cost_micros INTEGER (1 USD = 1,000,000) for stable accounting.CHECK (col IS NULL OR json_valid(col)) on every JSON-bearing column.AFTER UPDATE triggers on every table with updated_at — no app reliance.extraction_run_id immutability. ON DELETE switched to RESTRICT. Jobs outlive their facts (provenance preservation).ts_precision design vs DDL mismatch fixed. Added 'hour' and 'year' to the enum.utterances.text NOT NULL relaxed for image/audio/code/mixed modalities (text-only turns still require non-empty).26 base tables · 6 FTS5 virtuals · 6 + 1 tiers · trigger-driven cascades. Out of scope here: the deployed prod schema (41 tables, 890k blocks) which still uses the monolithic blocks shape. Live prod ERD: docs/architecture/erd-current-20260503.md. Full DDL: migrations/000_dev_db.sql.
/v1/query.What actually happens when a client calls POST /v1/query: the query is rewritten into 1–5 sub-queries (multi-hop, depending on category), and each sub-query fans out across five recall arms — one dense (Cloudflare Vectorize) and four lexical (FTS5). Results are union-fused with reciprocal rank, reranked by a cross-encoder, then judged by a small generative model, then synthesized by Kimi K2.5. Every arm exists to catch a different failure mode of the others.
Source: scripts/retrieval/pipeline.py + scripts/retrieval/synthesize.py. Each FTS5 arm targets a different surface that vector recall systematically misses (verbatim quotes, derived facts at two granularities, cross-conversation entity canonicalization). RRF makes the fusion robust to per-arm score scales; bge picks semantic relevance; the LLM-judge picks evidence-supportedness specifically. working_memory consult-first is the planned cheap-path optimization (mig 030 just landed, wiring next sprint): every retrieval consults the per-user 2000-cap hot tier first via FTS5; on a clean hit the query short-circuits straight to synth, on a miss the existing 5-arm fan-out runs.
Honest numbers from today's sprint. iter-16 baseline (n=100, F1=0.346) → main HEAD 04977e6 + one uncommitted patch (H2 cat-3 carve-out) (n=96 partial, F1=0.4894). Sprint target was 0.80; we are at 0.49. Per-category deltas:
| Category | iter-16 | today (n≈96) | Δ | What changed |
|---|---|---|---|---|
| cat-1 · multi-hop list | 0.105 | 0.282 | +0.177 | Refined ANSWER-TYPE & SCOPE prompt; _who_relation_subqueries helper; multi-hop sub-query expansion. |
| cat-2 · temporal | 0.054 | 0.413 | +0.359 | TIME RESOLUTION Step 5: forbid ISO; match evidence specificity (date-format prompt fix). |
| cat-3 · open-domain | 0.000 | 0.050 | +0.050 | H2 inferential force-answer carve-out for does/do/would/is-likely patterns (uncommitted). |
| cat-4 · single-hop | 0.269 | 0.447 | +0.178 | Harness --retrieval-user-id auto-detect (was filtering to non-existent tenant); refusal phrase fix. |
| cat-5 · adversarial / refusal | 0.913 | 0.826 | −0.087 | Canonical refusal phrase "No information available."; confidence floor 0.4 → 0.55; H3 entity-overlap hallucination guard (uncommitted, untested at n=100). |
Eval caveats. Dataset = LoCoMo10 (10 conversations, ~1,986 QA pairs total; the n=96 stratified seed=42 sample). Scoring = official LoCoMo F1 with literal-substring refusal-credit ("no information available" / "not mentioned") — non-canonical refusals score 0 even when correct. Cat-5 regressed because the cat-5 confidence floor was lifted from 0.4 → 0.55 (calibration trade — the H3 entity-overlap guard is uncommitted and was NOT in this run). Wave 2 (qwen3-30b judge + temporal canon + Vectorize v02) is rolled back per project_unblock_iter16_regression_20260504.md; baseline now is iter-8 + selected single-lever re-additions. See SPRINT-2026-05-04-F1-CONSOLIDATED.md for the full handoff.
Question: "What activities has Melanie done with her family?" Gold: "Pottery, painting, camping, museum, swimming, hiking" with 6 evidence dia_ids (D8:4, D8:6, D9:1, D6:4, D1:18, D3:14). Currently F1=0.04. The retrieval surfaces 0/6 gold dia_ids — instead it surfaces derived kimi_facts about Melanie. The synth then abstains, despite some kimi_facts directly mentioning camping and painting.
Diagnosis. The ranker prefers derived kimi_facts rows over the answer-bearing raw utterances rows because kimi_facts are short, on-topic, and embedded; raw utterances are noisier. The synth's abstain calibration is then too strict on cat-1 list questions — it would rather refuse than aggregate weak evidence into a list. Fix direction (Phase 5): tier-aware reranking that floors a minimum share of utterance-tier candidates per query, plus a list-aggregation prompt that explicitly permits combining multiple weak-but-cited fragments into a list answer.
Full E2E trace harness lives at scripts/eval/locomo_e2e_trace.py (see docs/architecture/retrieval-pipeline.md).
A grant and a listing are the same shape: a cap-token issued from owner to recipient with permissions and an expiry. A free team-share is a cap-token with price = 0 and recipient = team:<id>. A marketplace purchase is a cap-token with price > 0 and recipient = the buyer's wallet. The marketplace isn't bolted on; it's the natural extension of the access primitive.
That means three things compound at once:
listings.See the full reasoning in VISION.md and the marketplace categories in architecture/marketplace-categories.md.