Timeline Service — Technical Specification (Updated)

JIRA: GNCT-4254 (Epic: GNCT-4375 — Asset Level Timeline View)
Author: Priyanshi Soni
Date: July 30, 2026 (v2 — updated from Jul 27 draft)
Sprint: Control Sprint 6
Purpose: Comprehensive design for the timeline service. Standardized template for all future BFF endpoint specs.


1. Overview

What this service does

The Timeline Service stitches raw audit events (breadcrumbs) from DynamoDB into a coherent journey view for any asset — channels, catalogs, mappables, programs, images, or pre-PID entities being onboarded.

Key design decisions after standups

  1. Two endpoints per asset type — run list (lightweight) + run detail (heavy, on click)
  2. BFF internalizes all ID mapping — UI passes a context ID (progservId, mappableId, tmsId, zendeskId). BFF resolves to correlationIds internally. UI never coordinates multiple calls.
  3. Config-driven via ct_stage_classification — ALL display properties (statusMapping, displayLabel, functionalStep, depth, lane, order, iconHint) come from DDB config. Nothing hardcoded in Java.
  4. UI is a dumb renderer — no transformations, no hardcoded logic. UI renders exactly what BFF returns.
  5. Detail endpoint is authoritative — if detail returns newer data than the list, UI silently updates the list.
  6. Same engine for all asset types — same BFF code, different config entries per system.

What this spec does NOT cover


2. Endpoints

2.1 Channel Timeline (Linear — EPGS/SIP → Relay → NED pipeline)

# Endpoint Method Purpose Input from UI
1 /api/v1/timeline/{progservId}/runs GET Run list for a channel progservId
2 /api/v1/timeline/{progservId}/runs/{correlationId} GET Run detail (milestones + lanes + days grid) progservId + correlationId (from endpoint 1 response)

2.2 Catalog Timeline (Non-Linear — UDIS → OVD → NED pipeline)

# Endpoint Method Purpose Input from UI
3 /api/v1/timeline/{progservId}/catalog-runs GET Catalog UDIS run list progservId
4 /api/v1/timeline/{progservId}/catalog-runs/{correlationId} GET Catalog run detail (UDIS legs + threshold + downstream) progservId + correlationId

2.3 Entity Flows (shared across channel/catalog)

# Endpoint Method Purpose Input from UI
5 /api/v1/timeline/mappable/{mappableId} GET Mappable Flow (JIRA-style event log) mappableId
6 /api/v1/timeline/mappable?remoteId={}&schemeId={} GET Mappable Flow (resolve first) remoteId + schemeId
7 /api/v1/timeline/program/{tmsId} GET Program Flow (enrichment cycles) tmsId
8 /api/v1/timeline/image/{imageId} GET Image Flow (lifecycle) imageId

2.4 Pre-PID (Onboarding — no progservId exists yet)

# Endpoint Method Purpose Input from UI
9 /api/v1/timeline/source-tracking/{zendeskId} GET Pre-PID onboarding timeline zendeskId

3. Internal Flow (How BFF Processes Each Request)

3.1 The Engine (same for ALL endpoints)

UI sends context ID (progservId / mappableId / tmsId / imageId / zendeskId)
    │
    ▼
Step 1: RESOLVE — map context ID → correlationId(s)
    │   Source: ct_context_map DDB
    │   Example: progservId "199364" → ["Linear-CNN-2026-05-30T11:15:00", "Linear-CNN-2026-05-30T11:00:00", ...]
    │
    ▼
Step 2: QUERY — fetch all raw events for correlationId(s)
    │   Source: audit_event DDB
    │   PK = "EVENT#{correlationId}#0"
    │   Returns: ALL events from ALL systems for that run
    │
    ▼
Step 3: ENRICH — apply display config to each event
    │   Source: ct_stage_classification DDB (cached 5 min)
    │   Lookup key: PK={system}, SK={eventName}
    │   Applies: displayLabel, statusMapping, depth, lane, order, functionalStep, groupKey, iconHint, detailTemplate
    │   Unknown events → fall to __DEFAULT__ entry (still visible, just raw)
    │
    ▼
Step 4: GROUP — organize into lanes + milestones
    │   Lane grouping: events grouped by config.lane field
    │   Milestone strip: derived from config.functionalStep field
    │   Composite cards: events with same groupKey within 5s → collapse
    │
    ▼
