← back to Desktop Dotbar
dotbar/jev: price-independent daily call-COUNT cap as the real paid backstop
1ab5eb81a7cc849a99fe5b3a2b0fecfa3d1ea65b · 2026-09-23 12:45:20 -0700 · Steve Abrams
The dollar cap sums the ledger at a PLACEHOLDER $0.001/call rate, so with the
real TypeSafe price unknown it may never trip under cache-gated volume —
effectively uncapped in real dollars. Add maxPaidCallsPerDay (default 800, env
DOTBAR_JEV_MAX_CALLS) that bounds worst-case spend to (calls x unknown price)
regardless of the rate. countPaidCallsToday() counts today's app+api ledger
rows, fail-closes to Infinity on any read error, composes with the dollar cap
(paid fires only if under BOTH), reserves in-loop so a burst within one refresh
can't exceed the cap, and surfaces paidCallsToday/maxPaidCallsPerDay/
countCapBlocked on /api/jev. start-bar.command stages the arm flags OFF
(DOTBAR_JEV_PAID=0, auto-snapshotted in de2776c). Negative tests prove the
guard goes red on injected at-cap + burst faults and countPaidCallsToday
fail-closes; 12/12 pass. No paid call fired; bar stays on the $0 builtin.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M jev-dots.jsM test/jev-dots.test.js
Diff
commit 1ab5eb81a7cc849a99fe5b3a2b0fecfa3d1ea65b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 23 12:45:20 2026 -0700
dotbar/jev: price-independent daily call-COUNT cap as the real paid backstop
The dollar cap sums the ledger at a PLACEHOLDER $0.001/call rate, so with the
real TypeSafe price unknown it may never trip under cache-gated volume —
effectively uncapped in real dollars. Add maxPaidCallsPerDay (default 800, env
DOTBAR_JEV_MAX_CALLS) that bounds worst-case spend to (calls x unknown price)
regardless of the rate. countPaidCallsToday() counts today's app+api ledger
rows, fail-closes to Infinity on any read error, composes with the dollar cap
(paid fires only if under BOTH), reserves in-loop so a burst within one refresh
can't exceed the cap, and surfaces paidCallsToday/maxPaidCallsPerDay/
countCapBlocked on /api/jev. start-bar.command stages the arm flags OFF
(DOTBAR_JEV_PAID=0, auto-snapshotted in de2776c). Negative tests prove the
guard goes red on injected at-cap + burst faults and countPaidCallsToday
fail-closes; 12/12 pass. No paid call fired; bar stays on the $0 builtin.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
jev-dots.js | 65 ++++++++++++++++++++++++++++++++++++++++++++-------
test/jev-dots.test.js | 46 +++++++++++++++++++++++++++++++++++-
2 files changed, 102 insertions(+), 9 deletions(-)
diff --git a/jev-dots.js b/jev-dots.js
index 0c67a36..2b4f2c8 100644
--- a/jev-dots.js
+++ b/jev-dots.js
@@ -35,6 +35,11 @@ const CONFIG = {
paidEnabled: process.env.DOTBAR_JEV_PAID === '1',
// Hard daily spend cap (USD). Steve may set DOTBAR_JEV_CAP_USD=2; default $5.
capUsd: Number(process.env.DOTBAR_JEV_CAP_USD || '5'),
+ // Price-INDEPENDENT hard daily call-count cap — the real backstop when the true
+ // per-call price is unknown. Bounds worst-case spend to (maxPaidCalls x price)
+ // regardless of the rate. DOTBAR_JEV_MAX_CALLS overrides; default 800
+ // (800 x $0.001 placeholder = $0.80; even at 10x real price = $8).
+ maxPaidCalls: Number(process.env.DOTBAR_JEV_MAX_CALLS || '800'),
provider: process.env.DOTBAR_JEV_PROVIDER || 'typesafe',
timeoutMs: Number(process.env.DOTBAR_JEV_TIMEOUT_MS || '800'),
ledgerPath: path.join(os.homedir(), '.claude', 'cost-ledger.jsonl'),
@@ -51,10 +56,13 @@ const stats = {
builtinCalls: 0, // classifier decided via the $0 local path
paidCalls: 0, // classifier decided via the paid TypeSafe API
paidFellBack: 0, // paid attempted but errored -> builtin
- capBlocked: 0, // paid armed but daily cap reached -> builtin
+ capBlocked: 0, // paid armed but daily $ cap reached -> builtin
+ countCapBlocked: 0, // paid armed but daily call-COUNT cap reached -> builtin
paidEnabled: CONFIG.paidEnabled,
capUsd: CONFIG.capUsd,
+ maxPaidCallsPerDay: CONFIG.maxPaidCalls,
lastSpendUsd: 0,
+ paidCallsToday: 0, // today's paid-call count read from the ledger (last refresh)
updated: 0,
};
@@ -146,6 +154,32 @@ function spendTodayUsd(readLedger) {
}
}
+// ---- daily call-COUNT cap (price-independent, fail-closed) -------------------
+// Count today's desktop-dotbar PAID classification rows in the cost ledger — each
+// paid call logs exactly one typesafe_jev row (app+api+today). This is the real
+// backstop: it bounds worst-case paid volume regardless of the unknown per-call
+// price. On ANY read failure returns Infinity so the cap is treated as reached ->
+// paid path is skipped -> no spend. Mirrors spendTodayUsd's fail-closed discipline.
+function countPaidCallsToday(readLedger) {
+ try {
+ const raw = readLedger();
+ if (!raw) return 0;
+ const today = new Date().toISOString().slice(0, 10);
+ let n = 0;
+ for (const line of raw.split('\n')) {
+ if (!line.trim()) continue;
+ let e; try { e = JSON.parse(line); } catch { continue; }
+ if (e && e.app === CONFIG.costApp && e.api === CONFIG.costApiKey
+ && String(e.ts || '').slice(0, 10) === today) {
+ n += 1;
+ }
+ }
+ return n;
+ } catch {
+ return Infinity; // cannot confirm under cap -> never spend
+ }
+}
+
function logPaidCall(logSpend) {
// Log every paid call to the cost ledger via cost-tracker's log.js. Best-effort:
// a logging failure must never crash a refresh (but is counted).
@@ -225,6 +259,7 @@ function typeSafeClassify(state, deps) {
async function classifyDots(rows, opts = {}) {
const paidEnabled = opts.paidEnabled !== undefined ? opts.paidEnabled : CONFIG.paidEnabled;
const capUsd = opts.capUsd !== undefined ? opts.capUsd : CONFIG.capUsd;
+ const maxPaidCallsPerDay = opts.maxPaidCallsPerDay !== undefined ? opts.maxPaidCallsPerDay : CONFIG.maxPaidCalls;
const readLedger = opts.readLedger || (() => { try { return fs.readFileSync(CONFIG.ledgerPath, 'utf8'); } catch { return ''; } });
const paidTransport = opts.paidTransport || typeSafeClassify;
const logSpend = opts.logSpend || defaultLogSpend;
@@ -242,26 +277,40 @@ async function classifyDots(rows, opts = {}) {
misses.push({ tty: row.tty, state, hash });
}
- // Compute today's spend ONCE per refresh so a burst of misses can't each blow past the cap.
+ // Compute today's spend AND paid-call COUNT ONCE per refresh so a burst of misses
+ // can't each blow past either cap before the ledger flushes.
let spend = 0;
- if (paidEnabled) { spend = spendTodayUsd(readLedger); stats.lastSpendUsd = Number.isFinite(spend) ? spend : stats.lastSpendUsd; }
+ let paidCount = 0;
+ if (paidEnabled) {
+ spend = spendTodayUsd(readLedger);
+ stats.lastSpendUsd = Number.isFinite(spend) ? spend : stats.lastSpendUsd;
+ paidCount = countPaidCallsToday(readLedger);
+ stats.paidCallsToday = Number.isFinite(paidCount) ? paidCount : stats.paidCallsToday;
+ }
for (const m of misses) {
stats.classifications++;
let result = null;
- // Paid path is structurally UNREACHABLE unless armed AND provably under the cap.
- if (paidEnabled && spend < capUsd) {
+ const underDollarCap = spend < capUsd;
+ const underCountCap = paidCount < maxPaidCallsPerDay;
+ // Paid path is structurally UNREACHABLE unless armed AND provably under BOTH caps.
+ // The two caps compose: whichever limit is hit first wins.
+ if (paidEnabled && underDollarCap && underCountCap) {
try {
result = await paidTransport(m.state, {});
stats.paidCalls++;
logPaidCall(logSpend);
- spend += 0.001; // reserve the just-spent call so the in-loop cap holds before the ledger flushes
+ spend += 0.001; // reserve this call's $ so the in-loop $ cap holds before the ledger flushes
+ paidCount += 1; // reserve this call's COUNT so a burst within one refresh can't exceed the count cap
} catch {
stats.paidFellBack++;
result = classifyBuiltin(m.state); stats.builtinCalls++;
}
} else {
- if (paidEnabled) stats.capBlocked++; // armed but cap reached -> fall back, no spend
+ // armed but a cap reached -> fall back, no spend. Attribute the block to the
+ // binding cap ($ checked first, matching the original single-cap behavior).
+ if (paidEnabled && !underDollarCap) stats.capBlocked++;
+ else if (paidEnabled && !underCountCap) stats.countCapBlocked++;
result = classifyBuiltin(m.state); stats.builtinCalls++;
}
cache.set(m.tty, { hash: m.hash, color: result.color, source: result.source, confidence: result.confidence });
@@ -279,4 +328,4 @@ async function classifyDots(rows, opts = {}) {
function getStats() { return { ...stats, cacheSize: cache.size }; }
function _resetForTest() { cache.clear(); for (const k of Object.keys(stats)) if (typeof stats[k] === 'number') stats[k] = 0; }
-module.exports = { classifyDots, classifyBuiltin, extractState, stateHash, spendTodayUsd, getStats, LABELS, _resetForTest };
+module.exports = { classifyDots, classifyBuiltin, extractState, stateHash, spendTodayUsd, countPaidCallsToday, getStats, LABELS, _resetForTest };
diff --git a/test/jev-dots.test.js b/test/jev-dots.test.js
index 5a26b62..10af344 100644
--- a/test/jev-dots.test.js
+++ b/test/jev-dots.test.js
@@ -7,6 +7,9 @@ const jev = require('../jev-dots');
const row = (tty, color, label, variant) => ({ tty, color, label, variant: variant || '', live: true, parked: false });
const todayLine = (usd) => JSON.stringify({ app: 'desktop-dotbar', cost_usd: usd, ts: new Date().toISOString() });
+// A real paid-call ledger row carries app+api+cost (see defaultLogSpend / cost-tracker log.js).
+const paidLine = () => JSON.stringify({ app: 'desktop-dotbar', api: 'typesafe_jev', cost_usd: 0.001, ts: new Date().toISOString() });
+const nPaidLines = (n) => Array.from({ length: n }, paidLine).join('\n');
test('builtin classifies synthetic states to sane colors', () => {
const cases = [
@@ -95,7 +98,48 @@ test('paid fallback: transport error -> builtin color, never blank, never throws
assert.equal(map.get('a').source, 'builtin');
});
-test('paid used when armed + under cap + transport ok, and logs the spend', async () => {
+test('COUNT-CAP fail-closed: count >= cap -> paid transport is NEVER called, falls back to builtin', async () => {
+ jev._resetForTest();
+ let paidAttempts = 0;
+ const spyTransport = async () => { paidAttempts++; return { color: 'green', source: 'typesafe', confidence: 0.9 }; };
+ // capUsd high so the DOLLAR cap can't be the blocker; count cap of 3 with 3 rows already logged.
+ const map = await jev.classifyDots([row('a', 'purple', 'gated · 1'), row('b', 'green', 'working')], {
+ paidEnabled: true, capUsd: 100, maxPaidCallsPerDay: 3, readLedger: () => nPaidLines(3), paidTransport: spyTransport,
+ });
+ const s = jev.getStats();
+ assert.equal(paidAttempts, 0, 'no paid attempt once the call-count cap is reached');
+ assert.ok(s.countCapBlocked >= 2, 'count-cap block counted for each miss');
+ assert.equal(s.capBlocked, 0, 'the $ cap was NOT the binding limit here');
+ assert.equal(s.paidCalls, 0);
+ assert.equal(s.paidCallsToday, 3, 'reads the paid-call count from the ledger');
+ assert.ok(map.get('a').color && map.get('b').color, 'still shows colors (builtin fallback)');
+});
+
+test('COUNT-CAP boundary + burst: one call under cap fires, then the reserve blocks the next in the SAME refresh', async () => {
+ jev._resetForTest();
+ let paidAttempts = 0;
+ // cap=2, 1 paid row already today -> exactly ONE call of headroom. Two misses in one refresh:
+ // first fires (count 1<2), reserves to 2, second is blocked by the in-loop reservation.
+ const map = await jev.classifyDots([row('a', 'yellow', 'needs direction'), row('b', 'purple', 'gated · 1')], {
+ paidEnabled: true, capUsd: 100, maxPaidCallsPerDay: 2, readLedger: () => nPaidLines(1),
+ paidTransport: async () => { paidAttempts++; return { color: 'green', source: 'typesafe', confidence: 0.9 }; },
+ });
+ const s = jev.getStats();
+ assert.equal(paidAttempts, 1, 'exactly one paid call fires before the reserve hits the cap');
+ assert.equal(s.paidCalls, 1);
+ assert.equal(s.countCapBlocked, 1, 'the second miss in the same refresh is count-cap blocked');
+ assert.ok(map.get('a') && map.get('b'));
+});
+
+test('countPaidCallsToday is fail-closed: a ledger-read throw returns Infinity (treated as at-cap)', () => {
+ assert.equal(jev.countPaidCallsToday(() => { throw new Error('ledger unreadable'); }), Infinity);
+ assert.equal(jev.countPaidCallsToday(() => ''), 0, 'empty ledger = 0 paid calls');
+ assert.equal(jev.countPaidCallsToday(() => nPaidLines(4)), 4, 'counts only app+api+today rows');
+ // A dollar-only synthetic row (no api) must NOT count as a paid call.
+ assert.equal(jev.countPaidCallsToday(() => todayLine(5.0)), 0, 'a $-only row without api=typesafe_jev is not a paid call');
+});
+
+test('paid used when armed + under BOTH caps + transport ok, and logs the spend', async () => {
jev._resetForTest();
let logged = 0;
const map = await jev.classifyDots([row('a', 'yellow', 'needs direction')], {
← de2776c auto-data-snapshot: 2026-09-23T12:42:16 (1 data files) — sta
·
back to Desktop Dotbar
·
dotbar/jev: isolate tests from real cost ledger + reconcile a32c6bf →