Skip to main content

Effectiveness Dimensions

Reference

Reference definitions for the Effectiveness Leaderboard: what each dimension measures, exactly how it is computed, and the shared scoring rules that apply to all of them.

Teams, projects, and repositories pool their PR facts and apply the same PR-based dimension formulas within a comparison group of the same type. At least two eligible groups must provide comparable measurements in a complexity band. Group composites normalize configured weights over available dimensions; AI Practices is excluded because its signals are individual-level. Group history is recalculated with current settings and membership.

Productivity configuration API​

The admin-only GET/PUT/PATCH /api/v1/effectiveness/config endpoint stores productivity settings as a single JSON document.

MethodBehavior
GETReturns the stored configuration and shipped defaults.
PUTReplaces the full configuration document.
PATCHAtomically replaces only supplied top-level fields, preserves omitted fields, and returns the merged document in the response's config field.

PATCH configuration changes and their audit events commit together. Nested maps such as weights and practices are replaced as complete fields, so include every nested value you want to retain. Successful concurrent PATCH saves to different top-level fields preserve each other's changes. If contention persists, PATCH returns 409 Conflict; reload the configuration and retry your partial update.

The effectivenessBands field defines inclusive lower score boundaries: mediumMin, highMin, and eliteMin. Configure them in Settings → Productivity → AI Effectiveness. All three values are required when supplied, and must satisfy 0 < mediumMin < highMin < eliteMin ≤ 100. Defaults are { "mediumMin": 50, "highMin": 75, "eliteMin": 90 }. Scores below mediumMin are Low. Bands apply to the overall effectiveness score and scored dimensions independently of peer rank or group size; insufficient evidence remains unscored.

Threshold changes apply when live reports and histories are read, without recomputing numeric scores. Personal views and newly generated digests use the same thresholds. The legacy response identifiers below_median, above_median, top_25, and top_10 are retained for API compatibility and now identify Low, Medium, High, and Elite effectiveness ranges respectively. They no longer describe effectiveness percentiles. Productivity point comparisons continue to use peer-relative bands.

API clients can set effectivenessBands to null to restore the default thresholds. Omitting this field from a PATCH preserves the current thresholds; a complete PUT without this field uses the defaults.

AI adoption is displayed as percentages without performance bands. The legacy productivityBands configuration is retained for API compatibility and does not affect effectiveness bands or current adoption displays.

Productivity score​

points = Σ ((eligible added LOC + deleted LOC) × work-type weight)

Weights are 0–5, with at most two decimal places and at least one positive weight. Defaults are 1× for every type, including Unclassified. Modified lines are represented by diff additions/deletions and are not counted a third time.

Work type keyDisplay name
feature-developmentFeature development
bug-fixBug fix
refactoringRefactoring
infrastructure-changeInfrastructure change
reliability-securityReliability & security
maintenanceMaintenance
documentationDocumentation
testingTesting
engineering-enablementEngineering enablement
unknownUnclassified

Eligible merged PRs from all target branches of onboarded repositories contribute to live productivity scores. The repository's default branch remains metadata and does not restrict scoring. Optional target-branch and delivery-candidate filters apply consistently to PR counts, points, AI-assisted share, effectiveness, trends, and supporting PRs. Distinct PRs count separately, including backports and integration merges.

File inventory is bounded at 3,000 files per PR. Generated, vendor, and lockfile exclusions use the existing authored-path heuristic. Missing or stale counters remain unavailable. Missing or disputed classification uses Unclassified. Partial coverage remains visible beside the score.

Admin APIs:

  • GET/PUT /api/v1/productivity/score/settings: authoritative enable state and work-type weights. Writes require expectedVersion. Optional recomputeFrom and recomputeTo are epoch seconds describing a past window of up to 365 days; omitted bounds apply weights only to future merges.
  • GET /api/v1/productivity/overview: from, to (exclusive), view (teams, projects, repos, people), optional entity, team, project, repository, branches, excludeDelivery, q, sort, page, and pageSize. A response contains simultaneous metrics, coverage, breakdowns, and paginated PR details.
  • GET/POST /api/v1/productivity/scans: repository processing status or a new scan. Requests include an idempotency requestKey, optional repositoryID, and from/to, or retryFailed: true to resume failed checkpoints.

The overview API accepts these PR filters as URL query parameters:

