Skip to content

Close an import batch on bytes and report what it drops - #817

Merged
alexeyzimarev merged 2 commits into
mainfrom
fix/import-batch-byte-budget
Sep 8, 2026
Merged

alexeyzimarev merged 2 commits into
mainfrom
fix/import-batch-byte-budget

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Closes #814 — AI-2586

What & why

kcap import flushed a transcript batch every 100 lines whatever their size, and read the response status only for a strict caller, so a batch the server refused was counted as sent. A batch now also closes at 4 MiB of line content, a line over that budget is skipped rather than posted, and a lenient path reports a refused batch instead of swallowing it. Both reports travel on the import progress channel: the vendor sources get a per-run sink on the import context, so their warnings render above the live progress region instead of being erased by its next frame.

Where to look

TranscriptBatchBuffer carries the two limits and why 4 MiB. A dropped batch's lines still count toward the "lines sent" figure; the warning line above it is the record. Status retries stay off for the batch POST: a strict batch's normalization failure comes back as a 500, and retrying it would stall every strict import for the whole retry budget.

Verification

  • SessionImporterProgressTests: three 1.5 MiB lines post as 2 + 1; a line over budget is skipped with line_numbers [0,2] on the wire; a 413 yields BatchDropped for a lenient caller and a range-naming exception for a strict one.
  • CLI unit suite: 3998 passed, 19 skipped, 0 failed. Integration *Import* classes: 41 passed. AOT publish: 0 IL warnings.

A stderr line written under the Spectre live region is erased by its next
frame, so the warnings ride the progress channel and carry their session id
for the routed sources' shared sink.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-08T14:01:39.705460Z d9d3152 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Bound transcript batches by bytes and surface dropped content

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Caps transcript batches at 100 lines or 4 MiB to avoid oversized requests.
• Reports oversized lines and refused batches through session-scoped import progress warnings.
• Covers byte splitting, dropped content, strict failures, and progress routing with tests.
Diagram

graph TD
  Sources["Vendor Sources"] --> Context["Import Context"] --> Importer["Session Importer"] --> Buffer["Batch Buffer"] --> Api["Transcript API"]
  Importer --> Warnings["Import Warnings"] --> Progress["Progress Channel"] --> Display["CLI Display"]
  Api -->|refusal| Importer
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Budget serialized payload size
  • ➕ Enforces the limit against exact on-wire JSON size
  • ➕ Allows less conservatively sized batches for ordinary content
  • ➖ Requires repeated serialization or additional large intermediate buffers
  • ➖ Adds complexity and CPU cost to the import hot path
  • ➖ Still needs headroom for request metadata and deployment-specific limits
2. Raise the server request limit
  • ➕ Allows existing 100-line batches to accept larger transcript entries
  • ➕ Requires fewer client-side batching changes
  • ➖ Does not protect deployments using default or lower limits
  • ➖ Permits unbounded batch growth from repeated compacted content
  • ➖ Does not address silent handling of refused or undeliverable batches
3. Chunk individual oversized lines
  • ➕ Avoids dropping transcript content larger than 4 MiB
  • ➕ Supports arbitrarily large logical transcript entries
  • ➖ Requires protocol and server changes for reassembly
  • ➖ Breaks the current one-source-line to one-transcript-entry contract
  • ➖ Introduces ordering and partial-delivery failure modes

Recommendation: Keep the PR's conservative raw UTF-8 budget and explicit warning model. It avoids server changes, bounds pathological payload expansion, preserves line semantics, and makes unavoidable loss visible; exact serialized sizing would add substantial complexity for limited practical capacity gains.

Files changed (18) +416 / -132

Enhancement (3) +83 / -4
IImportSource.csAdd a shared progress sink to import context +8/-4

Add a shared progress sink to import context

• Extends 'ImportContext' with an optional per-run 'IProgress<ImportProgress>' sink so routed vendor sources can report session-scoped warnings.

src/Capacitor.Cli/Commands/IImportSource.cs

ImportProgress.csDefine skipped-line and dropped-batch warning events +24/-0

Define skipped-line and dropped-batch warning events

• Introduces the 'ImportWarning' hierarchy with session and optional subagent scope. Adds messages for oversized lines and refused or undeliverable batch ranges.

src/Capacitor.Cli/Commands/ImportProgress.cs

TranscriptBatchBuffer.csIntroduce count-and-byte transcript batching +51/-0

Introduce count-and-byte transcript batching

• Adds a buffer that tracks lines, source line numbers, and raw UTF-8 bytes. It caps batches at 100 lines or 4 MiB and exposes range metadata for diagnostics.

src/Capacitor.Cli/Commands/TranscriptBatchBuffer.cs

Bug fix (9) +133 / -128
ImportCommand.csRender import loss warnings through worker progress +14/-1

Render import loss warnings through worker progress

• Adds session warning callbacks for direct and chained imports, formats warnings for plain and Spectre displays, and routes 'ImportWarning' events above the live progress region.

src/Capacitor.Cli/Commands/ImportCommand.cs

SessionImporter.csEnforce byte-bounded batches and report delivery loss +96/-111

Enforce byte-bounded batches and report delivery loss

• Replaces count-only batching with 'TranscriptBatchBuffer', skips individually oversized lines, and excludes them from progress totals. Lenient failures emit 'BatchDropped', while strict failures throw range-specific exceptions; flushed lines remain counted as posted.

src/Capacitor.Cli/Commands/SessionImporter.cs

AntigravityImportSource.csRoute Antigravity transcript warnings to progress +5/-5

Route Antigravity transcript warnings to progress

• Passes the import progress sink through parent and child transcript delivery, including strict child batches.

src/Capacitor.Cli/Harness/Antigravity/AntigravityImportSource.cs

CopilotImportSource.csRoute Copilot transcript warnings to progress +2/-1

Route Copilot transcript warnings to progress

• Supplies the shared import progress sink when sending Copilot transcript batches.

src/Capacitor.Cli/Harness/Copilot/CopilotImportSource.cs

CursorImportSource.csRoute Cursor parent and child batch warnings +2/-0

Route Cursor parent and child batch warnings

• Passes progress reporting into Cursor parent and strict subagent deliveries while preserving quarantine checks before and after each batch.

src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs

GeminiImportSource.csRoute Gemini transcript and subagent warnings +6/-4

Route Gemini transcript and subagent warnings

• Threads the shared progress sink through Gemini parent imports and strict subagent transcript delivery.

src/Capacitor.Cli/Harness/Gemini/GeminiImportSource.cs

KiroImportSource.csRoute Kiro transcript warnings to progress +2/-1

Route Kiro transcript warnings to progress

• Supplies the import context's progress sink when Kiro sends transcript batches.

src/Capacitor.Cli/Harness/Kiro/KiroImportSource.cs

OpenCodeImportSource.csRoute OpenCode strict-delivery warnings +4/-4

Route OpenCode strict-delivery warnings

• Threads progress through parent and descendant transcript imports while retaining strict failure behavior and line-number offsets.

src/Capacitor.Cli/Harness/OpenCode/OpenCodeImportSource.cs

PiImportSource.csRoute Pi transcript warnings to progress +2/-1

Route Pi transcript warnings to progress

• Supplies the shared progress sink during Pi transcript batch delivery.

src/Capacitor.Cli/Harness/Pi/PiImportSource.cs

Tests (5) +186 / -0
ImportEndReassertTests.csInitialize warning callbacks in import integration tests +1/-0

Initialize warning callbacks in import integration tests

• Adds the newly required no-op session warning handler to the integration worker event fixture.

test/Capacitor.Cli.Tests.Integration/ImportEndReassertTests.cs

ImportChainsTests.csUpdate chain fixtures for session warnings +7/-0

Update chain fixtures for session warnings

• Adds no-op warning handlers to chain-worker test fixtures so existing scheduling, routing, and progress tests cover the expanded event contract.

test/Capacitor.Cli.Tests.Unit/Commands/ImportChainsTests.cs

ImportVisibilityTests.csUpdate visibility fixtures for session warnings +1/-0

Update visibility fixtures for session warnings

• Adds the required session warning callback to visibility-related chain worker test setup.

test/Capacitor.Cli.Tests.Unit/Commands/ImportVisibilityTests.cs

SessionImporterProgressTests.csCover byte splitting and transcript loss reporting +136/-0

Cover byte splitting and transcript loss reporting

• Tests 4 MiB batch closure for both importer paths, oversized-line skipping and line-number preservation, lenient 413 warnings, and strict range-specific exceptions.

test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs

TranscriptBatchBufferTests.csTest transcript buffer limits and UTF-8 sizing +41/-0

Test transcript buffer limits and UTF-8 sizing

• Verifies the 100-line cap, inclusive byte-budget fit behavior, clearing state, line range tracking, and UTF-8 byte measurement.

test/Capacitor.Cli.Tests.Unit/Commands/TranscriptBatchBufferTests.cs

Documentation (1) +14 / -0
CHANGES.mdDocument byte-bounded imports and loss reporting +14/-0

Document byte-bounded imports and loss reporting

• Explains the 4 MiB raw UTF-8 budget, JSON expansion headroom, oversized-line handling, rejected-batch warnings, and progress-channel routing rationale.

docs/CHANGES.md

@qodo-code-review

qodo-code-review Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 🔗 Cross-repo conflicts (2) 📜 Skill insights (0)

Grey Divider


Action required

1. Cursor imports skip blocked batches 🔗 Cross-repo conflict ≡ Correctness
Description
FlushBatchAsync treats Cursor's HTTP 409 as a lenient BatchDropped, clears the buffer, and lets
SendTranscriptBatches post later ranges. When Cursor normalization or persistence blocks within a
batch, kcap-server defines that response as a retry-from-gap signal, so later requests can advance
the watermark beyond the refused range.
Code

src/Capacitor.Cli/Commands/SessionImporter.cs[R632-635]

+        if (loss is not null) {
+            if (failOnError) throw new HttpRequestException($"transcript batch lines {first}-{last} rejected: {loss}");
+
+            progress?.Report(new BatchDropped(sessionId, agentId, first, last, loss));
Relevance

●●● Strong

Continuing after Cursor’s gap response risks watermark advancement and data loss, a correctness
issue in quarantine-sensitive import flow.

PR-#242

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Cursor caller leaves failOnError false, and the changed flush path explicitly reports
non-success then clears and continues. The server documents and implements Cursor 409 as a
halt-at-the-gap result, while subsequent requests accept any line above the old watermark and can
move that watermark beyond the blocked line.

src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[568-590]
src/Capacitor.Cli/Commands/SessionImporter.cs[624-640]
External repo: kurrent-io/kcap-server, src/Capacitor.Server/Sessions/SessionHookHandlers.cs [1531-1553]
External repo: kurrent-io/kcap-server, src/Capacitor.Server/Sessions/SessionWriter.TranscriptPipeline.cs [682-691]
External repo: kurrent-io/kcap-server, src/Capacitor.Server/Sessions/SessionWriter.TranscriptPipeline.cs [717-727]
External repo: kurrent-io/kcap-server, src/Capacitor.Server/Sessions/SessionWriter.TranscriptPipeline.cs [762-785]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Honor kcap-server's Cursor frontier protocol instead of continuing after an HTTP 409 blocked response.

## Issue Context
The server holds Cursor's next line at the failed record and expects the source to retry from that gap. Make the top-level Cursor batch call strict, or otherwise stop delivery specifically on 409, so a later import resumes from the server watermark rather than posting later ranges.

## Fix Focus Areas
- src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[568-590]
- src/Capacitor.Cli/Commands/SessionImporter.cs[624-640]
- test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs[232-274]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Quarantined Cursor imports continue 🐞 Bug ≡ Correctness
Description
SendTranscriptBatches reports an oversized line without invoking abortDelivery, and it returns
without calling FlushAsync when the remaining transcript has no sendable lines. If Cursor’s
quarantine marker appears after the final successful batch while the tail contains only blank or
oversized lines, Cursor proceeds to child delivery and session-end instead of taking its
close-and-fail path.
Code

src/Capacitor.Cli/Commands/SessionImporter.cs[R557-560]

+                if (bytes > TranscriptBatchBuffer.MaxBytes) {
+                    progress?.Report(new LineSkipped(sessionId, agentId, lineNumber, bytes));
+                } else {
+                    if (!batch.Fits(bytes)) await FlushAsync();
Relevance

●●● Strong

Quarantine handling is explicitly safety-critical; bypassing abort on an unsendable tail can
continue Cursor delivery incorrectly.

PR-#242

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed oversized-line branch skips directly to the next input line and abortDelivery is only
called by FlushAsync; EOF invokes that helper only for a nonempty batch. Cursor relies on the
resulting typed exception to avoid its child/session-end work, so the newly unsendable tail creates
a path that bypasses quarantine handling.

src/Capacitor.Cli/Commands/SessionImporter.cs[553-581]
src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[575-635]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SendTranscriptBatches` only evaluates `abortDelivery` inside `FlushAsync`. An over-budget line is now skipped without flushing, so a Cursor transcript whose remaining lines are blank or oversized can return normally after quarantine is marked.

## Issue Context
Cursor catches `TranscriptDeliveryAbortedException` to stop subsequent child delivery and close the session through its quarantine-specific failure path. Preserve that behavior even when no additional transcript batch is posted.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SessionImporter.cs[553-581]
- src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[575-635]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Warning types have ambiguous ownership 📘 Rule violation ⚙ Maintainability
Description
ImportProgress.cs adds the public ImportWarning, LineSkipped, and BatchDropped top-level
records alongside its existing primary type instead of placing each public type in a matching file.
Because the closely related hierarchy exception requires implementations to be private or internal,
later maintainers must treat one file as the owner of several independently visible types.
Code

src/Capacitor.Cli/Commands/ImportProgress.cs[R35-36]

+public sealed record LineSkipped(string SessionId, string? AgentId, int LineNumber, int Bytes)
+    : ImportWarning(SessionId, AgentId) {
Relevance

●●● Strong

Repository history accepts file-ownership and organization findings for maintainability; public
types make this rule directly applicable.

PR-#670

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3162234 requires one primary type per matching file and allows closely related implementations
in the same file only when they are private or internal. The changed file adds a public abstract
warning and two public concrete implementations, including the public LineSkipped declaration at
the diff location.

Rule 3162234: One primary type per file, with only narrow documented exceptions
src/Capacitor.Cli/Commands/ImportProgress.cs[28-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ImportProgress.cs` now contains multiple additional public top-level warning types, so their names do not match the file that owns them.

## Issue Context
The closely related hierarchy exception only permits implementations that are private or internal. Preserve the public API by moving each public record into a file whose name matches the type.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/ImportProgress.cs[28-43]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Operators lose failed line ranges ✓ Resolved 🐞 Bug ◔ Observability
Description
FlushBatchAsync catches HttpRequestException only when failOnError is false, so strict
transport failures bypass the new range-wrapping branch. When retries exhaust for a resumed parent
or strict subagent batch, the caller receives only generic retry-budget text and cannot identify
which transcript lines failed.
Code

src/Capacitor.Cli/Commands/SessionImporter.cs[628]

+        } catch (HttpRequestException ex) when (!failOnError) {
Relevance

●●● Strong

Strict transport failures lose range context; repository precedent favors preserving actionable
diagnostics for failed asynchronous operations.

PR-#526
PR-#344

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The transcript sender records line boundaries before posting, but its catch filter excludes strict
calls; only non-2xx responses reach the range-aware throw. The retry helper ultimately throws a
generic HttpRequestException after transport retry exhaustion, and resumed imports expose that
message directly as the session error.

src/Capacitor.Cli/Commands/SessionImporter.cs[618-635]
src/Capacitor.Cli.Core/HttpClientExtensions.cs[270-304]
src/Capacitor.Cli/Commands/ImportCommand.cs[3021-3066]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Strict transcript batches lose their line-range context when `PostWithRetryAsync` throws a transport `HttpRequestException`. The filtered catch handles only lenient calls, so strict failures never reach the range-aware exception currently used for HTTP refusals.

## Issue Context
Catch transport exceptions for both modes. Continue converting them into `BatchDropped` for lenient callers, but wrap and rethrow them with the buffered first and last line numbers for strict callers while preserving the original exception as the inner exception.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SessionImporter.cs[624-633]
- test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs[256-274]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Valid large lines are discarded 🔗 Cross-repo conflict ≡ Correctness
Description
SendTranscriptBatches unconditionally emits LineSkipped for any line over 4 MiB, even when that
line would fit by itself within kcap-server's request-body limit. This triggers for records such as
a 5 MiB mostly-ASCII JSON line, although the server's transcript contract has no per-line limit and
processes each supplied string independently.
Code

src/Capacitor.Cli/Commands/SessionImporter.cs[R557-559]

+                if (bytes > TranscriptBatchBuffer.MaxBytes) {
+                    progress?.Report(new LineSkipped(sessionId, agentId, lineNumber, bytes));
+                } else {
Relevance

●● Moderate

The concern is semantically plausible, but the PR explicitly defines oversized lines as unsendable
and tests that behavior.

PR-#129

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR discards every line above the client-side budget, while kcap-server binds an unrestricted
string array and passes each supplied line through its transcript pipeline; its binding middleware
identifies 413 as a whole-request rejection rather than a per-line contract.

src/Capacitor.Cli/Commands/SessionImporter.cs[553-565]
External repo: kurrent-io/kcap-server, src/Capacitor.Api.Public.Abstractions/Sessions/TranscriptBatch.cs [1-3]
External repo: kurrent-io/kcap-server, src/Capacitor.Api.Public/Hooks/HookEndpoints.cs [160-168]
External repo: kurrent-io/kcap-server, src/Capacitor.Server/Sessions/SessionWriter.TranscriptPipeline.cs [300-315]
External repo: kurrent-io/kcap-server, src/Capacitor.Api.Public/Hooks/HookPayloadBindingMiddleware.cs [111-123]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Do not discard a transcript line merely because it exceeds the normal 4 MiB batch budget; kcap-server can accept larger individual lines when the complete request remains within its body limit.

## Issue Context
The 4 MiB threshold is appropriate for closing multi-line batches, but it is not a server-defined per-line limit. Flush the current batch and attempt an oversized line as a single-line batch, reporting it as dropped only if the server refuses that request.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SessionImporter.cs[553-565]
- src/Capacitor.Cli/Commands/TranscriptBatchBuffer.cs[34-43]
- test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs[198-230]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 61 rules
✅ Cross-repo context — repo relationships
  Explored: repo: kurrent-io/kcap-server (sha: a1c5e08f)
Review mode: 🧠 Deep: This is a behavior-changing import pipeline refactor spanning batching, HTTP failure semantics, progress reporting, and seven vendor-specific source paths, with many independent edit sites and subtle loss/counting interactions that benefit from redundant review.

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +35 to +36
public sealed record LineSkipped(string SessionId, string? AgentId, int LineNumber, int Bytes)
: ImportWarning(SessionId, AgentId) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Warning types have ambiguous ownership 📘 Rule violation ⚙ Maintainability

ImportProgress.cs adds the public ImportWarning, LineSkipped, and BatchDropped top-level
records alongside its existing primary type instead of placing each public type in a matching file.
Because the closely related hierarchy exception requires implementations to be private or internal,
later maintainers must treat one file as the owner of several independently visible types.
Agent Prompt
## Issue description
`ImportProgress.cs` now contains multiple additional public top-level warning types, so their names do not match the file that owns them.

## Issue Context
The closely related hierarchy exception only permits implementations that are private or internal. Preserve the public API by moving each public record into a file whose name matches the type.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/ImportProgress.cs[28-43]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Capacitor.Cli/Commands/SessionImporter.cs Outdated
Comment on lines +557 to +560
if (bytes > TranscriptBatchBuffer.MaxBytes) {
progress?.Report(new LineSkipped(sessionId, agentId, lineNumber, bytes));
} else {
if (!batch.Fits(bytes)) await FlushAsync();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Quarantined cursor imports continue 🐞 Bug ≡ Correctness

SendTranscriptBatches reports an oversized line without invoking abortDelivery, and it returns
without calling FlushAsync when the remaining transcript has no sendable lines. If Cursor’s
quarantine marker appears after the final successful batch while the tail contains only blank or
oversized lines, Cursor proceeds to child delivery and session-end instead of taking its
close-and-fail path.
Agent Prompt
## Issue description
`SendTranscriptBatches` only evaluates `abortDelivery` inside `FlushAsync`. An over-budget line is now skipped without flushing, so a Cursor transcript whose remaining lines are blank or oversized can return normally after quarantine is marked.

## Issue Context
Cursor catches `TranscriptDeliveryAbortedException` to stop subsequent child delivery and close the session through its quarantine-specific failure path. Preserve that behavior even when no additional transcript batch is posted.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SessionImporter.cs[553-581]
- src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[575-635]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +557 to +559
if (bytes > TranscriptBatchBuffer.MaxBytes) {
progress?.Report(new LineSkipped(sessionId, agentId, lineNumber, bytes));
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Valid large lines are discarded 🔗 Cross-repo conflict ≡ Correctness

SendTranscriptBatches unconditionally emits LineSkipped for any line over 4 MiB, even when that
line would fit by itself within kcap-server's request-body limit. This triggers for records such as
a 5 MiB mostly-ASCII JSON line, although the server's transcript contract has no per-line limit and
processes each supplied string independently.
Agent Prompt
## Issue description
Do not discard a transcript line merely because it exceeds the normal 4 MiB batch budget; kcap-server can accept larger individual lines when the complete request remains within its body limit.

## Issue Context
The 4 MiB threshold is appropriate for closing multi-line batches, but it is not a server-defined per-line limit. Flush the current batch and attempt an oversized line as a single-line batch, reporting it as dropped only if the server refuses that request.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SessionImporter.cs[553-565]
- src/Capacitor.Cli/Commands/TranscriptBatchBuffer.cs[34-43]
- test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs[198-230]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +632 to +635
if (loss is not null) {
if (failOnError) throw new HttpRequestException($"transcript batch lines {first}-{last} rejected: {loss}");

progress?.Report(new BatchDropped(sessionId, agentId, first, last, loss));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Cursor imports skip blocked batches 🔗 Cross-repo conflict ≡ Correctness

FlushBatchAsync treats Cursor's HTTP 409 as a lenient BatchDropped, clears the buffer, and lets
SendTranscriptBatches post later ranges. When Cursor normalization or persistence blocks within a
batch, kcap-server defines that response as a retry-from-gap signal, so later requests can advance
the watermark beyond the refused range.
Agent Prompt
## Issue description
Honor kcap-server's Cursor frontier protocol instead of continuing after an HTTP 409 blocked response.

## Issue Context
The server holds Cursor's next line at the failed record and expects the source to retry from that gap. Make the top-level Cursor batch call strict, or otherwise stop delivery specifically on 409, so a later import resumes from the server watermark rather than posting later ranges.

## Fix Focus Areas
- src/Capacitor.Cli/Harness/Cursor/CursorImportSource.cs[568-590]
- src/Capacitor.Cli/Commands/SessionImporter.cs[624-640]
- test/Capacitor.Cli.Tests.Unit/Commands/SessionImporterProgressTests.cs[232-274]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

kcap import batches by line count and silently drops a batch the server rejects

1 participant