Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions migrations/108_entity_notion_mappings.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- Migration 106: entity_notion_mappings + projection_checkpoints (KG → Notion projection, Phase 0)
--
-- The Notion analogue of entity_rid_mappings (the vault projection table). Projects a
-- canonical personal-KOI entity onto a Notion page with independent sync state. Keyed on
-- (canonical_uri, notion_database_id) because a single entity may legitimately have one
-- page per Notion DB (Team Members DB vs the unified Entities DB). One LIVE page per
-- (canonical_uri, db) is enforced by a partial unique index; merged losers are archived,
-- not deleted, so history survives and the invariant holds.
--
-- Spec: ~/.claude/plans/kg-notion-entity-projection-spec.md §7.1. Additive; touches no
-- existing table. Safe to re-run (IF NOT EXISTS).

CREATE TABLE IF NOT EXISTS entity_notion_mappings (
notion_page_id TEXT UNIQUE NOT NULL,
canonical_uri TEXT NOT NULL, -- → entity_registry.fuseki_uri
notion_database_id TEXT NOT NULL,
entity_type TEXT,
name TEXT,
content_hash TEXT,
sync_status TEXT DEFAULT 'linked'
CHECK (sync_status IN ('linked','local_only','pending_sync','conflict','archived')),
relation_truncated BOOLEAN DEFAULT FALSE, -- degree > 100 (spec §5.5)
last_synced TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
visibility_scope TEXT DEFAULT 'public'
);

-- One LIVE page per (canonical_uri, db). Archived rows excluded so a merged loser can
-- coexist historically without colliding with the survivor.
CREATE UNIQUE INDEX IF NOT EXISTS uq_notion_live_uri_db
ON entity_notion_mappings (canonical_uri, notion_database_id)
WHERE sync_status <> 'archived';

CREATE INDEX IF NOT EXISTS idx_notion_mappings_canonical
ON entity_notion_mappings (canonical_uri);

-- Merge-poller checkpoint (Phase 3): monotonic high-water mark on entity_merge_log.id,
-- committed in the SAME transaction as the loser-row retire.
CREATE TABLE IF NOT EXISTS projection_checkpoints (
name TEXT PRIMARY KEY, -- e.g. 'notion_entity_merge_poller'
last_processed_id BIGINT NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
37 changes: 37 additions & 0 deletions migrations/109_entity_visibility_scope.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
-- 107_entity_visibility_scope.sql
-- Phase 1, KG two-viewport plan (~/.claude/plans/design-the-indigenomics-twinkly-dijkstra.md):
-- add the 4-value AUDIENCE visibility axis to entity_registry.
--
-- CORRECTED per read-only recon (B1): entity_registry ONLY. Do NOT add a CHECK to
-- entity_rid_mappings.visibility_scope — that is a SEPARATE, pre-existing 2-value axis
-- (public / node_private = Notion-projection privacy). A 4-value CHECK there would
-- CheckViolation-kill vault->registry sync on the first node_private write.
--
-- Additive + metadata-only (constant DEFAULT, no row rewrite on ~21k rows). No code reads
-- entity_registry.visibility_scope yet (recon: 0 readers), so the 'unclassified' default
-- cannot blank any read path. Reversible via 107_entity_visibility_scope_down.sql.
-- Apply directly: psql personal_koi -v ON_ERROR_STOP=1 -f 107_entity_visibility_scope.sql
BEGIN;

ALTER TABLE entity_registry
ADD COLUMN IF NOT EXISTS visibility_scope TEXT NOT NULL DEFAULT 'unclassified',
ADD COLUMN IF NOT EXISTS source_context TEXT,
ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ;

DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'entity_registry_visibility_scope_check'
AND conrelid = 'public.entity_registry'::regclass
) THEN
ALTER TABLE entity_registry
ADD CONSTRAINT entity_registry_visibility_scope_check
CHECK (visibility_scope IN ('public', 'team', 'confidential', 'unclassified'));
END IF;
END $$;

CREATE INDEX IF NOT EXISTS idx_entity_registry_visibility_scope
ON entity_registry (visibility_scope);

COMMIT;
17 changes: 17 additions & 0 deletions migrations/109_entity_visibility_scope_down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- 107_entity_visibility_scope_down.sql — reverses 107 (entity_registry only).
-- Does NOT touch entity_rid_mappings.visibility_scope (a separate pre-existing column).
-- Apply: psql personal_koi -v ON_ERROR_STOP=1 -f 107_entity_visibility_scope_down.sql
BEGIN;

DROP INDEX IF EXISTS idx_entity_registry_visibility_scope;

ALTER TABLE entity_registry
DROP CONSTRAINT IF EXISTS entity_registry_visibility_scope_check;

ALTER TABLE entity_registry
DROP COLUMN IF EXISTS visibility_scope,
DROP COLUMN IF EXISTS source_context,
DROP COLUMN IF EXISTS expires_at,
DROP COLUMN IF EXISTS revoked_at;

COMMIT;