Why we must do this
PR #34 added the has_subst flag to the tool-usage artifact. The flag shows command
substitution in a command. We need this flag to find the cause of the denials in #33.
We have too little data now. We must wait. Then we must look at the data.
When to do this
Do this after 2026-08-17. That date gives one week of data.
Do this before 2026-08-24. The artifacts stay for 14 days only. After that date the
first artifacts are gone.
What to measure
Count the gh pr review rows in the artifacts. Count them two times:
- Count all the attempts. Group them by
has_subst.
- Count only the denied attempts. Group them by
has_subst.
Then compare the two denial rates.
How to decide
The decision is simple:
- The denials are mostly
has_subst=true, and the attempts are not. Command substitution
is the cause. Then change the prompt. The reviewer must send the body without
substitution.
- The two rates are approximately equal. Command substitution is not the cause. Then
look for a different cause. The --body length is one other possible cause.
The data we have now
One run only. This is not enough data for a decision.
The run is monopoly PR#1696 (run 31436210556). The reviewer ran gh pr review one time.
That command had has_subst=true. The command was not denied.
This first result does not agree with the theory in #33. But one run proves nothing.
The numbers to compare against
These are the values before PR #34, from 170 runs:
| Measure |
Value |
gh pr review attempts |
241 |
| Attempts denied |
71 (29%) |
Denials that compound flagged |
1 |
| Extra cost for each affected run |
+$0.46 |
| Extra time for each affected run |
+73s |
How to get the numbers
The artifact for each run holds the counts. Read the commands and denied_commands fields.
gh api "repos/hotdata-dev/<repo>/actions/artifacts?per_page=100" \
--jq '.artifacts[] | select(.name|startswith("claude-tool-usage")) | [.id,.created_at] | @tsv'
gh api "repos/hotdata-dev/<repo>/actions/artifacts/<id>/zip" > a.zip
Do not use created=>DATE to filter the runs API. That filter returns zero rows always. Use
a range, like created=2026-08-17..2026-08-24. Or use no filter and compare the timestamps
in your own code.
Related
Why we must do this
PR #34 added the
has_substflag to the tool-usage artifact. The flag shows commandsubstitution in a command. We need this flag to find the cause of the denials in #33.
We have too little data now. We must wait. Then we must look at the data.
When to do this
Do this after 2026-08-17. That date gives one week of data.
Do this before 2026-08-24. The artifacts stay for 14 days only. After that date the
first artifacts are gone.
What to measure
Count the
gh pr reviewrows in the artifacts. Count them two times:has_subst.has_subst.Then compare the two denial rates.
How to decide
The decision is simple:
has_subst=true, and the attempts are not. Command substitutionis the cause. Then change the prompt. The reviewer must send the body without
substitution.
look for a different cause. The
--bodylength is one other possible cause.The data we have now
One run only. This is not enough data for a decision.
The run is monopoly PR#1696 (run 31436210556). The reviewer ran
gh pr reviewone time.That command had
has_subst=true. The command was not denied.This first result does not agree with the theory in #33. But one run proves nothing.
The numbers to compare against
These are the values before PR #34, from 170 runs:
gh pr reviewattemptscompoundflaggedHow to get the numbers
The artifact for each run holds the counts. Read the
commandsanddenied_commandsfields.Do not use
created=>DATEto filter the runs API. That filter returns zero rows always. Usea range, like
created=2026-08-17..2026-08-24. Or use no filter and compare the timestampsin your own code.
Related
gh pr reviewdenied on 29% of attempts despite being allowlisted #33 holds the bug and the evidence. Closegh pr reviewdenied on 29% of attempts despite being allowlisted #33 when this measurement gives an answer.