feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
---
|
|
|
|
|
title: Token Cost
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
```js
|
2026-05-11 17:58:18 +02:00
|
|
|
import {apiFetch, pollDelay, waitForVisible} from "./components/config.js";
|
2026-03-29 22:35:35 +02:00
|
|
|
import {refCell} from "./components/ref-cell.js";
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
const POLL = 60_000;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
2026-05-23 13:59:05 +02:00
|
|
|
const evidenceSel = Inputs.radio(
|
|
|
|
|
["Measured only", "Active evidence", "All evidence"],
|
|
|
|
|
{value: "Measured only", label: "Evidence"}
|
|
|
|
|
);
|
|
|
|
|
const sortSel = Inputs.select(
|
|
|
|
|
["Tokens Total", "Event Count"],
|
|
|
|
|
{label: "Sort by"}
|
|
|
|
|
);
|
|
|
|
|
const maxSel = Inputs.select(
|
|
|
|
|
[10, 20, 50, 100, 500],
|
|
|
|
|
{value: 20, label: "Show"}
|
|
|
|
|
);
|
|
|
|
|
display(html`<div style="display:flex;gap:1.5rem;align-items:flex-end;flex-wrap:wrap;margin:0.5rem 0 1.5rem">${evidenceSel}${sortSel}${maxSel}</div>`);
|
|
|
|
|
const evidenceMode = view(evidenceSel);
|
|
|
|
|
const sortOrder = view(sortSel);
|
|
|
|
|
const maxResults = view(maxSel);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function aggregatePath(mode) {
|
|
|
|
|
if (mode === "Measured only") return "/token-events/aggregate/?measurement_kind=measured&include_superseded=false";
|
|
|
|
|
if (mode === "All evidence") return "/token-events/aggregate/?include_superseded=true";
|
|
|
|
|
return "/token-events/aggregate/?include_superseded=false";
|
|
|
|
|
}
|
|
|
|
|
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
const tokenState = (async function*() {
|
2026-05-06 04:04:53 +02:00
|
|
|
let failures = 0;
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
while (true) {
|
2026-05-23 13:59:05 +02:00
|
|
|
let aggregate = null, quality = null, ok = false;
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
try {
|
2026-05-23 13:59:05 +02:00
|
|
|
const [r1, r2] = await Promise.all([
|
|
|
|
|
apiFetch(aggregatePath(evidenceMode)),
|
|
|
|
|
apiFetch("/token-events/quality/"),
|
2026-03-29 19:05:23 +02:00
|
|
|
]);
|
|
|
|
|
ok = r1.ok && r2.ok;
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
if (ok) {
|
2026-05-23 13:59:05 +02:00
|
|
|
aggregate = await r1.json();
|
|
|
|
|
quality = await r2.json();
|
2026-03-29 22:35:35 +02:00
|
|
|
}
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
} catch {}
|
2026-05-06 04:04:53 +02:00
|
|
|
failures = ok ? 0 : failures + 1;
|
2026-05-23 13:59:05 +02:00
|
|
|
yield {aggregate, quality, ok, ts: new Date()};
|
2026-05-11 17:58:18 +02:00
|
|
|
await waitForVisible(pollDelay({ok, base: POLL, failures}));
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}
|
|
|
|
|
})();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
2026-03-29 22:35:35 +02:00
|
|
|
function nameCell(name, fullName) {
|
|
|
|
|
const s = String(name ?? fullName ?? "—");
|
|
|
|
|
const full = String(fullName ?? name ?? "—");
|
|
|
|
|
const el = document.createElement("span");
|
|
|
|
|
el.title = full;
|
|
|
|
|
el.textContent = s.length > 80 ? s.slice(0, 80) + "…" : s;
|
|
|
|
|
return el;
|
|
|
|
|
}
|
2026-03-30 00:02:17 +02:00
|
|
|
|
|
|
|
|
function sortRows(rows, sortField) {
|
|
|
|
|
const s = [...rows];
|
2026-05-23 13:59:05 +02:00
|
|
|
if (sortField === "Event Count") s.sort((a, b) => (b.event_count || 0) - (a.event_count || 0));
|
|
|
|
|
else s.sort((a, b) => (b.tokens_total || 0) - (a.tokens_total || 0));
|
2026-03-30 00:02:17 +02:00
|
|
|
return s;
|
|
|
|
|
}
|
2026-05-23 13:59:05 +02:00
|
|
|
|
|
|
|
|
function dictRows(obj, labelKey) {
|
|
|
|
|
return Object.entries(obj ?? {})
|
|
|
|
|
.map(([label, tokens_total]) => ({[labelKey]: label, tokens_total}))
|
|
|
|
|
.sort((a, b) => b.tokens_total - a.tokens_total);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function metricRows(quality) {
|
|
|
|
|
if (!quality) return [];
|
|
|
|
|
return [
|
|
|
|
|
{metric: "Measured", value: quality.measured_event_count},
|
|
|
|
|
{metric: "Allocated", value: quality.allocated_event_count},
|
|
|
|
|
{metric: "Estimated", value: quality.estimated_event_count},
|
|
|
|
|
{metric: "Superseded", value: quality.superseded_event_count},
|
|
|
|
|
{metric: "Fallback", value: quality.fallback_event_count},
|
|
|
|
|
{metric: "Unattributed measured", value: quality.unattributed_measured_event_count},
|
|
|
|
|
{metric: "Missing provenance", value: quality.missing_provenance_event_count},
|
|
|
|
|
{metric: "Duplicate sources", value: quality.duplicate_source_count},
|
|
|
|
|
];
|
|
|
|
|
}
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
2026-05-23 13:59:05 +02:00
|
|
|
const aggregate = tokenState.aggregate ?? {
|
|
|
|
|
tokens_in: 0, tokens_out: 0, tokens_total: 0, event_count: 0,
|
|
|
|
|
by_repo: [], by_workstream: [], by_task: [], by_model: [],
|
|
|
|
|
by_measurement_kind: {}, by_source_provider: {},
|
|
|
|
|
};
|
|
|
|
|
const quality = tokenState.quality ?? null;
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
const _ok = tokenState.ok ?? false;
|
|
|
|
|
const _ts = tokenState.ts;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# Token Cost
|
|
|
|
|
|
|
|
|
|
```js
|
2026-03-30 00:02:17 +02:00
|
|
|
display(html`<div style="font-size:0.8rem;color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">
|
2026-05-23 13:59:05 +02:00
|
|
|
● ${_ok ? `Live · ${_ts?.toLocaleTimeString()} · ${aggregate.event_count.toLocaleString()} events · ${aggregate.tokens_total.toLocaleString()} tokens` : "API offline"}
|
2026-03-30 00:02:17 +02:00
|
|
|
</div>`);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
2026-05-23 13:59:05 +02:00
|
|
|
display(html`<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0.75rem;margin:1rem 0">
|
|
|
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
|
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Tokens</div>
|
|
|
|
|
<div style="font-size:1.4rem;font-weight:650">${aggregate.tokens_total.toLocaleString()}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
|
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Events</div>
|
|
|
|
|
<div style="font-size:1.4rem;font-weight:650">${aggregate.event_count.toLocaleString()}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
|
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Last Event</div>
|
|
|
|
|
<div style="font-size:1rem;font-weight:650">${aggregate.last_event_at ? new Date(aggregate.last_event_at).toLocaleString() : "—"}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
|
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Last Ingested</div>
|
|
|
|
|
<div style="font-size:1rem;font-weight:650">${aggregate.last_ingested_at ? new Date(aggregate.last_ingested_at).toLocaleString() : "—"}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>`);
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## By Repo
|
|
|
|
|
|
|
|
|
|
```js
|
2026-03-30 00:02:17 +02:00
|
|
|
{
|
2026-05-23 13:59:05 +02:00
|
|
|
const sorted = sortRows(aggregate.by_repo ?? [], sortOrder);
|
|
|
|
|
const rows = sorted.slice(0, maxResults);
|
2026-03-30 00:02:17 +02:00
|
|
|
if (rows.length === 0) {
|
|
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No token events with repo association yet.</p>`);
|
|
|
|
|
} else {
|
|
|
|
|
display(Plot.plot({
|
|
|
|
|
title: "Token consumption by repo",
|
|
|
|
|
marginLeft: 160,
|
|
|
|
|
width: Math.min(900, width),
|
|
|
|
|
x: {label: "Tokens", tickFormat: "~s"},
|
|
|
|
|
y: {label: null},
|
2026-05-23 13:59:05 +02:00
|
|
|
marks: [Plot.barX(rows, {x: "tokens_total", y: "label", fill: "#4e79a7", tip: true})],
|
2026-03-30 00:02:17 +02:00
|
|
|
}));
|
|
|
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
2026-05-23 13:59:05 +02:00
|
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total", "event_count"],
|
|
|
|
|
header: {_ref: "REF", label: "Repo", tokens_in: "Tokens In", tokens_out: "Tokens Out", tokens_total: "Total", event_count: "Events"},
|
2026-03-30 00:02:17 +02:00
|
|
|
format: {
|
2026-05-23 13:59:05 +02:00
|
|
|
_ref: (_, i) => refCell(i + 1, "repos", rows[i].label),
|
|
|
|
|
label: d => nameCell(d, d),
|
|
|
|
|
tokens_in: d => d.toLocaleString(),
|
|
|
|
|
tokens_out: d => d.toLocaleString(),
|
2026-03-30 00:02:17 +02:00
|
|
|
tokens_total: d => d.toLocaleString(),
|
|
|
|
|
},
|
2026-05-23 13:59:05 +02:00
|
|
|
width: {_ref: 50, label: 160, tokens_in: 110, tokens_out: 110, tokens_total: 110, event_count: 80},
|
2026-03-30 00:02:17 +02:00
|
|
|
}));
|
|
|
|
|
}
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## By Workplan
|
|
|
|
|
|
|
|
|
|
```js
|
2026-03-30 00:02:17 +02:00
|
|
|
{
|
2026-05-23 13:59:05 +02:00
|
|
|
const sorted = sortRows(aggregate.by_workstream ?? [], sortOrder);
|
|
|
|
|
const rows = sorted.slice(0, maxResults);
|
2026-03-30 00:02:17 +02:00
|
|
|
if (rows.length === 0) {
|
2026-07-08 16:08:32 +02:00
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No workplan data yet.</p>`);
|
2026-03-30 00:02:17 +02:00
|
|
|
} else {
|
|
|
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
2026-05-23 13:59:05 +02:00
|
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total", "event_count"],
|
2026-07-08 16:08:32 +02:00
|
|
|
header: {_ref: "REF", label: "Workplan", tokens_in: "Tokens In", tokens_out: "Tokens Out", tokens_total: "Total", event_count: "Events"},
|
2026-03-30 00:02:17 +02:00
|
|
|
format: {
|
2026-07-08 16:08:32 +02:00
|
|
|
_ref: (_, i) => refCell(i + 1, "workplans", rows[i].scope_id),
|
2026-05-23 13:59:05 +02:00
|
|
|
label: d => nameCell(d, d),
|
|
|
|
|
tokens_in: d => d.toLocaleString(),
|
|
|
|
|
tokens_out: d => d.toLocaleString(),
|
2026-03-30 00:02:17 +02:00
|
|
|
tokens_total: d => d.toLocaleString(),
|
|
|
|
|
},
|
2026-05-23 13:59:05 +02:00
|
|
|
width: {_ref: 50, label: 240, tokens_in: 110, tokens_out: 110, tokens_total: 110, event_count: 80},
|
2026-03-30 00:02:17 +02:00
|
|
|
}));
|
2026-05-23 13:59:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2026-03-30 00:02:17 +02:00
|
|
|
|
2026-05-23 13:59:05 +02:00
|
|
|
## By Evidence
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
{
|
|
|
|
|
const kindRows = dictRows(aggregate.by_measurement_kind, "kind");
|
|
|
|
|
const sourceRows = dictRows(aggregate.by_source_provider, "source");
|
|
|
|
|
if (kindRows.length === 0 && sourceRows.length === 0) {
|
|
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No evidence breakdown yet.</p>`);
|
|
|
|
|
} else {
|
|
|
|
|
display(html`<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:1rem">
|
|
|
|
|
<div>${Inputs.table(kindRows, {
|
|
|
|
|
columns: ["kind", "tokens_total"],
|
|
|
|
|
header: {kind: "Kind", tokens_total: "Tokens"},
|
|
|
|
|
format: {tokens_total: d => d.toLocaleString()},
|
|
|
|
|
})}</div>
|
|
|
|
|
<div>${Inputs.table(sourceRows, {
|
|
|
|
|
columns: ["source", "tokens_total"],
|
|
|
|
|
header: {source: "Source", tokens_total: "Tokens"},
|
|
|
|
|
format: {tokens_total: d => d.toLocaleString()},
|
|
|
|
|
})}</div>
|
|
|
|
|
</div>`);
|
2026-03-30 00:02:17 +02:00
|
|
|
}
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## By Model
|
|
|
|
|
|
|
|
|
|
```js
|
2026-05-23 13:59:05 +02:00
|
|
|
{
|
|
|
|
|
const rows = (aggregate.by_model ?? []).slice(0, maxResults);
|
|
|
|
|
if (rows.length === 0) {
|
|
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No model data yet.</p>`);
|
|
|
|
|
} else {
|
|
|
|
|
display(Plot.plot({
|
|
|
|
|
title: "Token consumption by model",
|
|
|
|
|
marginLeft: 200,
|
|
|
|
|
width: Math.min(700, width),
|
|
|
|
|
x: {label: "Total tokens", tickFormat: "~s"},
|
|
|
|
|
marks: [Plot.barX(rows, {x: "tokens_total", y: "label", fill: "#59a14f", tip: true})],
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Data Quality
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
if (!quality) {
|
|
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No quality data yet.</p>`);
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
} else {
|
2026-05-23 13:59:05 +02:00
|
|
|
display(Inputs.table(metricRows(quality), {
|
|
|
|
|
columns: ["metric", "value"],
|
|
|
|
|
header: {metric: "Signal", value: "Count"},
|
|
|
|
|
format: {value: d => d.toLocaleString()},
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}));
|
2026-05-23 13:59:05 +02:00
|
|
|
display(html`<p style="font-size:0.8rem;color:var(--theme-foreground-muted)">
|
|
|
|
|
Codex: ${quality.last_codex_ingested_at ? new Date(quality.last_codex_ingested_at).toLocaleString() : "—"}
|
|
|
|
|
· Claude: ${quality.last_claude_ingested_at ? new Date(quality.last_claude_ingested_at).toLocaleString() : "—"}
|
|
|
|
|
· Reconcile: ${quality.last_reconciliation_at ? new Date(quality.last_reconciliation_at).toLocaleString() : "—"}
|
|
|
|
|
</p>`);
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-30 00:02:17 +02:00
|
|
|
## Top Tasks by Tokens
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
|
|
|
|
|
```js
|
2026-03-30 00:02:17 +02:00
|
|
|
{
|
2026-05-23 13:59:05 +02:00
|
|
|
const sorted = sortRows(aggregate.by_task ?? [], sortOrder);
|
|
|
|
|
const rows = sorted.slice(0, maxResults);
|
2026-03-30 00:02:17 +02:00
|
|
|
if (rows.length === 0) {
|
|
|
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No task-level data yet.</p>`);
|
|
|
|
|
} else {
|
|
|
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
2026-05-23 13:59:05 +02:00
|
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total"],
|
|
|
|
|
header: {_ref: "REF", label: "Task", tokens_in: "In", tokens_out: "Out", tokens_total: "Total"},
|
2026-03-30 00:02:17 +02:00
|
|
|
format: {
|
2026-05-23 13:59:05 +02:00
|
|
|
_ref: (_, i) => refCell(i + 1, "tasks", rows[i].scope_id),
|
|
|
|
|
label: d => nameCell(d, d),
|
|
|
|
|
tokens_in: d => d.toLocaleString(),
|
|
|
|
|
tokens_out: d => d.toLocaleString(),
|
2026-03-30 00:02:17 +02:00
|
|
|
tokens_total: d => d.toLocaleString(),
|
2026-03-29 22:35:35 +02:00
|
|
|
},
|
2026-05-23 13:59:05 +02:00
|
|
|
width: {_ref: 50, label: 260},
|
2026-03-30 00:02:17 +02:00
|
|
|
}));
|
|
|
|
|
}
|
feat(token-tracking): record AI token consumption per task (CUST-WP-0029)
Introduces end-to-end token consumption tracking so agent work is
visible as a cost/effort metric alongside tasks and workplans.
- Migration o2j3k4l5m6n7: token_events table with FK indexes on
task_id, workstream_id, repo_id, created_at
- ORM model, Pydantic schemas (TokenEventCreate, TokenEventRead with
computed tokens_total, TokenSummary)
- Router: POST /token-events/, GET /token-events/ (7 filters),
GET /token-events/summary/ (task|workstream|repo|commit|release scope)
- MCP tools: record_token_event, get_token_summary (formatted table)
- update_task_status enriched with optional tokens_in/tokens_out
passthrough — one call creates status update + token event
- Dashboard token-cost.md page: by-repo bar, by-workplan table,
by-model bar, top-10 tasks by tokens
- ralph-workplan skill updated with token reporting guidance and
per-task heuristics for estimating counts
- Tests: test_token_events.py + test_token_passthrough.py (182 pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 17:46:46 +02:00
|
|
|
}
|
|
|
|
|
```
|