ParameterFormat and behavior
branchesJSON array of exact target-branch names, such as ["main","release/1"]. Omit it or pass [] to include all target branches. Use [""] to select PRs with unknown target branches. Select at most 100 names; each name is limited to 256 UTF-8 bytes and cannot contain carriage returns, line feeds, or null characters.
excludeDeliveryComma-separated values backport and/or integration. Omit it to include all delivery types. Exclusions use explicit PR-title hints, which identify candidates without verifying code ancestry or duplicate changes.

URL-encode the JSON and other parameter values. Malformed filters return 400 Bad Request. PR details expose baseBranch and deliveryHint; availableBranches supplies up to 200 branch suggestions for the selected scope, with availableBranchesTruncated indicating more values. An exact branch name can be selected even when it is outside the suggestion list.

Settings, queued jobs, and their audit events are committed transactionally. Weight versions are applied by PR merge time. Historical recalculation reuses retained facts. Frozen reports from the previous publication workflow remain unchanged and readable through the outcome report/history APIs.

PR workflow evidence API​

The administrator analytics API exposes PR creation-to-merge duration, Copilot review submissions, and cumulative request-to-submission time. Use an account-bound administrator credential and create a context with POST /api/v1/outcomes/analytics/context, setting includePRWorkflow: true. The returned opaque contextToken binds the scope, reporting period, timezone, and PR filters. Contexts created without this opt-in retain the original metric catalog.

Metric IDAggregate meaningDate basis
pr_time_to_mergeMedian creation-to-merge secondsPR merge time
pr_time_to_merge_p90Nearest-rank p90 creation-to-merge secondsPR merge time
copilot_reviewsNumber of submitted Copilot review objectsReview submission time
copilot_reviews_per_prMean lifetime pre-merge reviews per merged PRPR merge time
copilot_review_turnaroundMedian cumulative request-to-submission seconds per completely timed PRPR merge time
copilot_review_turnaround_p90Nearest-rank p90 cumulative request-to-submission seconds per completely timed PRPR merge time

Turnaround includes queueing. It does not measure Copilot execution runtime or developer effort. Counts represent submitted review objects, including reviews with no findings; they do not count comments or infer fix/review cycles. Local CLI reviews are not collected. Aggregate duration statistics pool individual PR samples rather than averaging daily or developer medians.

To inspect individual PRs, submit POST /api/v1/outcomes/analytics/evidence/pr-workflow:

{
"contextToken": "<returned context token>",
"metric": "copilot_review_turnaround",
"sort": "value_desc",
"limit": 25
}

A new capture returns 202 with a job ID. Poll GET /api/v1/outcomes/analytics/jobs/{id} until the result is complete. The result contains metric, dateBasis, a half-open range, items, totalCount, and nextCursor. To page, repeat the same request with the returned cursor. The cursor cannot change the metric, filters, page size, or authenticated owner.

The copilot_reviews selector returns PRs with submissions in the reporting period, including open and post-merge activity. Other selectors return PRs merged in the period, with lifetime evidence strictly before merge, including earlier reviews. Median and p90 selectors inspect the same individual PR duration. Optional entity, repository, project, prId, and literal search filters intersect the context scope. Supported sorts are value_asc, value_desc, merged_asc, merged_desc, title_asc, and title_desc. Missing values sort last; PR ID breaks ties. Page size is 1–100.

Each row includes four individual totals: pr_time_to_merge, copilot_reviews, copilot_reviews_per_pr, and copilot_review_turnaround, with aggregation: "total". Read each metric's unit, status, and reason, together with the row's workflow coverage. Partial count or timing evidence is a lower bound. Unknown history is not zero. Unmerged PRs have null completed merge and pre-merge burden metrics. Collection time does not establish complete event-time coverage.

Set a single prId and detailLimit from 1–100 to include review submissions, matched requests and durations, and request/removal events. Review and request arrays have separate totals and share a truncated flag; truncation does not mean provider history is incomplete. Detail beyond those bounded arrays is not paged.

Use POST /api/v1/outcomes/analytics/exports with the same contextToken, the result's snapshotId, source: "pr_workflow", format: "csv", and explicit fields. Supported fields are the four metric IDs plus prId, number, title, url, repository, repositoryId, author, state, openedAt, mergedAt, selectedMetric, dateBasis, activityRange, workflowEvidence, reviewDetails, metricEvidence, coverage, status, and reason. Exports include all retained rows, independently of page size. Missing numeric values are blank. selectedOnly is unsupported. Poll GET /api/v1/outcomes/analytics/exports/{id} for the download URL.

