openvibers/openvibe.contracts Commits

Attempts: 1. author Claude opus done; 2. review deepseek-v4.1-flash done

OP OpenVibers committed 1 Oct 2026 · f5e432b1e6 · 10 files, +222 −3

Done by the harness job contracts-plan-t5-lane-f-next-224438.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex df51a6b5..5b999709 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -10,6 +10,13 @@ Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabi
service instances; runtime, storage, delivery, agent, and harness offers. Each has valid and
invalid fixtures. ADR-034 is proposed. Generated types and bundle include all nine.
++`platform.usage-sample@1` can carry all 15 plan T5 usage fields in one reading (lane F). It gains four optional
++fields: `free_allowance_used` (non-negative number in the reading's own `unit`, at most `quantity`),
++`vibes_charged` (non-negative integer count of vibes-bits, Billing's ledger minor unit; absent means not rated),
++and `route_epoch` and `trace_id` (same types as in `platform.telemetry-sample@1`). Readings without these fields
++still validate. New fixtures: `all-fifteen-fields` and `minimal` (valid); a fractional or negative `vibes_charged` and
++a negative `free_allowance_used` (invalid). Additive.
++
## 0.83.0 — 2026-09-29
**Host site configuration** (plan T12 J3): the capability `host.site.config` (active, first-party, `host:write`, project
ddiff --git a/contracts/platform/usage-sample.v1.json b/contracts/platform/usage-sample.v1.json
iindex 3b93c0c7..d8324d64 100644
---- a/contracts/platform/usage-sample.v1.json
++++ b/contracts/platform/usage-sample.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"title": "UsageSample",
-- "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed).",
++ "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed). Consumers dedupe on `idempotency_key`.",
"$comment": "Additive optional fields may extend this v1 contract.",
"type": "object",
"additionalProperties": false,
@@@ -68,6 +68,23 @@
"minimum": 0,
"description": "Estimated USD"
},
++ "free_allowance_used": {
++ "type": "number",
++ "minimum": 0,
++ "description": "How much of this reading's `quantity` the free allowance covered (platform.rate-card@1 `free_allowance` for this provider/metric and period), in the reading's own `unit`, not in rate-card units, money or Vibes: a 1.5 GiB reading with 1 GiB free carries 1. Never more than `quantity`; 0 when nothing was free. Absent: not rated against a rate card."
++ },
++ "vibes_charged": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "Vibes charged for this reading, as an integer count of vibes-bits (Billing's ledger minor unit, currency `vibes-bits`; balance credit and ledger amounts use the same unit). Never whole Vibes, USD or a fraction: round once, when rating. Covers only `quantity` minus `free_allowance_used`; 0 means rated and nothing charged. Absent means not rated (yet). Records a charge already made in Billing's ledger; it is never a request to charge."
++ },
++ "route_epoch": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "trace_id": {
++ "type": "string"
++ },
"source": {
"type": "string"
}
@@@ -90,6 +107,28 @@
"at": "2026-09-30T10:00:00Z",
"cost_estimate": 0.03,
"source": "media.egress"
++ },
++ {
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": 1,
++ "vibes_charged": 4,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
}
]
}
ddiff --git a/fixtures/platform.usage-sample/invalid/free-allowance-used-negative.json b/fixtures/platform.usage-sample/invalid/free-allowance-used-negative.json
nnew file mode 100644
iindex 00000000..e16d6b9d
---- /dev/null
++++ b/fixtures/platform.usage-sample/invalid/free-allowance-used-negative.json
@@@ -0,0 +1,22 @@
++{
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": -1,
++ "vibes_charged": 4,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
++}
ddiff --git a/fixtures/platform.usage-sample/invalid/vibes-charged-fraction.json b/fixtures/platform.usage-sample/invalid/vibes-charged-fraction.json
nnew file mode 100644
iindex 00000000..b979e878
---- /dev/null
++++ b/fixtures/platform.usage-sample/invalid/vibes-charged-fraction.json
@@@ -0,0 +1,22 @@
++{
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": 1,
++ "vibes_charged": 1.5,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
++}
ddiff --git a/fixtures/platform.usage-sample/invalid/vibes-charged-negative.json b/fixtures/platform.usage-sample/invalid/vibes-charged-negative.json
nnew file mode 100644
iindex 00000000..03c8a221
---- /dev/null
++++ b/fixtures/platform.usage-sample/invalid/vibes-charged-negative.json
@@@ -0,0 +1,22 @@
++{
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": 1,
++ "vibes_charged": -1,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
++}
ddiff --git a/fixtures/platform.usage-sample/valid/all-fifteen-fields.json b/fixtures/platform.usage-sample/valid/all-fifteen-fields.json
nnew file mode 100644
iindex 00000000..9683737f
---- /dev/null
++++ b/fixtures/platform.usage-sample/valid/all-fifteen-fields.json
@@@ -0,0 +1,22 @@
++{
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": 1,
++ "vibes_charged": 4,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
++}
ddiff --git a/fixtures/platform.usage-sample/valid/minimal.json b/fixtures/platform.usage-sample/valid/minimal.json
nnew file mode 100644
iindex 00000000..e962fe66
---- /dev/null
++++ b/fixtures/platform.usage-sample/valid/minimal.json
@@@ -0,0 +1,10 @@
++{
++ "id": "use-3",
++ "idempotency_key": "media:delivery:3",
++ "service": "media",
++ "operation": "deliver",
++ "quantity": 0.25,
++ "unit": "GiB",
++ "at": "2026-09-30T10:05:00Z",
++ "source": "media.egress"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex db33bcc3..669bbc3d 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -58286,7 +58286,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"title": "UsageSample",
-- "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed).",
++ "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed). Consumers dedupe on `idempotency_key`.",
"$comment": "Additive optional fields may extend this v1 contract.",
"type": "object",
"additionalProperties": false,
@@@ -58352,6 +58352,23 @@
"minimum": 0,
"description": "Estimated USD"
},
++ "free_allowance_used": {
++ "type": "number",
++ "minimum": 0,
++ "description": "How much of this reading's `quantity` the free allowance covered (platform.rate-card@1 `free_allowance` for this provider/metric and period), in the reading's own `unit`, not in rate-card units, money or Vibes: a 1.5 GiB reading with 1 GiB free carries 1. Never more than `quantity`; 0 when nothing was free. Absent: not rated against a rate card."
++ },
++ "vibes_charged": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "Vibes charged for this reading, as an integer count of vibes-bits (Billing's ledger minor unit, currency `vibes-bits`; balance credit and ledger amounts use the same unit). Never whole Vibes, USD or a fraction: round once, when rating. Covers only `quantity` minus `free_allowance_used`; 0 means rated and nothing charged. Absent means not rated (yet). Records a charge already made in Billing's ledger; it is never a request to charge."
++ },
++ "route_epoch": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "trace_id": {
++ "type": "string"
++ },
"source": {
"type": "string"
}
@@@ -58374,6 +58391,28 @@
"at": "2026-09-30T10:00:00Z",
"cost_estimate": 0.03,
"source": "media.egress"
++ },
++ {
++ "id": "use-2",
++ "idempotency_key": "media:delivery:2",
++ "service": "media",
++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "resource": "object-1",
++ "provider": "local",
++ "node": "node-1",
++ "cell": "cell-west",
++ "region": "us-west",
++ "operation": "deliver",
++ "quantity": 1.5,
++ "unit": "GiB",
++ "at": "2026-09-30T10:00:00Z",
++ "cost_estimate": 0.03,
++ "source": "media.egress",
++ "free_allowance_used": 1,
++ "vibes_charged": 4,
++ "route_epoch": 3,
++ "trace_id": "tr-42"
}
]
}
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 90ee3224..16b02c3d 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -47369,7 +47369,7 @@ export interface CapacitySnapshot {
/** [email protected] (owner: network) */
/**
-- * One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed).
++ * One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed). Consumers dedupe on `idempotency_key`.
*/
export interface UsageSample {
id: string;
@@@ -47393,6 +47393,16 @@ export interface UsageSample {
* Estimated USD
*/
cost_estimate?: number;
++ /**
++ * How much of this reading's `quantity` the free allowance covered (platform.rate-card@1 `free_allowance` for this provider/metric and period), in the reading's own `unit`, not in rate-card units, money or Vibes: a 1.5 GiB reading with 1 GiB free carries 1. Never more than `quantity`; 0 when nothing was free. Absent: not rated against a rate card.
++ */
++ free_allowance_used?: number;
++ /**
++ * Vibes charged for this reading, as an integer count of vibes-bits (Billing's ledger minor unit, currency `vibes-bits`; balance credit and ledger amounts use the same unit). Never whole Vibes, USD or a fraction: round once, when rating. Covers only `quantity` minus `free_allowance_used`; 0 means rated and nothing charged. Absent means not rated (yet). Records a charge already made in Billing's ledger; it is never a request to charge.
++ */
++ vibes_charged?: number;
++ route_epoch?: number;
++ trace_id?: string;
source: string;
}
ddiff --git a/test/platform-fabric.test.js b/test/platform-fabric.test.js
iindex be0f6176..39fb73ec 100644
---- a/test/platform-fabric.test.js
++++ b/test/platform-fabric.test.js
@@@ -28,4 +28,30 @@ for (const name of names) {
}
}
}
++// One usage reading carries all 15 plan T5 fields; the money fields are strict, and readings
++// from before they existed still validate.
++const usage = contracts.schema('platform.usage-sample');
++const planFields = [
++ 'project', 'subject', 'service', 'operation', 'provider', 'resource', 'region', 'quantity', 'unit',
++ 'cost_estimate', 'free_allowance_used', 'vibes_charged', 'route_epoch', 'trace_id', 'at',
++];
++const full = usage.examples.find(e => planFields.every(f => f in e));
++assert.ok(full, 'a usage-sample example carries all 15 plan fields');
++assert.strictEqual(contracts.validate('platform.usage-sample', full).valid, true);
++for (const [field, value] of [['vibes_charged', 1.5], ['vibes_charged', -1], ['free_allowance_used', -1]]) {
++ assert.strictEqual(contracts.validate('platform.usage-sample', { ...full, [field]: value }).valid, false, `${field} ${value} is refused`);
++ checks++;
++}
++const telemetry = contracts.schema('platform.telemetry-sample').properties;
++for (const field of ['route_epoch', 'trace_id']) {
++ const { description, ...shape } = usage.properties[field];
++ assert.deepStrictEqual(shape, telemetry[field], `${field} has the telemetry-sample shape`);
++ checks++;
++}
++const legacy = Object.fromEntries(Object.entries(full).filter(([k]) => !['free_allowance_used', 'vibes_charged', 'route_epoch', 'trace_id'].includes(k)));
++assert.strictEqual(contracts.validate('platform.usage-sample', legacy).valid, true, 'a reading without the new fields validates');
++const minimal = Object.fromEntries(usage.required.map(k => [k, full[k]]));
++assert.strictEqual(contracts.validate('platform.usage-sample', minimal).valid, true, 'a reading with only the required fields validates');
++checks += 3;
++
console.log(`platform fabric: ${checks} example and required-field checks passed`);