claude-code
changed title from WIP: werkrevisor 0.66.0: the cost of the PR Lens diagrams in the usage record, the result comment and the statistics to WIP: werkrevisor 0.67.0: the cost of the PR Lens diagrams in the usage record, the result comment and the statistics2026-10-23 18:25:38 +02:00
YELLOW tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
hint writing rules violated in the changed docs: docs/prs/2026-10-23-PR#140-diagram-cost.md:94: paragraph with 6 sentences, more than 5
hint 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 12 changed file(s) agree with the PR document according to Jev
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
hint the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
Classifier
9 of 9 units passed all questions (1 PR document(s)).
5 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 56964, duration 733.0 s, cost unknown, classifier `jev-1.14.0` calls 9, tokens in 7181, cost 0.0003 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 311, cost 0.0033 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 35192, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 8.1 s, model_review 722.7 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).
Reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3 -->
<!-- werkrevisor-verdict:YELLOW -->
<!-- werkrevisor-blocks:63409cde741958f25faba06dad424b3c,0e4219813569a2d1c065c10ba1484a61,d21e9a7256a79225097563dbbbe8a1c0,279ceb5c617659daa18bd3a576d54d9b,bde4d00d50992e84778f1ef8ae10d041,1d81fca8c42dbc68a8d130de0a42144b,0876df68568537d89da1bce76aa5a6d8,dd7390dea91edd0b4fb6707f447bb596,d5faadd9a5721f2e14276d12358bdb94,307098a735b318115a5d947f02ed8df9,911fff4991e0402eb539885689b26d85,8a49ae3c2cc1553554b7e938e7d2d221,9c5fe7e4c38bdc08691e292081d93ce7,0dee2ccc6cb7f9df3b332adcba209d6c,97ae6d34524bf5ac1bcaae7cfe1abb0d,683904167c90880f9394e05f2239f937,706f24f14f3caae33a87765bc63bba3e,ce44fe9624a95971d7e1fb8a5c819c03,dfc5e3c3eb88b186fff7c7a8b8130f02,d65111f4d74aae37689965efe54df69c,f9e2e3fb1f55296ff5420943cde9126a,847171037bae7a881992c05215d913c6,5ffb2d9443d0febb9533622afa206898,88e964d4ce7a2ef100b2784bd3e03776,c953214c00ea0365b24d859338dea927,0d4b1f06bb63ae5eaa5ecdc9eee188db,0de1f38acc3b2dac8c4b35bbe70ec881,f1585989851248a2cdd3839f190db11a,9c3053e2775f4d0593a78067cded19d7,38711d39767625743151649b51835f88,8568dc89fa52197e9b0d00aa56585fa2,a6d371d72925492d60f8e4495736daf9,d8b5adf93f08f525491e9bfbe337be2f,2268c92ee72701d419ac138890534913,891cb918b9ed302be63a10634b2fbe8f,d0961487cc001692775811eb5a3fb2bf,7edee309ed0d536adf129697841d130d,f25b2d9d5b10a5bc84cac33c7fd4d44b,3a46395c910fbba407ca48bbd1c79c9b,d2ffa4118b6f1812521f1a38c0798580,8445171d6fd82c2c4d76f1d633bb7199,4cc22115da27696787cecd65bfb65952,8e822399fc5dcf5eb33b233ccbbe8215,50f471144dae8364c94ec7a04c245f2c -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟡 werkrevisor YELLOW for <code>f0b4e4a</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/140#issuecomment-7473">the result for <code>15dd358</code></a></summary>
## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind: `mixed`.
### Reasons
- **YELLOW** tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
- **hint** writing rules violated in the changed docs: docs/prs/2026-10-23-PR#140-diagram-cost.md:94: paragraph with 6 sentences, more than 5
- **hint** 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 12 changed file(s) agree with the PR document according to Jev
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
- **hint** the model review failed: TimeoutError: review exceeded 720 seconds (advisory: Jev judged every changed file)
### Classifier
9 of 9 units passed all questions (1 PR document(s)).
5 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 56964, duration 733.0 s, cost unknown, classifier `jev-1.14.0` calls 9, tokens in 7181, cost 0.0003 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 311, cost 0.0033 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 35192, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 8.1 s, model_review 722.7 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).<br>
Reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: YELLOW / HUMAN_REVIEW for f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3.
The result comment of werkrevisor holds the details.
werkrevisor: YELLOW / HUMAN_REVIEW for `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3 -->
YELLOW "tests lost or disabled: tests/test_pr_lens_script.py:62": the new test class is skipped only without Node.js. The PR-Agent image of the documented local test command has no Node.js; the rootfs of werkrevisor, where Werkator runs the tests, has /usr/bin/node, the same Node.js that runs PR Lens in production. No existing test is disabled. This is left to the human.
Hint "paragraph with 6 sentences" in the PR document: on purpose, see its section "Test of the Release of a Held Merge"; it stays until the decision on the YELLOW reason.
Answer to the review of `f0b4e4a`:
- YELLOW "tests lost or disabled: tests/test_pr_lens_script.py:62": the new test class is skipped only without Node.js. The PR-Agent image of the documented local test command has no Node.js; the rootfs of werkrevisor, where Werkator runs the tests, has `/usr/bin/node`, the same Node.js that runs PR Lens in production. No existing test is disabled. This is left to the human.
- Hint "paragraph with 6 sentences" in the PR document: on purpose, see its section "Test of the Release of a Held Merge"; it stays until the decision on the YELLOW reason.
[medium] Unknown diagram costs are presented as a complete total (Codex review)
When diagram_calls is positive but diagram_cost_usd is null, boundedSum silently drops the unknown cost and preserves the review's cost_status. For a reported review cost, statistics therefore show the partial total as known, with unknownCostReviews unchanged and no estimated-cost indicator. Distinguish an unpriced drawing from a run that drew nothing.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] Unknown diagram costs are presented as a complete total** (Codex review)
When diagram_calls is positive but diagram_cost_usd is null, boundedSum silently drops the unknown cost and preserves the review's cost_status. For a reported review cost, statistics therefore show the partial total as known, with unknownCostReviews unchanged and no estimated-cost indicator. Distinguish an unpriced drawing from a run that drew nothing.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 00a7b82: a record with diagram_calls > 0 and no diagram_cost_usd makes the cost of the review unknown, so it counts in unknownCostReviews. Test: Scenario#140.03 in UsageApiIntegrationTest.
Fixed in `00a7b82`: a record with `diagram_calls > 0` and no `diagram_cost_usd` makes the cost of the review unknown, so it counts in `unknownCostReviews`. Test: Scenario#140.03 in `UsageApiIntegrationTest`.
[medium] A timeout loses usage from completed calls (Codex review)
Usage is emitted only when drawing finishes or throws. If the first model call completes but the repair call exceeds timeout_seconds, subprocess.run kills Node without executing this catch block. prlens.draw then raises DiagramError without usage, so the record reports zero diagram calls and loses the first call's known cost.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] A timeout loses usage from completed calls** (Codex review)
Usage is emitted only when drawing finishes or throws. If the first model call completes but the repair call exceeds timeout_seconds, subprocess.run kills Node without executing this catch block. prlens.draw then raises DiagramError without usage, so the record reports zero diagram calls and loses the first call's known cost.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 00a7b82: pr_lens.mjs writes the usage to stderr after each call; on a timeout prlens.draw reads the last line from the killed process. Test: test_a_killed_pr_lens_names_the_usage_of_its_finished_calls.
Fixed in `00a7b82`: `pr_lens.mjs` writes the usage to stderr after each call; on a timeout `prlens.draw` reads the last line from the killed process. Test: `test_a_killed_pr_lens_names_the_usage_of_its_finished_calls`.
The changes add diagram usage accounting and reporting without changing authorization, credentials, merge decisions, or existing data. The identified risks concern incomplete cost reporting.
Findings
[medium] A timeout loses usage from completed calls at werkrevisor/pr_lens.mjs:97, as a line comment
[medium] Unknown diagram costs are presented as a complete total at statistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 459108 (cached 411648) / out 2508, 100.2 s, 14 commands; reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3`.
<!-- werkrevisor-codex:f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3 -->
<!-- werkrevisor-codex-critical:false -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: not critical
The changes add diagram usage accounting and reporting without changing authorization, credentials, merge decisions, or existing data. The identified risks concern incomplete cost reporting.
### Findings
1. **[medium] A timeout loses usage from completed calls** at `werkrevisor/pr_lens.mjs:97`, as a line comment
2. **[medium] Unknown diagram costs are presented as a complete total** at `statistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 459108 (cached 411648) / out 2508, 100.2 s, 14 commands; reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3`.
</sub>
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
ORANGE 2 finding(s) that Jev rates as probably real and severe: werkrevisor/pr_lens.mjs:22-30, werkrevisor/cli.py:1323-1328
YELLOW tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
hint 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 12 changed file(s) agree with the PR document according to Jev
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
hint 1 finding(s) of low severity or rated minor by Jev: werkrevisor/cli.py:1349-1362
hint model recommends changes before merge
Findings
[medium] Failed Call at werkrevisor/pr_lens.mjs:22-30, ORANGE, as a line comment
[medium] Callback Contract at werkrevisor/cli.py:1323-1328, ORANGE, as a line comment
[medium] Missing Cost at werkrevisor/cli.py:1349-1362, hint, as a line comment
Classifier
8 of 8 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57780, calls 1, tokens in 55744 / out 34265 (reasoning included in out), duration 404.1 s, cost 0.0578 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6459, cost 0.0003 USD, finding judge `jev-1.14.0` calls 3, tokens in 5047, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 33992, cost 0.0014 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.4 s, doc_check 0.7 s, context 7.7 s, model_review 393.5 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).
Reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
<!-- werkrevisor:15dd3582cc28789f6a20e96f0611848837dd5636 -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:63409cde741958f25faba06dad424b3c,134e9e7c4e20da097e94506deaddc9fe,d21e9a7256a79225097563dbbbe8a1c0,279ceb5c617659daa18bd3a576d54d9b,bde4d00d50992e84778f1ef8ae10d041,1d81fca8c42dbc68a8d130de0a42144b,0876df68568537d89da1bce76aa5a6d8,dd7390dea91edd0b4fb6707f447bb596,d5faadd9a5721f2e14276d12358bdb94,307098a735b318115a5d947f02ed8df9,911fff4991e0402eb539885689b26d85,8a49ae3c2cc1553554b7e938e7d2d221,9c5fe7e4c38bdc08691e292081d93ce7,0dee2ccc6cb7f9df3b332adcba209d6c,97ae6d34524bf5ac1bcaae7cfe1abb0d,683904167c90880f9394e05f2239f937,706f24f14f3caae33a87765bc63bba3e,ce44fe9624a95971d7e1fb8a5c819c03,dfc5e3c3eb88b186fff7c7a8b8130f02,d65111f4d74aae37689965efe54df69c,f9e2e3fb1f55296ff5420943cde9126a,847171037bae7a881992c05215d913c6,5ffb2d9443d0febb9533622afa206898,88e964d4ce7a2ef100b2784bd3e03776,c953214c00ea0365b24d859338dea927,0d4b1f06bb63ae5eaa5ecdc9eee188db,0de1f38acc3b2dac8c4b35bbe70ec881,f1585989851248a2cdd3839f190db11a,9c3053e2775f4d0593a78067cded19d7,38711d39767625743151649b51835f88,8568dc89fa52197e9b0d00aa56585fa2,a6d371d72925492d60f8e4495736daf9,d8b5adf93f08f525491e9bfbe337be2f,2268c92ee72701d419ac138890534913,891cb918b9ed302be63a10634b2fbe8f,d0961487cc001692775811eb5a3fb2bf,7edee309ed0d536adf129697841d130d,f25b2d9d5b10a5bc84cac33c7fd4d44b,3a46395c910fbba407ca48bbd1c79c9b,d2ffa4118b6f1812521f1a38c0798580,8445171d6fd82c2c4d76f1d633bb7199,4cc22115da27696787cecd65bfb65952,8e822399fc5dcf5eb33b233ccbbe8215,50f471144dae8364c94ec7a04c245f2c -->
<!-- werkrevisor:diagrams -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>15dd358</code>, superseded by <a href="http://git.javagil.de/mi/werkrevisor/pulls/140#issuecomment-7501">the result for <code>00a7b82</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 2 finding(s) that Jev rates as probably real and severe: werkrevisor/pr_lens.mjs:22-30, werkrevisor/cli.py:1323-1328
- **YELLOW** tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
- **hint** 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 12 changed file(s) agree with the PR document according to Jev
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
- **hint** 1 finding(s) of low severity or rated minor by Jev: werkrevisor/cli.py:1349-1362
- **hint** model recommends changes before merge
### Findings
1. **[medium] Failed Call** at `werkrevisor/pr_lens.mjs:22-30`, ORANGE, as a line comment
2. **[medium] Callback Contract** at `werkrevisor/cli.py:1323-1328`, ORANGE, as a line comment
3. **[medium] Missing Cost** at `werkrevisor/cli.py:1349-1362`, hint, as a line comment
### Classifier
8 of 8 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57780, calls 1, tokens in 55744 / out 34265 (reasoning included in out), duration 404.1 s, cost 0.0578 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6459, cost 0.0003 USD, finding judge `jev-1.14.0` calls 3, tokens in 5047, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 33992, cost 0.0014 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.4 s, doc_check 0.7 s, context 7.7 s, model_review 393.5 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).<br>
Reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
</sub>
### Diagrams
Drawn by a model from the diff with [PR Lens](https://github.com/coldteadotai/pr-lens): a view of what the PR touches, not a statement of what it does.


</details>
werkrevisor: ORANGE / FINDINGS for 15dd3582cc28789f6a20e96f0611848837dd5636.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `15dd3582cc28789f6a20e96f0611848837dd5636`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:15dd3582cc28789f6a20e96f0611848837dd5636 -->
Deps.draw_diagrams is still documented as returning a list of (name, SVG) tuples, but _draw now unconditionally reads .usage and .files from a Drawing. A callback following the documented contract raises AttributeError, so its diagrams and usage are skipped. Update the contract and callers, or support the former return shape.
**[medium] Callback Contract** (werkrevisor)
ORANGE; Jev: real 0.79, severity 2.9 of 3
`Deps.draw_diagrams` is still documented as returning a list of `(name, SVG)` tuples, but `_draw` now unconditionally reads `.usage` and `.files` from a `Drawing`. A callback following the documented contract raises `AttributeError`, so its diagrams and usage are skipped. Update the contract and callers, or support the former return shape.
Fixed in 00a7b82: the comment of Deps.draw_diagrams names the new contract, prlens.Drawing and prlens.DiagramError with its usage; all callers return a Drawing.
Fixed in `00a7b82`: the comment of `Deps.draw_diagrams` names the new contract, `prlens.Drawing` and `prlens.DiagramError` with its usage; all callers return a `Drawing`.
The new cost line is added only when at least one asset was attached. If PR Lens returns no SVGs, or if every create_comment_asset call fails, _draw has already recorded a paid model call but the result comment receives no Diagrams section and never names that cost. Add the usage section when usage exists even if no asset could be attached.
**[medium] Missing Cost** (werkrevisor)
hint; Jev: real 0.80, severity 2.4 of 3
The new cost line is added only when at least one asset was attached. If PR Lens returns no SVGs, or if every `create_comment_asset` call fails, `_draw` has already recorded a paid model call but the result comment receives no Diagrams section and never names that cost. Add the usage section when usage exists even if no asset could be attached.
Fixed in 00a7b82: a drawing that cost something but attached no diagram gets the section "Diagrams" with "No diagram was attached." and the cost line. Test: test_a_drawing_without_a_diagram_names_its_cost_in_the_comment.
Fixed in `00a7b82`: a drawing that cost something but attached no diagram gets the section "Diagrams" with "No diagram was attached." and the cost line. Test: `test_a_drawing_without_a_diagram_names_its_cost_in_the_comment`.
If originalFetch rejects after OpenRouter accepted the request, such as when the connection drops while reading the response, the wrapper never reaches its accounting block. The outer handler then emits calls: 0 and a known $0 cost even though the request may have been billed. Count the attempted call and mark its cost unknown when fetch rejects.
**[medium] Failed Call** (werkrevisor)
ORANGE; Jev: real 0.90, severity 2.9 of 3
If `originalFetch` rejects after OpenRouter accepted the request, such as when the connection drops while reading the response, the wrapper never reaches its accounting block. The outer handler then emits `calls: 0` and a known `$0` cost even though the request may have been billed. Count the attempted call and mark its cost unknown when `fetch` rejects.
Fixed in 00a7b82: the wrapper counts the call before fetch, and a rejected fetch makes the cost unknown. Test: test_a_call_without_an_answer_counts_with_an_unknown_cost.
Fixed in `00a7b82`: the wrapper counts the call before `fetch`, and a rejected `fetch` makes the cost unknown. Test: `test_a_call_without_an_answer_counts_with_an_unknown_cost`.
[medium] Unknown diagram costs become apparently complete totals (Codex review)
The script deliberately returns diagram_cost_usd=null when a response lacks cost information. boundedSum drops that null, while costStatus remains the review's original status. Thus a reported $0.10 review with paid diagram calls of unknown cost appears as a fully known $0.10 total, without increasing unknownCostReviews or marking the total estimated. Distinguish missing diagram costs after calls from diagrams that were never requested.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] Unknown diagram costs become apparently complete totals** (Codex review)
The script deliberately returns diagram_cost_usd=null when a response lacks cost information. boundedSum drops that null, while costStatus remains the review's original status. Thus a reported $0.10 review with paid diagram calls of unknown cost appears as a fully known $0.10 total, without increasing unknownCostReviews or marking the total estimated. Distinguish missing diagram costs after calls from diagrams that were never requested.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 00a7b82, the same finding as on f0b4e4a (this review was of 15dd358): diagram_calls > 0 without diagram_cost_usd makes the review cost unknown.
Fixed in `00a7b82`, the same finding as on `f0b4e4a` (this review was of `15dd358`): `diagram_calls > 0` without `diagram_cost_usd` makes the review cost unknown.
[medium] Completed-call usage is lost when drawing times out (Codex review)
Usage is emitted only when drawing finishes or throws. If the first paid response completes but a repair call exceeds timeout_seconds, subprocess.run kills Node before either output path runs. The Python timeout handler then supplies no usage, so the record reports zero diagram calls and no cost despite the completed paid call. Preserve usage incrementally and recover it on timeout.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] Completed-call usage is lost when drawing times out** (Codex review)
Usage is emitted only when drawing finishes or throws. If the first paid response completes but a repair call exceeds timeout_seconds, subprocess.run kills Node before either output path runs. The Python timeout handler then supplies no usage, so the record reports zero diagram calls and no cost despite the completed paid call. Preserve usage incrementally and recover it on timeout.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
The change adds diagram usage accounting and display without changing authorization, merge decisions, CI gates, or existing data. Its failure modes affect reported costs rather than security or merge safety.
Findings
[medium] Completed-call usage is lost when drawing times out at werkrevisor/pr_lens.mjs:88, as a line comment
[medium] Unknown diagram costs become apparently complete totals at statistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 398128 (cached 351360) / out 2045, 84.7 s, 8 commands; reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636`.
<!-- werkrevisor-codex:15dd3582cc28789f6a20e96f0611848837dd5636 -->
<!-- werkrevisor-codex-critical:false -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: not critical
The change adds diagram usage accounting and display without changing authorization, merge decisions, CI gates, or existing data. Its failure modes affect reported costs rather than security or merge safety.
### Findings
1. **[medium] Completed-call usage is lost when drawing times out** at `werkrevisor/pr_lens.mjs:88`, as a line comment
2. **[medium] Unknown diagram costs become apparently complete totals** at `statistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 398128 (cached 351360) / out 2045, 84.7 s, 8 commands; reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636`.
</sub>
⚠️ werkrevisor: ERROR / review not completed, human action needed
Change kind: mixed.
Reasons
ERROR the model answer reached the output token limit [model] max_output_tokens = 64000
YELLOW tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
hint 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
hint 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 12 changed file(s) agree with the PR document according to Jev
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
hint 1 finding(s) of low severity or rated minor by Jev: werkrevisor/pr_lens.mjs
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
Issue at werkrevisor/pr_lens.mjs, severity unknown, counted as medium
hint; Jev: real 0.15, severity 0.5 of 3
Classifier
8 of 8 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60003, calls 1, tokens in 57881 / out 64000 (reasoning included in out), duration 676.1 s, cost 0.0941 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6576, cost 0.0003 USD, finding judge `jev-1.14.0` calls 1, tokens in 576, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25869 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 36682, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 12224, cost 0.0005 USD, phases: path_judge 0.5 s, classifier 0.4 s, doc_check 0.5 s, context 6.5 s, model_review 667.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).
Reviewed head `00a7b8264a859317fcf75eb1a0e83443f0e5b48a` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
<!-- werkrevisor-error:00a7b8264a859317fcf75eb1a0e83443f0e5b48a -->
## ⚠️ werkrevisor: ERROR / review not completed, human action needed
Change kind: `mixed`.
### Reasons
- **ERROR** the model answer reached the output token limit [model] max_output_tokens = 64000
- **YELLOW** tests lost or disabled: tests/test_pr_lens_script.py:62 disables a test
- **hint** 9 changed file(s) that neither weaken the checks, touch security nor show a risk category of the repository config according to Jev
- **hint** 1 build change(s) of low risk according to Jev: statistics/build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 12 changed file(s) agree with the PR document according to Jev
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): werkrevisor/cli.py
- **hint** 1 finding(s) of low severity or rated minor by Jev: werkrevisor/pr_lens.mjs
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **Issue** at `werkrevisor/pr_lens.mjs`, severity unknown, counted as medium
hint; Jev: real 0.15, severity 0.5 of 3
### Classifier
8 of 8 units passed all questions (1 PR document(s)).
3 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60003, calls 1, tokens in 57881 / out 64000 (reasoning included in out), duration 676.1 s, cost 0.0941 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6576, cost 0.0003 USD, finding judge `jev-1.14.0` calls 1, tokens in 576, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25869 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 36682, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 12224, cost 0.0005 USD, phases: path_judge 0.5 s, classifier 0.4 s, doc_check 0.5 s, context 6.5 s, model_review 667.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main bad4782).<br>
Reviewed head `00a7b8264a859317fcf75eb1a0e83443f0e5b48a` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
</sub>
Some required checks failed
ai-review/codex queued ⏳: Codex reviews after werkrevisor
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
See the PR document under docs/prs/.
🤖 Generated with Claude Code
WIP: werkrevisor 0.66.0: the cost of the PR Lens diagrams in the usage record, the result comment and the statisticsto WIP: werkrevisor 0.67.0: the cost of the PR Lens diagrams in the usage record, the result comment and the statistics🟡 werkrevisor YELLOW for
f0b4e4a, superseded by the result for15dd358🟡 werkrevisor: YELLOW / HUMAN_REVIEW
Change kind:
mixed.Reasons
Classifier
9 of 9 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM failed (TimeoutError), tokens counted before the call 56964, duration 733.0 s, cost unknown, classifier `jev-1.14.0` calls 9, tokens in 7181, cost 0.0003 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 311, cost 0.0033 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 35192, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.5 s, doc_check 0.6 s, context 8.1 s, model_review 722.7 s, config: defaults + host + repository (.werkrevisor.toml at main5 answers ignored because of a confidence below [classifier] min_confidence.
bad4782).Reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: YELLOW / HUMAN_REVIEW for
f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3.The result comment of werkrevisor holds the details.
@@ -0,0 +91,4 @@### Test of the Release of a Held MergeThis paragraph breaks the writing rule of at most 5 sentences on purpose.Writing rule: paragraph with 6 sentences, more than 5 (werkrevisor writing rule)
Fix the line, or reply why it stays, and resolve this comment.
Fixed in
15dd358: the deliberate paragraph is removed; the test of the release moves to a simpler PR.Answer to the review of
f0b4e4a:/usr/bin/node, the same Node.js that runs PR Lens in production. No existing test is disabled. This is left to the human.Codex review of
f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3@@ -203,1 +204,3 @@boundedSum("cost_usd, excerpt_cost_usd and doc_summary_cost_usd", listOf(it, excerptCost, docSummaryCost))boundedSum("cost_usd, excerpt_cost_usd, doc_summary_cost_usd and diagram_cost_usd",listOf(it, excerptCost, docSummaryCost, diagramCost),[medium] Unknown diagram costs are presented as a complete total (Codex review)
When diagram_calls is positive but diagram_cost_usd is null, boundedSum silently drops the unknown cost and preserves the review's cost_status. For a reported review cost, statistics therefore show the partial total as known, with unknownCostReviews unchanged and no estimated-cost indicator. Distinguish an unpriced drawing from a run that drew nothing.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
00a7b82: a record withdiagram_calls > 0and nodiagram_cost_usdmakes the cost of the review unknown, so it counts inunknownCostReviews. Test: Scenario#140.03 inUsageApiIntegrationTest.@@ -68,5 +94,6 @@ try {? error.details.split("\n").map((l) => l.trim()).filter(Boolean).slice(0, 2).join(" ") : "";process.stderr.write(`pr-lens: ${error?.code ?? error?.name ?? "Error"}: ${error?.message ?? error}`+ `${detail ? ` (${detail})` : ""}\n`);process.stdout.write(JSON.stringify({ usage: usageOut() }));[medium] A timeout loses usage from completed calls (Codex review)
Usage is emitted only when drawing finishes or throws. If the first model call completes but the repair call exceeds timeout_seconds, subprocess.run kills Node without executing this catch block. prlens.draw then raises DiagramError without usage, so the record reports zero diagram calls and loses the first call's known cost.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
00a7b82:pr_lens.mjswrites the usage to stderr after each call; on a timeoutprlens.drawreads the last line from the killed process. Test:test_a_killed_pr_lens_names_the_usage_of_its_finished_calls.Codex review: not critical
The changes add diagram usage accounting and reporting without changing authorization, credentials, merge decisions, or existing data. The identified risks concern incomplete cost reporting.
Findings
werkrevisor/pr_lens.mjs:97, as a line commentstatistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 459108 (cached 411648) / out 2508, 100.2 s, 14 commands; reviewed head `f0b4e4aa186b8cf5db81a890c520f2f6e4f1fdc3`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟠 werkrevisor ORANGE for
15dd358, superseded by the result for00a7b82🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
Findings
[medium] Failed Call at
werkrevisor/pr_lens.mjs:22-30, ORANGE, as a line comment[medium] Callback Contract at
werkrevisor/cli.py:1323-1328, ORANGE, as a line comment[medium] Missing Cost at
werkrevisor/cli.py:1349-1362, hint, as a line commentClassifier
8 of 8 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57780, calls 1, tokens in 55744 / out 34265 (reasoning included in out), duration 404.1 s, cost 0.0578 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6459, cost 0.0003 USD, finding judge `jev-1.14.0` calls 3, tokens in 5047, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25495 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 33992, cost 0.0014 USD, build file judge `jev-1.14.0` calls 10, tokens in 10982, cost 0.0005 USD, phases: path_judge 0.6 s, classifier 0.4 s, doc_check 0.7 s, context 7.7 s, model_review 393.5 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
bad4782).Reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
Diagrams
Drawn by a model from the diff with PR Lens: a view of what the PR touches, not a statement of what it does.
werkrevisor: ORANGE / FINDINGS for
15dd3582cc28789f6a20e96f0611848837dd5636.The result comment of werkrevisor holds the details.
@@ -1321,0 +1320,4 @@def _draw(config, pr, deps, diff, result):"""The usage goes into result.diagrams, also of a drawing that failed after a model call."""try:drawing = deps.draw_diagrams(config, pr, diff)[medium] Callback Contract (werkrevisor)
ORANGE; Jev: real 0.79, severity 2.9 of 3
Deps.draw_diagramsis still documented as returning a list of(name, SVG)tuples, but_drawnow unconditionally reads.usageand.filesfrom aDrawing. A callback following the documented contract raisesAttributeError, so its diagrams and usage are skipped. Update the contract and callers, or support the former return shape.Fixed in
00a7b82: the comment ofDeps.draw_diagramsnames the new contract,prlens.Drawingandprlens.DiagramErrorwith its usage; all callers return aDrawing.@@ -1336,3 +1347,3 @@if not diff:returndrawn = deps.draw_diagrams(config, pr, diff)drawn = _draw(config, pr, deps, diff, result)[medium] Missing Cost (werkrevisor)
hint; Jev: real 0.80, severity 2.4 of 3
The new cost line is added only when at least one asset was attached. If PR Lens returns no SVGs, or if every
create_comment_assetcall fails,_drawhas already recorded a paid model call but the result comment receives no Diagrams section and never names that cost. Add the usage section when usage exists even if no asset could be attached.Fixed in
00a7b82: a drawing that cost something but attached no diagram gets the section "Diagrams" with "No diagram was attached." and the cost line. Test:test_a_drawing_without_a_diagram_names_its_cost_in_the_comment.@@ -13,0 +19,4 @@// OpenRouter adds the cost of the call to its usage only on request.request = { ...init, body: JSON.stringify({ ...JSON.parse(init.body), usage: { include: true } }) };}const response = await originalFetch(url, request);[medium] Failed Call (werkrevisor)
ORANGE; Jev: real 0.90, severity 2.9 of 3
If
originalFetchrejects after OpenRouter accepted the request, such as when the connection drops while reading the response, the wrapper never reaches its accounting block. The outer handler then emitscalls: 0and a known$0cost even though the request may have been billed. Count the attempted call and mark its cost unknown whenfetchrejects.Fixed in
00a7b82: the wrapper counts the call beforefetch, and a rejectedfetchmakes the cost unknown. Test:test_a_call_without_an_answer_counts_with_an_unknown_cost.Codex review of
15dd3582cc28789f6a20e96f0611848837dd5636@@ -203,1 +204,3 @@boundedSum("cost_usd, excerpt_cost_usd and doc_summary_cost_usd", listOf(it, excerptCost, docSummaryCost))boundedSum("cost_usd, excerpt_cost_usd, doc_summary_cost_usd and diagram_cost_usd",listOf(it, excerptCost, docSummaryCost, diagramCost),[medium] Unknown diagram costs become apparently complete totals (Codex review)
The script deliberately returns diagram_cost_usd=null when a response lacks cost information. boundedSum drops that null, while costStatus remains the review's original status. Thus a reported $0.10 review with paid diagram calls of unknown cost appears as a fully known $0.10 total, without increasing unknownCostReviews or marking the total estimated. Distinguish missing diagram costs after calls from diagrams that were never requested.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
00a7b82, the same finding as onf0b4e4a(this review was of15dd358):diagram_calls > 0withoutdiagram_cost_usdmakes the review cost unknown.@@ -60,6 +85,7 @@ try {flows: document.flows.length,attempts,truncated: truncatedAt !== undefined,usage: usageOut(),[medium] Completed-call usage is lost when drawing times out (Codex review)
Usage is emitted only when drawing finishes or throws. If the first paid response completes but a repair call exceeds timeout_seconds, subprocess.run kills Node before either output path runs. The Python timeout handler then supplies no usage, so the record reports zero diagram calls and no cost despite the completed paid call. Preserve usage incrementally and recover it on timeout.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
00a7b82, the same finding as onf0b4e4a(this review was of15dd358): the usage goes to stderr after each call and survives a timeout.Codex review: not critical
The change adds diagram usage accounting and display without changing authorization, merge decisions, CI gates, or existing data. Its failure modes affect reported costs rather than security or merge safety.
Findings
werkrevisor/pr_lens.mjs:88, as a line commentstatistics/src/main/kotlin/de/javagil/werkrevisor/statistics/UsageApiController.kt:206, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 398128 (cached 351360) / out 2045, 84.7 s, 8 commands; reviewed head `15dd3582cc28789f6a20e96f0611848837dd5636`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.⚠️ werkrevisor: ERROR / review not completed, human action needed
Change kind:
mixed.Reasons
Findings
Issue at
werkrevisor/pr_lens.mjs, severity unknown, counted as mediumhint; Jev: real 0.15, severity 0.5 of 3
Classifier
8 of 8 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 60003, calls 1, tokens in 57881 / out 64000 (reasoning included in out), duration 676.1 s, cost 0.0941 USD (reported), classifier `jev-1.14.0` calls 8, tokens in 6576, cost 0.0003 USD, finding judge `jev-1.14.0` calls 1, tokens in 576, cost 0.0000 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 1, tokens in 25869 / out 380, cost 0.0034 USD (reported), PR document check `jev-1.14.0` calls 12, tokens in 36682, cost 0.0015 USD, build file judge `jev-1.14.0` calls 10, tokens in 12224, cost 0.0005 USD, phases: path_judge 0.5 s, classifier 0.4 s, doc_check 0.5 s, context 6.5 s, model_review 667.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main3 answers ignored because of a confidence below [classifier] min_confidence.
bad4782).Reviewed head `00a7b8264a859317fcf75eb1a0e83443f0e5b48a` against base `bad4782b99657b8a662db8b9333722c04619962e`, werkrevisor 0.65.2.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.