Skip to content

perf(people): what kinds, message counts and the newest contact cost the people list - #342

Merged
leemour merged 1 commit into
mainfrom
perf/people-kinds
Nov 9, 2026
Merged

leemour merged 1 commit into
mainfrom
perf/people-kinds

Conversation

@leemour

@leemour leemour commented Nov 9, 2026

Copy link
Copy Markdown
Member

Task

Measure cli-core #2803 (a kind per person, message counts and the newest contact in people.list), after the owner's feedback on the People page (UX-133).

What

performance/people/kinds/: README and bench.mjs, on the store ../ties/bench.mjs seeds plus 1.5 million synthetic messages. About 0.5–0.6 s per list on a loaded machine, 1.9 s sorted by the newest contact, a search under 0.1 s.

🤖 Generated with Claude Code

…the people list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
leemour added a commit to WireCatLabs/cli-core that referenced this pull request Nov 9, 2026
…in the people list (#2803)

## Task

People, Clients and Digests plan (wirecat-desktop
`docs/dev/plans/2026-11-09-people-clients-digests.md`, section "The
people list contract"), after the owner's feedback on the People page
(UX-133): show the useful people, put newsletters and notifications
apart, companies in their own filter, nameless and unknown accounts in a
separate block, the contact on each row, sorted by how much the person
writes.

## Why

The page showed rows like «! null #97365», «Без имени», «CZECH AIRLINES»
and «OpenWeather Team» twice among real people. #2797 hid people with no
tie, but a tie alone does not tell a newsletter or an airline from a
person.

## What

- **`people.list({ kind, sort, query, limit, offset })`**: one kind at a
time, `person` by default; `counts` per kind. The first rule that holds
(`domain/person-kinds.ts`, `kindSignals` names it):
  1. the owner, a person added by hand, a task assignee: `person`;
2. a bot, a no-reply address, most mail from them in the bulk box,
memo's `bulk_never_replied`, a notification name: `automated`;
3. an identity an organization owns, a role address (`info@`), a legal
form or company word in the name: `organization`;
  4. a readable name and a tie: `person`;
  5. the rest (`no-tie`, `no-name`): `unsorted`.
- Rows gain `kind`, `kindSignals`, `contact` (phone, else email, else
`@username`), `messagesFrom` (messages and mail they sent) and
`lastContact`.
- `sort`: `interactions` (default), `recent`, `name`, `tasks`; the owner
first in each.
- `displayName` reads `null`, «Без имени», `Deleted Account` as no name.
`#<digits>` is not stripped: the `null #97365` on Desktop rows is
Desktop printing a missing username and an id.
- No migration. Message counts use `messages_by_sender_identity_id`
alone.

May break callers: `all` and `hidden` are gone (only memo uses
`people.list`, and memo has not released a version on it).

## Testing

- `people-list.test.ts`: one case per rule (bot, airline, notification
name, ООО, no-reply, role address, bulk box, organization link,
hand-added «Acme LLC» stays a person, «!» and an untied name unsorted,
«Без имени» with a username shown as `@kkkslav`), counts per kind,
interactions and recent order, `messagesFrom` and `lastContact`.
Placeholder names in `display-names.test.ts`.
- Local: lint, typecheck, `docs:check`, the people, version-61,
match-scan, private-people and command-lint tests, the contacts-sync
case.
- Benchmark: [cli-testing
#342](WireCatLabs/cli-testing#342)
(`performance/people/kinds`). On 51,000 people and 1.5 million synthetic
messages, on a loaded machine: a list about 0.5–0.6 s (it was about 0.1
s before kinds), `recent` 1.9 s, a name search 0.08 s, `similar` 0.01 s.
An index on `messages(sender_identity_id, sent_at)` would make `recent`
about 0.1 s, but it needs a migration, so it is left out. Not measured:
the owner's real store.
- `/code-review high` findings fixed in the second commit: deleted
messages no longer count toward the newest contact; bulk mail needs at
least 3 letters; a role address counts only when every address of the
person is one; name order folds accents; a search classifies only the
people it found; an unknown `kind` or `sort` is refused; `nameKey` gives
a placeholder name no key. Left as is: message counts include deleted
messages, because filtering them would read every message row instead of
the index alone.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@leemour
leemour merged commit fafff72 into main Nov 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant