feat(tcfeed): budget the account, and refresh the requests already sent - #122
Merged
Conversation
Refs #120. TCFEED_PR_MAX bounds a run. Nothing bounded the account, and on the first day that mattered: 33 unsolicited pull requests went out across 33 strangers' repositories in a single day, 25 still unanswered afterwards. Every run was individually inside its cap. That is the difference between a per-run cap and a budget, and prCommand's own docstring already said what it costs — bulk unsolicited pull requests are against GitHub's acceptable use policy however good the workflow is, and an account that sends them stops being able to send anything. budget() makes that paragraph executable. Two numbers, because they fail differently: TCFEED_PR_STANDING unanswered requests allowed to stand at once, default 30. The footprint — 33 open requests is what "bulk" looks like to a human reading the account, whenever they were sent. TCFEED_PR_PER_DAY requests opened in a rolling 24 hours, default 10. The velocity — 33 in one afternoon reads as automation even if the total is modest. Rolling, not since-midnight: the point is velocity, and midnight resets it to zero for an account that sent thirty at 23:00. Counted with search/issues total_count rather than by listing, because listing caps at 100 and a budget that silently undercounts once the number gets interesting is worse than no budget. A count that cannot be read stops the run rather than defaulting to zero, which would open the floodgates on exactly the failure this catches. Checked after the run's own cap and before anything opens. Dry runs are exempt because they open nothing, but still print the standing, which is the number worth seeing before deciding to send. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan67 finding(s) HIGH/CRITICAL: 11 | MEDIUM: 55 | LOW: 1
…and 17 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
This was referenced Aug 13, 2026
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.
Refs #120. The rail that was missing when 33 requests went out in one day.
The gap
TCFEED_PR_MAXbounds a run. Nothing bounded the account — and every one of those 33 runs was individually inside its cap. That is the whole difference between a per-run cap and a budget.prCommand's own docstring already said what it costs:This makes that paragraph executable instead of advisory.
Two numbers, because they fail differently
TCFEED_PR_STANDINGTCFEED_PR_PER_DAYTCFEED_PR_MAX, so one full--allrun is a day of sending.Rolling, not since-midnight: the point is velocity, and midnight resets it to zero for an account that sent thirty at 23:00.
Counted with
search/issuestotal_countrather than by listing, because listing caps at 100 and a budget that silently undercounts once the number gets interesting is worse than no budget at all. A count that cannot be read stops the run rather than defaulting to zero — defaulting to zero would open the floodgates on precisely the failure this exists to catch.Checked after the run's own cap and before anything opens. Dry runs are exempt (they open nothing) but still print the standing, which is the number worth seeing before deciding to send.
Verified against the live account
Refuses when over budget:
Trims rather than refuses when there is partial room, and says so — a run that quietly did two of three reads exactly like a run that did three:
Dry run reports and proceeds:
tsc --strict --noEmitclean.Note on the current standing
With defaults, this still refuses to send anything today — 33 went out in the last 24h against a cap of 20, so the account is 13 over. Standing has room for 6. The daily window clears on its own; the standing budget frees up as maintainers answer. Both env vars raise it deliberately, which is the point: going over should be a thing someone typed.
🤖 Generated with Claude Code
Also: refresh the requests already sent
The pack is read live from sh1pt so a corrected workflow reaches repositories — but that was only ever true of requests not yet sent. 33 went out carrying
npm install -g "…@latest", the pack was fixed an hour later, and all 24 still open kept showing the defective version to anyone who looked.Three reviewers found it independently:
SonarCloud — failed a quality gate,
githubactions:S8543CodeRabbit — scored a request 🟡 Moderate for "an unpinned scanner with access to a write-scoped job"
GlassHaven/Haven#532 — declined on exactly that ground:
noting the project hash-pins 13 tarballs in its ffmpeg stack alone. That is a maintainer applying their own published bar to us and finding us under it.
tcfeed check --refreshrewrites both files on every open request to whatever the pack currently renders, and reportsalready currentwhen there is nothing to do — so it is safe to run after any pack change without churning commits.Written against the contents API rather than a clone: the open requests include
seastarandlightning, and cloning a kernel-sized repository to rewrite two files under.githubis minutes of transfer for a diff that fits on a screen. It also cannot carry anything else along by accident, which on somebody else's review is the more important half.Run against all 24 open requests — 22 updated, 2 already current, verified independently on
lightningandmaltrail:Idempotency checked by running it twice on the same repo: second run reports
already currentand pushes nothing.