fix: build UI assets in CI
CI / build-and-test (pull_request) Successful in 1m20s
CI / build-and-test (push) Successful in 1m23s

This commit is contained in:
2026-07-15 05:14:09 +02:00
parent 4b3f0b1b55
commit e3a4a3c5c7
6 changed files with 552 additions and 17 deletions
+16
View File
@@ -18,6 +18,14 @@ jobs:
with:
go-version: '1.26'
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Enable pnpm
run: corepack enable
- name: Cache Go modules
uses: actions/cache@v4
with:
@@ -34,6 +42,14 @@ jobs:
- name: Tidy modules
run: go mod tidy
- name: Install UI dependencies
run: pnpm install --frozen-lockfile
working-directory: ui
- name: Build UI assets
run: pnpm run build
working-directory: ui
- name: Run tests
run: go test ./...
+526 -17
View File
@@ -110,6 +110,13 @@ CREATE SEQUENCE IF NOT EXISTS public.identity_thought_links_id
START 1
CACHE 1;
CREATE SEQUENCE IF NOT EXISTS public.identity_thought_learning_links_id
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
CREATE SEQUENCE IF NOT EXISTS public.identity_embeddings_id
INCREMENT 1
MINVALUE 1
@@ -332,6 +339,7 @@ CREATE TABLE IF NOT EXISTS public.thoughts (
id bigserial NOT NULL,
metadata jsonb DEFAULT '{}'::jsonb,
project_id bigint,
tenant_key text,
updated_at timestamptz DEFAULT now()
);
@@ -341,7 +349,8 @@ CREATE TABLE IF NOT EXISTS public.projects (
guid uuid NOT NULL DEFAULT gen_random_uuid(),
id bigserial NOT NULL,
last_active_at timestamptz DEFAULT now(),
name text NOT NULL
name text NOT NULL,
tenant_key text
);
CREATE TABLE IF NOT EXISTS public.thought_links (
@@ -352,6 +361,14 @@ CREATE TABLE IF NOT EXISTS public.thought_links (
to_id bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS public.thought_learning_links (
created_at timestamptz NOT NULL DEFAULT now(),
id bigserial NOT NULL,
learning_id bigint NOT NULL,
relation text NOT NULL DEFAULT 'source',
thought_id bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS public.embeddings (
created_at timestamptz DEFAULT now(),
dim integer NOT NULL,
@@ -375,6 +392,7 @@ CREATE TABLE IF NOT EXISTS public.stored_files (
project_id bigint,
sha256 text NOT NULL,
size_bytes bigint NOT NULL,
tenant_key text,
thought_id bigint,
updated_at timestamptz NOT NULL DEFAULT now()
);
@@ -390,6 +408,7 @@ CREATE TABLE IF NOT EXISTS public.chat_histories (
project_id bigint,
session_id text NOT NULL,
summary text,
tenant_key text,
title text,
updated_at timestamptz NOT NULL DEFAULT now()
);
@@ -424,6 +443,7 @@ CREATE TABLE IF NOT EXISTS public.learnings (
summary text NOT NULL,
supersedes_learning_id bigint,
tags text[] NOT NULL DEFAULT '{}',
tenant_key text,
updated_at timestamptz NOT NULL DEFAULT now()
);
@@ -450,6 +470,7 @@ CREATE TABLE IF NOT EXISTS public.plans (
status text NOT NULL DEFAULT 'draft',
supersedes_plan_id bigint,
tags text[] NOT NULL DEFAULT '{}',
tenant_key text,
title text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
@@ -1552,6 +1573,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thoughts'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.thoughts ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -1643,6 +1677,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'projects'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.projects ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -1708,6 +1755,71 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND column_name = 'created_at'
) THEN
ALTER TABLE public.thought_learning_links ADD COLUMN created_at timestamptz NOT NULL DEFAULT now();
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND column_name = 'id'
) THEN
ALTER TABLE public.thought_learning_links ADD COLUMN id bigserial NOT NULL;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND column_name = 'learning_id'
) THEN
ALTER TABLE public.thought_learning_links ADD COLUMN learning_id bigint NOT NULL;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND column_name = 'relation'
) THEN
ALTER TABLE public.thought_learning_links ADD COLUMN relation text NOT NULL DEFAULT 'source';
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND column_name = 'thought_id'
) THEN
ALTER TABLE public.thought_learning_links ADD COLUMN thought_id bigint NOT NULL;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -1955,6 +2067,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'stored_files'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.stored_files ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -2111,6 +2236,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'chat_histories'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.chat_histories ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -2475,6 +2613,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'learnings'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.learnings ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -2735,6 +2886,19 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'plans'
AND column_name = 'tenant_key'
) THEN
ALTER TABLE public.plans ADD COLUMN tenant_key text;
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -5177,6 +5341,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thoughts'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.thoughts
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -5338,6 +5525,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'projects'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.projects
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -5453,6 +5663,121 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thought_learning_links'
AND a.attname = 'created_at'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['timestamptz', 'timestamp with time zone']) THEN
ALTER TABLE public.thought_learning_links
ALTER COLUMN created_at TYPE timestamptz USING created_at::timestamptz;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thought_learning_links'
AND a.attname = 'id'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['bigint']) THEN
ALTER TABLE public.thought_learning_links
ALTER COLUMN id TYPE bigint USING id::bigint;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thought_learning_links'
AND a.attname = 'learning_id'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['bigint']) THEN
ALTER TABLE public.thought_learning_links
ALTER COLUMN learning_id TYPE bigint USING learning_id::bigint;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thought_learning_links'
AND a.attname = 'relation'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.thought_learning_links
ALTER COLUMN relation TYPE text USING relation::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'thought_learning_links'
AND a.attname = 'thought_id'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['bigint']) THEN
ALTER TABLE public.thought_learning_links
ALTER COLUMN thought_id TYPE bigint USING thought_id::bigint;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -5890,6 +6215,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'stored_files'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.stored_files
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -6166,6 +6514,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'chat_histories'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.chat_histories
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -6810,6 +7181,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'learnings'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.learnings
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -7270,6 +7664,29 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_type text;
BEGIN
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
INTO current_type
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = 'public'
AND t.relname = 'plans'
AND a.attname = 'tenant_key'
AND a.attnum > 0
AND NOT a.attisdropped;
IF current_type IS NOT NULL
AND current_type <> ALL(ARRAY['text']) THEN
ALTER TABLE public.plans
ALTER COLUMN tenant_key TYPE text USING tenant_key::text;
END IF;
END;
$$;
DO $$
DECLARE
current_type text;
@@ -9035,6 +9452,50 @@ BEGIN
END;
$$;
DO $$
DECLARE
current_pk_name text;
current_pk_matches boolean := false;
BEGIN
SELECT tc.constraint_name,
COALESCE(
ARRAY(
SELECT a.attname::text
FROM pg_constraint c
JOIN pg_class t ON t.oid = c.conrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
JOIN unnest(c.conkey) WITH ORDINALITY AS cols(attnum, ord)
ON TRUE
JOIN pg_attribute a
ON a.attrelid = t.oid
AND a.attnum = cols.attnum
WHERE c.contype = 'p'
AND n.nspname = 'public'
AND t.relname = 'thought_learning_links'
ORDER BY cols.ord
),
ARRAY[]::text[]
) = ARRAY['id']
INTO current_pk_name, current_pk_matches
FROM information_schema.table_constraints tc
WHERE tc.table_schema = 'public'
AND tc.table_name = 'thought_learning_links'
AND tc.constraint_type = 'PRIMARY KEY';
IF current_pk_name IS NOT NULL
AND NOT current_pk_matches
AND current_pk_name IN ('thought_learning_links_pkey', 'public_thought_learning_links_pkey') THEN
EXECUTE 'ALTER TABLE public.thought_learning_links DROP CONSTRAINT ' || quote_ident(current_pk_name) || ' CASCADE';
END IF;
-- Add the desired primary key only when no matching primary key already exists.
IF current_pk_name IS NULL
OR (NOT current_pk_matches AND current_pk_name IN ('thought_learning_links_pkey', 'public_thought_learning_links_pkey')) THEN
ALTER TABLE public.thought_learning_links ADD CONSTRAINT pk_public_thought_learning_links PRIMARY KEY (id);
END IF;
END;
$$;
DO $$
DECLARE
current_pk_name text;
@@ -9708,9 +10169,18 @@ CREATE INDEX IF NOT EXISTS idx_project_personas_project_id_persona_id
CREATE INDEX IF NOT EXISTS idx_arc_stage_parts_stage_id_part_id
ON public.arc_stage_parts USING btree (stage_id, part_id);
CREATE INDEX IF NOT EXISTS idx_thoughts_tenant_key_project_id
ON public.thoughts USING btree (tenant_key, project_id);
CREATE UNIQUE INDEX IF NOT EXISTS uidx_projects_tenant_key_name
ON public.projects USING btree (tenant_key, name);
CREATE INDEX IF NOT EXISTS idx_thought_links_from_id_to_id_relation
ON public.thought_links USING btree (from_id, to_id, relation);
CREATE UNIQUE INDEX IF NOT EXISTS uidx_thought_learning_links_thought_id_learning_id
ON public.thought_learning_links USING btree (thought_id, learning_id);
CREATE UNIQUE INDEX IF NOT EXISTS uidx_embeddings_thought_id_model
ON public.embeddings USING btree (thought_id, model);
@@ -9865,19 +10335,6 @@ BEGIN
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'projects'
AND constraint_name = 'ukey_projects_name'
) THEN
ALTER TABLE public.projects ADD CONSTRAINT ukey_projects_name UNIQUE (name);
END IF;
END;
$$;
DO $$
BEGIN
IF NOT EXISTS (
@@ -10328,6 +10785,38 @@ BEGIN
END IF;
END;
$$;DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND constraint_name = 'fk_thought_learning_links_learning_id'
) THEN
ALTER TABLE public.thought_learning_links
ADD CONSTRAINT fk_thought_learning_links_learning_id
FOREIGN KEY (learning_id)
REFERENCES public.learnings (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
END IF;
END;
$$;DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'thought_learning_links'
AND constraint_name = 'fk_thought_learning_links_thought_id'
) THEN
ALTER TABLE public.thought_learning_links
ADD CONSTRAINT fk_thought_learning_links_thought_id
FOREIGN KEY (thought_id)
REFERENCES public.thoughts (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
END IF;
END;
$$;DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
@@ -10912,15 +11401,15 @@ BEGIN
IF EXISTS (
SELECT 1 FROM pg_class c
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'identity_persona_arc_id'
WHERE c.relname = 'identity_persona_arc_persona_id'
AND n.nspname = 'public'
AND c.relkind = 'S'
) THEN
SELECT COALESCE(MAX(id), 0) + 1
SELECT COALESCE(MAX(persona_id), 0) + 1
FROM public.persona_arc
INTO m_cnt;
PERFORM setval('public.identity_persona_arc_id'::regclass, m_cnt);
PERFORM setval('public.identity_persona_arc_persona_id'::regclass, m_cnt);
END IF;
END;
$$;
@@ -10982,6 +11471,25 @@ BEGIN
END;
$$;
DO $$
DECLARE
m_cnt bigint;
BEGIN
IF EXISTS (
SELECT 1 FROM pg_class c
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'identity_thought_learning_links_id'
AND n.nspname = 'public'
AND c.relkind = 'S'
) THEN
SELECT COALESCE(MAX(id), 0) + 1
FROM public.thought_learning_links
INTO m_cnt;
PERFORM setval('public.identity_thought_learning_links_id'::regclass, m_cnt);
END IF;
END;
$$;
DO $$
DECLARE
m_cnt bigint;
BEGIN
@@ -11295,5 +11803,6 @@ $$;
+2
View File
@@ -9,6 +9,8 @@ alter table plans add column if not exists tenant_key text;
alter table chat_histories add column if not exists tenant_key text;
-- Project names are now unique inside a tenant rather than globally.
alter table projects drop constraint if exists ukey_projects_name;
alter table projects drop constraint if exists projects_name_key;
drop index if exists projects_name_key;
create unique index if not exists projects_tenant_key_name_idx
on projects (coalesce(tenant_key, ''), name);
+2
View File
@@ -3,6 +3,7 @@ Table stored_files {
guid uuid [unique, not null, default: `gen_random_uuid()`]
thought_id bigint [ref: > thoughts.id]
project_id bigint [ref: > projects.id]
tenant_key text
name text [not null]
media_type text [not null]
kind text [not null, default: 'file']
@@ -16,6 +17,7 @@ Table stored_files {
indexes {
thought_id
project_id
tenant_key
sha256
}
}
+4
View File
@@ -6,6 +6,7 @@ Table chat_histories {
channel text
agent_id text
project_id bigint [ref: > projects.id]
tenant_key text
messages jsonb [not null, default: `'[]'`]
summary text
metadata jsonb [not null, default: `'{}'`]
@@ -15,6 +16,7 @@ Table chat_histories {
indexes {
session_id
project_id
tenant_key
channel
agent_id
created_at
@@ -46,6 +48,7 @@ Table learnings {
source_type text
source_ref text
project_id bigint [ref: > projects.id]
tenant_key text
related_thought_id bigint [ref: > thoughts.id]
related_skill_id bigint [ref: > agent_skills.id]
reviewed_by text
@@ -58,6 +61,7 @@ Table learnings {
indexes {
project_id
tenant_key
category
area
status
+2
View File
@@ -6,6 +6,7 @@ Table plans {
status text [not null, default: 'draft'] // draft, active, blocked, completed, cancelled, superseded
priority text [not null, default: 'medium'] // low, medium, high, critical
project_id bigint [ref: > projects.id]
tenant_key text
owner text
due_date timestamptz
completed_at timestamptz
@@ -18,6 +19,7 @@ Table plans {
indexes {
project_id
tenant_key
status
priority
owner