Skip to content

(remote): fetch a grown transcript by byte range, stop one large file from stalling the cycle - #274

Merged
devsuitup merged 1 commit into
mainfrom
perf/257-incremental-fetch
Sep 11, 2026
Merged

devsuitup merged 1 commit into
mainfrom
perf/257-incremental-fetch

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

Closes #257 (audit finding 8). Measured tonight on v0.0.76: 65 remote poll cycles in 19 min, one transcript re-fetched whole in 52 of them — the live session's file, every time it grew.

What

  • remote-transport.js: fetchIncremental(alias, requests, destRoot) — for { rel, offset }, runs tail -c +<offset+1> '.claude/projects/<rel>' over the existing non-interactive ssh path (rel passes isSafeMirrorRelPath, offset must be a non-negative integer), captures stdout as a raw Buffer, writes local copy + tail to .part, renames. A failed tail leaves the previous mirror intact.
  • remote-mirror.js: per file, incremental when the remote size grew and the local prefix is trusted (manifest size matches the on-disk size); full fetch on shrink, same-size rewrite (mtime changed), missing manifest entry, torn prefix, or .meta.json sidecar. Cycle: transcripts first, then transfer size ascending, budget checked per file — cycleFull no longer latches on the first oversized file; skip/defer logged once per file per streak.
  • Docs: session-cache.md "Remote hosts — incremental fetch" (decision rule, invalidation, mechanics, ordering, known gaps: parse cost unchanged — the indexer still re-reads the file (perf(remote): rescan granularity is the project folder, not the file #216); a live session with no .jsonl yet is invisible to the inventory).

Proof

  • test/remote-transport-incremental.test.js (7): range command shape, exact byte count for a 4 KB append, binary fidelity, failure leaves the mirror intact, no local file → skipped, unsafe rel rejected.
  • test/remote-mirror.test.js (+5): growth → incremental, shrink → full, stale prefix → full, sidecars full, oversized straggler no longer defers smaller files.
  • Mutation: offset = 0 in the incremental branch → "only the new bytes are requested" red (4168 vs 4096 bytes); reverted.
  • task check: 1244 + 119 pass, 0 fail, pre-existing skips.

Not verified on the wire: tail -c against the real host (busybox/GNU both accept +N; tests only). Growth with an unchanged mtimeMs is accepted as append evidence — stated in the doc as a heuristic.

… from stalling the cycle

Closes #257. A mirrored .jsonl that only grew is completed with tail -c from
the local byte count over the existing ssh path, appended copy-then-rename;
a shrink, a same-size rewrite, a torn local prefix or a sidecar fall back to
the full scp. The cycle sorts transfers by size and checks the budget per
file instead of latching cycleFull on the first oversized one.
@devsuitup
devsuitup merged commit 23046ac into main Sep 11, 2026
10 checks passed
@devsuitup
devsuitup deleted the perf/257-incremental-fetch branch September 11, 2026 23:52
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.

(remote): bound re-download of active transcripts, incremental fetch instead of full scp every 15 s

1 participant