Repository navigation
feat(review): capture reviewer-stage model usage as ADR-012 records - #126
Conversation
) The reviewer is the one DevLoop stage whose model call runs in this repository rather than in mctl-agents, so its share of #50 lives here: capture usage at the model-call boundary, from the result entry claude-code-action writes, instead of inferring it from logs later. Each reviewer attempt is captured immediately after it runs (the fallback overwrites the primary's execution file), one record per (run, model), shaped as the body of mctl-api's POST /api/v1/usage/records: - absent counters stay absent, never zero; - no text is copied: only the result entry's scalar fields and the modelUsage counters are read; - no provider_reported_cost (subscription token) and no calculated_cost from the CLI's own price table, which has no mctl pricing_version; the ledger prices at ingest; - no id: the ledger derives it, which keeps redelivery idempotent. Records are collapsed on the ledger's dedupe key, keeping the first, so a fallback that re-reads the primary's file is not counted twice while a fallback that re-ran the model stays a second charge. Every usage step is always() and continue-on-error: measurement never blocks a review, and an attempt that left nothing readable is reported as unmeasured. Nothing is sent to the ledger: ingestion is admin-only, and an admin credential does not belong in every caller repository. The records are published to the job summary and a run artifact until a scoped ingestion path exists. Tests read the jq programs and the capture script out of the workflow itself and run them; workflow-tests.yml runs them in CI. Claude-Session: https://claude.ai/code/session_01TpRNAyt63CpPb9xpQPBy2G
|
Claude finished @mashkovd's task in 5m 17s —— View job Review: reviewer-stage model usage capture
Verdict: no P1/P2 findings — 4 P3 nits. Good to merge.The design holds up under scrutiny. I traced each of the stated invariants through the actual jq/bash and they are real, not aspirational:
The test file is unusually good: it extracts the programs from the shipped YAML rather than copying them, and P3 — nice to have, non-blocking1. The one unmeasured attempt that isn't annotated — The stated invariant is "an attempt with nothing readable is reported as not measured, never as zero." One path escapes it. If the fallback genuinely runs (and is genuinely charged) but crashes before rewriting Capturing nothing is the right call (the counters really are unknowable), but this is precisely the scenario the fallback exists for, so it deserves the same annotation as the other two. A cheap fix in the publish step: warn when the merged record count is lower than the summed per-attempt count. 2. Run-level fields are duplicated across per-model records —
3. The filter covers 4. Fixed artifact name across 18 callers —
What I could not verify
|
Antigravity (agy) review — informationalModel: gemini-3.8-flash-medium · commit Finding 1 (P2)
Finding 2 (P2)
Finding 3 (P3)
|
|
Review round on agy, both P2s are false positives. Checked against the file at this head:
agy's P3 (assert Claude: no P1/P2, 4 P3s. All non-blocking and left as follow-ups:
Merge gate: this PR edits |
Part of #50. The issue stays open: every other DevLoop stage lives in mctl-agents, and the end-to-end pilot needs the usage ledger released and deployed.
What
The reviewer is the one DevLoop stage whose model call runs in this repo (
claude-review.yml). This PR measures its model usage as ADR-012 usage records.POST /api/v1/usage/records. Each record carries repo, PR,agent=claude-reviewanddevloop_stage=reviewer, plus the token counters: input, output, cache read/write and web search. Each attempt is captured immediately, because the fallback overwrites the primary's output file.if: always()andcontinue-on-error: true, and sits before the existing fail-closed classification. An attempt with nothing readable is reported as not measured, never as zero.model-usage-recordsartifact. Nothing is sent to the ledger yet: its write endpoint is admin-only, and an admin credential does not belong in all 18 caller repos.workflow-tests.ymlruns the new tests in CI, because the reviewer cannot review its own workflow.Tests
.github/tests/test_claude_review_usage.py. They extract the scripts from the workflow file and run them. All pass.validate.py roadmap/epics.Merge note
This PR edits
claude-review.ymlitself, so the reviewer cannot approve it and it needs a human review. After merge, the 18 caller repos need their pinned SHA bumped to pick it up.Follow-ups (other repos)
USAGE_DB_URLandUSAGE_PRICING_CATALOG.🤖 Generated with Claude Code
https://claude.ai/code/session_01TpRNAyt63CpPb9xpQPBy2G