Effectiveness Dimensions
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.
| Method | Behavior |
|---|---|
GET | Returns the stored configuration and shipped defaults. |
PUT | Replaces the full configuration document. |
PATCH | Atomically 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 key | Display name |
|---|---|
feature-development | Feature development |
bug-fix | Bug fix |
refactoring | Refactoring |
infrastructure-change | Infrastructure change |
reliability-security | Reliability & security |
maintenance | Maintenance |
documentation | Documentation |
testing | Testing |
engineering-enablement | Engineering enablement |
unknown | Unclassified |
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 requireexpectedVersion. OptionalrecomputeFromandrecomputeToare 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), optionalentity,team,project,repository,branches,excludeDelivery,q,sort,page, andpageSize. 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 idempotencyrequestKey, optionalrepositoryID, andfrom/to, orretryFailed: trueto resume failed checkpoints.
The overview API accepts these PR filters as URL query parameters:
| Parameter | Format and behavior |
|---|---|
branches | JSON 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. |
excludeDelivery | Comma-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 ID | Aggregate meaning | Date basis |
|---|---|---|
pr_time_to_merge | Median creation-to-merge seconds | PR merge time |
pr_time_to_merge_p90 | Nearest-rank p90 creation-to-merge seconds | PR merge time |
copilot_reviews | Number of submitted Copilot review objects | Review submission time |
copilot_reviews_per_pr | Mean lifetime pre-merge reviews per merged PR | PR merge time |
copilot_review_turnaround | Median cumulative request-to-submission seconds per completely timed PR | PR merge time |
copilot_review_turnaround_p90 | Nearest-rank p90 cumulative request-to-submission seconds per completely timed PR | PR 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:
- Metric extraction per developer per complexity band, from that developer's merged PRs in the week.
- 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.
- Band aggregation: percentiles are combined across bands, weighted by each band's effective sample.
- Shrinkage: the result is pulled toward 50 by
n_eff / (n_eff + k)(defaultk = 5), wheren_effis the complexity-weighted PR sample. Small samples land near 50 by construction.
Dimensions
| Dimension | What it measures | How it is computed | Direction |
|---|---|---|---|
| AI-assisted work | How much of the developer's merged work is AI-assisted | AI PRs (proxy-witnessed sessions or self-declared markers) ÷ merged PRs, capped at the org target (default 80%) — share beyond the target earns nothing | Higher is better |
| Review efficiency | How little review churn the developer's PRs need | Average review rounds per PR, where rounds = change-requested reviews + 1 | Lower rounds → higher score |
| Model Fit | Whether the AI model tier matches task complexity | Share 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-provisioned | Higher is better |
| Time to merge | How quickly work goes from ready to merged | Average open→merge time; measured from ready-for-review when the PR started as a draft | Faster → higher score |
| Rework signals | How often merged work holds up | 1 − (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 discipline | AI spend discipline on witnessed work | Recorded AI cost per complexity-weighted PR, scored by closeness to the cohort median. This measures relative spending, not business value. Only AI-witnessed PRs count | Middle is best |
| AI practices | Adherence to the org's configured AI working practices | Met ÷ 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 weight | Higher 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 work | Review efficiency | Model Fit | Time to merge | Rework signals | Resource discipline |
|---|---|---|---|---|---|
| 0.20 | 0.20 | 0.15 | 0.15 | 0.15 | 0.05 |
Bands
Band assignment happens per cohort per week, over developers with at least low confidence, by composite score:
| Band | Rule | Minimum cohort |
|---|---|---|
top_10 | Top ⌈10%⌉ | 10 scored developers |
top_25 | Next, through ⌈25%⌉ | 4 scored developers |
above_median | Composite ≥ cohort median | — |
below_median | Everyone else scored | — |
insufficient | Complexity-weighted sample below 2 | shown 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):
| Label | Threshold |
|---|---|
high (Strong PR evidence) | n_eff ≥ 10 |
medium (Moderate PR evidence) | n_eff ≥ 5 |
low (Limited PR evidence) | n_eff ≥ 2 |
insufficient | below 2 — unranked |
PR Complexity Score
The 0–100 normalizer behind band comparisons and sample weights. Deterministic, computed per PR from:
| Feature | Weight | Scaling |
|---|---|---|
| Lines added | 0.30 | log-scaled, saturates ≈ 1,000 LOC |
| Files changed | 0.20 | saturates at 20 files |
| Module spread | 0.15 | distinct top-level directories beyond the first, saturates at 6 |
| Languages | 0.10 | distinct file extensions beyond the first, saturates at 5 |
| Test-file share | 0.10 | share of changed files that are tests |
| Risk paths | 0.15 | flags 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 path | Request | Successful response |
|---|---|---|
GET /settings | None | Configuration fields plus builtinVersion, limits, and queue |
PUT /settings | JSON expectedVersion (current integer version), enabled (boolean), and the complete rules array | Saved configuration, builtinVersion, and limits; queue is omitted |
POST /preview | JSON 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,id2 | One comma-separated parameter containing 1–100 distinct inventory IDs | items containing repository summaries in requested order; an unknown ID has only repositoryId and status: "not_found" |
GET /repositories/{id} | Inventory ID in the path | Repository summary with rule results and pinned configurations in its assessments |
GET /repositories/{id}/history | Optional limit (1–100, default 25) and returned cursor | items containing assessments with rule results, configurations containing their immutable configurations, and nullable nextCursor |
POST /repositories/{id}/scan | JSON requestKey (1–128 letters, digits, _, or -) and expectedConfigVersion | HTTP 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 field | Meaning |
|---|---|
id, name | Unique ID matching [a-z][a-z0-9-]{0,63} and nonempty display name (up to 120 characters) |
description, remediation | Explanatory and corrective UTF-8 text, each up to 1,024 bytes |
enabled, weight | Boolean and integer weight 0–100; disabled and zero-weight rules do not contribute to the denominator |
repositories | Case-insensitive owner/repository or owner/* selectors; an empty array applies to all repositories |
alternatives | 1–8 alternatives; any passing alternative satisfies the rule |
Alternative paths | 1–16 repository-relative glob patterns; path matching is case-sensitive and fixed excluded directory segments still apply |
Alternative check | nonempty or text |
Alternative pattern | Required 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
| Object | Fields and interpretation |
|---|---|
| Repository summary | repositoryId, availability status, collectionStatus, activeConfigVersion, evaluatorVersion, applicabilitySignature, nullable currentAssessment, nullable lastCompleteAssessment, nullable latestJob, stale, staleReasons, and nullable dueAt |
| Assessment identity | id, configVersion, evaluatorVersion, applicabilitySignature, branch, nullable commitSha, and nullable reusedFrom (prior assessment ID whose evidence was reused) |
| Assessment time | startedAt, nullable observedAt (source observation time), and completedAt; a scan verifies its source again before reusing evidence, and previous history entries remain unchanged |
| Assessment result | status, nullable reason, passedWeight, applicableWeight, nullable scoreBasisPoints, and nullable score |
| Detailed assessment | ruleResults; detail additionally embeds configuration, while history supplies configurations once per page |
| Rule result | ruleId, state, weight, nullable reason, nullable evidence, diagnostics, and diagnosticsTruncated |
| Evidence | path, resolvedPath, blobSha, byteCount, zero-based alternativeIndex, nullable one-based line, and commit-pinned url; file bodies and matching snippets are not returned |
| Diagnostic | path, zero-based alternativeIndex, and safe reason |
| Job | id, 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 field | Values |
|---|---|
Summary status | eligible, disabled, not_onboarded, out_of_scope, unsupported_provider, needs_access, or not_found |
Summary collectionStatus | not_scanned, complete, incomplete, or not_applicable |
Assessment status | complete, incomplete, or not_applicable |
Rule state | pass, fail, unknown, not_applicable, or disabled |
Job status | queued, running, retry_wait, succeeded, failed, superseded, or cancelled |
Summary staleReasons | Any 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 codes | Meaning |
|---|---|
missing, empty, pattern_mismatch | No candidate exists, supported content is empty, or the text condition did not match |
disabled, selector_mismatch, no_applicable_rules | Rule is disabled/zero-weight, repository selector did not match, or no rule contributes to this repository |
no_commit, needs_access, source_unavailable, rate_limited | No readable default-branch commit, insufficient access, unavailable provider data, or provider throttling |
tree_truncated, tree_entry_limit, tree_path_limit | Incomplete tree traversal or a tree metadata bound |
request_limit, candidate_limit, content_budget, regex_budget, path_budget, response_limit, evidence_limit, scan_timeout | A request, candidate, decoded-content, matching-work, path-resolution, response, retained-evidence, or elapsed-time bound prevented complete evaluation |
unsupported_content | Content 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_boundary | Symlink traversal could not safely establish same-repository evidence |
incomplete_evidence | One 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.