# Hummingbird Data Flow

LLMS index: [llms.txt](/llms.txt) | Full content: [llms-full.txt](/llms-full.txt)

---

Overview of all data sources, what is stored where, and how data moves between
systems.

---

## Data sources

| Source                                                   | What it provides                                                                       |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| **Jira REST API**                                        | CVE tracker ticket fields (status, timestamps, labels, custom fields)                  |
| **Jira whiteboard** (`customfield_10841`)                | Per-ticket timestamp cache written back by the analysis tool                           |
| **Red Hat Pulp** (`packages.redhat.com`)                 | SRPM/RPM publish timestamps for Hummingbird repos                                      |
| **Hummingbird container catalog API**                    | Image rebuild history (when a package first appeared in a rebuilt image)               |
| **Red Hat CSAF VEX feed** (`security.access.redhat.com`) | Advisory fix/not-affected status per CVE                                               |
| **OSIDB** (`osidb.prodsec.redhat.com`)                   | Subpackage-level affectedness; flaw `created_dt` and hummingbird-1 affect `created_dt` |
| **NVD / CVE list**                                       | CVE publish dates, product/version data                                                |
| **GitHub / GitLab**                                      | Upstream fix commit / PR / release dates                                               |
| **Fedora updates**                                       | Fedora update availability timestamps                                                  |
| **Konflux SNS/SQS events**                               | Pipeline run, snapshot, release, MR, push events                                       |

---

## PostgreSQL databases

There are two Postgres databases.

### 1. `hummingbird-status` — Konflux pipeline events

Fed by SNS/SQS events via the hummingbird-status ingestor.

| Table                   | Primary key           | What is stored                                          |
| ----------------------- | --------------------- | ------------------------------------------------------- |
| `gitlab_pushes`         | `(sha, repo, ref)`    | Git push events — sha, branch, commit list              |
| `components`            | `name`                | Konflux component definitions and git context           |
| `pipelineruns`          | `name`                | Build/test PLR outcomes, status, start/completion times |
| `snapshots`             | `name`                | Multi-component snapshots, source PLR, sha              |
| `releases`              | `name`                | Release outcomes, images, LLM analysis text             |
| `gitlab_merge_requests` | `(project, iid)`      | Current MR state, merge commit sha                      |
| `gitlab_mr_versions`    | `(project, iid, sha)` | Per-commit history of each MR head                      |

### 2. `hummingbird-dashboard` — CVE lifecycle + dashboard overlays

