You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Created by [@alanshurafa](https://github.com/alanshurafa)**
8
+
9
+
</div>
10
+
3
11
> Adds structured columns and utility functions to the Open Brain thoughts table for richer classification, full-text search, statistics, and connection discovery.
4
12
5
13
## What It Does
6
14
7
-
This schema extension adds six new columns to the `thoughts` table (`type`, `sensitivity_tier`, `importance`, `quality_score`, `source_type`, `enriched`) so thoughts can be classified, filtered, and ranked without parsing the metadata JSONB every time. It also upgrades `upsert_thought` so metadata-backed writes keep those structured columns in sync. It installs three utility RPC functions:
15
+
This schema extension adds six new columns to the `thoughts` table (`type`, `sensitivity_tier`, `importance`, `quality_score`, `source_type`, `enriched`) so thoughts can be classified, filtered, and ranked without parsing the metadata JSONB every time. It also installs four RPC functions:
8
16
9
17
-**`search_thoughts_text`** -- Full-text search with boolean operators, ILIKE fallback, pagination, and result counts.
10
18
-**`brain_stats_aggregate`** -- Returns total thought count, top types, and top topics as a single JSONB payload.
11
19
-**`get_thought_connections`** -- Finds thoughts that share metadata topics or people with a given thought.
20
+
-**`backfill_thought_types(p_allowed_types TEXT[])`** -- Populates the new top-level `type` column from `metadata->>'type'`. The default allowlist covers the canonical eight values (`idea`, `task`, `person_note`, `reference`, `decision`, `lesson`, `meeting`, `journal`). Pass a custom array to accept additional values, or pass `NULL` to backfill whatever `metadata->>'type'` contains.
12
21
13
22
## Prerequisites
14
23
@@ -36,19 +45,33 @@ SUPABASE (from your Open Brain setup)
36
45
2. Create a new query and paste the full contents of `schema.sql`
37
46
3. Click **Run** to execute the migration
38
47
4. Open **Table Editor** and select the `thoughts` table to confirm the new columns appear: `type`, `sensitivity_tier`, `importance`, `quality_score`, `source_type`, `enriched`
39
-
5. Navigate to **Database > Functions** and verify three new functions exist: `search_thoughts_text`, `brain_stats_aggregate`, `get_thought_connections`
40
-
6. Verify `upsert_thought` still exists. The enhanced version mirrors `metadata.type`, `metadata.source`, `metadata.importance`, `metadata.quality_score`, `metadata.sensitivity_tier`, and task/idea status into top-level columns.
41
-
7. If you have existing thoughts with `type` or `source` values stored in the metadata JSONB, the backfill statements at the bottom of the script will have populated the new columns automatically
48
+
5. Navigate to **Database > Functions** and verify the new functions exist: `search_thoughts_text`, `brain_stats_aggregate`, `get_thought_connections`, `backfill_thought_types`
49
+
6. If you have existing thoughts with `type` or `source` values stored in the metadata JSONB, the script automatically calls `backfill_thought_types()` with the default canonical allowlist. If your brain uses non-canonical `type` values, re-run `SELECT backfill_thought_types(ARRAY['your','custom','types']);` or `SELECT backfill_thought_types(NULL);` to accept any value
42
50
43
51
## Expected Outcome
44
52
45
53
After running the migration:
46
54
47
-
- The `thoughts` table has six new columns with dashboard-friendly defaults.
55
+
- The `thoughts` table has six new columns with sensible defaults:
56
+
-`sensitivity_tier TEXT DEFAULT 'standard'` (canonical values: `'standard'`, `'personal'`, `'restricted'`)
57
+
-`importance SMALLINT DEFAULT 3` (scale: 1-5, where 3 is the default)
58
+
-`quality_score NUMERIC(5,2) DEFAULT 50` (scale: 0-100, where 50 is the default)
59
+
-`enriched BOOLEAN DEFAULT false`
60
+
-`type TEXT` (nullable; populated by backfill or writers)
61
+
-`source_type TEXT` (nullable; populated by backfill or writers)
48
62
- New indexes on `type`, `importance`, `source_type`, and a GIN tsvector index on `content` for fast full-text search.
49
-
- Three new RPC functions callable via the Supabase client or REST API.
50
-
-`upsert_thought` remains the canonical write path, but now keeps structured dashboard columns synchronized with metadata payloads.
51
-
- Any existing thoughts with `type` or `source` in their metadata JSONB will have those values copied into the new top-level columns.
63
+
- Four new RPC functions callable via the Supabase client or REST API (`search_thoughts_text`, `brain_stats_aggregate`, `get_thought_connections`, `backfill_thought_types`).
64
+
- Any existing thoughts with `type` or `source` in their metadata JSONB will have those values copied into the new top-level columns (via `backfill_thought_types()` for `type` with the canonical allowlist, plus an inline `UPDATE` for `source_type`).
65
+
66
+
## Security
67
+
68
+
This schema follows stock Open Brain's "service_role only" posture:
69
+
70
+
-`brain_stats_aggregate` and `get_thought_connections` are `SECURITY DEFINER` with `SET search_path = public` (defense in depth against search-path hijacks). They can read the full `thoughts` table regardless of RLS.
71
+
-`search_thoughts_text` is `SECURITY INVOKER` and respects RLS.
72
+
-**None of the three RPCs are granted to `anon`.** Execute privilege is limited to `authenticated` and `service_role`. The publishable anon key cannot call them.
73
+
74
+
If you want to expose any of these to `anon` (for example, a public-read dashboard), add your own `GRANT EXECUTE ... TO anon;` in a follow-up migration and confirm that `p_exclude_restricted := true` (the default) plus your sensitivity-tier hygiene gives you the exposure surface you actually want. This is an explicit opt-in: the default stance is private.
52
75
53
76
## Troubleshooting
54
77
@@ -59,4 +82,4 @@ Solution: These are safe to ignore. The `ADD COLUMN IF NOT EXISTS` syntax preven
59
82
Solution: Confirm your thoughts have content populated. Try a simple query first (single word, no operators). If using boolean operators, ensure the syntax matches websearch format ("quoted phrases", word AND word, -excluded).
60
83
61
84
**Issue: brain_stats_aggregate returns empty types or topics**
62
-
Solution: The function filters by `created_at`. Pass `p_since_days := 0` for all-time stats. Also confirm that your thoughts have the `type` column populated (run the backfill UPDATE if needed).
85
+
Solution: The function filters by `created_at`. Pass `p_since_days := 0` for all-time stats. Also confirm that your thoughts have the `type` column populated. If you use non-canonical type values in `metadata->>'type'` (anything outside `idea`, `task`, `person_note`, `reference`, `decision`, `lesson`, `meeting`, `journal`), call the backfill RPC with your own allowlist, e.g. `SELECT backfill_thought_types(ARRAY['idea','task','article','quote']);`, or `SELECT backfill_thought_types(NULL);` to accept whatever is present.
0 commit comments