Contexts, results, cursors, and exports remain bound to the authenticated actor, current authorization, source revisions, timezone preferences, and expiration. Invalidation requires a fresh context or query. Capacity limits reject oversized captures without publishing partial pages. Re-collecting unchanged workflow history preserves retained results. Changes to evidence or coverage require a fresh query.

Leaderboard adoption percentage​

AI PR % = 100 × distinct merged PRs with recorded AI assistance ÷ eligible merged PRs.

Recorded assistance means a linked AI session, a self-declared AI marker, or a Copilot review. Evidence types can overlap; each PR counts once. Routine automation without AI attribution is excluded. This percentage is not complexity-weighted, capped, smoothed, or converted to a percentile. It remains visible for small samples; no recorded merged PRs produces a missing value, not zero.

Teams, projects, and repositories aggregate underlying PR facts and use the same pooled formulas for their group composites; they do not average individual percentages. Project filters scope both adoption and group scoring. Adoption remains a simple PR percentage, while the composite retains its complexity-band, confidence, shrinkage, and dimension-weighting rules. The existing AI-assisted work dimension below continues to use session-linked or declared work, so review-only assistance can affect adoption without changing that dimension.

Shared scoring rules​

Outcome dimension scores are 0–100 and follow this recipe. AI practices uses absolute adherence instead:

  1. Metric extraction per developer per complexity band, from that developer's merged PRs in the week.
  2. Percentile (midrank) against the other developers in the same (cohort × complexity band). A developer who is alone in a band scores 50 — there is nothing to compare against.
  3. Band aggregation: percentiles are combined across bands, weighted by each band's effective sample.
  4. Shrinkage: the result is pulled toward 50 by n_eff / (n_eff + k) (default k = 5), where n_eff is the complexity-weighted PR sample. Small samples land near 50 by construction.

Dimensions​

DimensionWhat it measuresHow it is computedDirection
AI-assisted workHow much of the developer's merged work is AI-assistedAI PRs (proxy-witnessed sessions or self-declared markers) ÷ merged PRs, capped at the org target (default 80%) — share beyond the target earns nothingHigher is better
Review efficiencyHow little review churn the developer's PRs needAverage review rounds per PR, where rounds = change-requested reviews + 1Lower rounds → higher score
Model FitWhether the AI model tier matches task complexityShare of AI-witnessed PRs without a tier mismatch: a planner-class model on trivial/low-complexity work is over-provisioned; a fast-class model on high-complexity work is under-provisionedHigher is better
Time to mergeHow quickly work goes from ready to mergedAverage open→merge time; measured from ready-for-review when the PR started as a draftFaster → higher score
Rework signalsHow often merged work holds up1 − (reverted + abandoned + follow-up-fix PRs + ½ × CI-failed PRs) ÷ PRs. A follow-up fix is a fix-typed PR touching the same files within 7 days; a CI-failed PR had at least one failing check suite and counts at half weight (churn, not a shipped defect)Higher is better
Resource disciplineAI spend discipline on witnessed workRecorded AI cost per complexity-weighted PR, scored by closeness to the cohort median. This measures relative spending, not business value. Only AI-witnessed PRs countMiddle is best
AI practicesAdherence to the org's configured AI working practicesMet ÷ evaluated across the enabled practices this week (defaults: concurrent sessions, multi-model use, ≥25% prompt-cache adoption). Met/not-met per practice — never raw counters — and absolute adherence, not a cohort percentile. Tracked but not weighted into the composite unless the org configures a weightHigher is better

Dimensions with no data for a developer (for example, Model Fit or Resource with no AI-witnessed PRs) are omitted and the composite's weights renormalize over the dimensions that scored — missing data is never imputed as 0 or 50.

Composite and default weights​

composite = Σ (weight × dimension score) ÷ Σ weight, over dimensions with data. Default weights (org-configurable):

AI-assisted workReview efficiencyModel FitTime to mergeRework signalsResource discipline
0.200.200.150.150.150.05

Bands​

Band assignment happens per cohort per week, over developers with at least low confidence, by composite score:

