openvibers/openvibe.contracts Commits

Reserve worker class capability names in the resource offer schema (#15)

OP OpenVibers committed 3 Oct 2026 · f177f2448b · 7 files, +90 −12

local checks ✓ 3/3 · CI ✓ 3/3 · review SHIP by Codex GPT-6 Sol

https://github.com/OpenVibers/OpenVibe.Contracts/pull/15

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 79818b39..47ecb1bd 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -54,10 +54,15 @@ path.
name its own project and id. Additive; no active contract changes.
**Reserved worker capability names for OpenVibe.Node** (plan T14 groundwork, additive): a Node advertises its runtimes
--as `worker:` capabilities in `platform.resource-offer@1`. The names `worker:function` and `worker:code` are reserved
--now; both already satisfy the `capabilities` pattern `^[a-z][a-z0-9-]*:[a-z0-9.-]+$`, so no schema change is needed.
--Only `worker:function` is advertised today; `worker:code` follows when the `code` runtime class ships. Four new valid
--fixtures for `platform.runtime-class@1` (`code`, `browser`, `linux`, `desktop`) complete its six `const` classes.
++as `worker:` capabilities in `platform.resource-offer@1`. The names are reserved in the contract now, not only by the
++`capabilities` pattern: `platform.resource-offer@1` gains `$defs.reservedWorkerCapabilities`, the six names
++`worker:function`, `worker:code`, `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` — one per
++`platform.runtime-class@1` class (`worker:` + the class) — and both the `capabilities` description and
++`platform.runtime-class@1` say so. A node advertises the name of each class it runs, so `worker:function` today and
++`worker:code` when the `code` class ships. Additive to validation: every existing offer stays valid, and other
++`worker:` names (`worker:ffmpeg`, `worker:ai-gpu`) stay ordinary capability names. New valid fixture `worker-classes`
++(an offer advertising all six reserved names). Four new valid fixtures for `platform.runtime-class@1` (`code`,
++`browser`, `linux`, `desktop`) complete its six `const` classes.
## 0.86.0 — 2026-10-03
ddiff --git a/contracts/platform/resource-offer.v1.json b/contracts/platform/resource-offer.v1.json
iindex cd827dad..8dfc66b5 100644
---- a/contracts/platform/resource-offer.v1.json
++++ b/contracts/platform/resource-offer.v1.json
@@@ -65,7 +65,7 @@
"type": "string",
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
},
-- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true"
++ "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name."
},
"capacity": {
"type": "object",
@@@ -400,5 +400,19 @@
}
}
}
-- ]
++ ],
++ "$defs": {
++ "reservedWorkerCapabilities": {
++ "description": "The reserved Fabric capability names of OpenVibe.Node's job worker, one per platform.runtime-class@1 value: `worker:` + the class. These six names are reserved in this contract — the `capabilities` pattern alone accepted any `worker:` name — and a node advertises only the classes it actually runs, in class order.",
++ "type": "string",
++ "enum": [
++ "worker:function",
++ "worker:code",
++ "worker:browser",
++ "worker:linux",
++ "worker:desktop",
++ "worker:gpu"
++ ]
++ }
++ }
}
ddiff --git a/contracts/platform/runtime-class.v1.json b/contracts/platform/runtime-class.v1.json
iindex b52ec5d5..78a280cb 100644
---- a/contracts/platform/runtime-class.v1.json
++++ b/contracts/platform/runtime-class.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/runtime-class.v1.json",
"title": "RuntimeClass",
-- "description": "platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names.",
++ "description": "platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names. Each class names one reserved Fabric capability in [email protected] — `worker:` + the class (worker:function, worker:code, worker:browser, worker:linux, worker:desktop, worker:gpu) — and a node advertises the name of each class it runs.",
"$comment": "Additive: a class may be added to v1; none is removed, renamed or reordered.",
"type": "string",
"oneOf": [
ddiff --git a/fixtures/platform.resource-offer/valid/worker-classes.json b/fixtures/platform.resource-offer/valid/worker-classes.json
nnew file mode 100644
iindex 00000000..61442da2
---- /dev/null
++++ b/fixtures/platform.resource-offer/valid/worker-classes.json
@@@ -0,0 +1,36 @@
++{
++ "offer_id": "off_worker_node_1",
++ "kind": "node",
++ "node_id": "worker-node-1",
++ "provider": "self",
++ "region": "home",
++ "trust": "user-owned",
++ "capabilities": [
++ "node:http",
++ "worker:function",
++ "worker:code",
++ "worker:browser",
++ "worker:linux",
++ "worker:desktop",
++ "worker:gpu"
++ ],
++ "capacity": {
++ "workers": {
++ "function": 2,
++ "code": 1,
++ "browser": 1,
++ "linux": 1,
++ "desktop": 1,
++ "gpu": 1
++ }
++ },
++ "health": {
++ "status": "up"
++ },
++ "pricing": {
++ "model": "per-second",
++ "marginal_usd_per_unit": 0.0001,
++ "unit": "s"
++ },
++ "updated_at": "2026-10-03T00:00:00Z"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex c35b77d4..a475063e 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -57806,7 +57806,7 @@
"type": "string",
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
},
-- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true"
++ "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name."
},
"capacity": {
"type": "object",
@@@ -58141,7 +58141,21 @@
}
}
}
-- ]
++ ],
++ "$defs": {
++ "reservedWorkerCapabilities": {
++ "description": "The reserved Fabric capability names of OpenVibe.Node's job worker, one per platform.runtime-class@1 value: `worker:` + the class. These six names are reserved in this contract — the `capabilities` pattern alone accepted any `worker:` name — and a node advertises only the classes it actually runs, in class order.",
++ "type": "string",
++ "enum": [
++ "worker:function",
++ "worker:code",
++ "worker:browser",
++ "worker:linux",
++ "worker:desktop",
++ "worker:gpu"
++ ]
++ }
++ }
}
},
"platform.workload-requirements": {
@@@ -60162,7 +60176,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/runtime-class.v1.json",
"title": "RuntimeClass",
-- "description": "platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names.",
++ "description": "platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names. Each class names one reserved Fabric capability in [email protected] — `worker:` + the class (worker:function, worker:code, worker:browser, worker:linux, worker:desktop, worker:gpu) — and a node advertises the name of each class it runs.",
"$comment": "Additive: a class may be added to v1; none is removed, renamed or reordered.",
"type": "string",
"oneOf": [
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 2a4510f0..19dedcd6 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -50749,7 +50749,7 @@ export type ResourceOffer = {
cell?: string;
trust: "first-party" | "user-owned" | "partner" | "community" | "external";
/**
-- * e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true
++ * e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name.
*/
capabilities: string[];
capacity?: {
@@@ -51442,7 +51442,7 @@ export interface HarnessOffer {
/** [email protected] (owner: network) */
/**
-- * platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names.
++ * platform.runtime-class@1: the kind of execution environment a platform.job@1 asks for (plan T14 Run; ADR-034 proposed). A name only: this contract defines no behavior, and a worker runs only the classes it advertises and refuses every other one (`nack`). `function` is the first class implemented (OpenVibe.Node's function worker); the others are reserved names. Each class names one reserved Fabric capability in [email protected] — `worker:` + the class (worker:function, worker:code, worker:browser, worker:linux, worker:desktop, worker:gpu) — and a node advertises the name of each class it runs.
*/
export type RuntimeClass = ("function" | "code" | "browser" | "linux" | "desktop" | "gpu") & string;
ddiff --git a/test/platform-run.test.js b/test/platform-run.test.js
iindex c1c99afd..98cad41a 100644
---- a/test/platform-run.test.js
++++ b/test/platform-run.test.js
@@@ -17,6 +17,15 @@ assert.deepStrictEqual(classes.map(c => c.const), ['function', 'code', 'browser'
for (const c of classes) ok(c.description && !c.description.includes('\n'), `runtime class ${c.const} has a one-line description`);
ok(contracts.schema('platform.job').properties.class.$ref === 'runtime-class.v1.json', 'a job names its class from platform.runtime-class@1');
++// Each runtime class maps to one reserved Fabric capability name in platform.resource-offer@1: `worker:` + the
++// class. The offer contract lists them in $defs.reservedWorkerCapabilities (Node advertises one per class it runs);
++// this pins the two lists together and holds each name to the offer's capabilities pattern.
++const offerSchema = contracts.schema('platform.resource-offer');
++const reserved = offerSchema.$defs.reservedWorkerCapabilities.enum;
++assert.deepStrictEqual(reserved, classes.map(c => `worker:${c.const}`), 'the reserved worker capability names are one per runtime class');
++checks++;
++for (const name of reserved) ok(new RegExp(offerSchema.properties.capabilities.items.pattern).test(name), `${name} matches the offer capabilities pattern`);
++
// A frame's job id is a platform.job@1 id.
const frame = contracts.schema('platform.job-frame');
assert.strictEqual(frame.$defs.job_id.pattern, contracts.schema('platform.job').properties.id.pattern);