projax.items_unified joins projax.items (deleted_at IS NULL) with
mai.projects so a single query feeds the tree UI. mai.projects.id is a
text key, so a deterministic placeholder UUID is derived from md5(p.id);
projax-native rows keep their gen_random_uuid().
When a projax item is created with an item_links row pointing back to a
mai.projects id (ref_type='mai-project'), the corresponding mai.projects
row drops out of the view — that's how the "Promote to projax" flow
makes the duplicate disappear without ever touching mai.projects.
Test coverage:
- both sources appear in the view
- promotion link hides the mai source row and surfaces the projax row