feat: attributed activity log for tasks and projects#133
Draft
FrkAk wants to merge 21 commits into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Task Reference: [MYMR-240]
Replaces the hardcoded binary
agent/userJSONBhistoryblob (tasks.history,projects.history) with a dedicated, RLS-gatedactivity_eventstable that records, for every discrete change, who (real human), how (which agent harness), and what (the specific entity touched).public.activity_eventstable (one row per changed scalar / per collection element), gated by the existing projects membership policy;metadata {from,to}populated forstatus_changed/priority_changed/estimate_changed/moved.AuthContextgains a durable-keys-onlyactordescriptor (sourceweb/mcp/system,userId, OAuthclientId). Web actor from the session; MCP actor from the JWTazpclaim. The write path never touchesneon_auth.service_role: name/avatar/harness are resolved at read time via twoSECURITY DEFINERfunctions modeled ontask_assignees_visible—activity_actors_visible(p_task_id)andoauth_client_name(p_client_id).getTaskFull*stops shippinghistory(egress win); new cursor-paginatedGET /api/task/[taskId]/events; the rebuiltActivitySectionrenders avatar + name + MCP harness badge + relative time (absolute on hover) with "show more".scripts/backfill-activity-events.sql(run with migration creds, not runtime).Type of change
Testing
bun run dev— not run in this environment (no dev stack); panel verification is a manual step, see notes.bun run lint)bun run typecheck)Full
bun testis green: 788 pass / 0 fail across 90 files, including RLS isolation foractivity_events(a non-member reads an empty page), the read-time SDF hydration, edge two-row writes,updateTaskdiscrete-diff splitting, and backfill mapping + idempotency.Notes for reviewer
service_roleanywhere in runtime code. Identity is resolved at read time through the two SDFs above (granted toapp_user, membership re-checked inside). The write path stores only durable keys (actor_user_id,source,actor_client_id).activity_eventswas added to the explicitENABLE/FORCE ROW LEVEL SECURITYlists and given an explicit grant indocker/*.sql(required bytests/db/rls-coverage.test.ts), beyond the Drizzle.enableRLS().listTaskActivityresolves the page and its read-time identity in a single RLS-scoped SQL statement that joins the SDFs inline (the codebase'stask+assigneesidiom, via alib/db/raw/statement builder), rather than the plan's two-step JS hydration —withUserContextRead's build callback is pure/non-awaiting, so the awaiting-callback shape the plan sketched isn't valid here.appendTaskHistoryManyWHERE task_id = ?semantics.tasks.history/projects.historyare left in place but unused for one release; a follow-up migration drops them once parity is verified (spec §G step 4).scripts/backfill-activity-events.sqlonce against the target DB with migration credentials; (2) eyeball the panel viabun run dev.