openvibers/openvibe.tools Commits

Send Billing usage readings under the tools manifest id

OP OpenVibers committed 4 Oct 2026 · 38f0791986 · 4 files, +6 −5

Asked: Follow-up of https://github.com/OpenVibers/OpenVibe.Network/pull/42 (Document that usage rollups are counts, never money, and guard it): its review asked for a change in OpenVibe.Tools that the PR cannot make from OpenVibe.Network. The review said: VERDICT: FIX
Harness job tools-follow-up-of-https-github-com-open-202714.

Changes

ddiff --git a/README.md b/README.md
iindex 93571fc..9cd3f53 100644
---- a/README.md
++++ b/README.md
@@@ -304,8 +304,9 @@ reload reattaches.
`GET /api/health` shows `jobs.usage` (pending, invalid).
- **Usage readings to OpenVibe.Billing** (`apps/_shared/billing.js`, `apps/_shared/jobs/usage.js`, plan T5 step 7):
every job that succeeds or fails (anyone's, not only a project's; a cancelled one is not billed) gets one
-- `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service and source
-- `openvibe.tools`, operation `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,
++ `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service `tools` (the
++ Contracts manifest id, so Billing keys ratings and budgets on the reading) and source `openvibe.tools`, operation
++ `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,
quantity 1, unit `jobs`, `at` the job's end, plus `project` and `trace_id` when the job has them and `subject`
(`user:usr_…`) only for a signed-in person's job, never an app or a browser session. It is stored in
`tool_job_billing_readings` in the transaction that records the end (no network call there); the pruner's timer
ddiff --git a/apps/_shared/jobs/usage.js b/apps/_shared/jobs/usage.js
iindex f5f32e6..330b170 100644
---- a/apps/_shared/jobs/usage.js
++++ b/apps/_shared/jobs/usage.js
@@@ -66,7 +66,7 @@ function readingOf(row) {
const r = {
id: `tools-job-${row.id}`,
idempotency_key: `tools:job:${row.id}`,
-- service: 'openvibe.tools',
++ service: 'tools', // the Contracts manifest id (services/tools.json): Billing keys ratings and budgets on it
operation: row.tool ? 'tools.tool.run' : 'tools.job.create',
resource: DIMENSION_RE.test(resource) ? resource : 'other',
quantity: 1,
ddiff --git a/apps/_shared/test/job-usage.test.js b/apps/_shared/test/job-usage.test.js
iindex 4fe4991..14237d7 100644
---- a/apps/_shared/test/job-usage.test.js
++++ b/apps/_shared/test/job-usage.test.js
@@@ -214,7 +214,7 @@ function define(system) {
for (const p of posts) {
const v = contracts.validate('platform.usage-sample@1', p);
assert.ok(v.valid, JSON.stringify(v.errors));
-- assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['openvibe.tools', 'openvibe.tools', 1, 'jobs']);
++ assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['tools', 'openvibe.tools', 1, 'jobs']);
}
const [person, appJob, failJob] = posts;
assert.deepStrictEqual([person.id, person.operation, person.resource, person.subject, person.trace_id, person.project], [`tools-job-${personId}`, 'tools.tool.run', 'image-resize', `user:${USER}`, TRACE, undefined]);
ddiff --git a/docs/cutover-tools-billing-readings.md b/docs/cutover-tools-billing-readings.md
iindex 1a4da6a..7dd2d88 100644
---- a/docs/cutover-tools-billing-readings.md
++++ b/docs/cutover-tools-billing-readings.md
@@@ -51,7 +51,7 @@ release wait on the same lock and find it applied.
- After step 5: `GET /api/health` on img, audio or docs shows `jobs.billing` with `pending` going back to 0
after a tick (5 minutes) and `posted` growing; `refused` with a `last_error` of `token: 400 invalid_scope` means the grant is
missing. `SELECT COUNT(*) FROM tool_job_billing_readings WHERE sent_at IS NULL` stays small.
--- In Billing: `GET /api/v1/usage?service=openvibe.tools` (`billing.ledger.admin`) lists the readings, one per
++- In Billing: `GET /api/v1/usage?service=tools` (`billing.ledger.admin`) lists the readings, one per
ended job, `idempotency_key` `tools:job:<job id>`.
```rehearse