fix(sessions): bound the merged assigned CLI set by a shared ceiling
The route cap bounded project-assigned CLI rows PER PROJECT at 200, which is not a bound on the payload: 200 rows times N projects grows with the project count. The review's own reproduction still held — 1,000 assigned conversations spread over 5 projects returned all 1,000, because no project exceeded its own 200 (#6659 review finding 1). Imported WebUI sidecars from all_sessions() never pass through a state.db cap, so this merged pass is the only place that can bound them. Give each project an EQUAL share of a global CLI_PROJECT_ASSIGNED_SCAN_CEILING (ceiling // projects, never below 1) once enough projects exist to make that share tighter than the per-project cap. Same shape as the model-side share of PROJECT_ASSIGNED_CLI_SCAN_CEILING, and for the same reason a flat global cap is unusable here: it would leave every project past it permanently unreachable on each rebuild (greptile P1). Profiles with a few projects keep the full 200, so the common case is unchanged. The ceiling is duplicated as a literal because api.models is imported below this cap in api/routes.py; a test pins the two equal, mirroring test_background_source_exclusion_literal_matches_the_constant.
This commit is contained in:
1 parent
b5ccf56c53
commit
76be369c54
2 files changed
+115
-1
No files matched your search
+34
-1
@@ -10494,12 +10494,23 @@ def _cli_visible_session_cap() -> int:
|
||||
# which no model-side cap applies to — so it is the only place that can actually
|
||||
# bound the assigned set (#6659 review finding 1).
|
||||
CLI_PROJECT_ASSIGNED_CAP = 200
|
||||
# Global ceiling on the assigned rows this cap will emit, mirroring
|
||||
# models.PROJECT_ASSIGNED_CLI_SCAN_CEILING so the route can never hand back more
|
||||
# assigned conversations than the model pass was allowed to recover. A flat
|
||||
# per-project bound does NOT bound the payload: 200 rows x N projects grows with
|
||||
# the project count, and 1,000 assigned rows spread over 5 projects still
|
||||
# returned all 1,000 — the exact reproduction from #6659 review finding 1.
|
||||
# Spelled as a literal because api.models is imported further down this module;
|
||||
# pinned equal to the model constant by
|
||||
# test_route_project_ceiling_matches_the_model_constant.
|
||||
CLI_PROJECT_ASSIGNED_SCAN_CEILING = 2000
|
||||
|
||||
|
||||
def _cap_recent_cli_sessions(
|
||||
sessions: list[dict],
|
||||
cli_cap: int | None = None,
|
||||
project_cap: int = CLI_PROJECT_ASSIGNED_CAP,
|
||||
project_ceiling: int = CLI_PROJECT_ASSIGNED_SCAN_CEILING,
|
||||
) -> list[dict]:
|
||||
"""Cap the default CLI list while retaining project-addressable rows.
|
||||
|
||||
@@ -10519,11 +10530,33 @@ def _cap_recent_cli_sessions(
|
||||
window is full. Past that bound they are dropped: keeping assigned rows past
|
||||
the *recent* cap is the fix, keeping them past *all* bounds just trades a
|
||||
vanishing session for a stalled sidebar.
|
||||
|
||||
That per-project budget is an EQUAL share of ``project_ceiling``
|
||||
(``ceiling // projects``, never below 1) once enough projects exist to exceed
|
||||
it, which is what makes the merged payload bounded no matter how many
|
||||
projects a profile has while keeping a project's allowance independent of how
|
||||
loud its neighbours are. Same shape as the model-side share of
|
||||
``PROJECT_ASSIGNED_CLI_SCAN_CEILING``, and for the same reason a flat global
|
||||
cap is not usable here: it would leave every project past it permanently
|
||||
unreachable on every rebuild (greptile P1 on #6659).
|
||||
"""
|
||||
if cli_cap is None:
|
||||
cli_cap = _cli_visible_session_cap()
|
||||
if cli_cap <= 0:
|
||||
return sessions
|
||||
# The share denominator has to be known before the first assigned row is
|
||||
# charged against it, so count the distinct assigned projects up front.
|
||||
assigned_projects = {
|
||||
project_id
|
||||
for session in sessions
|
||||
if _is_cli_session_for_settings(session)
|
||||
and (project_id := str(session.get("project_id") or "").strip())
|
||||
}
|
||||
effective_project_cap = project_cap
|
||||
if project_cap > 0 and project_ceiling > 0 and assigned_projects:
|
||||
effective_project_cap = min(
|
||||
project_cap, max(1, project_ceiling // len(assigned_projects))
|
||||
)
|
||||
kept = []
|
||||
recent_seen = 0
|
||||
unassigned_seen = 0
|
||||
@@ -10539,7 +10572,7 @@ def _cap_recent_cli_sessions(
|
||||
else:
|
||||
seen = assigned_seen.get(project_id, 0) + 1
|
||||
assigned_seen[project_id] = seen
|
||||
if project_cap > 0 and seen > project_cap:
|
||||
if effective_project_cap > 0 and seen > effective_project_cap:
|
||||
continue
|
||||
if recent_seen >= cli_cap:
|
||||
session = dict(session)
|
||||
|
||||
Reference in new issue
Block a user