Contacts now uses a three-section folder browser: expandable groups on the left, the current folder and people in the middle, and the selected person’s details on the right. It replaces the former Contacts grid/list and multi-group filter. Find someone keeps its existing any/all group scopes and compact recognition card. The expanded profile remains available through See all details.
Rule-based groups continue to derive membership from saved affiliations and intersect smart-ancestor rules. Their checklist entries are explained and read-only; Organize groups edits their rules. They do not acquire manual Add contacts controls. A manual group inside a rule-based ancestor still obeys that ancestor when displaying people. Removing a manual link does not rewrite smart rules or affiliations.
Combined contacts remain one logical person in the list. Inherited memberships are shown with their full paths and explained as coming from a combined contact. They remain read-only until the existing explicit Undo combining workflow, preserving original membership history and reversal behavior.
The details area reuses the existing cue and original-note components. Unreviewed summary labels, source links, original text and review state remain intact. Existing contact editing, import, photos, expanded profiles, deterministic recognition, review and explicit combine/undo remain available. Folder reads or membership changes never request AI processing.
The existing groups.parent_id and memberships model is reused. Owner-scoped, paginated library reads load all accessible contacts/groups; folder counts and search operate on that complete account library, not its first 500-row page. Group ordering now includes the ID as a stable tie-breaker. Counts/search documents evaluate the hierarchy once per person using the same existing manual/smart predicates.
PATCH /api/groups validates UUIDs and bounded, nonempty selections, enforces sign-in, approved-account status and same-origin, and calls add_contacts_to_group. The transaction checks every contact and the manual group belong to the caller, rejects merged secondary records, inserts unique links idempotently and advances revisions only for newly changed contacts. The existing owner lock serializes membership and profile writes. Mixed-owner or over-capacity batches do not partially save. Checklist replacement reuses the existing strict, owner-scoped save_contact_section operation.
Apply migrations in order with npx supabase migration up --local for the local development database, then rebuild. Do not reset a saved library.
Both migrations are applied in the local preview. No new packages, environment variables, credentials or services are required. A rollback to account-wide name uniqueness would require resolving newly allowed duplicate names explicitly; do not delete or silently rename groups to force a rollback. Prefer a forward corrective migration under the repository’s existing migration convention.
Existing bounds remain: 100 directly chosen groups per editable contact, 100-character group names, the existing validated hierarchy-depth limit, and 500 people per Add to group operation. Multiple additions can cover a larger library. The UI keeps drafts when a limit is exceeded.
All data pages load before browsing; this is not a new virtualized or incremental folder service. The 501-contact/501-subgroup browser fixture checks page completeness, not unlimited scale. Larger/deeper real-world libraries, full assistive-technology testing and this revision’s Firefox/WebKit behavior remain unverified. Drag-and-drop, new deletion workflows, persisted folder-view preferences and new integrations are deferred.
See acceptance cases, beta checklist and executed verification.