Skip to content

feat(gateway): operator status derives readiness from canonical rows (ADK D4(a)) - #428

Draft
LamaSu wants to merge 1 commit into
masterfrom
feat/d4a-operator-readiness
Draft

LamaSu wants to merge 1 commit into
masterfrom
feat/d4a-operator-readiness

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 28, 2026

Copy link
Copy Markdown
Owner

What

ADK D4(a), PX-10/R40: GET /api/operators/:slug/status derives readiness from canonical rows, with the interface agreed with gateway in bus #2622.

The route called an operator ready once a kernel, a capability and a channel existed. Nothing checked that a device could execute or that any run had produced real evidence, so an operator with only a mock device and a self-attested setup test read as ready.

The response gains an aggregate readiness object, and status: "ready" now also depends on it.

Field Meaning
devices {registered, executable}, adapterReady A registered machine whose adapter the kernel can build (listRegisteredMachineAdapters()), excluding the simulator and the generic-http refusal. Sensors, cameras and adapters the kernel cannot build as machines (modbus, sila) do not count.
verifiedRun A completed job on one of the operator's kernels whose evidence verifies against that kernel's registered signer. The check uses SEAM-2's verifyDeviceSignedEvidence and registeredSignerInputFromColumns, reused, not copied, and the settlement gate is untouched.
runEvidence, setupTestEvidence Evidence classes only: verified, invalid, unregistered-signer, unverifiable, test-key, gateway-placeholder, self-attest or none. Keys, signatures and job or bundle ids are never included.
payout Always "not_supported" (R41).
openOffers Open job offers for the operator's capability types, or null when the offer store is absent.

Other changes:

  • The docstring said the endpoint was PUBLIC. It is not on the api-gate allowlist, so the doc now says it needs a key. Gateway said in #2622 to fix the doc, not the code.
  • Owner-only detail waits for WP-A (N2).

Why a setup test does not count: POST /api/setup/test-job never exercises the operator's machine (bus #3367). A kernel with no devices gets a self-attested bundle. Otherwise the job runs on the gateway's in-process KernelService, whose emitter signs with the zero-address test key.

The work examined is bounded: 10 completed runs per kernel and 10 bundles per run. The bound errs toward "not ready", never toward "ready".

Consumers

Tests

  • 29 new unit tests with real Ed25519 keys (tweetnacl), in onboarding-readiness.test.ts.
  • The route test grows from 7 to 18 cases. The two "ready" cases now seed a verified run. New cases cover self-attest, the test key, a wrong key, an unregistered signer, a failed job, another operator's run, a flood of newer failed jobs, mock-only devices, no secrets served, and open offers.
  • 10 of 10 mutants are killed.
  • Gateway suite: 3021 passed, 6 skipped. tsc is clean.

Review

The owner of this route is gateway (0600b204), and gateway reviews. The PR touches evidence verification (read-only) and auth-adjacent copy, so it also needs a cross-family pack under the operator's current process.

🤖 Generated with Claude Code

…(ADK D4(a))

GET /api/operators/:slug/status called an operator "ready" once a kernel,
a capability and a channel existed. Nothing checked that a device could
execute or that any run had produced real evidence, so an operator with
only a mock device and a self-attested setup test read as ready.

The response gains an aggregate readiness object (interface agreed with
gateway in bus #2622), and "ready" now also needs it:

- devices {registered, executable}, adapterReady: a registered machine
  whose adapter the kernel can build (listRegisteredMachineAdapters), and
  not the simulator or the generic-http refusal. Sensors, cameras and
  adapters the kernel cannot build as machines (modbus, sila) never count.
- verifiedRun: a completed job on one of the operator's kernels whose
  evidence verifies against that kernel's REGISTERED signer, using SEAM-2's
  verifyDeviceSignedEvidence and registeredSignerInputFromColumns (reused,
  not copied; the settlement gate is untouched). Self-attested, test-key,
  gateway-placeholder, wrongly signed and unregistered-signer evidence
  never counts, nor does a run that did not complete. The setup test job
  cannot satisfy this today: it self-attests, or runs on the gateway's
  in-process kernel with the zero-address test key (bus #3367).
- runEvidence and setupTestEvidence: evidence CLASSES, never keys,
  signatures or job ids.
- payout: "not_supported" (R41); openOffers: open job offers for the
  operator's capability types, or null when the offer store is absent.

The route's docstring claimed the endpoint was PUBLIC; it is not on the
api-gate allowlist, so the doc now says it needs a key (gateway, #2622:
fix the doc, not the code). Owner-only detail waits for WP-A (N2).

Examined work is bounded (10 completed runs per kernel, 10 bundles per
run) and errs toward "not ready", never toward "ready".

Tests: 29 unit tests with real Ed25519 keys (tweetnacl); the route test
goes from 7 to 18 cases (the two "ready" cases now seed a verified run;
new cases for self-attest, test key, wrong key, unregistered signer,
failed job, another operator's run, a flood of newer failed jobs,
mock-only devices, no secrets served, open offers). 10 of 10 mutants
killed. Gateway suite 3021 passed, 6 skipped; tsc clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Oct 3, 2026
… the run, so a mockMode or downgraded adapter never passes (N59 r3)

Astra's r2 HIGH (F2): deviceIsSimulated() guessed from the adapter's class name. An
OctoPrintAdapter in mockMode, an IppAdapter defaulting to mock, or an IppAdapter that
downgrades to mock at runtime keeps its real-sounding name, so a simulated completion
could return passed:true. Reproduced on a real KernelService (no vi.mock) at f77375d:
an ipp row with no adapterConfig, an octoprint row with mockMode:true, and an ipp row
with mockMode:false whose optional 'ipp' package is missing (a real downgrade). Each
returned passed:true or simulated:false.

deviceIsSimulated() now reads the adapter's evidence source, source.simulated. Every
built-in machine adapter sets it in mock mode (OctoPrint, OPC UA, IPP, Opentrons,
Hamilton, SiLA, the mocks), and IPP sets it when it downgrades. The route reads it after
the run, so a downgrade during the run counts. Fail closed: an unknown device, an adapter
with no evidence source, or a marker that is anything but true or false counts as
simulated. The class-name guess remains only as a last fallback. The comments that
overclaimed "real adapter" and "REAL device" now say what the route checks: recorded
ownership and local dispatch, not physical identity (that is D4a, #428).

Tests: setup-test-job-simulation.test.ts drives the real route against a real
KernelService with real @pcc/kernel adapters (14 tests, written by test-writer-uniform,
reviewed in the lane). 6 fail at f77375d: the three repros, and three marker unit
tests (a true marker, a non-boolean marker, no evidence source). A neutral, non-simulated
control adapter still passes. With setup.test.ts, 59/59; gateway tsc clean.

Agent: test-writer-uniform (tests)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBTXKQ2uqtcrsUtPnQdtf8

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant