Skip to content

[docs] Plan per-parent grouping for revision queries - #6569

Open
mmabrouk wants to merge 1 commit into
release/v0.114.8from
docs/revision-query-grouping
Open

[docs] Plan per-parent grouping for revision queries#6569
mmabrouk wants to merge 1 commit into
release/v0.114.8from
docs/revision-query-grouping

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Sep 5, 2026

Copy link
Copy Markdown
Member

What this adds

A design workspace for one API change. It is a plan, not an implementation. No code changes here.

The problem it plans to fix

Six routers expose POST /<entity>/revisions/query with the same request shape, and all reach GitDAO.query_revisions: workflows, testsets, evaluators, environments, applications, and queries. None of them can answer "give me the newest revision of each of these parents".

windowing.limit applies to the whole result set, not to each parent. A limit of 5 over 5 workflows can return 5 revisions of the first workflow and none of the others. Callers have no safe option, so they drop the limit and take everything. The comment in web/packages/agenta-entities/src/workflow/api/api.ts says so:

When fetching for a single workflow, limit to 1 (latest) to reduce payload.
With multiple workflows the global limit would cut across all, so skip it.

On the project measured, 19 workflows hold 269 revisions, and two of them hold 179 revisions and 30 MB between them. A batch over those two returns 177 revisions and costs 1916 ms of synchronous CPU:

Stage Cost
Build models, including jsonschema.check_schema 385 ms
model_dump 146 ms
Serialize to JSON 92 ms, giving 76.4 MB
gzip at level 5 1293 ms, giving 18.9 MB
Total, none of it yielding to the event loop 1916 ms

That is how an agent turn failed with session <id> record log is unreadable; cannot rebuild the conversation. The record log was fine. The runner gives the API five seconds, and a blocked worker did not answer in time. Issue #6563 has the full incident trace.

The proposed interface

One optional field, a sibling of windowing, on all six request models:

{
  "workflow_refs": [{"id": "..."}, {"id": "..."}],
  "grouping":  { "by": "artifact" }
}

The first release returns at most one revision per requested parent. It carries no "newest N per parent" option and no paging over parents. api-design.md shows the worked case where a revision cursor makes one parent appear on two pages, which is why paging is rejected rather than redefined.

What to read

File Question it answers
README.md Reading order, and the words used in this folder.
context.md What breaks today, and why the fix belongs in the API.
research.md What the code does now, with the measurements.
api-design.md The interface, and the three shapes rejected with reasons.
plan.md What to build, in what order, and what to test.
status.md Decisions made, and what is still open.

Notes for reviewers

Codex reviewed this at medium effort and its findings are folded in. The one that changed the plan most: do not land the request field before the behavior works. The workflows and environments routers read await request.json() and expand it into a parser with a fixed keyword signature, inside a bare except Exception: pass. Verified on a running API:

parse_workflow_revision_query_request_from_body(**{"workflow_refs": [...], "grouping": {...}})
  -> TypeError: got an unexpected keyword argument 'grouping'

The router swallows that and leaves the body None, so the parent references are discarded and the query becomes project-wide. Shipping the field early would make this incident worse.

Two decisions in status.md are open and block implementation:

  • What counts as the newest revision, given that version 0 can be an auto-created placeholder or a real configured revision. selectMostRecentWorkflowRevision skips v0 and ranks by created_at, while SQL orders by UUID7 id, so a naive fold can pick a placeholder the frontend then discards.
  • Whether grouping means "the newest revision that matches the filter" or "the newest revision, returned only if it matches". The two differ whenever a filter is present.

The related testset fix is in #6568. It is independent and can merge without this.

Six routers expose POST /<entity>/revisions/query with the same request shape,
and all reach GitDAO.query_revisions. None of them can express "the newest
revision of each of these parents". windowing.limit applies to the whole result,
so a limit of 5 over 5 parents can return 5 revisions of the first and none of
the rest. Callers drop the limit and take everything.

Measured on a real project, a batch over two workflows returns 177 revisions and
costs 1916 ms of synchronous CPU: 385 ms building models, 146 ms model_dump,
92 ms serializing to 76.4 MB, and 1293 ms in gzip. None of it yields, so
unrelated requests on that worker time out.

The workspace records the incident, the measurements, the proposed field, the
shapes rejected and why, the execution plan, and the open decisions.

