An animated solve must fit its budget, and a burst must never be cut in half - #12
Merged
Merged
Conversation
…in half
`overallSolveTimeoutMs` / `overall_solve_timeout_ms` is 45s and it is sized for
ROUNDS — "a round costs ~4-7s, so six is the budget", which no-progress.test.ts
pins. Recording an animated challenge is not a round. It is a fixed extra stage
costing the burst, the slice, one MULTI-IMAGE inference (six keyframes, several
times a still's) and the wait for the widget to come back round to the frame the
model chose. Nothing in the 45s was ever set aside for it.
So every escalation to a recording ran the clock out, and reported a TIMEOUT —
a message about the model being slow, for a budget that had no room for what the
solver had just decided to do.
MEASURED, CaptchaKrakenFinetune Tier 3 run 32596340560 (2026-08-22):
python hcaptcha_fish_swim_different 45.7s, 48.5s, 49.2s
hcaptcha_number_with_highest_value_video 49.4s, 50.5s
hcaptcha_tile_flip_video 52.6s, 52.1s
-> "exceeded overall_solve_timeout_ms during recording the
animated challenge"
js hcaptcha_click_image_by_traits 52.4s, 49.7s
hcaptcha_connect_path 50.2s
hcaptcha_grid_3x3_property 49.7s, 49.4s
-> "Captcha solve timed out after 45000ms (attempt 6/6)"
The same fixtures solve in 11-20s on the rounds where the still path happens to
answer them. It is the escalation that does not fit, not the puzzle.
## The escalation buys its own budget, once
`video_budget_ms()` = burst + keyframe wait + one multi-image inference; 18s on
the defaults, granted the first time a recording starts and never again in the
same solve. DERIVED, so a longer burst carries its own budget rather than
quietly reintroducing this. Worst case is 63s, still bounded.
An EXTENSION rather than a looser default: a solve that never escalates keeps
exactly the deadline the caller configured, and a caller who set
`video_solve_enabled=False` gets no grant at all — that switch already means
"fail fast rather than spend the recording time".
Both ports, same arithmetic. CLAUDE.md 1c: a fixture that passed on one port and
timed out on the other reads as a driver bug.
## A half-recorded burst is worthless
The python port checked the overall deadline PER FRAME, so a burst that started
at 41s died at frame 27 of 40 — throwing away both the frames and the ~3s spent
making them. The slicer reads a clip's temporal structure; stopping early does
not yield a shorter answer, it yields a recording that may not contain the
screen the answer is on.
Checked ONCE now, before the first frame, and if the budget cannot fit a whole
burst it says so and names the knobs. The burst is fixed-length, so it is not
one of the places that "can legitimately spin for a long time" that
`_check_deadline` exists for; a separate, looser bound catches a genuinely hung
screenshot and reports it as that.
9 new tests. Both regression tests fail on the old behaviour with the exact
production message. Pre-existing on main and untouched here: 4 errors in
test_browser_compat.py (fixed on #10's branch).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner
Author
✓ Tier 3 driver-gate — passAggregate: 0.852 · 6/10 families passing · 13 pair(s) not solved
|
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.
overallSolveTimeoutMs/overall_solve_timeout_msis 45 s and it is sized for rounds — "a round costs ~4-7 s, so six is the budget", whichno-progress.test.tspins. Recording an animated challenge is not a round. It is a fixed extra stage costing the burst, the slice, one multi-image inference (six keyframes, several times a still's) and the wait for the widget to come back round to the frame the model chose. Nothing in the 45 s was ever set aside for it.So every escalation to a recording ran the clock out, and reported a timeout — a message about the model being slow, for a budget that had no room for what the solver had just decided to do.
Measured
CaptchaKrakenFinetune Tier 3 run 32596340560, 2026-08-22:
hcaptcha_fish_swim_differentexceeded overall_solve_timeout_ms during recording the animated challengehcaptcha_number_with_highest_value_videohcaptcha_tile_flip_videohcaptcha_click_image_by_traitsCaptcha solve timed out after 45000ms (attempt 6/6)hcaptcha_connect_pathhcaptcha_grid_3x3_propertyThe same fixtures solve in 11-20 s on the rounds where the still path happens to answer them. It is the escalation that does not fit, not the puzzle.
The escalation buys its own budget, once
video_budget_ms()= burst + keyframe wait + one multi-image inference; 18 s on the defaults, granted the first time a recording starts and never again in the same solve. Derived, so a longer burst carries its own budget rather than quietly reintroducing this. Worst case 63 s, still bounded.An extension, not a looser default: a solve that never escalates keeps exactly the deadline the caller configured, and
video_solve_enabled=Falsegets no grant at all — that switch already means "fail fast rather than spend the recording time".Both ports, same arithmetic, because CLAUDE.md 1c — a fixture that passed on one port and timed out on the other reads as a driver bug.
A half-recorded burst is worthless
The python port checked the overall deadline per frame, so a burst starting at 41 s died at frame 27 of 40 — throwing away both the frames and the ~3 s spent making them. The slicer reads a clip's temporal structure; stopping early does not yield a shorter answer, it yields a recording that may not contain the screen the answer is on.
Checked once now, before the first frame, and if the budget cannot fit a whole burst it says so and names the knobs. A separate, looser bound still catches a genuinely hung screenshot and reports it as that.
Tests
9 new. Both regression tests fail on the old behaviour with the exact production message:
python/tests405 passed / 15 skipped,js85 passed,tsc --noEmitclean.Pre-existing on main and untouched here: 4 errors in
test_browser_compat.py— identical on a cleanorigin/main, and already fixed on #10's branch.🤖 Generated with Claude Code