Report a task-wide gap from a batching connector too (#193) - #201
Merged
icebergai-review-bot[bot] merged 1 commit intoAug 19, 2026
Merged
Conversation
When a connector checkpoints, the engine's terminal submission carries only the remainder its progress batches did not. The task-wide scope gap recorded when a fetch dies partway — timeout, rate limit, connector error — was written onto the whole-task tally, which that submission no longer sent, so the gap never reached the API. The manifest then showed no blind spot for a scope the scan had only partly read, and `build_manifest` went on reporting a known scope size the scan never established. The same failure against a non-checkpointing connector reported the gap correctly. Two connector classes told different stories about the same event, which is the inconsistency a coverage manifest exists to prevent. The report now holds the batches' baseline rather than a pre-computed delta, so `coverage` stays the whole task's tally until `as_submission` derives the remainder — after `_record_task_gap` has run. That also puts the engine's gap-reference cap back on the task's real list rather than on a fresh one. Regression tests cover both exception paths (ConnectorError and the time limit's BaseException) and hold the two connector classes against each other. Refs ADR 0009. Claude-Session: https://claude.ai/code/session_012sohE85sRDt6t2w3936rGJ Co-authored-by: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict
APPROVE
Completed bounded review across 1 immutable scope(s). No actionable regressions found in the supplied change.
Scope health
Convergence: healthy. Review mode: initial.
Recommended action: CONTINUE_INCREMENTAL.
- No escalation signals.
Prior findings
| Finding | Status |
|---|---|
| — | No prior finding state |
New findings
No new findings.
Fix-induced regressions
- None evidenced.
Uncertainty
- No material uncertainty recorded.
Validation
- Reviewed the supplied immutable diff, including batching and failure-path coverage reporting changes and their tests.
Residual risks
- None identified.
icebergai-review-bot
Bot
deleted the
claude/codebase-review-cleanup-ovlli4
branch
August 19, 2026 23:31
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.
Closes #193.
The defect
When a connector declares
CHECKPOINTS, the engine flushes findings and a resume position mid-fetch, and its terminal submission carries only the remainder the progress batches did not (#143). The task-wide scope gap recorded when a fetch dies partway — a rate limit, a timeout, an unanticipated connector error — was written onto the whole-task tally, which that submission no longer sent. The gap never reached the API.The order is what made it invisible.
_fetch'sfinallyfroze the remainder while the exception was still unwinding;_record_task_gapran afterwards, inrun_task's handler, and wrote into a fieldas_submissionhad stopped reading.Downstream,
build_manifestuses a fetch task'sscope.gapsto decide it cannot claim a known scope size (fetch_scope_gap,apps/api/src/iceberg_api/scans/coverage.py). With no gap it goes on reporting one — so a scan that read an unknown fraction of a Confluence space, a Jira project, or a file-share root presented as having read all of it. That is the specific thing coverage manifests exist to prevent.The same failure against a connector without checkpoints reported the gap correctly. Two connector classes told different stories about the same event, which is how it surfaced.
Reproduction
A connector that yields three pages and then raises
RateLimitError, run twice — once resumable, once not:scope.gapsreasons1[{"outcome": "scope_gap", "reason": "rate_limited", "count": 1}]0[]The fix
TaskReportnow holds the batches' baseline (sent_coverage) rather than a pre-computed delta, andas_submissionderives the remainder — after_record_task_gaphas run.coveragestays the whole task's tally for its whole life, which is what the field already meant everywhere else.This is smaller than the alternative of folding a gap into an existing delta, and it removes the special case rather than adding one: both connector classes now take the same path through
_record_task_gap, and the gap-reference cap (MAX_COVERAGE_GAP_REFERENCES) applies to the task's real list rather than to a freshly-created one that always had room.Valid because a delta is only ever cut at a unit boundary and
gapsis append-only, so the gap appended last lands in the tail_coverage_deltaalready slices.Tests
Three in
apps/engine/tests/test_runner_batching.py, each confirmed to fail against the pre-fix code:BaseExceptionpath, which submits from its own handler, carries it too.make checkis green: ruff, mypy (278 files), docs check, 1953 passed / 2 skipped (the two file-share permission tests that cannot run as root).Operator impact
Manifests already written are not recomputed; the CHANGELOG entry says so and points at re-running the scan.
Generated by Claude Code