BandRuleMinimum cohort
top_10Top ⌈10%⌉10 scored developers
top_25Next, through ⌈25%⌉4 scored developers
above_medianComposite ≥ cohort median—
below_medianEveryone else scored—
insufficientComplexity-weighted sample below 2shown in a separate "Not scored this week" section

Cohorts smaller than the minimums split around their median only. Teams with fewer than 4 developers are scored against the whole org rather than against each other.

Confidence labels​

Confidence reflects n_eff, the complexity-weighted PR sample for the week (each PR with positive complexity weighs its complexity score ÷ 25; a score of zero uses a fallback weight of 1):

LabelThreshold
high (Strong PR evidence)n_eff ≥ 10
medium (Moderate PR evidence)n_eff ≥ 5
low (Limited PR evidence)n_eff ≥ 2
insufficientbelow 2 — unranked

PR Complexity Score​

The 0–100 normalizer behind band comparisons and sample weights. Deterministic, computed per PR from:

FeatureWeightScaling
Lines added0.30log-scaled, saturates ≈ 1,000 LOC
Files changed0.20saturates at 20 files
Module spread0.15distinct top-level directories beyond the first, saturates at 6
Languages0.10distinct file extensions beyond the first, saturates at 5
Test-file share0.10share of changed files that are tests
Risk paths0.15flags for schema/migration, API surface, dependency manifest, infrastructure, and security-sensitive paths (patterns org-configurable), saturates at 3 flags

Bands: trivial < 10 ≤ low < 30 ≤ medium < 60 ≤ high. Every score stores its inputs, so any complexity value can be audited after the fact.

Historical explanations​

Each recomputed dimension saves its normalized weight. The report uses that saved value, including an explicit zero for unweighted practices; missing historical weights are not replaced with current settings. Per-complexity-band evidence also saves the metric value, peer median, and comparison sample size. Older rollups gain these details after recomputation.

PR evidence is not attribution coverage. The latter reports PRs linked to observed AI sessions. Missing and zero-coalesced cost cannot establish a measured zero, so the spend table shows “No priced spend recorded.” Raw PR complexity and spend are not measures of business value.

Repository agent readiness API​

Agent readiness measures static repository context separately from productivity scores. These endpoints require administrator access and the GitHub delivery-attribution capability. All paths below start with /api/v1/productivity/readiness. Responses use Cache-Control: no-store.

GET /api/v1/config/capabilities reports repositoryAgentReadiness for readiness route availability. This requires the deployment capability, repository storage, and successful registry initialization in both normal and demo modes; seeded delivery-attribution data does not enable readiness. IDs are opaque strings; timestamps are RFC 3339 UTC strings. Nullable fields represent unavailable or inapplicable values; a null score is never a measured zero.

Method and pathRequestSuccessful response
GET /settingsNoneConfiguration fields plus builtinVersion, limits, and queue
PUT /settingsJSON expectedVersion (current integer version), enabled (boolean), and the complete rules arraySaved configuration, builtinVersion, and limits; queue is omitted
POST /previewJSON rules, repositoryName (owner/repository), and paths (literal sample paths)items, each containing ruleId, state, and matching paths; checks applicability and paths, never content or GitHub
GET /repositories?repositoryIds=id1,id2One comma-separated parameter containing 1–100 distinct inventory IDsitems containing repository summaries in requested order; an unknown ID has only repositoryId and status: "not_found"
GET /repositories/{id}Inventory ID in the pathRepository summary with rule results and pinned configurations in its assessments
GET /repositories/{id}/historyOptional limit (1–100, default 25) and returned cursoritems containing assessments with rule results, configurations containing their immutable configurations, and nullable nextCursor
POST /repositories/{id}/scanJSON requestKey (1–128 letters, digits, _, or -) and expectedConfigVersionHTTP 202 with a job; accepting a job does not mean its scan succeeded

Settings and preview bodies are limited to 256 KiB; scan bodies to 4 KiB. Send one JSON object containing only supported request fields. Preview accepts at most 1,000 sample paths, each at most 256 UTF-8 bytes. Reuse a scan request key with the same parameters to retrieve the same admitted job for up to 24 hours, while the repository remains eligible. New requests use the current settings version. Settings writes and new manual scan requests are audited atomically.

History follows publication order, newest first. Pass nextCursor unchanged to continue the same repository traversal; it preserves the first page's upper publication boundary. New observations do not enter that traversal. Historical states and configurations are never relabeled with current settings.

Configuration and rules​

A configuration contains version, enabled, rules, and createdAt. Unsaved built-in defaults have version 0 and createdAt: null. builtinVersion identifies the built-in rule set. queue contains queuedCount (queued, running, and retry-wait jobs combined) and nullable oldestDueAt (eligible overdue work not yet admitted). It does not treat admitted retries as unadmitted work.

Rule fieldMeaning
id, nameUnique ID matching [a-z][a-z0-9-]{0,63} and nonempty display name (up to 120 characters)
description, remediationExplanatory and corrective UTF-8 text, each up to 1,024 bytes
enabled, weightBoolean and integer weight 0–100; disabled and zero-weight rules do not contribute to the denominator
repositoriesCase-insensitive owner/repository or owner/* selectors; an empty array applies to all repositories
alternatives1–8 alternatives; any passing alternative satisfies the rule
Alternative paths1–16 repository-relative glob patterns; path matching is case-sensitive and fixed excluded directory segments still apply
Alternative checknonempty or text
Alternative patternRequired Go/RE2-style regular expression for text, up to 2 KiB; omit it for nonempty

Configurations have 1–50 rules and at least one enabled positive-weight rule. A text check searches supported text content; a path match alone is not a pass. Preview states are unknown for an applicable rule, disabled, or not_applicable; preview never reports a content-based readiness score.

The returned limits object publishes the active bounds: treeEntries, treePathBytes, requests, candidateBlobs, blobBytes, contentBytes, durationSeconds, treeResponseBytes, blobResponseBytes, symlinkHops, pathResolutionWorkBytes, rules, alternatives, pathsPerAlternative, pathBytes, patternBytes, configurationBytes, previewTextBytes, assessmentBytes, assessmentTextBytes, historyAssessmentBytes, detailResponseBytes, and excludedSegments. See the collection limits and recovery guide for operational behavior.

Summary, assessment, and job fields​

ObjectFields and interpretation
Repository summaryrepositoryId, availability status, collectionStatus, activeConfigVersion, evaluatorVersion, applicabilitySignature, nullable currentAssessment, nullable lastCompleteAssessment, nullable latestJob, stale, staleReasons, and nullable dueAt
Assessment identityid, configVersion, evaluatorVersion, applicabilitySignature, branch, nullable commitSha, and nullable reusedFrom (prior assessment ID whose evidence was reused)
Assessment timestartedAt, nullable observedAt (source observation time), and completedAt; a scan verifies its source again before reusing evidence, and previous history entries remain unchanged
Assessment resultstatus, nullable reason, passedWeight, applicableWeight, nullable scoreBasisPoints, and nullable score
Detailed assessmentruleResults; detail additionally embeds configuration, while history supplies configurations once per page
Rule resultruleId, state, weight, nullable reason, nullable evidence, diagnostics, and diagnosticsTruncated
Evidencepath, resolvedPath, blobSha, byteCount, zero-based alternativeIndex, nullable one-based line, and commit-pinned url; file bodies and matching snippets are not returned
Diagnosticpath, zero-based alternativeIndex, and safe reason
Jobid, status, configVersion, attempt, queuedAt, nullable startedAt, completedAt, retryAt, and reason

For a complete assessment, score is the passed applicable weight divided by all applicable weight, normalized to 0–100 and rounded to two decimal places. scoreBasisPoints is that score multiplied by 100. Incomplete assessments and assessments with no applicable rules have null scores. A previous complete assessment remains separate from the current incomplete attempt.

Status fieldValues
Summary statuseligible, disabled, not_onboarded, out_of_scope, unsupported_provider, needs_access, or not_found
Summary collectionStatusnot_scanned, complete, incomplete, or not_applicable
Assessment statuscomplete, incomplete, or not_applicable
Rule statepass, fail, unknown, not_applicable, or disabled
Job statusqueued, running, retry_wait, succeeded, failed, superseded, or cancelled
Summary staleReasonsAny combination of age, branch_changed, identity_changed, config_changed, evaluator_changed, applicability_changed, scan_failed, and unavailable

Reason and error codes​

Reasons are safe identifiers, never raw provider errors or file contents.

Reason codesMeaning
missing, empty, pattern_mismatchNo candidate exists, supported content is empty, or the text condition did not match
disabled, selector_mismatch, no_applicable_rulesRule is disabled/zero-weight, repository selector did not match, or no rule contributes to this repository
no_commit, needs_access, source_unavailable, rate_limitedNo readable default-branch commit, insufficient access, unavailable provider data, or provider throttling
tree_truncated, tree_entry_limit, tree_path_limitIncomplete tree traversal or a tree metadata bound
request_limit, candidate_limit, content_budget, regex_budget, path_budget, response_limit, evidence_limit, scan_timeoutA request, candidate, decoded-content, matching-work, path-resolution, response, retained-evidence, or elapsed-time bound prevented complete evaluation
unsupported_contentContent cannot be used as supported text, including binary, invalid UTF-8, Git LFS pointers, or oversized files
link_cycle, link_escape, link_depth, link_missing, link_excluded, submodule_boundarySymlink traversal could not safely establish same-repository evidence
incomplete_evidenceOne or more applicable rules remain unknown

Job reasons can also use availability codes above, identity_changed, superseded, or cancelled when current repository or configuration state invalidates queued or running work.

API errors contain error and code. Common responses are 400 invalid_request, 403 capability_disabled or demo_mode, 404 not_found, 409 version_conflict or an availability code, 413 body_too_large, 422 response_too_large, and 500 operation_failed. Scan admission limits return 429 scan_throttled or queue_full, with retryAt and a Retry-After header. Unavailable repository storage or failed registry initialization returns 503 storage_unavailable on every readiness route. Reload settings after a version conflict and respect the supplied retry time.

Personal PR workflow API​

Use /api/v1/dev/activity/me/pr-workflow to query the authenticated account's PR workflow metrics. Active admin, viewer, builder and developer accounts can use a personal account-bound credential. The server selects the current user; scope, user, tenant and catalog overrides are rejected.

Create a context with POST /api/v1/dev/activity/me/pr-workflow/context:

{
"period": {"preset": "last_30_days"},
"preferenceRevision": "1",
"prFilters": {"branches": [], "excludeDelivery": []}
}

Use your current reporting timezone preference revision. The response contains contextVersion: 1, the personal scope, resolved period and six PR workflow capabilities. This personal contract is separate from the Admin metric catalog.

Use the returned token with POST /api/v1/dev/activity/me/pr-workflow/overview:

{"contextToken":"<token>","resolution":"day","previousPeriod":true}

The response includes all six workflow metrics, the reporting period, aligned series, current and per-bucket PR cohorts, and optional previous-period metrics and differences. To select a custom before/after window, supply comparisonPeriod with a custom period instead of previousPeriod: true. Comparison windows must be complete, nonoverlapping and equal in local-day length. A zero baseline has no percentage change.

For selected metrics, use POST /api/v1/dev/activity/me/pr-workflow/trends:

{"contextToken":"<token>","resolution":"week","metrics":["pr_time_to_merge","copilot_reviews_per_pr","copilot_review_turnaround"]}

Personal responses contain workflow evidence and sample coverage. They do not contain peer scores or rankings. Counts, duration units, merge/submission date bases and cumulative turnaround semantics match the Admin personal scope.

Use /evidence for the evidence request described above, and /exports for its retained CSV export. Both paths are relative to /api/v1/dev/activity/me/pr-workflow. Evidence filters only narrow the caller's PRs. If entity is supplied, it must identify that same user. Exports support source: "pr_workflow" and the same evidence field catalog.

Queries can return HTTP 202 with statusUrl; poll that returned personal URL. Ready exports provide downloadUrl. Paging retains the original query scope. Personal and Admin contexts, jobs, cursors and exports cannot be interchanged, even for the same administrator. Account, ownership, timezone and source changes can invalidate results; recreate the reporting context when it becomes stale. Contexts expire within 15 minutes, and job/export creation does not extend them.

Requests use the shared analytics concurrency and database limits. JSON responses also obey the configured response-size limit. Every call checks current access; polls are not cached or combined. CSV downloads retain the 64 MiB export limit and allow one active download per replica, including response delivery. Their execution and socket writes share a deadline of at most 30 seconds, shortened by the configured timeout or an earlier request deadline. If admission is full, retry HTTP 503 responses using Retry-After.