openvibers/openvibe.codes Commits
Say usage counts are hourly reports and cost is on Billing
Changes
@@@ -34,7 +34,7 @@],"notYet": ["The playgrounds have never been used on production (0 playground runs); the one production end-to-end run used curl, and it is not a committed, repeatable check",-- "Usage covers only what services report in their hourly *.usage.recorded rollups (Network adds them up per day; the current hour is not counted); no trace or error dashboards beyond the sampled failures",++ "Usage counts come from the services' hourly *.usage.recorded rollups (Network adds them up per day; the current hour is not counted) and cost lives in Billing, not on the usage page; no trace or error dashboards beyond the sampled failures","Billing sandbox, Chat bot and Realtime playgrounds; CLI and scaffolding; templates","Consent screen for third-party production apps (a Network gap)","No JSON-LD or llms.txt on the public pages",@@@ -481,7 +481,7 @@ ${table(['Rule', 'Value'], [<li><strong>Past a limit</strong> the answer is a problem (<code>application/problem+json</code>) with a stable code: <code>429</code> for a rate or a count, <code>413</code> for a size. Rate limits say when to retry (<code>Retry-After</code> or <code>retry_after</code>).</li><li><strong>Trust tiers</strong> (unreviewed, reviewed, first-party; <a href="/docs/adr/ADR-013">ADR-013</a>) change defaults and discovery, never the grant check. Every tier meets the same limits today.</li><li><strong>Raising a limit</strong> is a per-project override staff set at the owning service; the numbers here are the defaults every project starts with.</li>--<li><strong>What a project used</strong> is on its usage page (<code>/projects/<project>/usage</code>, for the owner and admins): per day, service and capability, with the headroom of its recorded quotas and its recent failures. The services report it as hourly rollups, so it trails by an hour.</li>++<li><strong>What a project used</strong> is on its usage page (<code>/projects/<project>/usage</code>, for the owner and admins): per day, service and capability, with the headroom of its recorded quotas and its recent failures. The services report it as hourly rollups, so it trails by an hour. Counts are what services report each hour. Cost is on your Billing page.</li></ul>${answers.map(({ src, body, problem }) => html`<h2 id="${src.service}">${src.name}</h2>${problem ? problemBox(problem, { title: `${src.name}'s limits could not be read` }) : html`<p class="muted small">${src.public ? html`From <a href="${src.public}"><code>${src.public.replace('https://', '')}</code></a>` : html`From ${src.name}'s <code>/limits.json</code>`}${body.scope ? html`: ${body.scope}` : ''}.</p>@@@ -74,6 +74,7 @@ function usageBody({ project, usage: u, limits: limitAnswers, query }) {const byService = [...u.totals].sort((a, b) => a.service.localeCompare(b.service) || a.capability.localeCompare(b.capability));return html`<h1>Usage <small>${project.name}</small></h1><p class="muted">${u.freshness}${u.last_recorded_at ? html` Last rollup received ${time(u.last_recorded_at)}.` : ''} Showing ${u.range.from} to ${u.range.to} (UTC).</p>++<p class="muted small">Counts are what services report each hour. Cost is on your Billing page.</p>${filters(project.id, query)}<h2 id="summary">Summary</h2>@@@ -107,6 +107,7 @@ function usageFor(projectId) {assert.match(body, /Events published per minute/);assert.match(body, /30 a minute/);assert.match(body, /OpenVibe Tools does not publish its limits yet/);++ assert.ok(body.includes('Counts are what services report each hour. Cost is on your Billing page.'), 'the counts and cost note');// Errors.assert.match(body, /<code>events\.quota_exceeded<\/code>/);assert.match(body, /64<\/span> errors in these days/);
@@ -34,7 +34,7 @@
@@ -34,7 +34,7 @@
],
],
"notYet": [
"notYet": [
"The playgrounds have never been used on production (0 playground runs); the one production end-to-end run used curl, and it is not a committed, repeatable check",
"The playgrounds have never been used on production (0 playground runs); the one production end-to-end run used curl, and it is not a committed, repeatable check",
- "Usage covers only what services report in their hourly *.usage.recorded rollups (Network adds them up per day; the current hour is not counted); no trace or error dashboards beyond the sampled failures",
+ "Usage counts come from the services' hourly *.usage.recorded rollups (Network adds them up per day; the current hour is not counted) and cost lives in Billing, not on the usage page; no trace or error dashboards beyond the sampled failures",
"Billing sandbox, Chat bot and Realtime playgrounds; CLI and scaffolding; templates",
"Billing sandbox, Chat bot and Realtime playgrounds; CLI and scaffolding; templates",
"Consent screen for third-party production apps (a Network gap)",
"Consent screen for third-party production apps (a Network gap)",
"No JSON-LD or llms.txt on the public pages",
"No JSON-LD or llms.txt on the public pages",
@@ -481,7 +481,7 @@ ${table(['Rule', 'Value'], [
@@ -481,7 +481,7 @@ ${table(['Rule', 'Value'], [
<li><strong>Past a limit</strong> the answer is a problem (<code>application/problem+json</code>) with a stable code: <code>429</code> for a rate or a count, <code>413</code> for a size. Rate limits say when to retry (<code>Retry-After</code> or <code>retry_after</code>).</li>
<li><strong>Past a limit</strong> the answer is a problem (<code>application/problem+json</code>) with a stable code: <code>429</code> for a rate or a count, <code>413</code> for a size. Rate limits say when to retry (<code>Retry-After</code> or <code>retry_after</code>).</li>
<li><strong>Trust tiers</strong> (unreviewed, reviewed, first-party; <a href="/docs/adr/ADR-013">ADR-013</a>) change defaults and discovery, never the grant check. Every tier meets the same limits today.</li>
<li><strong>Trust tiers</strong> (unreviewed, reviewed, first-party; <a href="/docs/adr/ADR-013">ADR-013</a>) change defaults and discovery, never the grant check. Every tier meets the same limits today.</li>
<li><strong>Raising a limit</strong> is a per-project override staff set at the owning service; the numbers here are the defaults every project starts with.</li>
<li><strong>Raising a limit</strong> is a per-project override staff set at the owning service; the numbers here are the defaults every project starts with.</li>
-<li><strong>What a project used</strong> is on its usage page (<code>/projects/<project>/usage</code>, for the owner and admins): per day, service and capability, with the headroom of its recorded quotas and its recent failures. The services report it as hourly rollups, so it trails by an hour.</li>
+<li><strong>What a project used</strong> is on its usage page (<code>/projects/<project>/usage</code>, for the owner and admins): per day, service and capability, with the headroom of its recorded quotas and its recent failures. The services report it as hourly rollups, so it trails by an hour. Counts are what services report each hour. Cost is on your Billing page.</li>
</ul>
</ul>
${answers.map(({ src, body, problem }) => html`<h2 id="${src.service}">${src.name}</h2>
${answers.map(({ src, body, problem }) => html`<h2 id="${src.service}">${src.name}</h2>
${problem ? problemBox(problem, { title: `${src.name}'s limits could not be read` }) : html`<p class="muted small">${src.public ? html`From <a href="${src.public}"><code>${src.public.replace('https://', '')}</code></a>` : html`From ${src.name}'s <code>/limits.json</code>`}${body.scope ? html`: ${body.scope}` : ''}.</p>
${problem ? problemBox(problem, { title: `${src.name}'s limits could not be read` }) : html`<p class="muted small">${src.public ? html`From <a href="${src.public}"><code>${src.public.replace('https://', '')}</code></a>` : html`From ${src.name}'s <code>/limits.json</code>`}${body.scope ? html`: ${body.scope}` : ''}.</p>
@@ -74,6 +74,7 @@ function usageBody({ project, usage: u, limits: limitAnswers, query }) {
@@ -74,6 +74,7 @@ function usageBody({ project, usage: u, limits: limitAnswers, query }) {
const byService = [...u.totals].sort((a, b) => a.service.localeCompare(b.service) || a.capability.localeCompare(b.capability));
const byService = [...u.totals].sort((a, b) => a.service.localeCompare(b.service) || a.capability.localeCompare(b.capability));
return html`<h1>Usage <small>${project.name}</small></h1>
return html`<h1>Usage <small>${project.name}</small></h1>
<p class="muted">${u.freshness}${u.last_recorded_at ? html` Last rollup received ${time(u.last_recorded_at)}.` : ''} Showing ${u.range.from} to ${u.range.to} (UTC).</p>
<p class="muted">${u.freshness}${u.last_recorded_at ? html` Last rollup received ${time(u.last_recorded_at)}.` : ''} Showing ${u.range.from} to ${u.range.to} (UTC).</p>
+<p class="muted small">Counts are what services report each hour. Cost is on your Billing page.</p>
${filters(project.id, query)}
${filters(project.id, query)}
<h2 id="summary">Summary</h2>
<h2 id="summary">Summary</h2>
@@ -107,6 +107,7 @@ function usageFor(projectId) {
@@ -107,6 +107,7 @@ function usageFor(projectId) {
assert.match(body, /Events published per minute/);
assert.match(body, /Events published per minute/);
assert.match(body, /30 a minute/);
assert.match(body, /30 a minute/);
assert.match(body, /OpenVibe Tools does not publish its limits yet/);
assert.match(body, /OpenVibe Tools does not publish its limits yet/);
+ assert.ok(body.includes('Counts are what services report each hour. Cost is on your Billing page.'), 'the counts and cost note');
// Errors.
// Errors.
assert.match(body, /<code>events\.quota_exceeded<\/code>/);
assert.match(body, /<code>events\.quota_exceeded<\/code>/);
assert.match(body, /64<\/span> errors in these days/);
assert.match(body, /64<\/span> errors in these days/);