diff --git a/packages/kernel/src/__tests__/adapter-manifest-coverage.test.ts b/packages/kernel/src/__tests__/adapter-manifest-coverage.test.ts new file mode 100644 index 000000000..cf121308e --- /dev/null +++ b/packages/kernel/src/__tests__/adapter-manifest-coverage.test.ts @@ -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 }); + } + }); +}); diff --git a/packages/spec/src/__tests__/emitter-manifest.test.ts b/packages/spec/src/__tests__/emitter-manifest.test.ts index 265a9c7d2..0e0361285 100644 --- a/packages/spec/src/__tests__/emitter-manifest.test.ts +++ b/packages/spec/src/__tests__/emitter-manifest.test.ts @@ -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); } @@ -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 }); @@ -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); diff --git a/packages/spec/src/evidence/adapter-manifests.ts b/packages/spec/src/evidence/adapter-manifests.ts index a12116497..e1d94c5bb 100644 --- a/packages/spec/src/evidence/adapter-manifests.ts +++ b/packages/spec/src/evidence/adapter-manifests.ts @@ -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 @@ -91,6 +93,21 @@ export const ADAPTER_DEFAULT_MANIFESTS: Readonly