evaluation-controller¶
The remote skill-evaluation engine, and the one optional controller: deployments that never submit remote
evaluations simply do not run it. It serves the evolve-facing HTTP API
(workspace upload, submission, snapshot, SSE monitoring, cancellation) and runs the reconcilers that expand each
submitted Evaluation into EvaluationUnit children, schedule them through the sandboxed agent-Job machinery with
bounded concurrency, collect each pod's result stream, and expire finished evaluations on a TTL.
The division of knowledge is strict: evolve owns every evaluation semantic (specs, grading, the LLM judge, baselines),
co-located with the uploaded workspace bundle inside the pod, which runs evolve exec-unit from a per-harness
evolve-runner image. Patchy owns scheduling, sandboxing, and state — it interprets only the pod's result and fatal
events; the finished results entry is opaque payload stored in a per-unit ConfigMap the client reassembles locally.
Flags¶
The shared flags (--listen-addr is the API's own address), plus:
| Flag | Env | Default | Purpose |
|---|---|---|---|
--namespace |
PATCHY_NAMESPACE |
POD_NAMESPACE |
Namespace the Evaluations live in |
--kubeconfig |
PATCHY_KUBECONFIG |
in-cluster | Kubeconfig path for running outside the cluster |
--health-addr |
PATCHY_HEALTH_ADDR |
:8081 |
healthz/readyz probe listen address |
--auth-config |
PATCHY_AUTH_CONFIG |
(required) | Mounted authentication config (see below) |
--max-concurrent-units |
PATCHY_MAX_CONCURRENT_UNITS |
4 |
Evaluation units running at once, across all submissions |
--max-units-per-evaluation |
PATCHY_MAX_UNITS_PER_EVALUATION |
200 |
Largest accepted submission, in units |
--max-submission-bytes |
PATCHY_MAX_SUBMISSION_BYTES |
8388608 |
Largest accepted submission body |
--max-workspace-bytes |
PATCHY_MAX_WORKSPACE_BYTES |
67108864 |
Largest accepted workspace bundle |
--evaluation-ttl |
PATCHY_EVALUATION_TTL |
72h |
Retention of finished evaluations (spec.ttlSecondsAfterFinished overrides; 0 forever) |
--agent-namespace |
PATCHY_AGENT_NAMESPACE |
patchy-agents |
Namespace evaluation Jobs run in |
--agent-service-account |
PATCHY_AGENT_SERVICE_ACCOUNT |
patchy-agent |
Service account evaluation Jobs run as |
--job-deadline |
PATCHY_JOB_DEADLINE |
2h |
activeDeadlineSeconds on evaluation Jobs |
--job-ttl |
PATCHY_JOB_TTL |
1h |
ttlSecondsAfterFinished on evaluation Jobs |
--artifact-base-url |
PATCHY_ARTIFACT_BASE_URL |
source-controller svc :9790 |
Artifact endpoint agent pods fetch workspace bundles from |
--artifact-upload-url |
PATCHY_ARTIFACT_UPLOAD_URL |
source-controller svc :9791 |
source-controller's internal blob endpoint uploads stream to |
--internal-upload-token-file |
PATCHY_INTERNAL_UPLOAD_TOKEN_FILE |
(unset) | Shared bearer token for the internal blob endpoint (defense-in-depth atop NetworkPolicy) |
--evolve-claude-image |
PATCHY_EVOLVE_CLAUDE_IMAGE |
(unset) | evolve-runner image with the claude CLI; unset disables the claude evaluation runner |
--evolve-codex-image |
PATCHY_EVOLVE_CODEX_IMAGE |
(unset) | evolve-runner image with the codex CLI |
--evolve-copilot-image |
PATCHY_EVOLVE_COPILOT_IMAGE |
(unset) | evolve-runner image with the copilot CLI |
--evolve-fake-image |
PATCHY_EVOLVE_FAKE_IMAGE |
(unset) | Fake evolve runner for dev/e2e (replays fixtures, no credential) |
--harnesses |
PATCHY_HARNESSES |
(auto) | Restrict enabled harnesses (default: any configured runner whose credential Secret exists) |
Credentials reuse the shared --<harness>-secret{,-key,-env} flags
(investigation-controller): one Secret per model vendor in the agents namespace,
whichever fleet exercises it. The evolve-runner images ship from the evolve repository
(ghcr.io/bitwise-media-group/evolve-runner-<harness>), pinned by the operator like today's agent images; a
submission's clientVersion is recorded and skew is warned, never enforced.
Authentication configuration¶
--auth-config points at a YAML file, conventionally a mounted Secret (patchy-evaluation-auth, key config.yaml).
Unlike the status server there is no unauthenticated posture — every route except GET /api/v1/auth/info is
bearer-authenticated, so a missing or invalid file is a startup error.
mode: oidc # none | oidc
oidc: # mode: oidc only
issuerURL: https://sso.example.com
clientID: evolve # the PUBLIC (PKCE) client evolve logs in as — no client secret
# scopes: [openid, offline_access, profile, email, groups]
claims: # claim NAMES, mapped onto the identity
username: email
groups: groups
mode: none (dev/e2e) short-circuits authentication with a fixed identity and bypasses authorization entirely.
evolve login needs zero OIDC configuration client-side: it reads GET /api/v1/auth/info (issuer, clientID, scopes)
and runs the authorization-code + PKCE flow against the issuer with a localhost redirect. The operator prerequisite is a
public OIDC client (no secret) allowing http://127.0.0.1:*/callback redirects.
Authorization¶
Native Kubernetes RBAC on the evaluations resource is the entire authorization surface — no custom verbs, no
admission-policy involvement:
| Verb | Grants |
|---|---|
create |
Submit evaluations and upload workspace bundles |
get |
Read snapshots and stream the SSE monitor |
delete |
Cancel an evaluation |
Bind users or SSO groups (as mapped by the claims configuration) to a role carrying those verbs — the kustomize base
ships an example tier (patchy-evaluations-submitter in rbac.users.example.yaml), and the chart renders one behind
evaluationController.rbac.userRoles.
Workspace bundles¶
Bundles are content-addressed by sha256: HEAD /api/v1/workspaces/{digest} dedupes before upload,
PUT /api/v1/workspaces/{digest} streams the gzip tarball through to source-controller's NetworkPolicy-gated internal
endpoint (:9791), which verifies the digest before caching. The digest is also the fetch capability the agent pod
downloads the bundle with. Bundles unused for --workspace-retention (source-controller, default 168h) are swept; a
needed-but-swept bundle settles its unit as WorkspaceLost, prompting the client to re-upload and resubmit.