spore-commons/node/spore_node/db/migrations/002_primitives.sql

168 lines
7.2 KiB
SQL

-- Spore Agent Commons — Core Primitives
-- Claims, Evidence, Attestations, Intents, Commitments
-- ============================================================
-- Claims (knowledge claims in the commons)
-- ============================================================
CREATE TABLE IF NOT EXISTS claims (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rid TEXT UNIQUE NOT NULL,
proposer_rid TEXT NOT NULL,
content TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'proposed'
CHECK (status IN ('proposed', 'supported', 'challenged', 'superseded')),
confidence FLOAT DEFAULT 0.5,
anchor_type TEXT DEFAULT 'assertion',
embedding vector(1024),
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_claims_proposer ON claims (proposer_rid);
CREATE INDEX IF NOT EXISTS idx_claims_status ON claims (status);
CREATE INDEX IF NOT EXISTS idx_claims_metadata ON claims USING GIN (metadata);
-- ============================================================
-- Evidence (supports or challenges a claim)
-- ============================================================
CREATE TABLE IF NOT EXISTS evidence (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rid TEXT UNIQUE NOT NULL,
claim_id UUID NOT NULL REFERENCES claims(id) ON DELETE CASCADE,
relation TEXT NOT NULL CHECK (relation IN ('supports', 'challenges')),
body TEXT NOT NULL,
provenance JSONB NOT NULL DEFAULT '{}',
embedding vector(1024),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_evidence_claim ON evidence (claim_id);
CREATE INDEX IF NOT EXISTS idx_evidence_relation ON evidence (relation);
-- ============================================================
-- Attestations (endorse/dispute/abstain on a claim)
-- ============================================================
CREATE TABLE IF NOT EXISTS attestations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rid TEXT UNIQUE NOT NULL,
claim_id UUID NOT NULL REFERENCES claims(id) ON DELETE CASCADE,
attester_rid TEXT NOT NULL,
verdict TEXT NOT NULL CHECK (verdict IN ('endorse', 'dispute', 'abstain')),
strength FLOAT NOT NULL DEFAULT 1.0,
reasoning TEXT DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (claim_id, attester_rid)
);
CREATE INDEX IF NOT EXISTS idx_attestations_claim ON attestations (claim_id);
CREATE INDEX IF NOT EXISTS idx_attestations_attester ON attestations (attester_rid);
-- ============================================================
-- Claim strength (materialized view)
-- ============================================================
CREATE MATERIALIZED VIEW IF NOT EXISTS claim_strength AS
SELECT
c.id AS claim_id,
c.rid AS claim_rid,
c.status,
c.confidence,
COUNT(DISTINCT CASE WHEN a.verdict = 'endorse' THEN a.id END) AS endorse_count,
COUNT(DISTINCT CASE WHEN a.verdict = 'dispute' THEN a.id END) AS dispute_count,
COUNT(DISTINCT CASE WHEN a.verdict = 'abstain' THEN a.id END) AS abstain_count,
COUNT(DISTINCT CASE WHEN e.relation = 'supports' THEN e.id END) AS supporting_evidence,
COUNT(DISTINCT CASE WHEN e.relation = 'challenges' THEN e.id END) AS challenging_evidence,
COALESCE(
(COUNT(DISTINCT CASE WHEN a.verdict = 'endorse' THEN a.id END)::float -
COUNT(DISTINCT CASE WHEN a.verdict = 'dispute' THEN a.id END)::float) /
NULLIF(COUNT(DISTINCT a.id)::float, 0),
0
) AS net_sentiment,
GREATEST(
MAX(a.created_at),
MAX(e.created_at),
c.created_at
) AS last_activity
FROM claims c
LEFT JOIN attestations a ON a.claim_id = c.id
LEFT JOIN evidence e ON e.claim_id = c.id
GROUP BY c.id, c.rid, c.status, c.confidence;
CREATE UNIQUE INDEX IF NOT EXISTS idx_claim_strength_id ON claim_strength (claim_id);
-- ============================================================
-- Intents (need/offer/possibility)
-- ============================================================
CREATE TABLE IF NOT EXISTS intents (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rid TEXT UNIQUE NOT NULL,
publisher_rid TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
intent_type TEXT NOT NULL CHECK (intent_type IN ('need', 'offer', 'possibility')),
capacity JSONB NOT NULL DEFAULT '{}',
timing JSONB NOT NULL DEFAULT '{}',
governance_fit TEXT[] DEFAULT '{}',
state TEXT NOT NULL DEFAULT 'open'
CHECK (state IN ('open', 'matched', 'committed', 'expired', 'withdrawn')),
embedding vector(1024),
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_intents_publisher ON intents (publisher_rid);
CREATE INDEX IF NOT EXISTS idx_intents_type ON intents (intent_type);
CREATE INDEX IF NOT EXISTS idx_intents_state ON intents (state);
-- ============================================================
-- Intent matches (computed similarity pairs)
-- ============================================================
CREATE TABLE IF NOT EXISTS intent_matches (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
intent_a_id UUID NOT NULL REFERENCES intents(id) ON DELETE CASCADE,
intent_b_id UUID NOT NULL REFERENCES intents(id) ON DELETE CASCADE,
similarity FLOAT NOT NULL,
match_details JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (intent_a_id, intent_b_id),
CHECK (intent_a_id < intent_b_id)
);
CREATE INDEX IF NOT EXISTS idx_intent_matches_a ON intent_matches (intent_a_id);
CREATE INDEX IF NOT EXISTS idx_intent_matches_b ON intent_matches (intent_b_id);
-- ============================================================
-- Commitments (lifecycle state machine)
-- ============================================================
CREATE TABLE IF NOT EXISTS commitments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rid TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
state TEXT NOT NULL DEFAULT 'proposed'
CHECK (state IN ('proposed', 'verified', 'active', 'evidence_linked',
'redeemed', 'disputed', 'resolved', 'cancelled')),
proposer_rid TEXT NOT NULL,
acceptor_rid TEXT,
settlement_type TEXT DEFAULT 'attestation',
terms JSONB NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_commitments_state ON commitments (state);
CREATE INDEX IF NOT EXISTS idx_commitments_proposer ON commitments (proposer_rid);
CREATE INDEX IF NOT EXISTS idx_commitments_acceptor ON commitments (acceptor_rid);
-- ============================================================
-- Commitment-Evidence links
-- ============================================================
CREATE TABLE IF NOT EXISTS commitment_evidence (
commitment_id UUID NOT NULL REFERENCES commitments(id) ON DELETE CASCADE,
evidence_id UUID NOT NULL REFERENCES evidence(id) ON DELETE CASCADE,
linked_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (commitment_id, evidence_id)
);