QA Surfaces

JobCache's QA platform follows the same three-worker shape used everywhere else in the repo: API for state, MCP for agents, UI for humans. The shape is intentional — every product (CV, JobCache, future ones) has the same operator interface for QA evidence, so an agent that knows one knows them all.

The three workers

Worker Host Role
jobcache-qa-api api.qa.jobcache.corbet.ch Workflow state, queue records, trigger allowlist enforcement
jobcache-qa-mcp mcp.qa.jobcache.corbet.ch MCP tool surface for agents — same operations as the UI
jobcache-qa-ui qa.jobcache.corbet.ch SvelteKit dashboard: Queue view, per-item drill-down, manual triggers

The API is the source of truth. The MCP forwards calls to it through a service binding. The UI uses the API as its own backend. There is no parallel state path.

Triggering QA work

QA executions are not free — they consume CI quota, scrape quota, and LLM tokens. The trigger endpoint is allowlisted at the API:

  • The list of permitted workflow IDs is in the API's environment.
  • A trigger request for an unknown workflow ID returns 403, not 404, to make the allowlist behaviour explicit.
  • The UI's trigger page mirrors the same allowlist server-side so a forged client cannot bypass it.

This applies symmetrically to human triggers (from the UI) and agent triggers (from the MCP). Agents do not get a wider surface than humans.

The Queue view

Every trigger request creates a CockroachDB-backed queue item projected through /queue/[workflowId]. The row carries:

  • Workflow ID and trigger source (human, MCP agent, CI)
  • Start / finish timestamps
  • Status (queued / running / passed / failed / cancelled)
  • Evidence references (workflow URL, packages, logs)
  • Operator notes, when added later

The table is append-only for the same reason operator_actions is: the queue item is the evidence. If an item is misclassified, a follow-up row corrects the misunderstanding; the original row stays.

Device architecture QA

Distributed device work adds more evidence surfaces to the same QA platform: contract fixtures, fake devices, canary tasks, duplicate assignment checks, quarantine and rollback checks, resource-policy compliance, and the zero-server-compute guardrail. The day-zero gate list lives in jobcache/wiki/content/architecture/device-qa.md.

Do not add a trigger allowlist entry for a device gate until the matching CircleCI job or pinned spec dispatch exists. Unknown or placeholder device gates should fail closed instead of being silently mapped to runner_smoke.

The broker path in broker-flow.md is the QA spine for distributed ingest. QA should prove capability reports, lease assignment, observation submission, current ad payload cells, and device-trust updates with fake devices before an Adapter is allowed to run broadly. The CareerVector bottom-up path adds one mandatory proof: a workspace job.create/job.update that carries a public-ad Observation must commit the private workspace op and the D1 jobcache_commands submit_observation row in the same batch, and the drain must apply that Observation through submitObservation without direct Cockroach access from CareerVector or post-commit best-effort HTTP from /ops.

Current ops visibility maps that spine into GET /devices as brokerPath. The summary is deliberately cheap: it only rolls up the rows already read for the device fleet panel and MCP tool.

Spine step Ops field Backing proof
Device capability allowed now brokerPath.capability jobcache/ops/api/src/routers/devices.test.ts
Broker lease assignment brokerPath.lease jobcache/ops/api/src/routers/devices.test.ts
Accepted Observation / current cells brokerPath.observation and facts jobcache/ops/ui/src/lib/api-ops-client.test.ts
Device trust evidence brokerPath.trust jobcache/ops/api/src/routers/devices.test.ts

Why the MCP exists

An agent that can drive QA from outside the UI gives us the parity we promise everywhere else: anything the operator can do from the dashboard, an agent can do over MCP with the same auth posture and the same allowlist. The MCP tools are intentionally a strict subset of the UI's surface — discovery and triggering, not retrospective editing.

See also

  • jobcache/wiki/content/architecture/device-qa.md — day-zero QA contract for distributed devices.
  • jobcache/wiki/content/architecture/broker-flow.md — broker/observation path for distributed ingest.
  • jobcache/qa/api/README.md — route reference (if/when added).
  • jobcache/qa/ui/src/routes/queue/ — UI for the Queue view.
  • qa/behaviours/jobcache-qa-surfaces.feature — QA-side behaviours that VERIFY this surface.
Source: jobcache/wiki/content/architecture/qa-surfaces.md