You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: images were gated behind S3, silently; add a page per person
Every picture in the product required an object-storage bucket, and
nothing anywhere said so. `storageEnabled` needs all four of S3_ENDPOINT,
S3_BUCKET, S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY; miss one and it is
off with a neutral dot on the Health tab reading "not configured", which
looks like a feature you declined rather than the reason nobody has a
face.
Contact photos no longer touch S3. The sync used to skip fetching them
entirely when storage was off, so avatarUrl was never written and every
contact fell back to initials permanently. They are a few KB — they live
in the contacts table now, capped at 512KB, and the route serves the
bytes with an ETag off the row's mtime. The S3 branch stays only for
installs that already synced one.
Inbound photos were worse: the processor marked them `failed`, which is
terminal, so configuring a bucket later brought none of them back. A
missing bucket is a deployment state, not a property of the attachment,
so they stay pending and a sweep re-queues them once there is somewhere
to put the bytes — including a one-time rescue of the rows already
written off. The Health tab now says what the missing bucket costs and
how many files are waiting on it.
Then the page. Clicking someone's face — in the details panel, or on a
member of a group — opens /people/<id>: their profile, editable in
place, and every thread they are in. Not in the nav, deliberately. A
Contacts entry would frame it as a directory you go and browse, and this
is the answer to "who am I actually talking to", asked from inside a
conversation.
It is scoped to the contact rather than a thread, which is the point: the
thread list unions conversations linked to the contact with any group
they appear in as a participant, since a group carries no contactId and
keying on that column alone would show someone's one-to-one and silently
omit the four group chats they are in.
Three columns that existed but had no UI now have one. Identities can be
added and removed, so the same human texting from two numbers stops
being two strangers — refusing an address another contact already owns,
since moving it would quietly empty their history, and refusing to
remove the last one, since a contact with no address matches nothing and
would fork into a duplicate on their next message. optedOutAt gets a
switch: STOP has been enforced at every send path since the first
release, but nothing could read the flag, so the only way to discover it
was to be refused.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CY9GSvGD8JYnMyU5Q6BW5r
0 commit comments