Skip to content

Guard the capture-scope timeout bound - #181

Merged
bogatyrjov1 merged 1 commit into
mainfrom
guard-the-capture-scope-timeout
Aug 29, 2026
Merged

Guard the capture-scope timeout bound#181
bogatyrjov1 merged 1 commit into
mainfrom
guard-the-capture-scope-timeout

Conversation

@bogatyrjov1

@bogatyrjov1 bogatyrjov1 commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

The capture-scope timeout was pinned by nothing

checkCaptureScope is the pre-send preflight: before the hook reads a transcript it asks the worker whether capture is on for this repository, and a clean 200 {decision:'skip'} means nothing is read and nothing is sent. That request is bounded by an AbortController so a hung endpoint cannot stall the detached capture.

Every existing stub in captureScope.test.ts resolves on the same tick, so no test ever races that timer. The bound was therefore untested in both directions.

What that allows

Setting CAPTURE_SCOPE_TIMEOUT_MS to 0 keeps the whole cli suite green. Measured on the pre-PR tree (this file at origin/main), tsc clean: 952 tests, 952 pass, 0 fail — including all 143 tests across capture.test.ts, captureCommand.test.ts and fromHook.test.ts.

In production it removes the feature. A 0 ms abort always wins against a real network round-trip, so the preflight always throws into the catch, the catch fails open by design, and a repository the user excluded is captured anyway. Nothing anywhere reports it: failing open is deliberate and silent, and from the outside "the server said capture" and "we never waited for the answer" produce identical behaviour.

The test

One new case. The stubbed fetch answers after a real 40 ms and honours the abort signal — which is what a live preflight does — and the assertion is that the skip verdict still arrives.

40 ms is far quicker than any real call to the endpoint and far under the real bound, so this is not a timing-flaky test: it can only fail when the bound is small enough to abort a live request, which is precisely the defect.

Verified

  • With the bound at its real value: cli/src/captureScope.test.ts 10 pass / 0 fail; full cli/src/*.test.ts 953 pass / 0 fail.
  • With the bound mutated to 0: tsc -p cli/tsconfig.json clean, and this test is the only one in the suite that fails — 9 pass / 1 fail here, 143/143 green in the three capture suites.

Test-only. No version bump, no change to dist-bundle/.

Reasoning

written by the agent in-session via backthread/add-reasoning-to-prs

Decisions

  • The bound is pinned behaviourally, not by asserting the constant. CAPTURE_SCOPE_TIMEOUT_MS is module-private, and a test that reads the constant back only compares it to itself.
  • Only the "too small" direction is guarded. A test for the "never fires" direction would have to wait out the real bound, which costs about ten seconds of suite time for a defect nobody has hit.

Assumptions

  • 40 ms is assumed to be well inside the real bound on every machine that runs this suite. If a runner is slow enough that the abort timer beats a 40 ms setTimeout, this test goes red for a timing reason and not a real defect. Check: if it ever flakes, raise the real bound rather than lowering the delay.

Limitations

  • This says nothing about whether the preflight is asked for at all. That is covered separately by the "REAL wiring" tests in capture.test.ts, which assert the first request is /capture-scope.

The pre-send capture-scope check is the only thing that keeps an excluded
repository's transcript on the machine, and it is bounded by an
AbortController so a hung endpoint cannot stall a detached capture. Every
existing stub in this file resolves on the same tick, so none of them ever
raced that timer — which left the bound itself completely unguarded.

Setting it to 0 keeps all 953 cli tests green. In production it destroys the
feature: a 0 ms abort always beats a real round-trip, so every preflight
fails open and a repository the user excluded is captured anyway. Nothing
reports it, because failing open is deliberate and silent — from the outside
"the server said capture" and "we never waited for the answer" are the same.

The new test answers after a real 40 ms and honours the signal, which is
what a live preflight does, and asserts the skip verdict still arrives. It
is the only test in the suite that dies when the bound goes degenerate.
@bogatyrjov1
bogatyrjov1 merged commit 43826ea into main Aug 29, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants