Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
69ebb3a
feat(webapp,run-engine,core,clickhouse): surface total concurrency in…
matt-aitken Aug 29, 2026
47b3684
fix(run-engine,clickhouse,webapp): total gauges on enqueue paths; res…
matt-aitken Aug 29, 2026
2393672
fix(clickhouse): keep migration comments semicolon-free
matt-aitken Aug 29, 2026
00aee8e
fix(webapp): skip the total concurrency read when the queue has no cap
matt-aitken Aug 29, 2026
30cdc29
feat(webapp): show the Total column in the non-metrics queues table too
matt-aitken Aug 29, 2026
8a03067
feat(webapp): fold the total cap into the Limit column
matt-aitken Aug 29, 2026
cddd3ce
fix(webapp): saturate the total-cap warning on keyed runs only
matt-aitken Aug 29, 2026
0f367c9
refactor(webapp,core,clickhouse): combined concurrency in responses, …
matt-aitken Aug 29, 2026
e22f1b9
feat(webapp): bracketed combined limit in the Limit column
matt-aitken Aug 29, 2026
20e74d6
fix(webapp): combined-limit tooltip renders beside the cell link
matt-aitken Aug 29, 2026
fc1a235
Better tooltip message
matt-aitken Aug 31, 2026
42bcaec
docs(core): combined.current is the declared cap, clamped at admit time
matt-aitken Aug 31, 2026
10a732f
fix(run-engine): sample the combined gauge after batch admission
matt-aitken Aug 31, 2026
49f3f69
refactor(run-engine,webapp): drop per-key override admit reads and li…
matt-aitken Aug 31, 2026
f0eb9f7
refactor(webapp): concurrency keys resource stops reading per-key ove…
matt-aitken Aug 31, 2026
108f81a
refactor(run-engine): drop the now-unreferenced ck-limits key builders
matt-aitken Aug 31, 2026
8c651f7
chore: lift the run-queue knip ignore
matt-aitken Aug 31, 2026
d55fc7a
fix(run-engine,webapp,clickhouse): review fixes for the metrics tier
matt-aitken Aug 31, 2026
d07b9dc
chore: drop the pre-rename changeset superseded by the combined one
matt-aitken Aug 31, 2026
6b950c8
perf(run-engine): share the combined-limit read between the admit gat…
matt-aitken Aug 31, 2026
d70653d
perf(run-engine): dequeue gauges sample once, at return
matt-aitken Aug 31, 2026
b1819dd
test(run-engine): pin dequeue-emitted gauges so a sampling regression…
matt-aitken Aug 31, 2026
7502896
test(run-engine): wait for the metrics emitter connection before exer…
matt-aitken Aug 31, 2026
e2022c5
test(run-engine,metrics-pipeline): bound emitter-readiness waits and …
matt-aitken Aug 31, 2026
1d570d6
test(run-engine): abort the readiness race timer so its losing branch…
matt-aitken Aug 31, 2026
c1811b9
test(run-engine): close the emitter when the readiness wait times out
matt-aitken Aug 31, 2026
13c362e
test(metrics-pipeline,run-engine): readiness wait for the per-stream …
matt-aitken Aug 31, 2026
86db78e
test(run-engine): fire-and-forget the emitter close on readiness timeout
matt-aitken Aug 31, 2026
597bb2d
rename(webapp,clickhouse): concurrency vocabulary for the unlaunched …
matt-aitken Sep 6, 2026
50985a4
fix(webapp,clickhouse,run-engine): rename follow-ups from review
matt-aitken Sep 6, 2026
f8fedbe
fix(webapp,clickhouse): pre-cap history keeps its truthful gap in the…
matt-aitken Sep 6, 2026
9e66221
fix(webapp): carry-guard column reaches the chart points, Total namin…
matt-aitken Sep 6, 2026
4278c4c
feat(webapp): rename the Concurrency page to Concurrency limits
matt-aitken Sep 6, 2026
60372d1
fix(webapp): deeplinks, favorites and report links follow the concurr…
matt-aitken Sep 6, 2026
16dd423
chore(webapp): remove the combined concurrency override and reset end…
matt-aitken Sep 6, 2026
b398b3d
fix(webapp): agent page labels follow the concurrency-limits rename
matt-aitken Sep 6, 2026
534edd1
fix(run-engine): base-queue gauges reuse the shared total-limit read
matt-aitken Sep 7, 2026
a46314d
fix(run-engine): base-queue admit paths enforce the total limit for k…
matt-aitken Sep 7, 2026
6518fd6
fix(run-engine): the total concurrency limit spans keyed and keyless …
matt-aitken Sep 7, 2026
63b3715
fix(run-engine): keyless releases drain the total-concurrency group set
matt-aitken Sep 7, 2026
17b6f27
fix(run-engine): the repair clear drains the group set for keyless runs
matt-aitken Sep 7, 2026
3cac6a8
fix(run-engine): the keyless repair clear derives the group key from …
matt-aitken Sep 7, 2026
535b2cd
fix(run-engine): the TTL sweep's defensive removal drains the group s…
matt-aitken Sep 7, 2026
c6dcfb0
feat(run-engine): exact per-gate queued counters
matt-aitken Sep 7, 2026
e85f6fa
fix(run-engine): gate queued counters dedupe by base and re-anchor on…
matt-aitken Sep 7, 2026
7d27dd5
fix(webapp): limit rows mirror the declared shape exactly
matt-aitken Sep 7, 2026
4008df9
feat(run-engine): expose the per-gate queued counts on the engine
matt-aitken Sep 7, 2026
e99aac1
feat(webapp): concurrency limits management API
matt-aitken Sep 7, 2026
749f8da
chore(webapp): keep concurrency limit mapping helpers module-local
matt-aitken Sep 7, 2026
46b5a40
fix(webapp,run-engine): recoverable resets, conflict-guarded limit mu…
matt-aitken Sep 7, 2026
a4b4956
fix(webapp): limit mutations answer concurrent changes with a 409
matt-aitken Sep 7, 2026
6dbe660
test(webapp): focused coverage for concurrency limit mutations
matt-aitken Sep 7, 2026
1304bac
fix(webapp,run-engine): limit mutations survive every race direction
matt-aitken Sep 7, 2026
063c077
fix(webapp): reset compensation converges instead of restoring a stal…
matt-aitken Sep 7, 2026
f2533c2
fix(webapp): reset failures keep their original error and every path …
matt-aitken Sep 7, 2026
97549ae
fix(webapp): engine writes settle before any failure is reported
matt-aitken Sep 7, 2026
9163996
fix(webapp): no compensation on the not-overridden validation error
matt-aitken Sep 7, 2026
60e9bf4
feat(webapp): named concurrency limits appear on the queues page
matt-aitken Sep 7, 2026
a5e9995
feat(webapp): the Queues page becomes Concurrency
matt-aitken Sep 7, 2026
e87c22e
fix(webapp): limit rows interleave only on the dashboard list
matt-aitken Sep 7, 2026
4ba0e01
fix(webapp): format the renamed task routes, trigger URIs resolve to …
matt-aitken Sep 7, 2026
c176c29
fix(webapp): limit rows on the Concurrency page stay list-level, over…
matt-aitken Sep 7, 2026
8de71bc
fix(webapp): limit rows only warn at their total bound, paused banner…
matt-aitken Sep 7, 2026
f3d0c04
fix(webapp): limit convergence compares enforced values, zero thresho…
matt-aitken Sep 7, 2026
c548f0d
docs(webapp): the value fixpoint comment states the real soundness ar…
matt-aitken Sep 7, 2026
817fbac
docs(webapp): the value fixpoint comment names both residuals precisely
matt-aitken Sep 7, 2026
17843b3
feat(core,webapp): version-discriminated queue reads, combined never …
matt-aitken Sep 7, 2026
ff469fc
fix(core): queue reads from servers predating the version field parse…
matt-aitken Sep 7, 2026
362f888
chore(webapp): the queue limit bound types stay module-local
matt-aitken Sep 7, 2026
cf90ba2
fix(core,webapp): default-queue inline limits are manageable, V1 endp…
matt-aitken Sep 7, 2026
172fc92
fix(webapp): the limits surface preserves pauses, percent sources, an…
matt-aitken Sep 7, 2026
dedd295
chore(webapp): the retire queries use bounded in-filters
matt-aitken Sep 7, 2026
d220661
fix(webapp): boundless named limits stay cappable, retirement is race…
matt-aitken Sep 7, 2026
515c3e4
fix(webapp): retirement removes engine keys before nulling bounds, re…
matt-aitken Sep 7, 2026
56e3a64
fix(webapp): a retirement losing its guard restores the winner's engi…
matt-aitken Sep 7, 2026
259d7eb
fix(webapp): retirement failures heal the engine and are never silent
matt-aitken Sep 8, 2026
063e454
fix(webapp): retirement removals fully settle before healing from a f…
matt-aitken Sep 8, 2026
e7eb809
fix(webapp): title the Concurrency page tab Concurrency
matt-aitken Sep 17, 2026
7fc0b51
fix(webapp): the Limit column tooltip teaches the concurrency option,…
matt-aitken Sep 17, 2026
f439c98
fix(webapp): the Limit cell label hugs its number when the total hint…
matt-aitken Sep 17, 2026
d6fea23
feat(webapp): the override dialog edits per-key and total limits on q…
matt-aitken Sep 17, 2026
7cef3c0
fix(webapp): single-bound overrides remove cleanly and blank dialog f…
matt-aitken Sep 17, 2026
415818b
fix(webapp): override dialog fields reset on open and the server pars…
matt-aitken Sep 17, 2026
3968a54
feat(webapp): the override dialog blocks a per-key limit above the total
matt-aitken Sep 17, 2026
44c95d4
fix(webapp): a pre-existing per-key/total conflict stays editable in …
matt-aitken Sep 17, 2026
3a11b20
fix(webapp): a pre-existing bound conflict only submits when the edit…
matt-aitken Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/task-concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export const generateSummary = task({
`perKey` caps each `concurrencyKey` pool and `total` caps across everything, keys or not. The queue-level `concurrencyLimit` option keeps working unchanged and is deprecated in favor of `concurrency`. Enforcement happens server-side; servers without support accept the option but do not enforce it yet.

Manage limits at runtime with the new `concurrencyLimits` namespace: `list()` and `retrieve(name)` report each limit's bounds plus its live `running` and `queued` counts, `override(name, { perKey, total })` changes only the given bounds (overriding `total` to `0` pauses the limit), and `reset(name)` restores the declared values.

Queue reads (`queues.list()` and `queues.retrieve()`) now report a `version` that discriminates the shape: `V1` queues keep today's fields (their own `concurrencyLimit` and its override state), while `V2` queues (tasks declared with `concurrency`) carry no queue-level concurrency, since their limits are read and overridden through `concurrencyLimits` (a task's inline limit under its derived `task/<task-id>` name). Existing reads keep compiling: a `V2` queue reports `concurrencyLimit` as null and `concurrency` as undefined.
8 changes: 4 additions & 4 deletions apps/webapp/app/components/billing/OrgBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { useOptionalProject, useProject } from "~/hooks/useProject";
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
import { v3BillingLimitsPath, v3BillingPath, v3QueuesPath } from "~/utils/pathBuilder";
import { v3BillingLimitsPath, v3BillingPath, concurrencyPath } from "~/utils/pathBuilder";
import { ENVIRONMENT_PAUSE_SOURCE_BILLING_LIMIT } from "~/utils/environmentPauseSource";

function getUpgradeResetDate(): Date {
Expand Down Expand Up @@ -54,7 +54,7 @@ export function OrgBanner() {
showSelfServe,
});

const hideQueuesButton = location.pathname.endsWith("/queues");
const hideConcurrencyButton = location.pathname.endsWith("/concurrency");
const hideBillingLimitBanner = location.pathname.endsWith("/settings/billing-limits");

switch (bannerKind) {
Expand All @@ -70,7 +70,7 @@ export function OrgBanner() {
return isArchived ? (
<ArchivedEnvironmentBanner />
) : (
<PausedEnvironmentBanner hideButton={hideQueuesButton} />
<PausedEnvironmentBanner hideButton={hideConcurrencyButton} />
);
default:
return null;
Expand Down Expand Up @@ -209,7 +209,7 @@ function PausedEnvironmentBanner({ hideButton }: { hideButton: boolean }) {
hideButton ? undefined : (
<LinkButton
variant="tertiary/small"
to={v3QueuesPath(organization, project, environment)}
to={concurrencyPath(organization, project, environment)}
>
Manage
</LinkButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
} from "./unread-counts";
import { AgentPanelColumn } from "./panel-layout";
import { markerAfterActiveChat, markerAfterActivity } from "./thinking-marker";
import { concurrencyPath } from "~/utils/pathBuilder";
import { concurrencyLimitsPath } from "~/utils/pathBuilder";

function serializePageContext(pageContext: AgentPageContext): string | undefined {
try {
Expand Down Expand Up @@ -131,7 +131,7 @@ export function DashboardAgentPanel({
const currentPage = agentPageLabel(pageContext, location.pathname);

const pagePaths = useMemo<Record<string, string>>(
() => ({ raise_env_limit: concurrencyPath(organization, project, environment) }),
() => ({ raise_env_limit: concurrencyLimitsPath(organization, project, environment) }),
[organization, project, environment]
);

Expand Down
3 changes: 2 additions & 1 deletion apps/webapp/app/components/dashboard-agent/page-label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const KIND_LABELS: Record<Exclude<AgentPage["kind"], "other">, string> = {
alerts: "Alerts",
apikeys: "API keys",
envvars: "Environment variables",
concurrency: "Concurrency",
concurrency: "Concurrency limits",
regions: "Regions",
settings: "Settings",
waitpoints: "Waitpoints",
Expand All @@ -50,6 +50,7 @@ const SECTION_LABELS: Record<string, string> = {
"bulk-actions": "Bulk actions",
branches: "Branches",
concurrency: "Concurrency",
"concurrency-limits": "Concurrency limits",
dashboards: "Dashboards",
deployments: "Deployments",
"dev-branches": "Branches",
Expand Down
5 changes: 3 additions & 2 deletions apps/webapp/app/components/navigation/favoritePages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ const ENV_PAGE_META: Record<string, PageMeta> = {
logs: { icon: "logs", name: "Logs" },
errors: { icon: "errors", name: "Errors", singular: "Error" },
query: { icon: "query", name: "Query" },
queues: { icon: "queues", name: "Queues", singular: "Queue" },
queues: { icon: "queues", name: "Concurrency" },
concurrency: { icon: "queues", name: "Concurrency" },
dashboards: { icon: "dashboards", name: "Dashboards", singular: "Dashboard" },
deployments: { icon: "deployments", name: "Deploys", singular: "Deploy" },
"environment-variables": { icon: "environment-variables", name: "Environment variables" },
Expand All @@ -234,7 +235,7 @@ const ENV_PAGE_META: Record<string, PageMeta> = {
"bulk-actions": { icon: "bulk-actions", name: "Bulk actions", singular: "Bulk action" },
apikeys: { icon: "apikeys", name: "API keys" },
alerts: { icon: "alerts", name: "Alerts", singular: "Alert" },
concurrency: { icon: "concurrency", name: "Concurrency" },
"concurrency-limits": { icon: "concurrency", name: "Concurrency limits" },
limits: { icon: "limits", name: "Limits" },
schedules: { icon: "schedules", name: "Schedules", singular: "Schedule" },
test: { icon: "test", name: "Test", singular: "Test" },
Expand Down
12 changes: 6 additions & 6 deletions apps/webapp/app/components/navigation/sideMenuSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
type OrgForPath,
type ProjectForPath,
branchesPath,
concurrencyPath,
concurrencyLimitsPath,
limitsPath,
queryPath,
regionsPath,
Expand All @@ -39,7 +39,7 @@ import {
v3ProjectAlertsPath,
v3ProjectSettingsIntegrationsPath,
v3PromptsPath,
v3QueuesPath,
concurrencyPath,
v3WaitpointTokensPath,
} from "~/utils/pathBuilder";
import { AlphaBadge, NewBadge } from "../FeatureBadges";
Expand Down Expand Up @@ -160,10 +160,10 @@ export function buildSideMenuSections({
} satisfies SideMenuItemConfig,
{
id: "queues",
name: "Queues",
name: "Concurrency",
icon: QueuesIcon,
activeIconColor: "text-queues",
to: v3QueuesPath(organization, project, environment),
to: concurrencyPath(organization, project, environment),
dataAction: "queues",
} satisfies SideMenuItemConfig,
{
Expand Down Expand Up @@ -269,10 +269,10 @@ export function buildSideMenuSections({
? [
{
id: "concurrency",
name: "Concurrency",
name: "Concurrency limits",
icon: ConcurrencyIcon,
activeIconColor: "text-text-bright",
to: concurrencyPath(organization, project, environment),
to: concurrencyLimitsPath(organization, project, environment),
dataAction: "concurrency",
} satisfies SideMenuItemConfig,
]
Expand Down
Loading