feat(timeline): structured event_data for system timeline events#1176
feat(timeline): structured event_data for system timeline events#1176timothyfroehlich merged 13 commits intomainfrom
Conversation
Design and implementation plan for PinPoint-cks: converting timeline events from plain-text ProseMirror strings to structured JSON payloads with full cutover and prod migration validation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parses ProseMirror-wrapped text strings into structured JSON for all system events. Uses LIKE patterns for status labels (avoiding POSIX regex ambiguity with multi-word labels) and \S+ regex for single-word severity/priority/frequency labels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
createTimelineEvent now accepts a TimelineEventData discriminated union instead of a plain string, storing raw enum values in the eventData column. All 6 call sites in issues.ts updated accordingly; unused label helper imports removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract TimelineEventData type and formatTimelineEvent into src/lib/timeline/types.ts (no server imports) so client components can import without pulling in postgres/server-db modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Updates to Preview Branch (feat/structured-timeline-events) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
There was a problem hiding this comment.
Pull request overview
This PR migrates system timeline events from plain-text ProseMirror content to structured JSON payloads stored in a new issue_comments.event_data (jsonb) column, enabling type-driven rendering and easier querying while keeping a legacy fallback.
Changes:
- Add
event_datajsonb column toissue_comments+ migration to backfill existing system events. - Update write path to emit structured
TimelineEventDataobjects and read path to render viaformatTimelineEventwith legacy fallback. - Update unit + integration tests to assert structured
eventDatarather than parsing plaintext.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/server/db/schema.ts | Adds eventData jsonb column typing to issueComments. |
| drizzle/0023_glorious_warbound.sql | Adds column and backfills existing system events into event_data. |
| drizzle/meta/0023_snapshot.json | Drizzle snapshot updated for migration 0023. |
| drizzle/meta/_journal.json | Records migration 0023 in the Drizzle journal. |
| src/lib/timeline/types.ts | Introduces client-safe TimelineEventData + formatTimelineEvent. |
| src/lib/timeline/events.ts | Updates createTimelineEvent to store structured payloads and re-exports formatting/types. |
| src/services/issues.ts | Updates issue service call sites to write structured timeline event payloads. |
| src/components/issues/IssueTimeline.tsx | Renders system events from eventData with fallback to legacy content. |
| src/lib/types/issue.ts | Extends IssueCommentWithAuthor to include eventData. |
| src/lib/timeline/format.test.ts | Adds unit tests for formatTimelineEvent. |
| src/test/integration/supabase/issue-services.test.ts | Updates integration assertions to validate structured eventData. |
| docs/superpowers/specs/2026-04-07-structured-timeline-events-design.md | Design spec for structured timeline events. |
| docs/superpowers/plans/2026-04-07-structured-timeline-events.md | Implementation plan and validation steps. |
- Use `satisfies` instead of `as` assertion for empty ProseMirror doc - Import TimelineEventData from client-safe types.ts in schema and tests - Use optional chaining instead of Record cast in test assertions - Leave event_data NULL for unmapped status labels instead of 'unknown', so the read path falls back to legacy content text gracefully Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… for system events System events now rely solely on event_data for their payload. The content column is made nullable so createTimelineEvent no longer needs to write a meaningless empty ProseMirror doc. Existing migrated system events have their redundant content NULLed out. The read path fallback (docToPlainText) remains for any unmigrated rows — that will be removed in a follow-up after prod verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move psql and find deny rules to global user settings where they belong. Project settings should only contain project-specific safety rules (like blocking supabase db push). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Replaces plain-text ProseMirror system events with structured JSON payloads stored in a new
event_datajsonb column onissue_comments. This gives us type-safe, queryable timeline events instead of parsing display text.TimelineEventDatadiscriminated union covering 6 event types:status_changed,severity_changed,priority_changed,frequency_changed,assigned,unassignedevent_datawith fallback to legacycontenttextChanges
Schema & Migration
event_datajsonb column onissue_comments(migration 0023)ELSE 'unknown'fallbackWrite Path (
src/services/issues.ts,src/lib/timeline/events.ts)createTimelineEvent()now accepts structuredTimelineEventDataobjects instead of building ProseMirror stringsRead Path (
src/components/issues/IssueTimeline.tsx,src/lib/timeline/types.ts)event_datafirst, fall back todocToPlainText(content)for any unmigrated rowstypes.tsmodule avoids pulling server-side DB imports into the browser bundleTesting
formatTimelineEvent(all 6 types + unknown enum fallback)eventDatapayloads directlyProduction migration validation
Validated against prod backup (2026-04-12):
Test plan
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com