Step 5: RETURN — assembled JSON response to UI
    │   UI renders exactly what it receives — no transformations

3.2 Resolution Strategies (per endpoint)

Endpoint ID from UI Resolution path
Channel runs progservId ct_context_map: PK=PROGSERV#{id}, SK begins_with JOB# → list of correlationIds
Channel run detail correlationId Direct — already resolved (came from run list response)
Catalog runs progservId Same as channel (different events come back — UDIS systems)
Mappable (by ID) mappableId ct_context_map: entity-index GSI → progservId → correlationIds
Mappable (by remoteId+scheme) remoteId + schemeId New NDS API → mappableId → then same as above
Program tmsId ct_context_map: entity-index GSI → progservId → correlationIds
Image imageId ct_context_map: entity-index GSI → progservId → correlationIds
Source Tracking zendeskId Zendesk API / CRM events (onboarding not yet in ct_context_map)

4. Config-Driven Architecture (ct_stage_classification)

4.1 The Config Table

One DDB table serves BOTH Lambda (metrics) AND BFF (display). Key: PK={system}, SK={eventName}.

Example entry:

{
  "PK": "SIP",
  "SK": "Scheduler",
  "pipeline_stage": "INGESTION",
  "subStage": "SCHEDULE",
  "stageOrder": 1,
  "displayLabel": "Feed fetch initiated by scheduler",
  "functionalStep": "Fetching the feed",
  "depth": 2,
  "order": 10,
  "lane": "ingestion",
  "groupKey": "sip_fetch",
  "detailTemplate": "run ID: ${correlationId} · source: ${attributes.feedName}",
  "statusMapping": {"SUCCESS": "Done", "FAILURE": "Failed", "IN_PROGRESS": "In flight"},
  "iconHint": "scheduler",
  "enabled": true
}

4.2 What Each Field Controls

Field What it does Who decides the value
displayLabel Text shown on UI for this event Config (never hardcoded)
statusMapping Translates raw status to display text Config (e.g., SUCCESS → "Done")
functionalStep Which milestone this event belongs to Config
depth Importance: 1=milestone, 2=sub-step, 3=detail Config
lane Which parallel track (ingestion/mapping/enrichment/schedule) Config
order Sort position in milestone strip Config
groupKey Events with same key within 5s → composite card Config
detailTemplate Detail line with ${field} interpolation Config
iconHint Which icon UI renders Config
enabled Show or hide this event type Config

4.3 The Rule: Nothing Hardcoded

NOT this (hardcoded):
    if (status == "SUCCESS") return "Done";
    if (system == "SIP") lane = "ingestion";

YES this (config-driven):
    displayStatus = config.getStatusMapping().getOrDefault(event.getStatus(), event.getStatus());
    lane = config.getLane();

The Java code is a generic engine that reads config and applies it. Adding a new system, changing a label, or adding a new lane = DDB row change, not code change.

4.4 Default Entry (Catch-All)

{
  "PK": "__DEFAULT__",
  "SK": "__DEFAULT__",
  "displayLabel": "${eventName}",
  "depth": 3,
  "lane": null,
  "statusMapping": {"SUCCESS": "Done", "FAILURE": "Failed", "IN_PROGRESS": "Pending"}
}

Unknown events: visible at depth=3, raw eventName as label, in "main" lane. No crash.


5. Data Sources

What we need Source Access Pattern Latency
Correlation ID resolution ct_context_map DDB PK=PROGSERV#{id}, SK begins_with JOB# < 5ms
Raw audit events audit_event DDB PK=EVENT#{correlationId}#0 < 5ms
Display config ct_stage_classification DDB Full scan, cached 5 min < 1ms (cached)
Channel/Catalog name Source Service API (NDS) GET /sources/{progservId} 200-500ms (cached 1hr)
Mappable ID resolution New NDS API (to be built) GET /mappables?remoteId=&schemeId= TBD
Zendesk ticket status Zendesk API GET /tickets/{zendeskId} 200-500ms

Infrastructure (already set up):


6. Response Shapes

6.1 Run List Response (Endpoint 1 — Channel)

