-- 0007_backfill_mai_projects.sql -- One-time backfill: for every row in mai.projects that has not already been -- promoted into projax (no item_links row with ref_type='mai-project' yet), -- create a projax-native item under the heuristic-chosen area, link it back -- to mai.projects via item_links, and (if the source row has a repo) record a -- gitea-repo pointer. -- -- Idempotent — re-running adds nothing because the existence guard checks the -- item_links pointer. CREATE OR REPLACE FUNCTION projax.guess_parent_area(p_slug text, p_repo text) RETURNS uuid LANGUAGE sql STABLE AS $$ -- Pull the seven seeded areas once; choose by heuristic. WITH area AS ( SELECT slug, id FROM projax.items WHERE parent_id IS NULL ) SELECT id FROM area WHERE slug = ( CASE WHEN p_slug = 'mhealth' THEN 'health' WHEN p_slug IN ('msports', 'manjin') THEN 'sports' WHEN p_slug IN ('kanzlai', 'hlckm', 'work', 'mworkrepo', 'paliad') OR (p_repo IS NOT NULL AND p_repo LIKE 'HL/%') THEN 'work' WHEN p_slug = 'mhome' THEN 'home' ELSE 'dev' END ); $$; DO $backfill$ DECLARE rec record; parent_id uuid; new_id uuid; proj_status text; BEGIN FOR rec IN SELECT p.id, p.name, p.repo, p.path, p.memory_group, p.status, p.goal, p.metadata FROM mai.projects p WHERE NOT EXISTS ( SELECT 1 FROM projax.item_links l WHERE l.ref_type = 'mai-project' AND l.ref_id = p.id ) LOOP parent_id := projax.guess_parent_area(rec.id, rec.repo); proj_status := CASE rec.status WHEN 'active' THEN 'active' WHEN 'sleeping' THEN 'archived' WHEN 'archived' THEN 'archived' WHEN 'done' THEN 'done' ELSE 'active' END; INSERT INTO projax.items (kind, title, slug, parent_id, content_md, metadata, status, management, tags) VALUES ( ARRAY['project']::text[], rec.name, rec.id, parent_id, COALESCE(rec.goal, ''), jsonb_build_object( 'repo', COALESCE(rec.repo, ''), 'path', COALESCE(rec.path, ''), 'memory_group', COALESCE(rec.memory_group, '') ) || COALESCE(rec.metadata, '{}'::jsonb), proj_status, ARRAY['mai']::text[], '{}'::text[] ) RETURNING id INTO new_id; INSERT INTO projax.item_links (item_id, ref_type, ref_id, rel) VALUES (new_id, 'mai-project', rec.id, 'derived-from') ON CONFLICT (item_id, ref_type, ref_id, rel) DO NOTHING; IF rec.repo IS NOT NULL AND rec.repo <> '' THEN INSERT INTO projax.item_links (item_id, ref_type, ref_id, rel, metadata) VALUES ( new_id, CASE WHEN rec.repo LIKE 'github.com/%' THEN 'github-repo' ELSE 'gitea-repo' END, rec.repo, 'source', '{}'::jsonb ) ON CONFLICT (item_id, ref_type, ref_id, rel) DO NOTHING; END IF; END LOOP; END $backfill$; -- Any item linked back to mai.projects must carry 'mai' in management, even -- if it was promoted before Phase 1.5 (when the column didn't exist). UPDATE projax.items SET management = array_append(management, 'mai') WHERE NOT ('mai' = ANY(management)) AND id IN (SELECT item_id FROM projax.item_links WHERE ref_type = 'mai-project');