Security Full Data Set - Backend
<!--IssueSummary start-->
<details>
<summary>
Everyone can contribute. [Help move this issue forward](https://handbook.gitlab.com/handbook/marketing/developer-relations/contributor-success/community-contributors-workflows/#contributor-links) while earning points, leveling up and collecting rewards.
</summary>
- [Label this issue](https://contributors.gitlab.com/manage-issue?action=label&projectId=278964&issueIid=596809)
- [Close this issue](https://contributors.gitlab.com/manage-issue?action=close&projectId=278964&issueIid=596809)
</details>
<!--IssueSummary end-->
## TL;DR
Remove the `MAX_FINDINGS_COUNT = 25` cap in the MR security report comparer and expose the full set of `added`/`fixed` findings per report type through cursor-paginated GraphQL connections on `ComparedSecurityReport`, so the new MR Reports tab is unblocked from the 25-finding truncation.
## Scope
### In Scope
- Add a feature flag for rollout
- Remove `MAX_FINDINGS_COUNT = 25` cap so the cache contains all findings per report type
- Convert `added` and `fixed` as independent cursor-paginated GraphQL connections on findingReportsComparer.report
- Provide accurate `count` per connection (supersedes the FE "25+" workaround)
- Validate Redis/ReactiveCaching payload behavior under realistic volumes
### Out of Scope
- FE widget changes and new Reports-tab page
- Severity-breakdown aggregation
- Moving pagination to DB/finder level — Option B, requires reworking ReactiveCaching; tracked separately on work_items/18313
- Ingestion / storage changes for findings
## Phase
WIP
## Problem Statement
The MR security widget currently caps `added` and `fixed` findings at 25 each via `MAX_FINDINGS_COUNT`. The FE shows `"25+" `when the cap is hit, and users cannot see or triage findings beyond that.
We need a paginated backend interface per report type, per diff side (`added`/`fixed`), without discarding the existing `ReactiveCaching` performance model.
## Proposed Approach
**Option A - In-memory pagination over cached data**
* Keep `ReactiveCaching` intact
* Remove the `MAX_FINDINGS_COUNT` cap; all findings enter the cache
* Convert `added` and `fixed` from `[FindingType]` to `FindingType.connection_type`
* Paginate `added` and `fixed` independently; both map 1:1 to existing cached arrays
* `count` comes for free via `CountableConnectionType`
* Example graphql query:
```graphql
query FindingReportsComparer(
$fullPath: ID!
$iid: String!
$reportType: ComparableSecurityReportType!
$scanMode: ScanModeEnum
$first: Int
$after: String
) {
project(fullPath: $fullPath) {
id
mergeRequest(iid: $iid) {
id
findingReportsComparer(reportType: $reportType, scanMode: $scanMode) {
status
report {
added(first: $first, after: $afterAdded) {
nodes {
...FindingFields
}
pageInfo {
hasNextPage
endCursor
}
count
}
fixed(first: $first, after: $afterFixed) {
nodes {
...FindingFields
}
pageInfo {
hasNextPage
endCursor
}
count
}
}
}
}
}
}
```
**Option B (DB-level pagination via reworked `ReactiveCaching`)**
This option is too much scope/risk for Q2 2026 target at the moment. (see: https://gitlab.com/gitlab-org/gitlab/-/work_items/594166#note_3253916782)
## Feature Flag
- [ ] Required
- [ ] **Flag name:** `security_mr_report_full_finding_set`
- [ ] **Default state:** Disabled
- [ ] **Rollout plan:** (WIP - Create rollout issue)
## Success Criteria
- [ ] Changes live behind feature flag
- [ ] `MAX_FINDINGS_COUNT` is removed from `SecurityFindingsReportsComparer` and the comparer returns the complete undismissed added/fixed set.
- [ ] `ComparedSecurityReport.added` and `ComparedSecurityReport.fixed` each accept independent `first`/`after` arguments and expose `nodes`, `pageInfo`, and `count`.
- [ ] Paginated query returns stable, complete results on an MR with \>100 findings per report type (no duplicates, no missing findings across pages).
- [ ] GraphQL reference docs regenerate with the new field shapes under the `experiment` marker.
## Dependencies
- **Blocked by:** none known.
- **Blocks:** ~frontend of https://gitlab.com/groups/gitlab-org/-/work_items/20760+
## Implementation Notes
_Example graphql query:_
```graphql
query FindingReportsComparer(
$fullPath: ID!
$iid: String!
$reportType: ComparableSecurityReportType!
$scanMode: ScanModeEnum
$first: Int
$after: String
) {
project(fullPath: $fullPath) {
id
mergeRequest(iid: $iid) {
id
findingReportsComparer(reportType: $reportType, scanMode: $scanMode) {
status
report {
added(first: $first, after: $afterAdded) {
nodes {
...FindingFields
}
pageInfo {
hasNextPage
endCursor
}
count
}
fixed(first: $first, after: $afterFixed) {
nodes {
...FindingFields
}
pageInfo {
hasNextPage
endCursor
}
count
}
}
}
}
}
}
```
## Open Questions
| Question | Owner | Status |
|----------|-------|--------|
| | | |
| | | |
| | | |
| | | |
## Resources
- **Parent epic:** https://gitlab.com/groups/gitlab-org/-/work_items/20760+s
- **Spike:** https://gitlab.com/gitlab-org/gitlab/-/work_items/594166+
* **Backend investigation :thread:** : https://gitlab.com/gitlab-org/gitlab/-/work_items/594166#note_3253916782+
- **Main Project epic:** https://gitlab.com/groups/gitlab-org/-/work_items/18313+
issue