8000 feat(timeline): emoji reactions and comments on activities by adeelahmad · Pull Request #570 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(timeline): emoji reactions and comments on activities - #570

Merged
adeelahmad merged 11 commits into
mainfrom
claude/project-thread-pf8kii
Oct 18, 2026
Merged

adeelahmad merged 11 commits into
mainfrom
claude/project-thread-pf8kii

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: the Timeline page listed what came in, and each person could mark items reviewed, important or hidden for themselves alone. There was no way to react to an item or talk about it with the others in the namespace, and nothing an agent could read of what people thought of an item.

After: the timeline is a feed everyone in a namespace talks on. An opened item has a React button with a palette (👍 ❤️ 🎉 👀 😂 😮 😢 🙏) and shows everyone's reactions as chips (yours highlighted; a click on yours takes it back). Its comments open as threads with Reply, where Enter sends. A closed line sums its items' reactions and says "3 comments". A comment on an asset's arrival is a comment on the asset itself, so the same thread shows on its page. Agents react and comment over MCP (react_to_activity, activity_comments, comment_on_activity) and through POST /api/v1/activities/{id}/react and /activities/{id}/comments; GET /activities carries everyone's reactions and each item's comment count.

Each reaction is a reaction.added activity (Like, noisy, by the person, with the activity as its object) and each comment a comment.added whose target is the activity, following the activity's audience, so automations and agents can act on them.

How: reactions are one event_reaction row per person and activity (as review marks are), read for a page in one query; comments reuse the comment table with an activity field and about (the summary), dual-keyed to the asset for create-recording-<id>. New tables and fields only, so no migration. No model call anywhere.

Motivation and Context

Step 3 of the activity stream design (thread above): a structured feed without costing tons, where people and agents interact on items. Steps 1 and 2 (the stream API and the Timeline page) were already built.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (make hooks checks it on commit).
  • I added or updated tests.
  • My change requires documentation updates, and I have updated docs/ accordingly.
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.md.

Done questions (AGENTS.md)

  • Goal: reactions and comments on any timeline item, shared, for people and agents; merged on green CI.
  • Agents: REST (/activities/{id}/react, /activities/{id}/comments) and MCP (react_to_activity, activity_comments, comment_on_activity).
  • Zero setup: new tables are defined at start; nothing to configure.
  • Fewest clicks: one click reacts from the palette or a chip; Enter sends a comment; Shift+Enter for a new line.
  • Configurable: n/a, no settings; the palette is fixed so every client can draw it.
  • Light: two indexed tables and two grouped queries per page; no model call.
  • Private and secure: comments and reactions on an owner-only activity stay with the owner (audience follows the activity); writing needs a session or write-scope key. Same surface as resource comments (already in the threat model), so no new entry.
  • Cost: no model call; nothing in the ledger to log.
  • User decides: nothing acts on its own; a reaction or comment only logs an activity that automations can be made to follow.
  • Upgrades: new table event_reaction and new optional fields on comment; DEFINE ... IF NOT EXISTS, safe on existing installs, no migration.
  • Honest: docs/timeline.md's status table lists this as built and @agent, graph actors and For you as planned; the README's built line names it.
  • Tested and documented: test_timeline_social.py (4 tests), jest timeline-ui.test.tsx and timeline-social-model.test.ts; docs timeline.md, api.md, mcp.md, automations.md, requirements.md, CHANGELOG; make openapi regenerated the client.
  • Open standards: AS2 Like for reactions, Note with inReplyTo for comments; no new dependency.
  • Reported: result goes back in the thread; refine-later items are in docs/timeline.md (who reacted on hover, editing and resolving from the timeline, a deep link from comment.added to the item).
  • Reuse: reactions mirror event_marks.py; comments reuse comments.py, the comments routes' _comments_out, and the recording page's thread model and writer helpers.

Local CI (ci/local-ci.sh) on this head: FastAPI embedded and server, Next.js, pre-commit, PR title and upgrade all pass.


Generated by Claude Code

The timeline becomes a feed everyone in a namespace talks on. An opened item has a React button with a palette and shows everyone's reactions as chips; its comments open as threads with Reply, where Enter sends. A closed line sums its items' reactions and says how many comments it has. A comment on an asset's arrival is a comment on the asset, so the same thread shows on its page.

Each reaction is a reaction.added activity (noisy, by the person, with the activity as its object) and each comment a comment.added with the activity as its target, following the activity's audience, so automations and agents can act on them. Agents react and comment over MCP (react_to_activity, activity_comments, comment_on_activity) and through POST /activities/{id}/react and /activities/{id}/comments.

Reactions are one event_reaction row per person and activity, as marks are; comments reuse the comment table with an activity field. No model call is involved.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ln25ykE6uBDqx21WBU94SX
Release 0.15.0 carries #400, so the requirements check failed on main
with it still under In progress; it moves to the built list with its
checks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ln25ykE6uBDqx21WBU94SX
Copy link
Copy Markdown
Owner Author

Clicks and keystrokes per feature is red on this head (786db63): the phone survey's chat-ask journey times out waiting for the answer (locator.waitFor: Timeout 60000ms exceeded), while the computer survey passes. The same journey fails the same way on main's own run of the check (run 39106023492 on e641898, after #561), and it passed on this PR's previous head (75a1f08, base 99c0c3e), so it isn't this PR's change: nothing here touches Chat. The API log shows the question posted and answered (POST /chats/2/messages 200) and the later chat-regenerate journey finds that answer, so the phone page shows the answer late or not at all after Enter in a new conversation. No fix for it is on main yet; I'm reproducing it locally with ci/clicks/run.sh --device phone and will port the fix here when I have it.


Generated by Claude Code

… raced the stream

The page moves to /chat/{id} as a new conversation's first answer
streams. On a slow device that first load of the conversation is still
in flight when the answer ends, and the query cache folds the refetch
asked for then into the load, which predates the answer: the page showed
"No answer was saved for this question" until reloaded (the phone
clicks survey's chat-ask journey in CI). After an answer, when the
conversation read back lacks it, it is read once more.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ln25ykE6uBDqx21WBU94SX
Copy link
Copy Markdown
Owner Author

Fix for the phone Clicks failure pushed in 7f260ec. CI's screenshot (failed-chat-ask-phone.png) showed the conversation with "No answer was saved for this question": the page moves to /chat/{id} as a new conversation's first answer streams, and on the slow CI phone that first load of the conversation was still in flight when the stream ended, so the refetch asked for then was folded into the load by the query cache, and the load predated the saved answer. refreshAnswered (components/chat/data.ts) now reads the conversation once more when it lacks the answer, with a jest test for the race. The journey passes locally on a phone either way, so CI's rerun on this head is the check.


Generated by Claude Code

…rst load raced the stream"

This reverts commit 7f260ec: #582 carries
the same fix for main (refetchFresh in every chat panel, and the answer
written on a thread of its own), so this PR stays one concern.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ln25ykE6uBDqx21WBU94SX
….d fragment, and name them in the README

The release commit folds Unreleased into the version section and #581 moved new entries to changelog.d/, so the
entry becomes changelog.d/timeline-social.md. The README's built line lost its reactions and comments clause in an
earlier merge; it is back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ln25ykE6uBDqx21WBU94SX
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

0