| Table                                          | Primary key / unique            | What is stored                                                                                                                                       |
| ---------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cve_ticket_events`                            | `(ticket_key, event_type)`      | **One row per lifecycle milestone per ticket.** `occurred_at` = timestamp; `metadata` = JSONB (see below). The source of all R-Time computations.    |
| `cve_analysis_log`                             | `id`                            | Each analysis tool run: timestamp, ticket count, log output                                                                                          |
| `cve_ticket_claims`                            | `ticket_key`                    | Claim/lock for deduplicating concurrent analysis runs                                                                                                |
| `cve_ticket_blame`                             | `(ticket_key, category)` unique | Human-assigned R-Time blame categories (`hummingbird`, `prodsec`, `konflux`, `testing`, `pulp`, `other`); a ticket may have multiple                 |
| `dashboard_settings`                           | `key`                           | Runtime flags: auto-rerun enabled, analysis enabled, etc.                                                                                            |
| `auto_rerun_log`                               | `id`                            | History of automatic retrigger attempts                                                                                                              |
| `blocked_error_patterns`                       | `id`                            | Regex patterns that suppress auto-rerun                                                                                                              |
| `blocked_snapshots`                            | `snapshot_name`                 | Manually blocked snapshots                                                                                                                           |
| `analysis_log` / `analysis_costs`              | `id`                            | LLM failure analysis runs and token costs                                                                                                            |
| `blocked_push_builds` / `push_build_rerun_log` | `id`                            | Push build blocking and retry history                                                                                                                |
| `package_lifecycle`                            | `(package, event_type)`         | Package-scoped lifecycle milestones (e.g. `rpm_first_published`). Distinct from `cve_ticket_events` — one row per package/milestone, not per ticket. |

#### `cve_ticket_events` lifecycle milestone `event_type` values

These are the canonical names after the HUM-5918 migration:

| `event_type`                | Meaning                                                                                               |
| --------------------------- | ----------------------------------------------------------------------------------------------------- |
| `cve_published`             | CVE published date (NVD / CVE list)                                                                   |
| `osidb_flaw_created`        | OSIDB flaw `created_dt` (when ProdSec ingested the CVE)                                               |
| `osidb_affect_created`      | hummingbird-1 affect `created_dt` for this ticket's `pscomponent` (when a HUM tracker could be filed) |
| `hum_ticket_created`        | HUM Jira ticket creation date                                                                         |
| `hum_ticket_closed`         | HUM Jira ticket resolution date                                                                       |
| `upstream_fix_merged`       | Upstream fix commit / release merged                                                                  |
| `fedora_update_available`   | Fedora update containing the fix became available                                                     |
| `rpm_fix_published_to_pulp` | Fix RPM published to Hummingbird Pulp repo                                                            |
| `image_rebuilt_on_quay`     | Hummingbird container image rebuilt with the fix                                                      |
| `vex_resolved`              | Red Hat CSAF VEX advisory confirmed the resolution (HUM-5843)                                         |

`rpm_fix_published_to_pulp` and `image_rebuilt_on_quay` are **mutable** — they
are updated when a newer delivery event supersedes the previous one. All other
event types are **write-once**: once set, `occurred_at` is never overwritten.

#### `cve_ticket_events.metadata` JSONB fields

Each row carries a `metadata` blob that reflects the ticket's state at the time
of the most recent analysis run:

| Field                  | Source                   | Description                                                                                                                                                               |
| ---------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `computed_resolution`  | analysis tool            | Hummingbird's computed fix status                                                                                                                                         |
| `jira_status`          | Jira `status` field      | Current Jira issue status                                                                                                                                                 |
| `labels`               | Jira `labels` field      | All labels on the ticket                                                                                                                                                  |
| `fixed_in_build`       | Jira `customfield_10578` | SRPM set manually in the "Fixed in Build" field                                                                                                                           |
| `detected_fixed_build` | Pulp / SRPM detection    | SRPM filename auto-detected from Pulp repodata                                                                                                                            |
| `vex_status`           | Red Hat VEX feed         | `fixed` / `known_not_affected` / …                                                                                                                                        |
| `vex_match_state`      | reconciliation logic     | `matched` / `pending` / `mismatch`                                                                                                                                        |
| `vex_resolved`         | VEX reconciliation       | Timestamp when VEX first agreed with Jira resolution                                                                                                                      |
| `catalog_image_source` | collector catalog map    | `true` when the package is a catalog image SBOM source (R-Time delivery is image publish); `false` means RPM publish. Missing on old rows: dashboard still requires image |

---

## Jira whiteboard (`customfield_10841`)

The CVE analysis tool uses the Jira whiteboard field as a **per-ticket
timestamp cache**. This is being phased out in favour of Postgres as the
durable store (HUM-5917), but is still the source for a subset of fields.

The whiteboard holds compact JSON. The relevant sub-key is `cve_cycle`:

| `cve_cycle` key | Meaning                  | Mutable?                        |
| --------------- | ------------------------ | ------------------------------- |
| `cve_published` | CVE publish date         | No — write-once                 |
| `jira_created`  | HUM ticket creation date | No — write-once                 |
| `jira_closed`   | HUM ticket close date    | No — disabled (255-char limit)  |
| `upstream_fix`  | Upstream fix timestamp   | No — write-once                 |
| `fedora_fix`    | Fedora update timestamp  | No — write-once                 |
| `hb_rpm_fix`    | RPM published to Pulp    | **Yes** — re-evaluated each run |
| `hb_image_fix`  | Image rebuilt on Quay    | **Yes** — re-evaluated each run |

The whiteboard is read and written only by the **CVE analysis tool**. The
dashboard never reads it directly; its authoritative source is always
`cve_ticket_events`.

---

## Jira fields read by the CVE analysis tool

On each run the analysis tool fetches every open HUM CVE tracker ticket and
reads the following fields from the Jira REST API:

| Jira field / custom field                | Purpose                                          |
| ---------------------------------------- | ------------------------------------------------ |
| `summary`                                | Ticket title / package detection                 |
| `status`, `resolution`, `resolutiondate` | Ticket lifecycle state                           |
| `created`                                | Jira ticket creation date → `hum_ticket_created` |
| `description`, `comment`                 | CVE ID extraction, fix evidence                  |
| `labels`                                 | `cve-next-release`, fix-in-progress, etc.        |
| `security`                               | Embargo level                                    |
| `assignee`                               | Ticket owner                                     |
| `customfield_10578` (Fixed in Build)     | Manually set SRPM identifying the fix build      |
| `customfield_10841` (Whiteboard)         | Cached `cve_cycle` timestamps (read + write)     |
| `customfield_10667` (CVE ID)             | Structured CVE identifiers                       |
| `customfield_10860` (Embargo Status)     | Embargo flag                                     |
| `customfield_10020` (Sprint)             | Sprint membership                                |
| `customfield_10014` (Epic Link)          | Parent epic                                      |

---

## Data flow

```text
┌─────────────────────────────────────────────────────────────────┐
│  External data sources                                          │
│                                                                 │
│  Jira REST API ─────────────────────────────────────┐           │
│  NVD / CVE list ────────────────────────────────────┤           │
│  GitHub / GitLab (upstream fix commits) ────────────┤           │
│  Fedora updates ────────────────────────────────────┤           │
│  Red Hat Pulp (RPM publish times) ─────────────────►│           │
│  Hummingbird catalog API (image rebuild times) ─────┤           │
│  Red Hat CSAF VEX feed ─────────────────────────────┤           │
│  OSIDB (subpackage affectedness) ───────────────────┘           │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│  CVE analysis tool (cron, hummingbird-cve-analysis)              │
│                                                                  │
│  • Reads Jira ticket fields + whiteboard cache                   │
│  • Resolves fix SRPM from Pulp repodata                          │
│  • Resolves image rebuild timestamp from catalog API             │
│  • Checks upstream PR/release dates (GitHub/GitLab)              │
│  • Fetches CVE publish date (NVD/CVE list)                       │
│  • Reconciles VEX status (CSAF feed)                             │
│  • Checks subpackage affectedness (OSIDB)                        │
│                                                                  │
│  Writes back to:                                                 │
│  ├── Jira whiteboard (cve_cycle timestamp cache)                 │
│  ├── Jira labels / Fixed-in-Build field                          │
│  ├── Jira comments                                               │
│  └── dashboard DB → cve_ticket_events (one row per milestone)    │
│                      cve_analysis_log (run record)               │
└──────────────────────────────┬───────────────────────────────────┘
                               │
           ┌───────────────────┴───────────────────┐
           │                                       │
           ▼                                       ▼
