8 views
This guide covers streaming code patches with production-focused examples and trade-offs.
Waiting for a full unified diff before showing anything is how IDE agents feel slow even when the model is streaming. The unfair advantage is hunk-oriented streaming: parse patch frames as tokens arrive, validate each hunk against the live tree, and apply only ranges that still match — so time-to-first-diff collapses without accepting stale or overlapping edits.⚡ TL;DR: Stream NDJSON hunk frames (path,oldStart,oldLines,newLines,hash). Validate context lines + file SHA before apply; skip or re-request failed hunks. Never buffer the whole answer thengit apply. Pair with LLM Output Validators and AST-Guided Edits when mechanical transforms beat freeform hunks.
Frame the stream as typed hunks — streaming code patches
typescript
❌ Treating raw // patch/stream-frames.ts
export type HunkFrame = {
path: string;
oldStart: number;
oldLines: string[]; // exact context expected on disk
newLines: string[];
baseSha: string; // blob hash at plan time
seq: number;
};
export function parseFrame(line: string): HunkFrame | null {
try {
const f = JSON.parse(line) as HunkFrame;
if (!f.path || !Array.isArray(f.oldLines) || !Array.isArray(f.newLines)) return null;
return f;
} catch {
return null; // ✅ ignore incomplete SSE chunks
}
}
@@ markdown fences as trusted once the stream ends — by then the file may have changed under the cursor.
Validate before every apply
typescript
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
export function validateHunk(f: HunkFrame): "ok" | "stale" | "mismatch" {
const disk = readFileSync(f.path, "utf8");
const sha = createHash("sha1").update(disk).digest("hex");
if (sha !== f.baseSha) return "stale";
const lines = disk.split("\n");
const slice = lines.slice(f.oldStart - 1, f.oldStart - 1 + f.oldLines.length);
// ✅ Exact context match — fuzzy apply is how you corrupt JSX
if (slice.join("\n") !== f.oldLines.join("\n")) return "mismatch";
return "ok";
}
export function applyHunk(f: HunkFrame) {
if (validateHunk(f) !== "ok") throw new Error(`hunk_rejected:${f.path}:${f.seq}`);
const lines = readFileSync(f.path, "utf8").split("\n");
lines.splice(f.oldStart - 1, f.oldLines.length, ...f.newLines);
// write atomically via tmp+rename in production
}
Pipeline: parse → validate → apply → UI paint
typescript
// agent/stream-apply.ts — illustrative Node consumer
async function consumePatchStream(readable: AsyncIterable<string>) {
let buf = "";
for await (const chunk of readable) {
buf += chunk;
const parts = buf.split("\n");
buf = parts.pop() ?? "";
for (const line of parts) {
const frame = parseFrame(line);
if (!frame) continue;
const v = validateHunk(frame);
if (v === "ok") {
applyHunk(frame);
ui.paintDiff(frame); // ✅ first paint in <1s typical
} else {
// ❌ Don't invent offsets — re-request this path only
await agent.rerequestHunk(frame.path, frame.seq, reason: v);
}
}
}
}
Conflict and abort rules
| Event | Action |
|---|---|
| User typed in same file | Mark baseSha stale; pause apply |
| Hunk overlap | Serialize by seq; reject later overlapping |
| Client abort | Keep applied hunks; write undo journal |
| Parse fail mid-frame | Drop incomplete; wait for next newline |
Closing checklist
✅ Dos – ✅ Stream NDJSON hunk frames with base blob SHA – ✅ Exact context match before splice – ✅ Paint UI on first valid hunk – ✅ Re-request only failed paths – ✅ Keep an undo journal per session ❌ Don’ts – ❌ Don’tgit apply a mega-diff after the stream ends
– ❌ Don’t fuzzy-match context “close enough”
– ❌ Don’t apply overlapping hunks out of order
– ❌ Don’t ignore user edits mid-stream
– ❌ Don’t skip parse/typecheck after the last hunk
Related reading
- LLM Output Validators: Parse Trees Before Accepting Any Patches
- AST-Guided Edits: LLMs Propose Intents and Codemods Apply Them
- Agentic Git Workflows: Atomic Commits From Noisy LLM Diffs
- Lambda Plus Bedrock: Stream Tokens Without API Gateway Caps
Related: Day 69: Policy as Code for Shell and Apply · Terraform for Developers: Infrastructure as Code From Zero to Production AWS · Day 2: Transformer Attention as a Systems Diagram
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- Css Animation Performance: Performance-Optimized Motion for Modern UIs
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