{
  "openRuns": [
    {
      "correlationId": "Linear-CNN-2026-05-28T14:00:00",
      "status": "Stuck",
      "started": "2026-05-28T14:00:00Z",
      "stuckSince": "2026-05-29T14:00:00Z",
      "lastStep": "Mapping",
      "stuckReason": "3 mappables paused on Allocation Engine",
      "impactedScheduleDays": ["2026-06-01", "2026-06-04"]
    }
  ],
  "runs": [
    {
      "correlationId": "Linear-CNN-2026-05-30T11:15:00",
      "status": "Successful",
      "started": "2026-05-30T11:15:00Z",
      "finished": "2026-05-30T11:23:48Z",
      "duration": "8m 48s",
      "source": "HBO-Feed-A",
      "summary": {"scheduleDays": 5, "slots": 72, "mappables": 68, "paused": 3}
    }
  ],
  "total": 142,
  "hasMore": true
}

6.2 Run Detail Response (Endpoint 2 — Channel)

{
  "run": {
    "correlationId": "Linear-CNN-2026-05-30T11:15:00",
    "progservId": "199364",
    "status": "Successful",
    "started": "2026-05-30T11:15:00Z",
    "finished": "2026-05-30T11:23:48Z",
    "duration": "8m 48s",
    "source": "HBO-Feed-A"
  },
  "milestoneStrip": [
    {"step": "Fetching the feed", "status": "done", "timestamp": "2026-05-30T11:15:32Z", "order": 1},
    {"step": "Parsing the feed", "status": "done", "timestamp": "2026-05-30T11:18:11Z", "order": 2},
    {"step": "IDM Setup", "status": "done", "timestamp": "2026-05-30T11:21:04Z", "order": 3, "duration": "2m 53s"},
    {"step": "Delivered to NED", "status": "done", "timestamp": "2026-05-30T11:21:08Z", "order": 4, "duration": "4s"},
    {"step": "Ingestion of Mappables", "status": "done", "timestamp": "2026-05-30T11:23:48Z", "order": 5, "duration": "2m 40s", "detail": "68 mappables"},
    {"step": "Mapping", "status": "in_progress", "timestamp": "2026-05-30T11:45:12Z", "order": 6, "duration": "21m 24s", "detail": "65/68 mapped"},
    {"step": "Handoff", "status": "pending", "timestamp": null, "order": 7}
  ],
  "scheduleDays": [
    {"date": "2026-05-28", "dayOfWeek": "THU", "slotsInBatch": 14, "mappables": 14, "unmapped": 0},
    {"date": "2026-05-31", "dayOfWeek": "SUN", "slotsInBatch": 16, "mappables": 15, "unmapped": 3}
  ],
  "summary": {"scheduleDays": 5, "slots": 72, "mappables": 68, "paused": 3},
  "dataPoints": {
    "channelName": "CNN",
    "channelType": "LINEAR",
    "feedName": "HBO-Feed-A"
  },
  "meta": {
    "total": 24,
    "page": 1,
    "size": 20,
    "hasMore": true,
    "distinctSystems": ["SIP", "Relay", "NED-NDL", "Starlight", "Automatch Consumer"],
    "distinctStages": ["INGESTION", "MAPPING"]
  }
}

6.3 Mappable Flow Response (Endpoint 5)

{
  "entity": {
    "mappableId": "8c41ffc825aeba3ad95f519a6cb86188",
    "title": "CNN Newsroom · 06:00 slot",
    "remoteId": "CNN-EP-2026-0530-0600",
    "scheme": "EPGS US Linear",
    "mappedTmsId": null,
    "currentStatus": "Automatch pending",
    "scheduleDate": "2026-05-31",
    "slotTime": "06:00 - 06:30",
    "currentOwnerSystem": "Automatch Consumer",
    "timeInCurrentStage": "3h 42m"
  },
  "stageStrip": [
    {"name": "Ingestion", "status": "done"},
    {"name": "Mapping", "status": "current"},
    {"name": "Enrichment", "status": "pending"},
    {"name": "Schedule Day", "status": "pending"}
  ],
  "lastError": {
    "code": "AUTOMATCH_CONFIDENCE_BELOW_THRESHOLD",
    "system": "Starlight",
    "timestamp": "2026-05-30T11:23:14Z",
    "detail": "score: 0.42 · threshold: 0.85"
  },
  "events": [
    {
      "timestamp": "2026-05-30T11:23:14Z",
      "system": "Starlight",
      "displayLabel": "Automatch attempted · routed for manual allocation",
      "detail": "corrId: corr-8c41ff-2026-05-30 · retry 0/3",
      "status": "Pending",
      "depth": 1,
      "isComposite": true,
      "childCount": 3,
      "children": [...]
    }
  ],
  "meta": {
    "total": 12,
    "distinctSystems": ["SIP", "Starlight", "Automatch Consumer", "NED-NDL"]
  }
}

