Draft: feat(orbit-cli): read graph-status from the GitLab GraphQL API
What does this MR do and why?
orbit graph-status now reads the GitLab GraphQL API, not REST graph_status. After this change, the CLI no longer calls REST graph_status, so GitLab can remove that endpoint. Design: snippet 6070846. Server side: !3034 (merged).
How it works
One query. The CLI sends one POST /api/graphql and reads the first orbit object that is not null.
query($fullPath: ID!) {
group(fullPath: $fullPath) { orbit { ...status } }
project(fullPath: $fullPath) { orbit { ...status } }
}
fragment status on OrbitNamespace {
indexing { state areas { name state } projects { indexed totalKnown gaps } }
itemCounts { domains { name entities { name count } } }
}ID flags. --namespace-id and --project-id make one extra REST GET to read the full path. Then the CLI sends the same query.
Output. The terminal summary shows one row per indexing area, then Projects. JSON output is the GraphQL orbit object. --response-format llm prints that object as TOON.
BREAKING CHANGE: the JSON and llm output now have the GraphQL orbit shape (indexing, itemCounts), not the REST graph_status body.
Exit codes.
| Exit | When |
|---|---|
| 0 | The status was read. |
| 1 | A GraphQL error. If only fields under orbit failed, the CLI prints the data that resolved and the errors on stderr. |
| 2 | No such group or project. Or no indexing, and the status endpoint says Orbit is not available for the user. |
| 4 | No indexing, and the status endpoint says the user has Orbit, so Orbit is off for the scope or the role is below Reporter. Also an orbit that is null for the token. |
The status endpoint always returns 200, so the CLI reads its body and does not use the HTTP code.
Related Issues
Related to #263
- Duo Agent Platform tokens need gitlab-org/gitlab!264673 (merged). Release the CLI only after that change is deployed on gitlab.com.
- GitLab MR 1 of the same stack raises the item count limit to 30 per minute per user.
Testing
cargo nextest run -p orbit-cli: 267/267. Strict clippy (--all-features -D warnings) passes.- Tests cover a full path, the origin remote default, both ID flags, TOON output and a missing scope.
- They also cover both status-body branches, a null
orbit, partial data with a field error, and a top-level error. - vm-2 e2e: the CLI output matches REST
graph_statusfor 4 token types and 3 scope flags. The comparison checks 31 entity counts, project coverage and the state.
| Token | --full-path |
--namespace-id |
--project-id |
|---|---|---|---|
PAT api |
equal | equal | equal |
PAT read_api |
equal | equal | equal |
OAuth ai_workflows + mcp |
equal | equal | equal |
| glab credential helper | equal | equal | equal |
Terminal summary, before and after, for the same group:
before: SDLC data indexed · Code indexed · Projects 1 of 1 indexed
after: agents, ci, code_review, container_registry, core, packages, plan,
security, source_code: indexed · Projects 1 of 1 indexedThe "In the graph" card and the next-step line do not change.
Performance Analysis
One GraphQL request replaces one REST request. The ID flags add one small GET.
Follow-up: GitLab has no error code for a GraphQL rate limit, so a rate limit exits 1 with the message on stderr, not 5.
- This merge request does not introduce any performance regression. If a performance regression is expected, explain why.
Agent context: long-form analysis, file-by-file walkthroughs, profiler output, alternatives considered
- REST and GraphQL calls share one private
post_json. It keeps the same auth header and base URL. The ID lookup uses the existingget_bytes. - A field error has a
pathwithorbitat index 1. Errors without such a path print nothing and exit 1. The CLI does not match message text. - The missing-status check reuses
status::select_status_outputanderror::build_not_found_error. - Nullable GraphQL fields are
Optiontypes. A null count or state reads as 0 or unknown. - No consumer reads the old CLI JSON keys in this repo. The e2e robot suite calls REST
graph_statusdirectly; a follow-up MR removes it withGetGraphStatus.
%{all_commits}