Refs #6563
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agenta-documentation Ready Ready Preview Sep 5, 2026 9:28am UTC

Request Review

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📘 Docs preview

Status ✅ Ready
Preview https://pr-6569-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit 1f45812dddf1657bc6ce55f95e53e93fa8cc1130

This comment updates in place on every push.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Summary

Summary by CodeRabbit

  • Documentation
    • Added design documentation for planned revision query grouping.
    • Documented proposed options to request the newest revision for each artifact or variant.
    • Described API behavior, compatibility expectations, constraints, implementation planning, research findings, and status.
    • Recorded performance considerations for large batch revision queries and bounded result sets.

Walkthrough

This change adds design documentation for grouping revision queries by artifact or variant. It defines the proposed API contract, records performance research, outlines implementation and validation work, and tracks current decisions and open questions.

Changes

Revision Query Grouping Design

Layer / File(s) Summary
Problem context and research
docs/design/revision-query-grouping/context.md, docs/design/revision-query-grouping/research.md
Documents the revision query performance issue, shared endpoint path, client behavior, measured costs, and the proposed DISTINCT ON SQL pattern.
Grouping API contract
docs/design/revision-query-grouping/api-design.md
Defines the optional grouping field, RevisionGrouping values, selection rules, rejected combinations, and first-release limits.
Implementation and validation plan
docs/design/revision-query-grouping/plan.md
Plans coordinated parser, model, service, DAO, client, batch-caller, integration, and performance changes.
Design index and status
docs/design/revision-query-grouping/README.md, docs/design/revision-query-grouping/status.md
Adds the design-folder reading order, terminology, current decisions, open questions, and related performance findings.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🟡 Moderate · up to 1f458

This PR does not change runtime behavior, but its design contract remains ambiguous about grouping scope, limits, and cursor behavior. Resolve those inconsistencies and add the core acceptance cases before using the workspace as the implementation specification.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the documentation change and the planned per-parent grouping for revision queries.
Description check ✅ Passed The description accurately explains that the pull request adds a design workspace, the revision-query problem, the proposed grouping interface, and the open implementation decisions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/revision-query-grouping

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
docs/design/revision-query-grouping/plan.md (1)

85-95: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add positive coverage for both grouping modes and parent semantics.

The test plan covers several variants and rejected combinations, but it does not explicitly require by: "artifact" versus by: "variant" or verify that windowing.limit counts parents. Add these assertions to the six-path acceptance tests because they are core API rules.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Team

Run ID: b404c206-84b5-4e39-8918-41ec154391bb

📥 Commits

Reviewing files that changed from the base of the PR and between 76139d2 and 1f45812.