7. Component Design

7.1 Classes to Create

ct-bff-timeline/src/main/java/com/ct/bff/timeline/
├── TimelineController.java              ← All 9 endpoints
├── RunListService.java                  ← Endpoints 1, 3 (run list)
├── RunDetailService.java                ← Endpoints 2, 4 (run detail)
├── EntityFlowService.java              ← Endpoints 5-8 (mappable/program/image)
├── SourceTrackingService.java          ← Endpoint 9 (pre-PID)
├── LaneGroupingService.java            ← Groups enriched cards into lanes
├── MilestoneStripDeriver.java          ← Derives milestone progress bar
├── DataPointResolutionService.java     ← Config-driven supplementary data
├── RunSummaryAssembler.java            ← Builds run metadata
└── model/
    ├── RunListResponse.java
    ├── RunDetailResponse.java
    ├── EntityFlowResponse.java
    ├── Lane.java
    ├── MilestoneStep.java
    └── RunSummary.java

7.2 Breadcrumb Engine Integration

Timeline calls breadcrumb via HTTP interface bean (internal, same JVM):

BreadcrumbResponse response = breadcrumbApi.query(
    correlationIds, entityFilterType, entityFilterId,
    depth, systems, statuses, from, to, page, size
);

Breadcrumb handles: DDB query + config lookup + enrichment + composite grouping + filtering + pagination.

Timeline adds on top: lane grouping, milestone strip, run summary, data points.


8. Lane Grouping

How lanes work

Each event's lane comes from ct_stage_classification config. Events are grouped by lane value.

Lane ID Display Name Systems that publish to this lane
ingestion Ingestion SIP, EPGS, Relay, NDL
mapping Mapping Starlight, Automatch, Crossmapper, myWork
enrichment Enrichment Program Gaps, MediaHub, Auto-Translation
schedule Schedule Day Rummage, TVPOP, CDC
request Request Zendesk, CRM
main Primary Anything without lane config (default)

Lane grouping logic

public List<Lane> groupIntoLanes(List<BreadcrumbCard> cards) {
    // Group by card.getLane() (null → "main")
    // Sort events within each lane by timestamp ASC
    // Sort lanes by earliest event timestamp
    // Lane with latest event IN_PROGRESS → endTime = null
}

9. Error Handling

Scenario Behavior
correlationId not found Return 404
ct_stage_classification empty All events in "main" lane with raw labels
System API timeout (>500ms) Return fallback value; response not blocked
Unknown lane value Lane created dynamically
New NDS API not ready Mappable search by remoteId returns 503 with message
1000+ events Pagination limits response

10. Performance

Target: <2s p95

Step Latency
ct_context_map resolution < 5ms
audit_event query (500 events) < 50ms
Config cache lookup < 1ms
Enrichment (500 events) < 20ms
Lane grouping + milestone < 10ms
Data points (cache hit) < 5ms
Data points (cache miss, API) 200-500ms
Total (warm) < 100ms
Total (cold) < 600ms

11. Dependencies

Dependency Owner Status
ct_context_map DDB read access DevOps / Aravinda Done
audit_event DDB read access DevOps / Aravinda Done
ct_stage_classification config entries Aravinda To be seeded
Source Service API (channel name) NDS team Available (NDS exists)
New NDS API (remoteId + scheme → mappableId) NDS team TO BE REQUESTED
Zendesk API access External SaaS API key needed
EKS + CI/CD pipeline DevOps Done

12. Open Questions

# Question For Status
1 New NDS API for remoteId + scheme → mappableId NDS team Need to request
2 Per-lane pagination needed for v1? Abhishek Not decided
3 Zendesk API — do we have API key? DevOps Unknown
4 Catalog UDIS run detail — does it include threshold gate data? Aravinda Confirm from events
5 Who populates ct_stage_classification for UDIS/OVD events? Aravinda To discuss

13. References