openvibers/openvibe.contracts Commits
Attempts: 1. author Claude opus done; 2. review deepseek-v4.1-flash done
Changes
@@@ -10,6 +10,13 @@ Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabiservice instances; runtime, storage, delivery, agent, and harness offers. Each has valid andinvalid 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@@@ -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"}]}nnew file mode 100644@@@ -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"++}nnew file mode 100644@@@ -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"++}nnew file mode 100644@@@ -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"++}nnew file mode 100644@@@ -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"++}nnew file mode 100644@@@ -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"++}@@@ -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"}]}@@@ -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;}@@@ -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`);
@@ -10,6 +10,13 @@ Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabi
@@ -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
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.
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
## 0.83.0 — 2026-09-29
**Host site configuration** (plan T12 J3): the capability `host.site.config` (active, first-party, `host:write`, project
**Host site configuration** (plan T12 J3): the capability `host.site.config` (active, first-party, `host:write`, project
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"title": "UsageSample",
"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.",
"$comment": "Additive optional fields may extend this v1 contract.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
@@ -68,6 +68,23 @@
@@ -68,6 +68,23 @@
"minimum": 0,
"minimum": 0,
"description": "Estimated USD"
"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": {
"source": {
"type": "string"
"type": "string"
}
}
@@ -90,6 +107,28 @@
@@ -90,6 +107,28 @@
"at": "2026-09-30T10:00:00Z",
"at": "2026-09-30T10:00:00Z",
"cost_estimate": 0.03,
"cost_estimate": 0.03,
"source": "media.egress"
"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"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -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"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -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"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -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"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -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"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -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"
+}
@@ -58286,7 +58286,7 @@
@@ -58286,7 +58286,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"$id": "https://openvibe.network/contracts/platform/usage-sample.v1.json",
"title": "UsageSample",
"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.",
"$comment": "Additive optional fields may extend this v1 contract.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
@@ -58352,6 +58352,23 @@
@@ -58352,6 +58352,23 @@
"minimum": 0,
"minimum": 0,
"description": "Estimated USD"
"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": {
"source": {
"type": "string"
"type": "string"
}
}
@@ -58374,6 +58391,28 @@
@@ -58374,6 +58391,28 @@
"at": "2026-09-30T10:00:00Z",
"at": "2026-09-30T10:00:00Z",
"cost_estimate": 0.03,
"cost_estimate": 0.03,
"source": "media.egress"
"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"
}
}
]
]
}
}
@@ -47369,7 +47369,7 @@ export interface CapacitySnapshot {
@@ -47369,7 +47369,7 @@ export interface CapacitySnapshot {
/** [email protected] (owner: network) */
/** [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 {
export interface UsageSample {
id: string;
id: string;
@@ -47393,6 +47393,16 @@ export interface UsageSample {
@@ -47393,6 +47393,16 @@ export interface UsageSample {
* Estimated USD
* Estimated USD
*/
*/
cost_estimate?: number;
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;
source: string;
}
}
@@ -28,4 +28,30 @@ for (const name of names) {
@@ -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`);
console.log(`platform fabric: ${checks} example and required-field checks passed`);