fix(skill): correct the GHAS budget hard-stop procedure, which could not run - #1303
Merged
Conversation
…not run
Step 7 of github-metered-billing-leak-diagnosis shipped a `gh api` POST that
fails against the live API. It omitted every required body field
(`budget_type`, `budget_product_sku`, `budget_scope`, `budget_entity_name`,
`budget_alerting`), so the call returns `400 Missing required fields`. Even
with those supplied, the implied `ProductPricing` + `ghas` combination returns
500 — GHAS budgets are only accepted at `SkuPricing` granularity.
The working procedure was already established empirically in session
c8d15a6c (2026-08-24), which created both GHAS license budgets successfully
after three API rejections. That evidence never made it into the skill.
Changes:
- Replace step 7 with the verified `SkuPricing` procedure, looping over both
`ghas_secret_protection_licenses` and `ghas_code_security_licenses` —
capping only one leaves the other accruing. The emitted JSON body is
byte-identical to the request that succeeded on 08-24. Adds a pre-check
listing existing budgets.
- Add trap 5 covering the three distinct rejections: the stop flag is
`prevent_further_usage`, the ProductPricing 500 text ("except for GHAS
SKUs") should be read as permission rather than refusal, and
`budget_amount` counts licenses rather than dollars for license-based
products. `budget_amount: 0` therefore means zero seats, not zero spend.
- Document the SKU-enumeration technique: posting an invalid SKU makes the
API return the full valid-SKU list. Flagged as a POST that leaves the
otherwise read-only path, though it creates nothing.
- Correct the Safety section, which described the hard stop in dollar terms
and did not list the enumeration probe as mutating.
Verified: bash/jq syntax checks pass, generated body matches the 08-24
success, and the no-headings autoinstall lint passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jinon86
approved these changes
Aug 26, 2026
jinon86
left a comment
Contributor
There was a problem hiding this comment.
Approved after explicit operator authorization using the Seoseo-held jinon86 credential.
`gh pr update-branch` produced a `web-flow` merge commit that did not raise a `pull_request` synchronize event, so none of the six required checks ran on the new head and the PR sat at BLOCKED with zero reported checks. The workflows carry no path filters, so this was a missing event rather than a skipped trigger. This empty commit exists only to raise that event. It carries no file changes, and the squash merge collapses it, so it leaves nothing on main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Step 7 of
github-metered-billing-leak-diagnosis— the skill's only mutatingstep — shipped a
gh apicall that cannot succeed against the live API:gh api -X POST "/organizations/$ORG/settings/billing/budgets" \ -f budget_amount=0 -F prevent_further_usage=trueEvery required body field is missing (
budget_type,budget_product_sku,budget_scope,budget_entity_name,budget_alerting), so this returns400 Missing required fields. Supplying them under the impliedProductPricing+ghascombination then returns 500 — GHAS budgets areaccepted only at
SkuPricinggranularity.The correct procedure was already established empirically in session
c8d15a6c(2026-08-24), which hit three separate API rejections beforecreating both GHAS license budgets successfully. That evidence never reached
the skill file; the skill was written from the same session but encoded the
first, failing attempt.
Changes
Step 7 replaced with the verified procedure. Loops over both
ghas_secret_protection_licensesandghas_code_security_licenses— GHASbills as two license SKUs and capping one leaves the other accruing. The
generated JSON body is byte-identical to the request that returned
Budget successfully created.on 08-24. A pre-check listing existing budgetswas added ahead of it.
Trap 5 added, covering the three rejections in order:
prevent_further_usage, notstop_usage_when_budget_exhausted;ProductPricing+ghas500 text — "High watermark products cannothave PreventFurtherUsage budget alerting except for GHAS SKUs" — reads as
permission, not refusal;
budget_amountcounts licenses, not dollars, for license-basedproducts. GitHub's parameter doc: "The budget amount in whole dollars. For
license-based products, this represents the number of licenses." So
budget_amount: 0means zero seats, not zero spend.SKU-enumeration technique documented. Posting a deliberately invalid SKU
makes the API return the full valid-SKU list, which is how
ghas_secret_protection_licenseswas found. Flagged explicitly as a POST thatleaves the otherwise read-only path, even though it creates nothing.
Safety section corrected. It previously described the hard stop in dollar
terms and did not list the enumeration probe among the mutating operations.
Verification
bash -non the step 7 snippet passesjq-generated body matches the 08-24 success byte for byteautoinstall.shno-headingslint passesthe diff contains nothing else
Note
Sources are the origin session transcript and the current GitHub REST budgets
documentation. The SKU names and the 500 error text are quoted from a live
jinwon-intresponse, so they reflect that org's plan; the docs confirm thegeneral rule.
🤖 Generated with Claude Code