feat(api): add ListStageSummaries and WatchStageSummaries RPCs#6159
Draft
jacobboykin wants to merge 10 commits intoakuity:mainfrom
Draft
feat(api): add ListStageSummaries and WatchStageSummaries RPCs#6159jacobboykin wants to merge 10 commits intoakuity:mainfrom
jacobboykin wants to merge 10 commits intoakuity:mainfrom
Conversation
Adds a lightweight Stage projection for list and graph views that need
metadata and current state for many Stages at once but do not need the
full Stage CR. The summary omits FreightHistory entries beyond the
current FreightCollection, PromotionTemplate step configuration, and
Verification configuration. Use GetStage to retrieve the full Stage
resource when detail fields are needed.
Both endpoints are available in ConnectRPC and REST
(GET /v1beta1/projects/{project}/stage-summaries, including
?watch=true for SSE). Requests accept an optional freightOrigins
filter and a resourceVersion for the standard list-then-watch pattern.
Signed-off-by: Jacob Boykin <boykinmusic@gmail.com>
Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
✅ Deploy Preview for docs-kargo-io ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6159 +/- ##
==========================================
+ Coverage 57.67% 57.75% +0.08%
==========================================
Files 474 476 +2
Lines 40506 40628 +122
==========================================
+ Hits 23362 23465 +103
- Misses 15749 15762 +13
- Partials 1395 1401 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This comment was marked as outdated.
This comment was marked as outdated.
Buf lint enforces lower_snake_case for proto field names. Updated the new StageSummary / ListStageSummaries / WatchStageSummaries messages accordingly. Generated Go struct names (StageSummaries, FreightOrigins, etc.) and protojson wire format (camelCase via the json= tag) are unchanged. Swagger and TypeScript bindings regenerated to match. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
…geSummary
Adds a batch RPC that returns the raw health output blob for a specified
set of Stages in a project, and updates stageToSummary to always leave
Status.Health.Output nil in the summary response. Intended for clients
that use ListStageSummaries for the stage list and want to resolve
per-argocd-app health only for the Stages currently in viewport (React
Flow virtualizes to ~10-30 visible nodes at a time).
The raw health output is typically ~2 KB per Stage and is the single
largest remaining field in StageSummary. Moving it behind a lazy fetch
drops the summary to roughly 1-2 KB per stage, reducing both wire
transfer and the heap footprint of the cached list.
The new endpoint is available in ConnectRPC and REST
(GET /v1beta1/projects/{project}/stage-health-outputs?stageNames=a&stageNames=b).
Stages that do not exist or have no recorded health output are omitted
from the response map; the endpoint has best-effort semantics.
Signed-off-by: Jacob Boykin <boykinmusic@gmail.com>
Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
krancour
reviewed
Apr 24, 2026
Comment on lines
+105
to
+119
| // @id ListStageSummaries | ||
| // @Summary List Stage Summaries | ||
| // @Description List a lightweight projection of Stage resources from a | ||
| // @Description project's namespace. Intended for UI list and graph views that | ||
| // @Description need metadata and current state for many Stages at once but do | ||
| // @Description not need full FreightHistory, PromotionTemplate steps, or | ||
| // @Description Verification configuration. Use GetStage for detail fields. | ||
| // @Tags Core, Project-Level | ||
| // @Security BearerAuth | ||
| // @Produce json | ||
| // @Param project path string true "Project name" | ||
| // @Param freightOrigins query []string false "Warehouse name(s) to filter by" collectionFormat(multi) | ||
| // @Success 200 {object} svcv1alpha1.ListStageSummariesResponse | ||
| // @Router /v1beta1/projects/{project}/stage-summaries [get] | ||
| func (s *server) listStageSummaries(c *gin.Context) { |
Member
There was a problem hiding this comment.
Is there a defensible reason for this being a dedicated endpoint instead of just adding an optional summary=true query param to the existing endpoint, in which case, you'll just drop the fields that "summary" excludes?
Reconciles with akuity#6153's Warehouse-filter helper: drops the duplicate stageMatchesAnyWarehouse and warehouseNameSet helpers and routes the summary list/watch paths through the shared []string-based matcher in list_stages_v1alpha1.go, which also checks Origin.Kind explicitly. filterStagesByWarehouses normalizes with nonEmptyStrings so ?freightOrigins=&freightOrigins= still behaves as no filter. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
…y flag Replaces the dedicated ListStageSummaries / WatchStageSummaries RPCs and their StageSummary / StageSpecSummary / StageStatusSummary messages with a `summary` bool on ListStagesRequest / WatchStagesRequest. When set, heavy fields are stripped from each returned Stage in place: - status.freightHistory truncated to the current element (index 0) - spec.promotionTemplate.spec.steps[*].config cleared (kind/as/name kept) - status.health.output cleared (use GetStageHealthOutputs for lazy fetch) The UI continues to derive has-verification from `spec.verification != nil` and promotion-step-count from `len(spec.promotionTemplate.spec.steps)`, so no derived fields or dedicated projection types are needed. ListStagesResponse gains resource_version and WatchStagesRequest gains summary and resource_version so list-then-watch works across both full and summary modes. GetStageHealthOutputs is unchanged — it remains a separate batch RPC for lazy-loading argocd health blobs per viewport. Addresses review feedback asking why a dedicated endpoint was used instead of a flag on the existing ListStages/WatchStages pair. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
Promotes two helpers that were living in pkg/server to pkg/api/stage.go, alongside GetStage, ListFreightAvailableToStage, and the rest of the Stage-domain operations. Both are now exported and unit-tested: - StripStageForSummary: in-place projection that clears heavy fields (freightHistory[1..], promotionTemplate step configs, health.output) from a Stage. Previously a package-private helper in pkg/server/list_stages_v1alpha1.go. - ListStageHealthOutputs: given a project and a set of Stage names, returns map[name]raw-health-output. Previously inlined (twice — once for the ConnectRPC handler and once for the REST handler) in pkg/server/get_stage_health_outputs_v1alpha1.go, alongside a private uniqueNonEmptyStrings helper that is now folded into the exported function. The server handlers are now thin adapters that validate transport-level concerns (batch-size cap) and delegate domain work to pkg/api. This matches the convention akuity#6163 establishes for StageMatchesAnyWarehouse / ListStagesByWarehouses. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
…n/stage-summary-api Applies Kent's pkg/api Stage-domain consolidation ahead of its merge so this branch is ready to land cleanly once akuity#6163 is in main. Kent's PR promotes stageMatchesAnyWarehouse + listStagesByWarehouses from pkg/server into pkg/api (exported, unit-tested, options-struct shaped). Our REST listStages handler now calls api.ListStagesByWarehouses directly. Our ConnectRPC ListStages handler does its list inline so it can surface list.ResourceVersion for the list-then-watch response field; filtering still goes through api.StageMatchesAnyWarehouse to keep the matching logic in one place. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
… cache update - Remove the meta/v1/generated.proto import from service.proto; it was only used by the StageSummary messages that this PR replaced with a summary flag on ListStages. - The new required ListStagesResponse.resource_version field requires UI callers constructing the response shape to populate it. The pipeline watcher's cache-update path has no authoritative RV on hand (it's merging incremental Stage watch events), so it passes an empty string. The list-then-watch RV flow itself continues to be handled by ListStages's own response, not by this cache-update path. Signed-off-by: Jacob Boykin <boykinmusic@gmail.com> Signed-off-by: Jacob Boykin <jacob.boykin@akuity.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a lightweight Stage projection for clients rendering list and graph views of many Stages at once. Instead of a new endpoint, a
summaryflag is threaded through the existingListStagesandWatchStagesRPCs. When set, heavy fields are stripped from each returned Stage in place:status.freightHistorytruncated to the current element (index 0)spec.promotionTemplate.spec.steps[*].configcleared (kind/as/name kept)status.health.outputcleared — fetch lazily via the newGetStageHealthOutputsRPC for Stages currently in viewportThe UI continues to derive
hasVerificationfromspec.verification != nilandpromotionStepCountfromlen(spec.promotionTemplate.spec.steps)API
ListStages/WatchStagesgainsummary: boolListStagesResponseandWatchStagesRequestgainresource_versionfor list-then-watchGetStageHealthOutputs(project, stage_names[])RPC + REST endpoint for lazy per-Stage health output fetch (batch cap 1000)Structure
Reusable Stage-domain helpers are exported in
pkg/api/stage.goalongside the existingGetStage,ListFreightAvailableToStage, etc.:api.StripStageForSummary(*Stage)— in-place summary projectionapi.ListStageHealthOutputs(ctx, c, project, names)— batched health-output fetchServer handlers are thin adapters.
Measurements
Measured on a live deployment of this branch behind the real ingress with seeded production-shape status,
summary=truedelivers up to 13x gzipped wire reduction onListStages, ~5x reduction in JS heap retained per cached list (17.8 MB down to 3.7 MB), and ~3.5x fasterJSON.parse(20.5 ms down to 5.9 ms). A heavy real stage with 778 promotion steps shrinks 10x (170 KB to 17 KB) with step skeletons preserved so the UI can still derive step kind and count without falling back toGetStage.