Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/kernel/src/__tests__/adapter-manifest-coverage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Every machine adapter the kernel can build has a default evidence-emitter
* manifest in @pcc/spec. Without one, auto-onboarding writes free-text tier-0
* evidence for the device and the provenance planner sees it supply nothing,
* which is how the liquid handlers were capped at tier 0.
*/

import { describe, it, expect } from "vitest";
import { ADAPTER_DEFAULT_MANIFESTS } from "@pcc/spec";
import { listRegisteredMachineAdapters } from "../adapter-factory.js";

describe("adapter default manifests cover the kernel's machine adapters", () => {
it("has a default manifest for every registered machine adapter", () => {
const registered = listRegisteredMachineAdapters();
expect(registered.length).toBeGreaterThan(0);
const missing = registered.filter((type) => !(type in ADAPTER_DEFAULT_MANIFESTS));
expect(missing).toEqual([]);
});

it("includes the liquid handlers", () => {
for (const type of ["opentrons", "hamilton"]) {
expect(listRegisteredMachineAdapters()).toContain(type);
expect(ADAPTER_DEFAULT_MANIFESTS[type]?.subject).toEqual({ kind: "adapter", ref: type });
}
});
});
12 changes: 10 additions & 2 deletions packages/spec/src/__tests__/emitter-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe("buildEvidenceTiersFromEmits — the digital-receipt core is tier-1 eli

describe("adapter default manifests", () => {
it("ships a manifest for every kernel adapter family", () => {
for (const t of ["octoprint", "ipp", "opcua", "sila", "modbus", "generic-http", "mock"]) {
for (const t of ["octoprint", "ipp", "opcua", "sila", "modbus", "opentrons", "hamilton", "generic-http", "mock"]) {
expect(getAdapterManifest(t)).toBeDefined();
expect(EvidenceEmitterManifestSchema.safeParse(getAdapterManifest(t)).success).toBe(true);
}
Expand All @@ -152,7 +152,7 @@ describe("adapter default manifests", () => {
});

it("every digital adapter is eligible-by-default at tier 1", () => {
for (const t of ["octoprint", "ipp", "opcua", "sila", "modbus", "generic-http"]) {
for (const t of ["octoprint", "ipp", "opcua", "sila", "modbus", "hamilton", "generic-http"]) {
const { evidence, structured } = buildAdapterEvidence(t);
expect(structured).toBe(true);
const report = computeCsdEligibility({ url: `pcc://adapter/${t}`, evidence });
Expand All @@ -173,6 +173,14 @@ describe("adapter default manifests", () => {
expect(computeCsdEligibility({ url: "pcc://cam", evidence }).eligibleTier).toBe(1);
});

it("opentrons claims only the tier-0 floor until its events map to the vocabulary", () => {
// Its real run path emits protocol_uploaded / run_action / run_* and no
// real completion event (evidence review of #418).
const { evidence, structured } = buildAdapterEvidence("opentrons");
expect(structured).toBe(true);
expect(computeCsdEligibility({ url: "pcc://opentrons", evidence }).eligibleTier).toBe(0);
});

it("mock is honestly capped at tier 0 (cannot attest a real execution)", () => {
const { evidence } = buildAdapterEvidence("mock");
expect(computeCsdEligibility({ url: "pcc://mock", evidence }).eligibleTier).toBe(0);
Expand Down
23 changes: 20 additions & 3 deletions packages/spec/src/evidence/adapter-manifests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/**
* Adapter + device-role DEFAULT emitter manifests — the supply-side DATA.
*
* One entry per adapter family present in packages/kernel/src/adapters/
* (octoprint / ipp / opcua / sila / modbus / generic-http / mock) and per
* evidence-only device ROLE (camera / sensor). Each declares, in the bounded
* One entry per machine adapter the kernel's factory registers
* (packages/kernel/src/adapter-factory.ts: octoprint / ipp / opcua / sila /
* modbus / opentrons / hamilton / generic-http / mock; a kernel test keeps the
* two in step) and per evidence-only device ROLE (camera / sensor).
* PyLabRobot is not a kernel AdapterType, so it has no entry here. Each declares, in the bounded
* vocabulary, which primitives that adapter/role can EMIT. Auto-discovery reads
* these to write structured `evidence.tierN.primitives[]` instead of free-text
* `required[]`, turning an onboarded device from tier-0-capped into
Expand Down Expand Up @@ -91,6 +93,21 @@ export const ADAPTER_DEFAULT_MANIFESTS: Readonly<Record<string, EvidenceEmitterM
// PLC / fieldbus via Modbus.
modbus: adapterManifest("modbus", [...DIGITAL_RECEIPT_CORE]),

// Liquid handlers (evidence review of #418, #3251). Neither adapter feeds
// LogCaptureService (only printer-log-adapter does), so no
// machine.execution_log, and neither emits telemetry.
// - hamilton (kernel/src/adapters/hamilton-adapter.ts): real runs emit the
// vocabulary's execution_started / execution_completed /
// execution_failed, inside the kernel job pipeline whose EvidenceEmitter
// signs each job-step bundle and marks mock/simulated events fabricated
// (kernel/src/evidence-emitter.ts). It carries the receipt core.
// - opentrons (kernel/src/opentrons/adapter.ts): real runs emit only
// non-vocabulary types (protocol_uploaded, run_action, run_*) and no
// real completion event, so it claims the tier-0 floor only, until its
// events are mapped to the vocabulary (evidence offered the mapping).
hamilton: adapterManifest("hamilton", [...DIGITAL_RECEIPT_CORE]),
opentrons: adapterManifest("opentrons", [{ id: "decl.self_attested" }]),

// Arbitrary external HTTP capability. Adds target-system confirmation via the
// upstream's own channel (api) — a natural fit for HTTP-backed work.
"generic-http": adapterManifest("generic-http", [
Expand Down
Loading