The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
8.7 KiB
Database indexes for personinsights tables
Every personinsights query must use an index scan. Before designing your RPC, verify your WHERE clause matches one of these indexes.
All indexes below are sourced from rust/persons_migrations/ SQL files — the single source of truth for these tables.
insights_person
Partitioned by team_id (64 hash partitions).
Primary key is composite (team_id, id).
| Index name | Type | Columns | Notes |
|---|---|---|---|
insights_person_new_pkey |
PK | (team_id, id) |
Partition-pruned lookup |
insights_person_new_uuid_idx |
UNIQUE | (team_id, uuid) |
Partition-pruned uuid lookup |
insights_person_p{i}_id_idx |
INDEX | (id) |
Per-partition index on id (64 indexes, one per partition) |
Constraints: check_properties_size — pg_column_size(properties) <= 655360 (on old unpartitioned table; personinsights_person_tmp has equivalent).
Typical query patterns:
WHERE team_id = $1 AND id = $2→ PK scanWHERE team_id = $1 AND uuid = $2→insights_person_new_uuid_idx(partition-pruned)WHERE team_id = $1 AND id = ANY($2)→ PK scanWHERE team_id = $1 AND uuid = ANY($2)→insights_person_new_uuid_idxscan
insights_persondistinctid
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_distinct_id_for_team |
UNIQUE | (team_id, distinct_id) |
Primary lookup path |
insights_persondistinctid_person_id_5d655bba |
INDEX | (person_id) |
Join back to person |
insights_persondistinctid_person_id_fkey |
FK | (team_id, person_id) → insights_person(team_id, id) |
NOT VALID (added during migration) |
Typical query patterns:
WHERE team_id = $1 AND distinct_id = $2→unique_distinct_id_for_teamscanWHERE person_id = $1→insights_persondistinctid_person_id_5d655bbascanJOIN insights_person p ON p.id = d.person_id AND p.team_id = d.team_id WHERE d.team_id = $1 AND d.distinct_id = $2→ unique index + PK
insights_personlessdistinctid
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_personless_distinct_id_for_team |
UNIQUE | (team_id, distinct_id) |
Primary lookup path |
insights_group
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_team_group_key_group_type |
UNIQUE | (team_id, group_key, group_type_index) |
Primary lookup path |
insights_group_team_id_b3aed896 |
INDEX | (team_id) |
Team-level scans |
Typical query patterns:
WHERE team_id = $1 AND group_type_index = $2 AND group_key = $3→unique_team_group_key_group_typescanWHERE team_id = $1 AND group_type_index = $2→ partial unique index scan (leading columns)WHERE team_id = $1→insights_group_team_id_b3aed896scan
insights_grouptypemapping
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_group_types_for_project |
UNIQUE | (project_id, group_type) |
Uniqueness |
unique_group_type_index_for_project |
UNIQUE | (project_id, group_type_index) |
Uniqueness |
insights_group_type_proj_idx |
INDEX | (project_id, group_type) |
Redundant with unique above |
insights_group_type_i_proj_idx |
INDEX | (project_id, group_type_index) |
Redundant with unique above |
insights_grouptypemapping_project_id_239c0515 |
INDEX | (project_id) |
Project-level scans |
insights_grouptypemapping_team_id_5fb54d04 |
INDEX | (team_id) |
Team-level scans |
insights_grouptypemapping_detail_dashboard_id_54b0edbb |
INDEX | (detail_dashboard_id) |
Dashboard FK |
Constraints: group_type_index_is_less_than_or_equal_5, group_type_project_id_is_not_null.
Typical query patterns:
WHERE project_id = $1→insights_grouptypemapping_project_id_239c0515or leading column of unique indexesWHERE project_id = $1 AND group_type_index = $2→unique_group_type_index_for_projectWHERE team_id = $1→insights_grouptypemapping_team_id_5fb54d04
insights_cohortpeople
| Index name | Type | Columns | Notes |
|---|---|---|---|
insights_coh_cohort__89c25f_idx |
INDEX | (cohort_id, person_id) |
Composite lookup |
insights_cohortpeople_cohort_id_1f371733 |
INDEX | (cohort_id) |
Cohort-level scans |
insights_cohortpeople_person_id_33da7d3f |
INDEX | (person_id) |
Person-level scans |
No FK — the FK to insights_person was dropped during the partitioning migration and not re-added.
Typical query patterns:
WHERE cohort_id = $1 AND person_id = $2→insights_coh_cohort__89c25f_idxscanWHERE cohort_id = $1→insights_cohortpeople_cohort_id_1f371733scanWHERE person_id = $1→insights_cohortpeople_person_id_33da7d3fscan
insights_featureflaghashkeyoverride
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_hash_key_for_user_team_feature_flag |
UNIQUE | (team_id, person_id, feature_flag_key) |
Primary lookup path |
insights_featureflaghashkeyoverride_person_id_7e517f7c |
INDEX | (person_id) |
Person-level scans |
insights_featureflaghashkeyoverride_team_id_b626eed2 |
INDEX | (team_id) |
Team-level scans |
No FK — the FK to insights_person was dropped during the partitioning migration and not re-added.
Typical query patterns:
WHERE team_id = $1 AND person_id = $2→ partialunique_hash_key_for_user_team_feature_flagscan (leading columns)WHERE team_id = $1 AND person_id = $2 AND feature_flag_key = $3→ full unique index scan
insights_personoverride
| Index name | Type | Columns | Notes |
|---|---|---|---|
unique_override_per_old_person_id |
UNIQUE | (team_id, old_person_id) |
Primary lookup path |
insights_personoverride_old_person_id_4c1deac0 |
INDEX | (old_person_id) |
Single-column lookup |
insights_personoverride_override_person_id_9f32aab1 |
INDEX | (override_person_id) |
Reverse lookup |
insights_personoverride_team_id_92291e67 |
INDEX | (team_id) |
Team-level scans |
exclude_override_person_id_from_being_old_person_id |
EXCLUDE | GiST on (team_id, override_person_id, old_person_id) |
Integrity |
Constraints: old_person_id_different_from_override_person_id — old_person_id != override_person_id.
FKs: old_person_id and override_person_id both reference insights_personoverridemapping(id).
If you need a new index
If your query can't use an existing index, you need a new migration in rust/persons_migrations before adding the RPC.
Discuss with the team first — new indexes on large partitioned tables have operational implications.