Skip to content

feat(timeline): complete structured event migration with zero residual debt#1179

Merged
timothyfroehlich merged 8 commits intomainfrom
feat/remove-timeline-content-fallback
Apr 18, 2026
Merged

feat(timeline): complete structured event migration with zero residual debt#1179
timothyfroehlich merged 8 commits intomainfrom
feat/remove-timeline-content-fallback

Conversation

@timothyfroehlich
Copy link
Copy Markdown
Owner

@timothyfroehlich timothyfroehlich commented Apr 13, 2026

Summary

Completes the migration from string-based timeline events to structured eventData JSON. After this PR merges, every system event write path produces typed eventData, the read path has no fallbacks, and DB constraints enforce data integrity.

Follow-up to #1176 (initial migration). Started as a simple fallback removal (PP-6g8) but a full audit revealed 8 residual items. This PR fixes all of them.

Changes

New event type: title_changed

  • Added { type: "title_changed"; from: string; to: string } to TimelineEventData union
  • Extracted updateIssueTitle() service function (follows updateIssueStatus pattern: transaction, fetch old value, no-op check, timeline event)
  • updateIssueTitleAction now delegates to service function instead of direct db.update

Fallback removal (commit 1)

  • Removed docToPlainText(content) fallback from IssueTimeline.tsx system event rendering
  • System events now render eventData ? formatTimelineEvent(eventData) : null — no fallback path

comment_deleted event type (commit 2)

  • Added { type: "comment_deleted"; deletedBy: "author" | "admin" } to union
  • Migrated deleteCommentAction to write eventData instead of ProseMirror content

Type safety & DB integrity (commit 3)

  • Added exhaustive default case with satisfies never for compile-time + runtime safety
  • Added CHECK (NOT is_system OR event_data IS NOT NULL) constraint (migration 0026)
  • Fixed createIssue missing actorId on initial assignment timeline event
  • Fixed stale test fixture still using old content pattern
  • Cleaned up redundant double import in events.ts

Tests

  • 1 new unit test: title_changed formatting
  • 5 new integration tests: assignIssue (assign, unassign, no-op) + updateIssueTitle (changed, no-op)
  • 1 new E2E assertion: assignment timeline event text visibility
  • Updated delete-comment-audit.test.ts stale system event mock

Files changed (12)

  • src/lib/timeline/types.ts — union + formatter + exhaustive default
  • src/lib/timeline/events.ts — clean import pattern
  • src/services/issues.tsupdateIssueTitle service + createIssue actorId fix
  • src/app/(app)/issues/actions.ts — delegate to service function
  • src/server/db/schema.ts — CHECK constraint
  • drizzle/0026_premium_katie_power.sql — migration
  • src/lib/timeline/format.test.ts — title_changed test
  • src/test/integration/supabase/issue-services.test.ts — 5 new tests
  • src/test/unit/delete-comment-audit.test.ts — stale fixture fix
  • e2e/smoke/issues-crud.spec.ts — assignment E2E assertion

Testing

  • ✅ TypeScript: no errors
  • ✅ Unit tests: 851/851 passed
  • ✅ Integration tests: 87/87 passed (12 in issue-services alone)
  • ✅ Build: clean
  • db:generate: "No schema changes" (migration consistent)

Related Issues

Closes PP-6g8

🤖 Generated with Claude Code

…rendering

All 220 prod system events are migrated to event_data and content is NULLed.
The legacy fallback is unreachable; removing it simplifies the render path
and drops the docToPlainText dependency from the timeline component.

Closes PP-6g8

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 13, 2026 02:43
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pin-point Ready Ready Preview, Comment Apr 18, 2026 1:07am

@supabase
Copy link
Copy Markdown

supabase bot commented Apr 13, 2026

Updates to Preview Branch (feat/remove-timeline-content-fallback) ↗︎

Deployments Status Updated
Database Sat, 18 Apr 2026 01:05:19 UTC
Services Sat, 18 Apr 2026 01:05:19 UTC
APIs Sat, 18 Apr 2026 01:05:19 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Sat, 18 Apr 2026 01:05:20 UTC
Migrations Sat, 18 Apr 2026 01:05:20 UTC
Seeding Sat, 18 Apr 2026 01:05:20 UTC
Edge Functions Sat, 18 Apr 2026 01:05:20 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes the legacy docToPlainText(content) fallback path when rendering system timeline events, based on the assumption that all system events are now represented via structured event_data.

Changes:

  • Drops the docToPlainText import from IssueTimeline.tsx.
  • Simplifies system-event rendering to only display formatTimelineEvent(event.eventData).

Comment thread src/components/issues/IssueTimeline.tsx
Migrate deleteCommentAction from writing ProseMirror content to writing
structured eventData. Adds comment_deleted to the TimelineEventData
discriminated union with "author"/"admin" variants.

Also updates all integration/unit tests to use eventData for system
events, fixing a pre-existing bug where tests called .includes() on
ProseMirrorDoc objects via `as any` casts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@timothyfroehlich timothyfroehlich changed the title refactor(timeline): remove docToPlainText fallback from system event rendering refactor(timeline): remove fallback and add comment_deleted structured event Apr 13, 2026
… and full test coverage

- Add title_changed event type to TimelineEventData discriminated union
- Extract updateIssueTitle service function (follows updateIssueStatus pattern)
- Add exhaustive default case with satisfies never for runtime safety
- Add CHECK constraint: system events require non-null event_data (migration 0026)
- Fix createIssue missing actorId on initial assignment timeline event
- Fix stale test fixture in delete-comment-audit using old content pattern
- Clean up redundant double import in events.ts
- Add integration tests for assignIssue (assign, unassign, no-op)
- Add integration tests for updateIssueTitle (changed, no-op)
- Add unit test for title_changed formatting
- Add E2E assertion for assignment timeline event text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 13, 2026 14:37
@timothyfroehlich timothyfroehlich changed the title refactor(timeline): remove fallback and add comment_deleted structured event feat(timeline): complete structured event migration with zero residual debt Apr 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread drizzle/0026_premium_katie_power.sql
Comment thread src/lib/timeline/types.ts
timothyfroehlich and others added 2 commits April 17, 2026 19:17
The CHECK constraint (migration 0026) requires event_data to be non-null
for system events. The seed data was still using plain content strings,
causing the constraint to fail on Supabase branch DBs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The chk_system_event_data constraint fails on persistent preview/branch DBs
that were seeded before the structured eventData migration — those rows
have is_system=true with event_data IS NULL, so Postgres rejects the
ALTER TABLE ADD CONSTRAINT. Delete them in the same migration so existing
branch DBs can catch up cleanly.
The seed script was writing is_system=true rows with only content and
no event_data, violating the CHECK constraint added in migration 0026.
The comment said 'match service logic' but createIssue no longer emits
this event — drift between seed and service.

This unblocks the 'Migrate & Seed Branch DB' CI job on preview branches.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comment thread drizzle/0026_premium_katie_power.sql
Comment thread src/lib/timeline/types.ts Outdated
Replace awkward 'satisfies never as { type: string }' cast with a
dedicated assertUnreachableEvent(_event: never) helper. Same compile-time
exhaustiveness guarantee, no unsafe runtime cast.

Addresses Copilot review comment on types.ts:54.
@timothyfroehlich timothyfroehlich merged commit 86d3c0f into main Apr 18, 2026
23 checks passed
@timothyfroehlich timothyfroehlich deleted the feat/remove-timeline-content-fallback branch April 18, 2026 01:18
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.

3 participants