6.9 KiB
Task EP.1.5 Timeline Projection Foundation - 2026-07-13
Scope
- introduced first production CRM Business Projection: Timeline
- added
crm_timeline_projectionmaterialized read model schema - added Timeline event builder and Activity event mapping
- added Timeline projection consumer contract and active CRM registration
- added Timeline list/query storage service
- added Timeline rebuild and legacy adapter contracts
- added reusable Timeline UI components
- added optional Activity detail Timeline integration seam
- preserved Activity, Audit Log, Approval History, follow-up APIs, Dashboard, Reports, Notifications, and current detail-page behavior
Review Summary
Reviewed before and during implementation:
AGENTS.mdplans/task-ep.1.5.mdLAYOUT.mddocs/standards/engineering-constitution.mddocs/standards/project-foundations.mddocs/standards/architecture-rules.mddocs/standards/ui-ux-rules.mddocs/business/relationship-sales-workspace-blueprint-v1.mddocs/security/crm-authorization-boundaries.mddocs/implementation/task-ep1.4-projection-foundation-delivery-reliability-2026-07-13.mddocs/implementation/task-ep1.4.1-transactional-outbox-activation-worker-runtime-2026-07-13.md- existing Activity business event adapter
- existing Projection Runtime and Worker
- existing CRM detail/audit UI patterns
Implementation Summary
Timeline Schema
Added table:
crm_timeline_projection
Generated migration:
drizzle/0005_freezing_wendigo.sql
The table stores organization-scoped, rebuildable read-model rows with entity references, event provenance, visibility metadata, display fields, and projection version. It has indexes for event id/version idempotency, organization chronology, customer chronology, and activity/event lookup.
Timeline Builder
Added:
src/features/crm/timeline/server/builder.ts
Builder maps Business Events into business-readable Timeline items. Initial active mappings cover Activity lifecycle events:
activity.createdactivity.assignedactivity.reassignedactivity.startedactivity.rescheduledactivity.completedactivity.cancelledactivity.deleted
Generic mapping is available for registered future CRM events until their business copy is specialized.
Timeline Consumer
Added:
src/features/crm/timeline/server/consumer.tssrc/features/crm/timeline/server/registration.ts
The consumer builds a Timeline item and persists it with idempotent eventId + sourceProjectionVersion storage. Foundation default consumers remain safe skeletons; CRM worker startup can opt into CRM_TIMELINE_PROJECTION_CONSUMERS for the active Timeline projection set.
Timeline Storage And Query Contract
Added:
src/features/crm/timeline/api/types.tssrc/features/crm/timeline/server/service.ts
Supported filters include organization, customer, lead, opportunity, quotation, activity, event, category, actor, search, date range, and cursor/limit contract. Timeline remains read-only.
Legacy Adapter And Rebuild Contracts
Added:
src/features/crm/timeline/server/legacy-adapters.tssrc/features/crm/timeline/server/rebuild.ts
Legacy adapters are explicitly read-only rebuild inputs for lead follow-up, opportunity follow-up, quotation follow-up, activity, approval, and audit history. No data migration was introduced.
Timeline UI
Added:
src/features/crm/timeline/components/timeline-list.tsx
Components:
TimelineListTimelineCardTimelineEmptyTimelineSkeleton
The UI follows the existing dashboard card language, uses deterministic shared date formatting, shadcn primitives, and app icon registry. It preserves a calm enterprise density while adding a clear vertical chronology.
Workspace Integration
Added an optional timelineItems prop to Activity detail. Existing callers are unaffected; when supplied, Activity detail renders an additive Timeline card below the existing Activity detail card.
Security Notes
- Timeline rows are organization-scoped.
- Timeline visibility stores product type, pricing sensitivity, and internal-only flags.
- Pricing-sensitive/internal-only event payloads continue to rely on source event minimization.
- Timeline query service is storage-ready; route/API exposure must still apply CRM resolved access, branch/product scope, ownership, and pricing visibility before becoming public.
- Timeline never mutates source domains and does not replace Audit Log.
Delivery Matrix
Initial production path:
| Business Event | Timeline Item | Workspace | Consumer | Projection |
|---|---|---|---|---|
activity.created |
Activity created | Customer / Lead / Opportunity / Quotation / Activity | timeline.projection.consumer |
crm_timeline_projection |
activity.completed |
Activity completed | Customer / Lead / Opportunity / Quotation / Activity | timeline.projection.consumer |
crm_timeline_projection |
activity.cancelled |
Activity cancelled | Customer / Lead / Opportunity / Quotation / Activity | timeline.projection.consumer |
crm_timeline_projection |
Additional Activity events are supported in code and documented in the builder mapping.
UX Review
- Layout: follows
LAYOUT.mddetail-card and timeline entry patterns. - Density: Timeline cards stay compact, scannable, and fit existing CRM dashboard surfaces.
- Accessibility: items use semantic
article,time, non-color badges, and text labels. - Loading/empty states:
TimelineSkeletonandTimelineEmptyincluded. - Dark mode: tone classes include dark-mode variants.
- Date rendering: uses
src/lib/date-format.ts.
Verification
npm run typechecknode --disable-warning=ExperimentalWarning --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --experimental-strip-types --test src/features/foundation/business-events/*.test.ts src/features/foundation/projections/*.test.ts src/features/crm/activities/server/business-events.test.ts src/features/crm/timeline/server/*.test.tsnpx oxlint src/features/crm/timeline src/features/foundation/projections/registry.ts src/features/foundation/projections/consumers.ts src/features/foundation/projections/server/store.ts src/features/crm/activities/components/activity-detail.tsx src/db/schema.tsnpm run db:generate
Residual Risks / Follow-up
- Public Timeline route handlers and server-side CRM resolved-access filtering remain future work before exposing Timeline broadly.
- Customer, Lead, Opportunity, and Quotation detail pages are not yet wired to fetch Timeline data; Activity detail has a backward-compatible render seam.
- Approval, quotation, opportunity, customer, and PO event mappings have generic fallback support but need specialized business copy when those domains publish durable events.
- Legacy adapters are contract-only in this slice; source-query rebuild implementation should be expanded as each workspace adopts Timeline.
- EP.1.6 Calendar can reuse the same projection runtime and Activity event delivery path.