feat(tasks): add artifact comments and collaboration activity - #76407
feat(tasks): add artifact comments and collaboration activity#76407puemos wants to merge 60 commits into
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
posthog-app/_parent/products/ai_observability/frontend/datasets/AIObservabilityDatasetScene.js |
34.9 KiB | 🔺 +11.7 KiB (+50.3%) |
exporter/src/exporter/scenes/ExporterDashboardScene.js |
287.0 KiB | 🔺 +1.2 KiB (+0.4%) |
posthog-app/_parent/products/customer_analytics/frontend/CustomerAnalyticsScene.js |
157.9 KiB | 🔺 +1.1 KiB (+0.7%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.26 MiB · 22 files | no change | ███░░░░░░░ 27.9% of 4.51 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.18 MiB · 3,042 files | 🔺 +9.3 KiB (+0.1%) | ████████░░ 84.2% of 9.71 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 285.5 KiB | ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 237.5 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 231.5 KiB | ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/module.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 105.0 KiB | src/lib/api.ts |
| 94.7 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.20 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.20 MiB · 17 files | no change | ████░░░░░░ 38.4% of 5.72 MiB |
| Deferred (lazy) | 2.08 MiB · 33 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 721.9 KiB | dist/toolbar/toolbar-app-EFK53Z4U.css |
| 552.0 KiB | dist/toolbar/chunk-chunk-RKI77OFN.js |
| 484.6 KiB | dist/toolbar/chunk-chunk-BTPSXDW3.js |
| 133.6 KiB | dist/toolbar/chunk-chunk-VQYFWLUH.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-T5KY5WYR.js |
| 71.0 KiB | dist/toolbar/toolbar-app-BKQSZS7N.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-27JL52RE.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-JP2XV73C.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-IW6IKQIA.js |
| 12.2 KiB | dist/toolbar/chunk-chunk-PIK3PADE.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +190.9 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1394.00 MiB · 🔺 +190.9 KiB (+0.0%)
ℹ️ MCP UI apps size — 32 app(s), 17071.7 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 599.6 KB | 187.7 KB |
| action | 457.8 KB | 187.7 KB |
| action-list | 564.4 KB | 187.7 KB |
| cohort | 456.8 KB | 187.7 KB |
| cohort-list | 563.4 KB | 187.7 KB |
| email-template | 456.6 KB | 187.7 KB |
| error-details | 472.4 KB | 187.7 KB |
| error-issue | 457.5 KB | 187.7 KB |
| error-issue-list | 564.3 KB | 187.7 KB |
| experiment | 561.5 KB | 187.7 KB |
| experiment-list | 565.2 KB | 187.7 KB |
| experiment-results | 563.2 KB | 187.7 KB |
| feature-flag | 567.2 KB | 187.7 KB |
| feature-flag-list | 570.9 KB | 187.7 KB |
| feature-flag-testing | 461.0 KB | 187.7 KB |
| insight-actors | 562.2 KB | 187.7 KB |
| invite-email-preview | 456.0 KB | 187.7 KB |
| llm-costs | 559.5 KB | 187.7 KB |
| session-recording | 458.6 KB | 187.7 KB |
| session-summary | 463.9 KB | 187.7 KB |
| survey | 458.4 KB | 187.7 KB |
| survey-global-stats | 562.2 KB | 187.7 KB |
| survey-list | 565.1 KB | 187.7 KB |
| survey-stats | 562.2 KB | 187.7 KB |
| trace-span | 457.2 KB | 187.7 KB |
| trace-span-list | 564.3 KB | 187.7 KB |
| workflow | 457.1 KB | 187.7 KB |
| workflow-list | 563.7 KB | 187.7 KB |
| loops-review | 461.4 KB | 187.7 KB |
| query-results | 748.0 KB | 187.7 KB |
| render-ui | 828.6 KB | 187.7 KB |
| visual-review-snapshots | 461.6 KB | 187.7 KB |
⚠️ MCP snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Snapshots: MCP unit test snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Snapshots have been automatically updated to match current output
Next steps:
- Review the changes to ensure they're intentional
- If unexpected, investigate what caused the output to change
⚠️ Django migration SQL — 2 new migrations to review
We've detected new migrations on this PR. Review the SQL output for each migration:
posthog/migrations/1291_oauthaccesstoken_sandbox_task_id.py
BEGIN;
--
-- Add field sandbox_task_id to oauthaccesstoken
--
ALTER TABLE "posthog_oauthaccesstoken" ADD COLUMN "sandbox_task_id" uuid NULL;
COMMIT;products/tasks/backend/migrations/0083_taskcommentactivity.py
BEGIN;
--
-- Create model TaskCommentActivity
--
CREATE TABLE "posthog_task_comment_activity" ("id" uuid NOT NULL PRIMARY KEY, "activity_at" timestamp with time zone NOT NULL, "read_at" timestamp with time zone NULL, "kind" varchar(32) NOT NULL, "comment_id" uuid NOT NULL, "root_comment_id" uuid NOT NULL, "task_id" uuid NOT NULL, "team_id" integer NOT NULL, "user_id" integer NOT NULL, CONSTRAINT "task_comment_activity_unique" UNIQUE ("team_id", "user_id", "comment_id"));
ALTER TABLE "posthog_task_comment_activity" ADD CONSTRAINT "posthog_task_comment_task_id_c13f2c36_fk_posthog_t" FOREIGN KEY ("task_id") REFERENCES "posthog_task" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_task_comment_activity_task_id_c13f2c36" ON "posthog_task_comment_activity" ("task_id");
CREATE INDEX "posthog_task_comment_activity_team_id_0091a9a8" ON "posthog_task_comment_activity" ("team_id");
CREATE INDEX "posthog_task_comment_activity_user_id_63018694" ON "posthog_task_comment_activity" ("user_id");
CREATE INDEX "task_comment_activity_feed" ON "posthog_task_comment_activity" ("team_id", "user_id", "activity_at", "id");
CREATE INDEX "task_comment_activity_unread" ON "posthog_task_comment_activity" ("team_id", "user_id") WHERE "read_at" IS NULL;
COMMIT;Last updated: 2026-08-05 22:24 UTC (eb245c2)
✅ Django migration risk — migration analysis complete
We've analyzed your migrations for potential risks.
Summary: 2 Safe | 0 Needs Review | 0 Blocked
✅ Safe
Brief or no lock, backwards compatible
posthog.1291_oauthaccesstoken_sandbox_task_id
└─ #1 ✅ AddField
Adding nullable field requires brief lock
model: oauthaccesstoken, field: sandbox_task_id
tasks.0083_taskcommentactivity
└─ #1 ✅ CreateModel
Creating new table is safe
model: TaskCommentActivity
│
└──> ℹ️ INFO:
ℹ️ Skipped operations on newly created tables (empty tables
don't cause lock contention).
📚 How to Deploy These Changes Safely
AddField:
This operation acquires a brief lock but doesn't rewrite the table.
Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up.
Last updated: 2026-08-05 22:24 UTC (eb245c2)
875f8a0 to
7f374e9
Compare
|
React Doctor found 16 issues in 8 files · 16 warnings. 16 warnings
Reviewed by React Doctor for commit |
e6fcef0 to
f433ca1
Compare
f433ca1 to
a35dec3
Compare
fed6437 to
e647c5d
Compare
🦔 ReviewHog reviewed this pull requestFound 3 must fix, 12 should fix, 24 consider. Published 39 findings (view the review). |
|
ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
There was a problem hiding this comment.
ReviewHog Report
Data model
Issues: 2 issues
Files (3)
products/tasks/backend/models.pyproducts/tasks/backend/migrations/0083_taskcommentactivity.pyproducts/tasks/backend/logic/services/comment_activity.py
What were the main changes
- Adds TaskCommentActivity model (append-only, per-user, per-comment) with a raw-SQL upsert (record_many) that dedupes and never regresses activity_at
- New comment_activity service computes target accessibility, notification eligibility (#me suppression), and recipient projection with mention > reply > ownership precedence, excluding the comment author
- Migration creates posthog_task_comment_activity with feed/unread indexes and a per-user-per-comment uniqueness constraint
Business logic
Issues: 1 issue
Files (3)
products/tasks/backend/logic/services/task_comments.pyproducts/tasks/backend/facade/api.pyproducts/tasks/backend/facade/contracts.py
What were the main changes
- New task_comments service: list_artifacts (relational + legacy run-artifact + canvas-event fallback), list_comments/retrieve_comment with cursor pagination and thread-state resolution
- Facade gains record_comment_activity, task_comment_mentions_allowed, task_comment_target_is_accessible, list_task_artifacts/list_task_comments/retrieve_task_comment, and merges TaskCommentActivity rows into the task activity feed (list_task_activity, mark_task_activity_read, count_unread_task_activity)
- New DTOs: TaskArtifactDTO, TaskCommentTargetDTO/SummaryDTO/EntryDTO/DetailDTO/PageDTO, and TaskActivityDTO extended with latest_comment_* fields
Api
Issues: 2 issues
Files (5)
posthog/api/comments.pyproducts/canvas/backend/comment_access.pytach.tomlproducts/platform_features/frontend/generated/api.schemas.tsproducts/platform_features/frontend/generated/api.zod.ts
What were the main changes
- Generic Comment API/serializer adds task, task_artifact, and desktop_canvas scopes with task-visibility-gated access checks and reply-context inheritance
- New canvas comment_access module (canvas_belongs_to_task, canvas_owner_id) is the narrow boundary Comments delegates to instead of importing Canvas directly, declared via a new tach.toml interface
- Mentions are suppressed for inaccessible task targets; unscoped queries now exclude task/task_artifact/desktop_canvas scopes to prevent leaking through the generic endpoint
- created_by becomes nullable in the Comment schema (regenerated types)
Api
Issues: 1 issue
Files (10)
products/tasks/backend/presentation/serializers.pyproducts/tasks/backend/presentation/views/api.pyproducts/tasks/backend/presentation/views/channels_api.pyposthog/migrations/1287_oauthaccesstoken_sandbox_task_id.pyposthog/models/oauth.pyposthog/temporal/oauth.pyproducts/tasks/backend/temporal/oauth.pyproducts/tasks/frontend/generated/api.schemas.tsproducts/tasks/frontend/generated/api.tsproducts/tasks/frontend/generated/api.zod.ts
What were the main changes
- OAuthAccessToken gains a server-minted sandbox_task_id so task-scoped endpoints can't be tricked by a caller-supplied task header; token minting threads it through _mint_oauth_access_token and create_sandbox_oauth_access_token
- New /tasks/{id}/artifacts, /comments, /comments/{root_comment_id} endpoints, gated by _sandbox_bound_task_id which requires a PostHog-Code-issued sandbox token bound to the exact task
- Activity kind enum and mark-read serializer extended with thread_reply/owned_item_comment and per-activity read markers
Feature
Issues: 1 issue
Files (9)
services/mcp/src/tools/tasksContext.tsservices/mcp/src/tools/index.tsservices/mcp/src/hono/request-state-resolver.tsservices/mcp/schema/tool-definitions-all.jsonservices/mcp/schema/tool-definitions.jsonservices/mcp/src/api/generated.tsservices/mcp/src/generated/platform_features/api.tsservices/mcp/src/tools/generated/platform_features.tsproducts/tasks/skills/working-with-task-comments/SKILL.md
What were the main changes
- New tasks-artifacts-list, tasks-comments-list, tasks-comments-retrieve MCP tools, scoped to the current PostHog Code task via context.api.config.taskId
- These tools are excluded from the tool list unless the client is a PostHog Code consumer with an active task id
- New skill doc teaches agents to reach these tools only through the exec dispatcher, with pagination and read-only boundaries
Business logic
Issues: 3 issues
Files (6)
products/desktop/packages/api-client/src/generated.tsproducts/desktop/packages/api-client/src/posthog-client.tsproducts/desktop/packages/core/src/sessions/sessionService.tsproducts/desktop/packages/ui/src/features/sessions/components/useComments.tsproducts/desktop/packages/ui/src/features/sessions/commentNavigationStore.tsproducts/desktop/packages/ui/src/features/sessions/mentionAvailability.tsx
What were the main changes
- New CommentScope/ResourceComment API client types plus getResourceComments/createResourceComment (single and fanned-out across targets)
- React-query hooks (useCommentsQuery, useCommentsForTargetsQuery, useCreateComment, useSetCommentResolved) with optimistic updates scoped to a target-matching cache filter
- New cross-tree commentNavigationStore bridges the Comments list and artifact/canvas surfaces for focus/scroll requests and anchor-resolution state
- mentionAvailability context suppresses @-mentions in #me/private spaces
Frontend
Issues: 2 issues
Files (6)
products/desktop/packages/ui/src/features/sessions/components/CommentComposer.tsxproducts/desktop/packages/ui/src/features/sessions/components/CommentThreadCard.tsxproducts/desktop/packages/ui/src/features/code-editor/components/SelectionCommentOverlay.tsxproducts/desktop/packages/ui/src/features/canvas/components/MentionComposer.tsxproducts/desktop/packages/ui/src/features/code-editor/components/DocumentPreviewHeader.tsxproducts/desktop/packages/ui/src/features/sessions/components/ArtifactDocumentCommentAction.tsx
What were the main changes
- New CommentComposer (mention-aware textarea + submit/cancel) and CommentThreadCard (reply/resolve/reopen, GitHub-vs-PostHog thread rendering)
- SelectionCommentOverlay generalized beyond code review to host a full comment composer with expand/dismiss and viewport clamping
- New document-level comment action popover reused across markdown, HTML, and image artifact previews
Frontend
Issues: 2 issues
Files (2)
products/desktop/packages/ui/src/features/sessions/components/ArtifactPreview.tsxproducts/desktop/packages/ui/src/features/sessions/components/ArtifactTextAnnotations.tsx
What were the main changes
- ArtifactPreview wires comment target/query/create/resolve state per artifact type and locates a focused thread's anchor
- New ArtifactTextAnnotations renders persistent highlight rects over rendered markdown text, tracks selection to open the comment overlay, and recalculates on resize/scroll/mutation
Frontend
Issues: 5 issues
Files (5)
products/desktop/packages/ui/src/features/sessions/components/AnnotatedArtifactImage.tsxproducts/desktop/packages/ui/src/features/sessions/components/AnnotatedArtifactHtml.tsxproducts/desktop/packages/ui/src/features/sessions/components/artifactHtmlCommentBridge.tsproducts/desktop/packages/ui/src/features/sessions/components/artifactPreviewDocument.tsproducts/desktop/packages/ui/src/primitives/SafeImagePreview.tsx
What were the main changes
- New pin-based image commenting (click-to-place region anchors) and HTML artifact commenting via a sandboxed-iframe postMessage bridge (selection, highlight, locate, activate)
- artifactPreviewDocument now injects the comment bridge for HTML artifacts and adds an SVG-in-
preview path
- ZoomableImage gains a scale-aware overlay slot for comment pins
Frontend
Issues: 2 issues
Files (4)
products/desktop/packages/ui/src/features/canvas/components/taskArtifactRows.tsproducts/desktop/packages/ui/src/features/canvas/components/TaskArtifactsList.tsxproducts/desktop/packages/core/src/panels/panelStoreHelpers.tsproducts/desktop/packages/ui/src/features/panels/panelLayoutStore.ts
What were the main changes
- Extracts buildRows/commentSources/commentTargets/canvasDashboardId from TaskArtifactsList into a shared module so the Comments tab can reuse it
- TaskArtifactsList now shows an open-comment-count badge per artifact/canvas row via one fanned-out comments query
- New activeArtifactId helper (panelStoreHelpers/useActiveArtifactId) lets the Comments tab default its source filter to whatever artifact is on screen
Frontend
Issues: 5 issues
Files (9)
products/canvas/packages/canvas_builder/build.mjsproducts/desktop/packages/ui/src/features/canvas/freeform/BuiltCanvas.tsxproducts/desktop/packages/ui/src/features/canvas/freeform/CanvasFrameHost.tsxproducts/desktop/packages/ui/src/features/canvas/freeform/CanvasFramePlaceholder.tsxproducts/desktop/packages/ui/src/features/canvas/freeform/FreeformCanvas.tsxproducts/desktop/packages/ui/src/features/canvas/freeform/canvasFrameStore.tsproducts/desktop/packages/ui/src/features/canvas/freeform/canvasHostMessageRouter.tsproducts/desktop/packages/ui/src/features/canvas/freeform/sandboxRuntime.tsproducts/desktop/packages/ui/src/features/canvas/freeform/CanvasSelectionCommentAction.tsx
What were the main changes
- Injects selection-tracking and highlight-rendering runtimes into every built canvas (published canvas_builder bundle and dev sandboxRuntime), using CSS Custom Highlight API where available
- Host-side canvas components thread onTextSelection/onCommentActivate/commentHighlights/clearTextSelectionKey through BuiltCanvas, FreeformCanvas, and the frame store/router
- New CanvasSelectionCommentAction turns a canvas text selection into a comment via the overlay, then opens the Comments tab and focuses the new thread
Frontend
Issues: 3 issues
Files (4)
products/desktop/packages/ui/src/features/canvas/freeform/FreeformCanvasView.tsxproducts/desktop/packages/ui/src/features/canvas/freeform/CanvasSidePanel.tsxproducts/desktop/packages/ui/src/features/canvas/components/WebsiteLayout.tsxproducts/desktop/packages/ui/src/features/canvas/stores/canvasChatPanelStore.ts
What were the main changes
- Canvas side panel gains a Chat/Comments tab switch (canvasChatPanelStore) and renders the canvas's comment highlights keyed to the currently displayed version
- FreeformCanvasView computes open-thread highlights from unresolved, version-matched comments and dismisses the selection action when the version changes
- WebsiteLayout's canvas breadcrumb shows an open-comment-count badge that opens the Comments tab
Frontend
Issues: 8 issues
Files (2)
products/desktop/packages/ui/src/features/canvas/components/TaskCommentsList.tsxproducts/desktop/packages/ui/src/features/canvas/components/taskCommentThreads.ts
What were the main changes
- New Comments tab: lists every thread across a task's artifacts, canvases, the task itself, and its PRs, with source/state filters and a shared composer
- taskCommentThreads normalizes PostHog resource comments and GitHub review/conversation comments into one TaskCommentThread shape with per-origin reply/resolve behavior
- Supports focus-driven scroll/pulse navigation and canvas-version-aware thread opening
Frontend
Issues: 1 issue
Files (8)
products/desktop/packages/core/src/canvas/taskActivity.tsproducts/desktop/packages/shared/src/domain-types.tsproducts/desktop/packages/ui/src/features/canvas/components/ActivityHoverCard.tsxproducts/desktop/packages/ui/src/features/canvas/components/ActivityPanel.tsxproducts/desktop/packages/ui/src/features/canvas/components/ActivityView.tsxproducts/desktop/packages/ui/src/features/canvas/components/activityFeed.tsproducts/desktop/packages/ui/src/features/canvas/hooks/useMarkTaskActivityRead.tsproducts/desktop/packages/ui/src/features/canvas/hooks/useTaskActivity.ts
What were the main changes
- Activity feed rows now key by row id (not task id) since comment notifications are individual entries alongside collapsed task-lifecycle rows
- Adds thread_reply/owned_item_comment activity kinds and headline copy, and navigates a comment row to its exact canvas/artifact/task/thread
- Mark-read now distinguishes collapsed task activity (by seen_before timestamp) from individual comment activity (by activity_id) in both the mutation and optimistic cache update
Frontend
Issues: 1 issue
Files (8)
products/desktop/packages/shared/src/git-domain.tsproducts/desktop/packages/workspace-server/src/services/git/schemas.tsproducts/desktop/packages/workspace-server/src/services/git/service.tsproducts/desktop/packages/ui/src/features/git-interaction/usePrDetails.tsproducts/desktop/packages/ui/src/features/pr-review/usePrCommentsForUrls.tsproducts/desktop/packages/ui/src/features/pr-review/usePrReviewThreadsForUrls.tsproducts/desktop/packages/ui/src/features/code-review/components/PrCommentThread.tsxproducts/desktop/packages/ui/src/features/editor/components/githubMarkdownPlugins.ts
What were the main changes
- PR review/conversation comment schemas and the GitHub GraphQL/REST fetchers now flag bot authors so they can be filtered out of the Comments tab
- New usePrCommentsForUrls/usePrReviewThreadsForUrls/usePrTitles batch several PRs' comments in parallel for the task-wide Comments tab
- Extracts githubRehypePlugins (raw HTML + sanitize) as a shared module used by both PrCommentThread and the new CommentThreadCard
Other findings (outside the changed lines)
Valid issues on this PR's files that sit on lines GitHub won't let us comment on inline.
Optimistic mark-read has no rollback/reconciliation, and the query never becomes stale to self-correct
Priority: should_fix | File: products/desktop/packages/ui/src/features/canvas/hooks/useMarkTaskActivityRead.ts:18-76 | Category: bug
Why we think it's a valid issue
- Checked: the PR-head
useMarkTaskActivityRead.ts(full hook),client.markTaskActivityReadinposthog-client.ts,useTaskActivity.tsquery config, and the sibling optimistic hooks in the same directory. - Found: the hook is
mutationFn+onMutateonly — nocancelQueries, noprevioussnapshot, noonError, noonSettled.onMutatewritesis_unread: falseand decrementsunread_countstraight into the cache. - Found:
markTaskActivityReadthrows on any non-OK response (posthog-client.ts:2780-2784), so a failed request rejects the mutation with no path to undo the optimistic write; the caller (ActivityViewmarkRead/markAllRead) fires-and-forgets, so the rejection is invisible to the user. - Found:
useTaskActivitysetsstaleTime: Number.POSITIVE_INFINITY(useTaskActivity.ts:43) with norefetchInterval, so the feed does not auto-refetch to reconcile.useChannelStars.ts:46-60in the same directory implements the exact cancelQueries→snapshot→onError-rollback→onSettled-invalidate pattern this hook omits. - Impact: a network blip during "mark all as read" (a batch with no compensating navigation) optimistically clears genuine unread notifications — mentions, replies, comments — from the cache with no rollback, silently hiding them. Because comment notifications are marked read individually by
activity_id, reaching the task by another route may not reconcile them, so server truth staysunreadwhile the cache shows read. This is a real failure mode with a concrete user-facing consequence and a verified in-repo convention it breaks. - Priority: keeping
should_fix. The defect is real but self-heals after the defaultgcTimeon unmount or on any explicit invalidation (so not strictly "permanent"), and requires a request failure to trigger — a genuine reliability gap worth fixing, not a must-fix data-loss.
Issue description
useMarkTaskActivityRead's onMutate (now materially more complex — it builds two lookup structures and branches per-row on latest_comment_id/activity_id) writes the optimistic 'read' state directly into the cache but has no cancelQueries, no snapshot of the previous cache state, no onError rollback, and no onSettled invalidation. client.markTaskActivityRead (posthog-client.ts) throws on any non-OK response, so a failed request simply rejects the mutation silently — the row stays marked read in the cache with no code path to undo it. Every sibling optimistic-update hook in this same directory (useChannelStars.ts, useTaskViewed.ts, usePinnedTasks.ts) follows the cancelQueries → snapshot → onError-rollback → onSettled-invalidate pattern; this hook is the one exception. Because useTaskActivity sets staleTime: Number.POSITIVE_INFINITY, the feed query never automatically refetches to reconcile, so a network blip during 'mark as read' (or 'mark all as read', which can send a large batch) permanently and silently hides a real notification (mention, reply, comment) from the user until some unrelated event invalidates the query.
Suggested fix
Follow the established pattern: await queryClient.cancelQueries({ queryKey: TASK_ACTIVITY_QUERY_KEY }), capture const previous = queryClient.getQueryData(...) and return it as { previous } context from onMutate, add onError: (_e, _vars, context) => { if (context?.previous) queryClient.setQueryData(TASK_ACTIVITY_QUERY_KEY, context.previous); }, and consider onSettled invalidation so the client reconciles with server truth.
Combine task artifact commenting in the desktop app with backend activity-feed delivery for comment mentions. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Validate task ownership for comment reads and writes, add whole-document and canvas comments, deep-link activity mentions to their exact thread, and bound task-wide comment polling. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Avoid private cross-product model imports in comments API tests and prevent a task-run fixture from shadowing TestCase.run. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Give the mutable nested API payload an explicit dictionary type so mypy accepts updating its task context. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Run task-comment ownership validation through TeamAndOrgViewSetMixin.safely_get_object so standard permission checks remain intact and API schema generation can import the viewset. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Record the task_id query parameter added to task-owned comment reads in the generated MCP tool schema snapshot. Generated-By: PostHog Code Task-Id: 49201d1f-5d24-4117-b923-cc1d240c8ebc
Authorize and navigate canvas comments through the new canvas records while preserving task mention activity across canvas source versions. Generated-By: PostHog Code Task-Id: 56b16387-fd8b-4cd9-a1ed-21feda91f1b8
Allow comments to be created from selected canvas text and surface task-wide comments alongside canvas chat. Open and focus the comments view after comment creation. Generated-By: PostHog Code Task-Id: 56b16387-fd8b-4cd9-a1ed-21feda91f1b8
Generated-By: PostHog Code Task-Id: 56b16387-fd8b-4cd9-a1ed-21feda91f1b8
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
Generated-By: PostHog Code Task-Id: f928ad48-02db-4e87-a508-bb7e4f6f8f9b
ab268fb to
8dbbae9
Compare
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Generated-By: PostHog Code Task-Id: f2636fdd-513a-416d-8564-47923eb21248
Adds the cloud contract for anchored comments on tasks, artifacts, and canvases: comment API access checks and mention gating, TaskCommentActivity projection with after-commit retry, the sandbox-task-id OAuth binding, task read endpoints, three read-only MCP tools (shipped disabled), the cloud canvas selection runtime, and regenerated API types. Inert until a client calls it. Layer 1 of 3 in a stack split out of #76407. Generated-By: PostHog Code Task-Id: f7eb12dd-00a4-4293-a013-8156f71c4e2a
Adds the cloud contract for anchored comments on tasks, artifacts, and canvases: comment API access checks and mention gating, TaskCommentActivity projection with after-commit retry, the sandbox-task-id OAuth binding, task read endpoints, three read-only MCP tools (shipped disabled), the cloud canvas selection runtime, and regenerated API types. Inert until a client calls it. Layer 1 of 3 in a stack split out of #76407. Generated-By: PostHog Code Task-Id: f7eb12dd-00a4-4293-a013-8156f71c4e2a
Adds the cloud contract for anchored comments on tasks, artifacts, and canvases: comment API access checks and mention gating, TaskCommentActivity projection with after-commit retry, the sandbox-task-id OAuth binding, task read endpoints, three read-only MCP tools (shipped disabled), the cloud canvas selection runtime, and regenerated API types. Inert until a client calls it. Layer 1 of 3 in a stack split out of #76407. Generated-By: PostHog Code Task-Id: f7eb12dd-00a4-4293-a013-8156f71c4e2a
Problem
Agent output is reviewed in artifacts and canvases, but feedback loses context without a precise attachment point. Reviewers can now discuss exact text, image regions, or documents and return to the reviewed version.
Changes
#meprivate and excludes comment authors from notifications.System architecture
The comment is the durable source of truth. Anchors locate feedback, MCP exposes bounded reads, and Activity projects collaboration events.
1. Create an anchored comment
sequenceDiagram actor Reviewer participant Surface as Canvas or artifact participant Desktop as Desktop host participant API as Comment API participant Store as Comment store participant Facade as Tasks facade Reviewer->>Surface: Select text or an image region Surface->>Desktop: Send the normalized anchor Reviewer->>Desktop: Submit the comment Desktop->>API: Create a root comment API->>API: Validate task and target access API->>Store: Save the comment, anchor, and canvas version API->>Facade: Project collaboration Activity API-->>Desktop: Return the saved thread Desktop-->>Reviewer: Render the highlight, pin, or document threadReplies inherit the root target and anchor context. Resolve and reopen events update thread state without appearing as human replies.
2. Return to the reviewed content
sequenceDiagram actor Reviewer participant Comments as Comments or Activity participant Nav as Comment navigation participant Versions as Canvas versions participant Surface as Review surface Reviewer->>Comments: Open a comment Comments->>Nav: Pass the task, target, version, and thread alt The target is a versioned canvas Nav->>Versions: Load the immutable canvas version Versions-->>Surface: Render the reviewed content else The target is an artifact or task Nav->>Surface: Open the matching review surface end Nav->>Surface: Focus the thread Surface-->>Reviewer: Scroll to text, show a pin, or open the document thread3. Expose MCP tools only to the current task agent
sequenceDiagram participant Host as PostHog Code host participant MCP as MCP request resolver participant Tool as Current-task tool participant API as Tasks API participant Auth as OAuth access token Host->>MCP: Start with a host-stamped task ID MCP->>MCP: Verify the PostHog Code consumer profile alt Consumer and task ID are present MCP-->>Host: Expose the three current-task tools Host->>Tool: Call a tool without a task ID argument Tool->>API: Use the task ID from MCP context API->>Auth: Require a PostHog Code OAuth client Auth->>API: Provide the sandbox-bound task ID API->>API: Require both task IDs to match API-->>Tool: Return current-task data else Context is missing or belongs to another client MCP-->>Host: Hide all three tools endThe tools are read-only and accept no caller-controlled task ID. A legacy token without a sandbox task binding cannot read comments.
4. Discover artifacts and comment threads through MCP
sequenceDiagram participant Agent as Task agent participant Artifacts as tasks-artifacts-list participant Comments as tasks-comments-list participant Service as Task comment service participant Data as Task data Agent->>Artifacts: List review targets Artifacts->>Service: Read the current task Service->>Data: Read relational artifacts Service->>Data: Read bounded legacy artifact manifests Service->>Data: Read bounded canvas-created events Service-->>Agent: Return deduplicated artifacts and canvases Agent->>Comments: List root threads Comments->>Service: Apply artifact, resolved, limit, and cursor filters Service->>Data: Read task, artifact, and canvas comments Service->>Service: Exclude lifecycle events from reply counts Service->>Service: Bound content and selected text Service-->>Agent: Return summaries and an opaque next cursortasks-comments-listexcludes resolved threads by default. Its optional artifact filter applies to artifacts and canvases, not task-level comments.5. Read a complete thread through MCP
sequenceDiagram participant Agent as Task agent participant Tool as tasks-comments-retrieve participant Service as Task comment service participant Data as Comment store Agent->>Tool: Send a root comment ID Tool->>Service: Request the current-task thread Service->>Data: Read the root, replies, and latest state Service->>Service: Remove resolve and reopen events from the conversation Service->>Service: Bound anchors and apply the response content budget Service-->>Agent: Return chronological comments and a next cursor alt One comment body was truncated Agent->>Tool: Send its comment ID and byte offset Tool->>Service: Continue only that comment body Service-->>Agent: Return the next bounded chunk else More comments remain Agent->>Tool: Send the opaque thread cursor Tool->>Service: Continue after the last returned comment Service-->>Agent: Return the next chronological page endThread retrieval accepts only root IDs from the current task. It returns bounded text anchors, canvas version IDs, authors, timestamps, and resolution state.
6. Select Activity recipients and retry projection
sequenceDiagram participant Comment as Comment API participant Projector as Activity projector participant Queue as Retry queue participant Activity as Activity store Comment->>Projector: Send the saved comment and mentions Projector->>Projector: Reject unsupported or private targets alt New root comment Projector->>Projector: Select the canvas, artifact, or task owner else Reply Projector->>Projector: Select existing thread participants end Projector->>Projector: Apply mention precedence and remove the author Projector->>Activity: Append one row per recipient alt Projection raises an error Comment->>Queue: Schedule an after-commit retry by reference Queue->>Projector: Replay the idempotent projection endA mention wins over reply and ownership reasons. A mentioned user joins later reply notifications only after participating in the thread.
7. Merge, open, and read Activity
sequenceDiagram actor User participant Feed as Activity service participant TaskRows as Task Activity participant CommentRows as Comment Activity participant Desktop as Desktop Activity view User->>Feed: List Activity with a cursor Feed->>TaskRows: Read visible task rows Feed->>CommentRows: Read visible, non-deleted comment rows Feed->>Feed: Merge and sort both streams Feed-->>Desktop: Return one page and the total unread count User->>Desktop: Open an entry Desktop-->>User: Navigate to the exact target and thread alt Comment Activity Desktop->>Feed: Mark that Activity row read else Task Activity Desktop->>Feed: Mark task rows through the seen timestamp read endVisibility is checked again when Activity is read, so stale rows cannot reveal tasks that the user can no longer access.
How did you test this code?
👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Automatic notifications
Docs update
No user-facing docs are affected.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
PostHog Code implemented and reviewed the change with
/debugging-ci-failures,/improving-drf-endpoints,/implementing-mcp-tools,/django-migrations,/writing-tests,/writing-user-facing-copy,/writing-code-comments,/authoring-ci-workflows, and/writing-pr-descriptions.Review feedback moved retry dispatch behind the tasks facade, tightened untrusted anchor metadata, restored strict CSP behavior, and removed stale desktop state races.
Created with PostHog Code