📒 Files selected for processing (6)
  • docs/design/revision-query-grouping/README.md
  • docs/design/revision-query-grouping/api-design.md
  • docs/design/revision-query-grouping/context.md
  • docs/design/revision-query-grouping/plan.md
  • docs/design/revision-query-grouping/research.md
  • docs/design/revision-query-grouping/status.md

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment on lines +47 to +58
grouping: Optional[RevisionGrouping] = Field(
default=None,
description=(
"Return the newest revisions within each parent instead of a flat list. "
"When set, `windowing.limit` and `windowing.next` count parents, not revisions."
),
)
```

The two limits compose, and that is a sign the split is correct. `grouping.limit` says how
many revisions to keep inside each parent. `windowing.limit` still caps how many parents
come back. They answer different questions, so they sit side by side.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Remove unsupported first-release semantics from the request contract.

RevisionGrouping contains only by, and lines 68-69 reject windowing.next. This section instead documents grouping.limit and says windowing.next counts parents. Document the first release consistently: windowing.limit caps parents, windowing.next is rejected, and grouping.limit is not supported.

Comment on lines +63 to +75
2. `grouping` needs a non-empty list of parent references, and the API caps how many.
The result is then bounded by the number of parents the caller named.
3. Selection order is fixed by the server, and `windowing.order` does not change it.
Ascending order would otherwise select the oldest revision inside each parent, which
contradicts the name of the feature.
4. `grouping` with `windowing.next`, `windowing.newest`, or `windowing.oldest` returns a
client error. A revision cursor cannot page over parents. See the next section.
5. `grouping` with the environments `references` filter returns a client error. That
filter compares neighboring rows in a history, so it needs the history intact.
6. A parent with no eligible revision produces no row. It does not produce an empty one.
7. `grouping.by: "variant"` groups by the variant that owns the revision.
`grouping.by: "artifact"` groups by the artifact above the variant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define which references select the grouped parents.

The contract requires a non-empty list of “parent references,” but each request exposes separate <entity>_refs and <entity>_variant_refs fields while by accepts artifact or variant. State the required reference field for each value and define or reject requests that provide both or the wrong scope. Without this matrix, clients cannot know which parents are grouped.

Comment on lines +185 to +187
**The `limit` field, for the newest N per parent.** Every caller we found wants exactly
one. Supporting N forces a second SQL path with `ROW_NUMBER`, and it makes the result size
unbounded again, which is the problem we set out to fix.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the newest-N rationale.

With an API cap on parent references and windowing.limit capping returned parents, grouping.limit = N would bound rows to parents × N. It would require a ROW_NUMBER SQL path, but it would not make the result unbounded. Keep the scope decision if desired, but replace this reason with the actual product or implementation reason.

Comment on lines +51 to +52
1. Let a caller ask for the newest N revisions of each parent, in one request.
2. Push the fold into SQL, so the discarded rows never reach Python.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align the goal with the first-release contract.

This goal says the feature returns the newest N revisions per parent. docs/design/revision-query-grouping/api-design.md and docs/design/revision-query-grouping/status.md limit the first release to one revision per parent and remove grouping.limit. Mark newest-N as a future goal, or change this text to newest one.

Comment on lines +111 to +112
A single revision costs 2.2 ms to build. The project holds 19 workflows and 269
revisions, and two of those workflows hold 179 revisions and 30 MB between them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🤖 get_repo_knowledge executed:

get_repo_knowledge Agenta-AI/agenta /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/conventions /tmp/coderabbit-repo-knowledge/agenta-ai-agenta-4b53879a/learnings

Length of output: 18462


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
fd -i 'context.md|research.md' . | head -50
printf '%s\n' '--- research section ---'
if [ -f docs/design/revision-query-grouping/research.md ]; then
  cat -n docs/design/revision-query-grouping/research.md | sed -n '95,120p'
fi
printf '%s\n' '--- context references ---'
rg -n -C 4 '258|269|revision|workflow|30 MB|2\.2 ms' docs/design/revision-query-grouping --glob '*.md'

Repository: Agenta-AI/agenta

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
fd -i 'context.md|research.md' . | head -50
printf '%s\n' '--- research section ---'
cat -n docs/design/revision-query-grouping/research.md | sed -n '95,120p'
printf '%s\n' '--- context references ---'
rg -n -C 4 '258|269|revision|workflow|30 MB|2\.2 ms' docs/design/revision-query-grouping --glob '*.md'

Repository: Agenta-AI/agenta

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- document headers and measurement context ---'
cat -n docs/design/revision-query-grouping/context.md | sed -n '1,40p'
cat -n docs/design/revision-query-grouping/research.md | sed -n '1,18p'
printf '%s\n' '--- exact count occurrences in this design folder ---'
rg -n -F -e '258' -e '269' docs/design/revision-query-grouping --glob '*.md'
printf '%s\n' '--- provenance terms ---'
rg -n -i 'timestamp|date|snapshot|run|measured|measurement|project.*data|real data|at the time' docs/design/revision-query-grouping --glob '*.md'

Repository: Agenta-AI/agenta

Length of output: 6706


🏁 Script executed:

#!/bin/bash
set -e
cat -n docs/design/revision-query-grouping/context.md | sed -n '1,40p'
cat -n docs/design/revision-query-grouping/research.md | sed -n '1,18p'
printf '%s\n' '--- counts ---'
rg -n -F -e '258' -e '269' docs/design/revision-query-grouping --glob '*.md'
printf '%s\n' '--- provenance ---'
rg -n -i 'timestamp|date|snapshot|run|measured|measurement|project.*data|real data|at the time' docs/design/revision-query-grouping --glob '*.md'

Repository: Agenta-AI/agenta

Length of output: 6612


Reconcile the revision-count snapshot.

research.md attributes its 269-revision measurement to agenta-oss-team on image v0.114.5, but context.md gives 258 revisions without snapshot details. Add provenance to context.md and explain the difference, or align the count because these values support the payload and latency claims.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant