Skip to content

feat(people): remember what you deal with each person about and keep their contact details - #579

Merged
johnrufusone merged 15 commits into
mainfrom
claude/project-thread-p00hbu
Oct 20, 2026
Merged

johnrufusone merged 15 commits into
mainfrom
claude/project-thread-p00hbu

Conversation

@johnrufusone

@johnrufusone johnrufusone commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The contact keeper adds contacts, but the Executive knows nothing about what the owner deals with them about or when they were last in touch. Asking about a contact in chat returns "not shared", because peer memory only learns from people who chat. Only a role is read from a signature, so a contact's phone, website and address are never saved, and teammates get none of this.

Approach

  • Memory note (people/contact_notes.py): on the same background pass, each contact and teammate gets the dates they were last emailed, heard from and met, their next meeting, and a short note with topics. The note is written only from the owner's own sent mail (no quoted text, nothing sent by Handle it for me) and meetings the owner organized. It is rewritten only on new activity, at most 5 a pass and 20 model calls a day. The Executive reads it on the owner's own turn in list_people and ask_about_person so it can suggest who to ask. The person's page shows it to the owner as What I remember.
  • Contact details (people/contact_info.py, table person_details): the signature read also copies phone, mobile, website, LinkedIn, another profile and postal address. Each is kept only if the mail writes it. A website must be on the sender's own non-freemail domain, and a profile must be on a known site. Each detail is a change with Undo, and a kind someone typed is left alone. Details can also be edited on People and set from chat.
  • Team members get the same signature read (role only when empty) and the same note. Notes and details reach the Executive only on the owner's own turn, and the people routes return details only to the owner and to that person.

Checklist

  • Working implementation, no stubs or TODO placeholders
  • Tests added/updated for new behavior (test_contact_keeper.py, test_contact_info.py, test_people_details_access.py, contactChanges.test.mjs, contactDetails.test.mjs)
  • ruff check and mypy pass
  • UI builds if touched (tsc, eslint, npm test)
  • Eval scenarios added for a new agent or prompt change (n/a: no domain prompt changed)
  • Architecture docs updated (org, api, agents, schemas; guide People page)
  • No secrets, credentials, or personal data committed

🤖 Generated with Claude Code

johnrufusone and others added 4 commits October 20, 2026 10:52
The contact keeper now keeps, for every contact, when you last emailed,
heard from and met them, their next meeting, and a short note with a few
topics on what you work on together (people/contact_notes.py).

The note is written only from your own sent mail to them (without the
quoted mail below it, and nothing Handle it for me sent) and the titles of
meetings you set up, so a contact's own mail can't write it. It is
rewritten only when that mail or those meetings change, at most 5 a pass
and 20 a day.

The Executive reads it on your own turns in list_people and
ask_about_person, so it can suggest who to ask. Before this,
ask_about_person on a contact always said "not shared", since contacts
never chat. The contact's page shows it as "What I remember". Archiving a
contact deletes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers too

The signature read now copies every contact detail it shows (phone,
mobile, website, LinkedIn, another profile, postal address) into a new
person_details list, not only the role. Each is kept only when the mail
writes it, a website only on the sender's own non-freemail domain and a
profile only on a known site (people/contact_info.py). One of each kind
is the keeper's and a newer one replaces it; typed ones are never
touched. Each is a change with Undo.

Team members get the same signature read (role when empty, details) and
the same memory note, still the principal's alone and only from the
principal's own mailbox. People and chat can type details too.

Arch-Docs: n/a - the guide change is the People page text, not the user_guide section

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… turn only

A teammate's mobile or address in list_people reached any turn, a
stranger's inbound mail included. They now come only on the principal's
own verified turn, like the notes. An address is also capped at 14 words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@johnrufusone johnrufusone self-assigned this Oct 20, 2026
…-p00hbu

# Conflicts:
#	packages/core/openexecutive/architecture/prebuilt/agents.json
#	packages/core/openexecutive/architecture/prebuilt/api.json
Comment thread packages/core/openexecutive/api/routes/people.py Outdated
Comment thread packages/core/openexecutive/people/contact_notes.py Outdated
Comment thread packages/core/openexecutive/api/routes/people.py
Comment thread packages/core/openexecutive/people/contact_keeper.py Outdated
Comment thread packages/core/openexecutive/api/routes/contacts.py
johnrufusone and others added 2 commits October 20, 2026 11:17
A bad detail on POST or PATCH /people answered 422 after the person was
already created or updated. It is now checked first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ard from

