feat(stats): limit-headroom meter — session budget saved % for subscription users

Claude Code Pro/Max users don't spend dollars, they spend a 5-hour /
weekly usage limit. /caveman-stats now also reports the savings in
their currency: 'Session budget saved: ~X% of your usage this session
(est.)', and the lifetime view gets 'Est. budget saved: ~X% of tracked
usage (est.)'.

Honesty rules baked in:
- % = saved / (saved + used) from tokens we actually count — nothing
  else. budgetSavedPct returns null (line omitted) when no savings are
  measured: honest zero, no claim.
- no plan-limit sizes are assumed or hardcoded (Anthropic doesn't
  publish token quotas); the footer says so explicitly.
- clearly labeled (est.); USD lines stay for API users.

Checksums refreshed for the caveman-stats.js change. 5 new tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
This commit is contained in:
Julius Brussee
2026-07-02 15:17:35 +02:00
co-authored by Claude Fable 5
parent bceaa0cf6d
commit 8a5ab60ef0
3 changed files with 110 additions and 5 deletions
+31 -4
View File
@@ -182,6 +182,21 @@ function aggregateHistory(historyPath, sinceMs) {
return { sessions: latestPerSession.size, outputTokens, estSavedTokens, estSavedUsd };
}
// Limit-headroom meter. Subscription users (Claude Code Pro/Max) spend a
// 5-hour/weekly usage limit, not dollars — for them the meaningful number is
// what share of their would-be usage caveman freed. Computed ONLY from token
// counts we actually have: saved / (saved + used). We never assume a plan's
// limit size (Anthropic doesn't publish token quotas), so this is a share of
// usage, never "% of your weekly limit". Returns a rounded percent, or null
// when there is nothing measured to divide.
function budgetSavedPct(savedTokens, usedTokens) {
if (!Number.isFinite(savedTokens) || !Number.isFinite(usedTokens)) return null;
if (savedTokens <= 0 || usedTokens < 0) return null;
const total = savedTokens + usedTokens;
if (total <= 0) return null;
return Math.round((savedTokens / total) * 100);
}
function humanizeTokens(n) {
if (!Number.isFinite(n) || n <= 0) return '0';
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
@@ -196,11 +211,15 @@ function formatHistory({ sessions, outputTokens, estSavedTokens, estSavedUsd, si
return `\nCaveman Stats — Lifetime${window}\n${sep}\nNo sessions logged yet — run /caveman-stats inside any session to start tracking.\n${sep}\n`;
}
const usdLine = estSavedUsd > 0 ? `Est. saved (USD): ~${formatUsd(estSavedUsd)}\n` : '';
const pct = budgetSavedPct(estSavedTokens, outputTokens);
const budgetLine = pct !== null
? `Est. budget saved: ~${pct}% of tracked usage (est.)\n`
: '';
return `\nCaveman Stats — Lifetime${window}\n${sep}\n` +
`Sessions: ${sessions.toLocaleString()}\n${sep}\n` +
`Output tokens: ${outputTokens.toLocaleString()}\n` +
`Est. tokens saved: ${estSavedTokens.toLocaleString()}\n` +
usdLine + sep + '\n';
budgetLine + usdLine + sep + '\n';
}
// Single-line tweetable summary. Stays human-friendly when no ratio is known.
@@ -250,9 +269,17 @@ function formatStats({ outputTokens, cacheReadTokens, turns, mode, model, sessio
} else {
footer = 'Savings est. from benchmarks/ (mean per-task). Actual varies by task.';
}
savings = `Est. without caveman: ${estNormal.toLocaleString()}\n` +
// Limit-headroom framing for subscription (Pro/Max) users — see
// budgetSavedPct. USD stays above for API users.
const pct = budgetSavedPct(estSaved, outputTokens);
let budgetLine = '';
if (pct !== null) {
budgetLine = `Session budget saved: ~${pct}% of your usage this session (est.)\n`;
footer += ' Budget % = est. saved / (saved + used) tokens; no plan-limit size assumed.';
}
savings = (`Est. without caveman: ${estNormal.toLocaleString()}\n` +
`Est. tokens saved: ${estSaved.toLocaleString()} (~${Math.round(ratio * 100)}%)\n` +
usdLine.replace(/\n$/, '');
usdLine + budgetLine).replace(/\n$/, '');
} else if (mode && mode !== 'off') {
savings = `No savings estimate for '${mode}' mode — only 'full' has benchmark data.`;
} else {
@@ -349,5 +376,5 @@ if (require.main === module) main();
module.exports = {
formatStats, formatShare, formatHistory, aggregateHistory, parseDuration, deriveSavings,
parseSession, priceForModel, formatUsd, COMPRESSION, MODEL_OUTPUT_PRICE_PER_M,
findCompressedPairs, summarizeCompressed, humanizeTokens,
findCompressedPairs, summarizeCompressed, humanizeTokens, budgetSavedPct,
};
+1 -1
View File
@@ -2,7 +2,7 @@
c0b77891d1f8aaef3a7fbf76533ba63a42eaa96431fb7ece611ccff7474de2ba caveman-config.js
52ba51f7c51749fda1e65c63060cec03ffad36c35856c01068c0f63fa33e5b40 caveman-activate.js
a5a532fd57203aa96c5c6eeeef647df2770869343f31be009ba9da318da95301 caveman-mode-tracker.js
be1129b422b7d3edfa8c97df3d8a81f1d1c94cf4e1fd7d6f3a253afebd93fed7 caveman-stats.js
c841fa940e016f2a3000f814d51d9f96b0a37cc5cbaa01b63cb4f9820640960c caveman-stats.js
d2deff457d0a5d8e1848193e6af6a68a0ebdba4fbdf250889400d5ea231e088f caveman-statusline.sh
1690c639f05940cbff39e0383a27053898b30c224aa651043db29b2842cb524a caveman-statusline.ps1
9f2601e8551653609f0b9d700c9bd75bffa43747d2db18ffe83fb73de5dc3607 cavecrew-model-overrides.js
+78
View File
@@ -460,5 +460,83 @@ test('mode tracker forwards --share to stats script', (tmp) => {
assert.match(parsed.reason, /^🪨 Saved 650 output tokens/);
});
// ── Limit-headroom meter (subscription users spend usage limit, not $) ─────
test('budgetSavedPct = saved / (saved + used), null when nothing saved', () => {
const { budgetSavedPct } = require(STATS);
assert.strictEqual(budgetSavedPct(650, 350), 65);
assert.strictEqual(budgetSavedPct(1, 3), 25);
assert.strictEqual(budgetSavedPct(0, 350), null); // no measured savings → no claim
assert.strictEqual(budgetSavedPct(-5, 350), null);
assert.strictEqual(budgetSavedPct(650, -1), null);
assert.strictEqual(budgetSavedPct(NaN, 350), null);
assert.strictEqual(budgetSavedPct(650, Infinity), null);
});
test('shows session budget saved % (est.) alongside tokens when savings measured', (tmp) => {
const sess = makeSession(tmp, [
{ type: 'assistant', message: { model: 'claude-sonnet-4-7', usage: { output_tokens: 350 } } },
]);
const claudeDir = path.join(tmp, '.claude');
fs.writeFileSync(path.join(claudeDir, '.caveman-active'), 'full');
const out = execFileSync(process.execPath, [STATS, '--session-file', sess], {
encoding: 'utf8',
env: { ...process.env, CLAUDE_CONFIG_DIR: claudeDir },
});
// saved 650 / (650 saved + 350 used) = 65%
assert.match(out, /Session budget saved:\s+~65% of your usage this session \(est\.\)/);
// Dollars stay for API users.
assert.match(out, /Est\. saved \(USD\):/);
// Honesty: the % must be labeled as estimate math, never a plan-limit claim.
assert.match(out, /no plan-limit size assumed/);
assert.ok(!/weekly limit|5-hour limit/i.test(out), 'must not fabricate Anthropic quota sizes');
});
test('omits budget line when no savings estimate exists (lite mode)', (tmp) => {
const sess = makeSession(tmp, [
{ type: 'assistant', message: { usage: { output_tokens: 100 } } },
]);
const claudeDir = path.join(tmp, '.claude');
fs.writeFileSync(path.join(claudeDir, '.caveman-active'), 'lite');
const out = execFileSync(process.execPath, [STATS, '--session-file', sess], {
encoding: 'utf8',
env: { ...process.env, CLAUDE_CONFIG_DIR: claudeDir },
});
assert.ok(!/budget saved/i.test(out), 'no measured savings → no budget % claim');
});
test('--all lifetime output includes est. budget saved % when savings tracked', (tmp) => {
const claudeDir = path.join(tmp, '.claude');
fs.mkdirSync(claudeDir, { recursive: true });
const history = [
{ ts: Date.now(), session_id: 'a', output_tokens: 350, est_saved_tokens: 650, est_saved_usd: 0.01 },
{ ts: Date.now(), session_id: 'b', output_tokens: 650, est_saved_tokens: 350, est_saved_usd: 0.005 },
];
fs.writeFileSync(
path.join(claudeDir, '.caveman-history.jsonl'),
history.map(h => JSON.stringify(h)).join('\n') + '\n',
);
const out = execFileSync(process.execPath, [STATS, '--all'], {
encoding: 'utf8',
env: { ...process.env, CLAUDE_CONFIG_DIR: claudeDir },
});
// saved 1000 / (1000 saved + 1000 used) = 50%
assert.match(out, /Est\. budget saved:\s+~50% of tracked usage \(est\.\)/);
});
test('--all lifetime output omits budget line when nothing saved', (tmp) => {
const claudeDir = path.join(tmp, '.claude');
fs.mkdirSync(claudeDir, { recursive: true });
fs.writeFileSync(
path.join(claudeDir, '.caveman-history.jsonl'),
JSON.stringify({ ts: Date.now(), session_id: 'a', output_tokens: 350, est_saved_tokens: 0, est_saved_usd: 0 }) + '\n',
);
const out = execFileSync(process.execPath, [STATS, '--all'], {
encoding: 'utf8',
env: { ...process.env, CLAUDE_CONFIG_DIR: claudeDir },
});
assert.ok(!/budget saved/i.test(out), 'zero savings → honest zero, no % line');
});
console.log(`\n${passed} passed, ${failed} failed`);
process.exit(failed ? 1 : 0);