┌──────────────────────────┐      ┌───────────────────────────────┐
│  Jira whiteboard         │      │  dashboard DB                 │
│  (per-ticket JSON cache) │      │  cve_ticket_events table      │
│  cve_cycle timestamps    │      │  one row per (ticket,         │
│  ← being phased out      │      │    event_type) milestone      │
│    (HUM-5917)            │      └───────────────┬───────────────┘
└──────────────────────────┘                      │
                                                  ▼
                               ┌──────────────────────────────────┐
                               │  hummingbird-dashboard API       │
                               │                                  │
                               │  Pivots cve_ticket_events rows   │
                               │  into per-ticket dicts, computes:│
                               │  • entry-level R-Time fields     │
                               │    (HUM-5920)                    │
                               │  • duration legs inside stages   │
                               │    (HUM-5921)                    │
                               │                                  │
                               │  Serves JSON + Jinja templates   │
                               └──────────────────────────────────┘


```

```mermaid
flowchart LR
    EVENTS[GitLab pushes, pipelines, jobs, MRs<br/>Kubernetes PipelineRuns, TaskRuns, Snapshots, Releases]
    SNS[(SNS topic)]
    ARCHIVER[S3 archiver]
    S3[(Deterministic archive)]
    STATUS[hummingbird-status ingestor]
    STATUS_DB[(hummingbird-status DB)]
    DASHBOARD[Dashboard API]
    TIMELINE[Timeline queue and worker]
    TIMELINE_DB[(pipeline_timeline DB)]

    EVENTS --> SNS
    SNS --> ARCHIVER --> S3
    SNS --> STATUS --> STATUS_DB --> DASHBOARD
    SNS -. future .-> TIMELINE --> TIMELINE_DB
```

---

## API field names

The dashboard API exposes these computed fields per R-Time entry:

### Entry-level fields

| API field                       | Meaning                                                                                                                                                                                                            |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cve_published_at`              | Start of R-Time: earlier of CVE published and HUM created (Include CVE-HUM), or HUM created (Exclude)                                                                                                              |
| `fix_delivered_at`              | Done timestamp: latest of delivery and Jira close (Exclude VEX), plus VEX when Include VEX is on; unset until the required gates exist. Delivery is image publish for catalog image sources, RPM publish otherwise |
| `cve_to_delivery_hours`         | Completed R-Time only (`cve_published_at` → `fix_delivered_at`); `None` while still accumulating                                                                                                                   |
| `cve_to_hum_created_hours`      | Filing lag after NVD: `max(HUM created − NVD, 0)`; `0.0` when HUM is first                                                                                                                                         |
| `advisory_to_vex_hours`         | Completed ADV-VEX only (`hum_ticket_closed` → `vex_resolved`); `None` until VEX exists                                                                                                                             |
| `display_advisory_to_vex_hours` | ADV-VEX hours, or elapsed-to-now when the VEX feed has not updated yet                                                                                                                                             |
| `fix_before_cve_published`      | True when the delivery timestamp predates notification (informational)                                                                                                                                             |
| `hum_ticket_open`               | True until the selected done gates exist                                                                                                                                                                           |
| `deferred_to_next_release`      | True for `cve-next-release` tickets                                                                                                                                                                                |
| `display_duration_hours`        | R-Time hours, or elapsed-to-now when the ticket is not yet done                                                                                                                                                    |

### Duration legs (`stages` dict)

| API field                            | Interval                                                |
| ------------------------------------ | ------------------------------------------------------- |
| `cve_published_to_osidb_flaw`        | CVE publish → OSIDB flaw created                        |
| `osidb_flaw_to_osidb_affect`         | OSIDB flaw created → hummingbird-1 affect created       |
| `osidb_affect_to_hum_created`        | hummingbird-1 affect created → HUM ticket opened        |
| `cve_published_to_hum_created`       | CVE publish → HUM ticket opened                         |
| `cve_published_to_upstream_fix`      | CVE publish → upstream fix merged                       |
| `hum_created_to_upstream_fix`        | HUM ticket → upstream fix merged                        |
| `upstream_fix_to_fedora_update`      | Upstream fix → Fedora update available                  |
| `fedora_update_to_rpm_fix_published` | Fedora update → fix RPM in Pulp                         |
| `upstream_fix_to_rpm_fix_published`  | Upstream fix → fix RPM in Pulp                          |
| `hum_created_to_rpm_fix_published`   | HUM ticket → fix RPM in Pulp                            |
| `rpm_fix_published_to_image_rebuilt` | Fix RPM in Pulp → image rebuilt on Quay                 |
| `image_rebuilt_to_hum_closed`        | Image rebuild → HUM ticket closed                       |
| `hum_closed_to_vex_resolved`         | HUM ticket closed (advisory MR merge) → VEX feed update |

### Aggregate stats

| API field                            | Meaning                                                    |
| ------------------------------------ | ---------------------------------------------------------- |
| `avg_hum_created_to_rpm_fix_hours`   | Mean HUM ticket → RPM fix (skips missing legs)             |
| `avg_rpm_fix_to_image_rebuilt_hours` | Mean RPM publish → image rebuild                           |
| `avg_advisory_to_vex_hours`          | Mean Done-Errata close → VEX feed (completed ADV-VEX only) |

---

## Package lifecycle data

`rpm_first_published` is the earliest timestamp at which any SRPM for a given
package appeared in the Hummingbird Pulp repo. It is **package-scoped** — one
row per package — as opposed to `cve_ticket_events` which is ticket-scoped.

### Collection

```bash
scripts/collect_rpm_first_published --prod --rpms-repo /path/to/rpms
```

Flow:

1. Loads the package list from `rpms_repo.load_package_map_from_metadata()`.
2. For each package: calls `pulp.fetch_earliest_srpm_time()`, which browses
   `packages.redhat.com/.../source/Packages/{letter}/`, parses the HTML
   listing already used by the analysis tool, and returns the earliest upload
   timestamp across all SRPMs for that package.
3. POSTs all results in one request to `/api/cve-import` (same endpoint used
   for `cve_ticket_events` sync). The dashboard upserts with
   `COALESCE(LEAST(existing, incoming), existing, incoming)` — re-runs only
   update `occurred_at` if the incoming timestamp is earlier, and NULLs are
   never stored over a real value.

### Surfaced in `dump_lifecycle`

```bash
scripts/dump_lifecycle --prod HUM-1234
```

After fetching ticket milestones from `cve_ticket_events`, the script resolves
the package name from those rows and makes a second call to
`/api/cve-export?section=package_lifecycle` to fetch `rpm_first_published` for
that package. It is shown at the bottom of the text output and under
`rpm_first_published` in the JSON output.