A note or topic with words like "system" or "prompt" was dropped and
not tried again until new mail. Only links, addresses and tags are
dropped now; the note comes only from the owner's own mail. "Last heard
from" was read only when a note was rewritten, so it went stale; it is
now read for up to 20 people a pass whatever the note budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_notes.py
Comment thread packages/core/openexecutive/people/contact_keeper.py
Comment thread packages/core/openexecutive/api/routes/people.py
@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review summary: 🔴 2, 🟡 1, 🟣 0.

The people routes returned Person.details (some read from a teammate's
email signature in the principal's mailbox) to any signed-in caller.
They now go only to the principal and to that person.

A refused mailbox read (auth or rate limit) in the signature or
last-heard lookups now stops those lookups for the run instead of
aborting it, so the notes are still written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_keeper.py
Comment thread packages/core/openexecutive/api/routes/people.py
Comment thread packages/core/openexecutive/people/contact_keeper.py
A note the model wrote with a link or address no longer wipes the one
there was: the activity is marked read and the old note stays.

A refused mailbox read in the signature or last-heard lookups still lets
the notes run, then is raised so the run is recorded as failing rather
than looking healthy.

Adds tests for /people/by-scope details and the details filter failing
closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/api/routes/contacts.py
Comment thread packages/core/openexecutive/people/contact_keeper.py Outdated
Comment thread packages/core/openexecutive/people/contact_info.py Outdated
The details loop in upsert_person reused `kind`, so the person's kind
became the last detail's kind and every chat call with details failed.
Adds the chat details test and the note route's 404 for the principal's
own row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_keeper.py
@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review summary: 🔴 1, 🟡 0, 🟣 0. The one issue is that signature details can be added next to a value the owner typed (see the inline comment).

…omain

The daily note cap counted written rows, so failed or repeated calls never
used it up; contact_note_tries now counts every call. A signature website
on a parent of the sender's mail domain (co.uk for corp.co.uk) no longer
counts as theirs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_notes.py Outdated
A signature phone was added next to a phone the owner typed. The keeper
now skips any kind holding a value it didn't set, as it does for role.

Arch-Docs: n/a - the guide change is the People page text, not the user_guide section

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/ui/src/lib/contactChanges.ts
@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review: 🔴 0 · 🟡 2 · 🟣 0. No must-fix issues found. The two optional notes are inline.

A failed calendar read erased every stored next meeting and meeting
count until the next good pass. Those fields now stay as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 422 from the people routes showed "Unprocessable Entity"; it now
shows the server's text naming the line that isn't a phone, site or
address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_info.py
Comment thread packages/core/openexecutive/people/contact_changes.py Outdated
@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review summary: 🔴 1, 🟡 1, 🟣 0. I read the diff from contacts.py through the start of write_note in contact_notes.py. The store.py and models.py changes, the rest of write_note, and the UI/test files were not reviewed.

Undo on a team member now acts only on role and the contact details, so a
name or alias change row for a teammate can't rewrite them. The contact
detail validator and people-route access tests move into their own files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/core/openexecutive/people/contact_keeper.py
@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review summary: 🔴 0 · 🟡 1 · 🟣 0. Privacy gating on /people, list_people and ask_about_person held up, and detail values from signatures are validated against the mail text.

@johnrufusone
johnrufusone merged commit f719738 into main Oct 20, 2026
6 checks passed
@johnrufusone
johnrufusone deleted the claude/project-thread-p00hbu branch October 20, 2026 11:52
johnrufusone added a commit that referenced this pull request Oct 20, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.2](v0.6.1...v0.6.2)
(2026-10-20)


### Added

* **briefing:** show the live brief as a Right now card on Home
([#566](#566))
([9121b20](9121b20))
* **chat:** approve held steps with a tap instead of saying go
([#573](#573))
([0bb0d5a](0bb0d5a))
* **chat:** let the Executive change things in the app from chat
([#541](#541))
([f36818b](f36818b))
* **chat:** let the owner choose when outside content holds a turn
([#571](#571))
([96c6510](96c6510))
* **delegation:** add Suggested actions On and a clearer training layout
([#560](#560))
([0c96fd4](0c96fd4))
* **delegation:** answer meeting invites from a card, and on its own
under Handle it for me
([#568](#568))
([85f457e](85f457e))
* **delegation:** attach Drive files to drafts and let a go run held
steps first
([#555](#555))
([c887632](c887632))
* **delegation:** let the Executive rename and move the company's own
Drive files, with a tap card for the rest
([#569](#569))
([7124221](7124221))
* **delegation:** let the person choose sender only or everyone on a
reply card
([#556](#556))
([43561cf](43561cf))
* **delegation:** show reply cards closed with a one-line summary, open
on tap ([#570](#570))
([59ef8e0](59ef8e0))
* **delegation:** show What it does for you beside each Delegate tab
([#561](#561))
([4648051](4648051))
* **integrations:** approve cards with buttons in Telegram
([#542](#542))
([62bde0b](62bde0b))
* **integrations:** read Excel files in chat and images attached to
emails ([#537](#537))
([322659d](322659d))
* **integrations:** transcribe voice notes and add a mic to web chat
([#539](#539))
([b3233e0](b3233e0))
* **mcp_server:** let an AI app's turn take actions when the request
allows it
([#533](#533))
([30319d9](30319d9))
* **people:** keep contacts up to date from sent mail and meetings
([#557](#557))
([1aa8966](1aa8966))
* **people:** reach people in the chat app they last talked in
([#544](#544))
([45ad765](45ad765))
* **people:** remember what you deal with each person about and keep
their contact details
([#579](#579))
([f719738](f719738))
* pick chat apps by name, describe watches, tap answers on replies
([#543](#543))
([0baf9a1](0baf9a1))
* **ui:** add How it learns card and rename the screen to Learning
([#550](#550))
([d1c50e5](d1c50e5))
* **ui:** keep the Topic view short with one-line topics and five per
band ([#538](#538))
([511e1a9](511e1a9))
* **ui:** show each chat's app logo instead of a channel pill
([#536](#536))
([3a9e2e9](3a9e2e9))


### Fixed

* **briefing:** call people in the live header by name, not their
address
([#572](#572))
([3f697e2](3f697e2))
* **briefing:** make briefs report the reader's day, not their own
messages
([#545](#545))
([4b450ba](4b450ba))
* **briefing:** name waiting drafts and say what approved app cards did
([#558](#558))
([80bfc74](80bfc74))
* **briefing:** treat the reader's own messages as background, not news
([#548](#548))
([029a7ab](029a7ab))
* **chat:** start each tool round's text on a new paragraph
([#562](#562))
([dda3fc5](dda3fc5))
* **delegation:** draft a reply when a contact proposes a time
([#559](#559))
([0413f78](0413f78))
* **delegation:** draft a reply when an answer to a call request leaves
the time open
([#554](#554))
([479baf6](479baf6))
* **delegation:** read the person's own last email before sorting a
reply ([#553](#553))
([9b594bb](9b594bb))
* **delegation:** stop drafts writing the sender's name twice above the
signature
([#535](#535))
([c049abb](c049abb))
* **delegation:** work a typed answer into the reply draft instead of
pasting it
([#567](#567))
([a182d80](a182d80))
* **integrations:** reply to a voice note with just the answer
([#547](#547))
([c14ff9e](c14ff9e))
* **memory:** let a name reach a lesson and keep the grader's proof to
the person's words
([#551](#551))
([528e546](528e546))
* **memory:** review chats that read mail and count misses the product
recorded
([#549](#549))
([fbcf3d2](fbcf3d2))
* **scheduler:** stand down a follow-up already handled in chat
([#540](#540))
([4ac9af2](4ac9af2))
* **ui:** show last period's count instead of a signed change on Done
for you
([#552](#552))
([15964d9](15964d9))


### Changed

* **orchestrator:** shrink the cached chat prefix
([#546](#546))
([50a8f1b](50a8f1b))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

2 participants