Series: Claude Code 自動化指南 (1/3)
Table of Contents
Claude Code is Anthropic’s CLI tool that lets developers collaborate with Claude on code and docs straight from the terminal. It supports a “skill” extension point — custom workflow scripts. This site’s post skill turns conversation or notes into structured Markdown articles; under the hood, the ingest.ts script handles secret redaction, calls an LLM to extract metadata, and finally writes an article file with complete frontmatter.
TL;DR
Run make ingest FILE=<conversation-file> to turn an engineering conversation into a structured technical article in one step: ingest.ts automatically redacts secrets, extracts frontmatter metadata, and applies a debug-article template. The output is ~80% usable; the remaining 20% is human work — filling in the technical root cause and the takeaways.
Background and challenge
When several people debug together, the conversation usually looks like this:
[10:32] @alice: My D1 query keeps timing out, I'm on wrangler 2.x
[10:33] @bob: Can you paste the error log?
[10:34] @alice: Error: D1_EXEC_ERROR: Error in line 1: ...SQLITE_BUSY
[10:35] @alice: I already added a retry but it still fails
[10:47] @bob: Are you using batch()?
[10:51] @alice: No, should I be?
[10:52] @bob: Try it, I got bitten by this last time too
[11:08] @alice: batch() works! But inserts still die occasionally
A 40-minute thread interleaved with code snippets and error messages is a puzzle-solving process for the people involved, but pure noise for a later reader. Paste it straight onto a blog? Completely unreadable.
Worse, this kind of conversation usually carries secrets: API tokens, internal URLs, staging database IDs. Publishing it as-is would be a security problem.
The goal is to turn this conversation into a complete technical article — “The D1 SQLITE_BUSY error and the batch() fix” — without rewriting the whole thing by hand. And this isn’t a one-off: almost every engineering team has a pile of valuable debug knowledge buried in chat logs that never gets organized.
Designing the solution
The most obvious approach: throw the whole conversation at Claude and ask it to “summarize” and “list the steps.” That hits problems fast: the model tends toward free-form prose, producing an essay rather than a structured article you can paste into a blog. Run the same conversation twice and the output format differs; sometimes the section order changes, sometimes a key error message gets dropped.
Second attempt: use Claude Code’s templated output, prefixing the prompt with explicit field requirements (background, problem, attempts, solution, lessons) and formatting rules (Markdown, fenced code blocks, a list of sensitive terms). Now the structure was consistent, but it still needed a human to fill in the “why it happens” section — the model only described what happened, not why.
Another problem was secret handling. Manually listing “please remove the following terms” in the prompt was unreliable: the model sometimes ignored them, sometimes rewrote them into something that meant something different. What I needed was a filter that runs before the prompt reaches the model.
The final approach: split it into two stages. First, make ingest runs ingest.ts to handle secret redaction and basic metadata extraction automatically; then a human fills in the “why it happens” and “what I learned” sections. Automation does the mechanical work; humans keep the parts that require judgment.
Implementation details
Step 1: Prepare the input file
Copy the conversation into a plain-text file, strip platform metadata (Slack reactions, read receipts, etc.), and keep the timestamps and speaker labels. Save it as debug-session.txt.
Note: you don’t need to clean the content beforehand — ingest.ts’s secret filter handles that. Just make sure it’s readable plain text.
Step 2: Run ingest
make ingest FILE=debug-session.txt
This produces a new Markdown file under src/content/posts/, named automatically from the extracted title.
ingest.ts does three things:
-
Scans for and redacts secrets (API tokens, internal URL patterns, database ID formats)
-
Calls
llama-3.1-8bto extract title, tags, tldr, and description, emitting them as YAML frontmatter -
Applies a section template based on the article type (bug/debugging), instructing the model to emit a complete Markdown structure
The core of the prompt (inside ingest.ts):
You are a technical blog editor. Below is an engineering conversation.
Output a well-structured Traditional Chinese technical article in this format:
---
title: ""
date: <today>
category: "tech"
tags: []
lang: zh-TW
tldr: ""
description: ""
draft: false
---
## TL;DR
## Context
## Problem
## Attempts
## Solution
## Why it happens
## What I learned
Rules:
- Use fenced code blocks with a language tag
- Secrets are already redacted; do not reconstruct them
- "Why it happens" must explain the root cause, not just describe symptoms
- Each section needs at least 2-3 sentences; single-line bullets are not accepted
Step 3: Human review and additions
The output article is usually 80% usable. The parts that need a human:
-
The technical depth of “why it happens” (the model tends to describe symptoms rather than the root cause)
-
Citing specific error messages (the model sometimes skips the key stack trace)
-
The judgment in the conclusion (when to use batch(), when not to)
Review usually takes 10-15 minutes — versus 60-90 minutes to write a 1000-word technical article from scratch, a roughly 75% time saving.
Input → output comparison
Input (raw conversation snippet):
[10:34] @alice: Error: D1_EXEC_ERROR: SQLITE_BUSY
[10:52] @bob: Try batch()
[11:08] @alice: batch() works
Output (the corresponding section after ingest):
## Solution
Switch to `DB.batch()` to merge multiple statements into a single transaction:
```ts
await DB.batch([
DB.prepare('INSERT INTO logs ...').bind(...),
DB.prepare('UPDATE posts ...').bind(...),
]);
```
batch() wraps multiple operations into an atomic transaction, avoiding lock
contention under concurrent writes.
Results
Once the conversation-to-article workflow ran smoothly, the team’s knowledge-leakage problem improved concretely: each week’s debug sessions yield about 3-4 cases worth recording. That knowledge used to vanish entirely after the next sprint; now each case takes about 20 minutes to turn into a searchable article. Over a few months, the article library grew from zero to 40+ posts — and every one is a real problem we hit, not a textbook example.
What I learned
Specify the output format explicitly: asking for “Markdown output” isn’t enough. You need to specify section headings, code formatting rules, and which fields are required. The more explicit the format, the less post-processing. A practical trick is to paste a blank template (headings only, no content) directly into the prompt so the model “fills in the blanks” instead of “free-styling.”
Automation is an accelerator, not a replacement: ingest.ts handles mechanical work — formatting, secret redaction, metadata extraction. The judgment work (explaining the technical root cause, getting the conclusion right) still needs a human. Treat automation as a “draft generator” rather than “one-click publish” and quality improves a lot.
Keep an index to the original conversation: don’t delete the original debug-session.txt after publishing. Months later, the original timestamps and conversational context often supply context that isn’t in the article.
Batched processing beats one shot: if the conversation is long (over 200 lines), split it into a “problem description” segment and a “solution” segment, ingest them separately, then merge by hand. This usually beats throwing the whole thing at the model at once — the model catches key details more easily within a shorter context.
The root cause of D1 SQLITE_BUSY: D1 (Cloudflare’s SQLite) is prone to SQLITE_BUSY under concurrent writes, because SQLite’s write lock is database-wide, not a row or table lock. batch() wraps multiple statements into a single transaction, sharply cutting lock contention: N individual lock requests become one atomic operation.
References
Answers come from this article only. Click any prompt below or open the chat at the bottom right.
🇺🇸 English
Ever sat in a debugging session that went on for forty minutes? Three people, a wall of error messages, code snippets flying back and forth — and at the end, you fixed it. But a week later? That knowledge is gone. Buried in a chat log nobody will ever scroll back to. That's the problem we're tackling today: how to turn those messy conversations into real, searchable technical articles — almost automatically — using Claude Code.
So let me set the scene. Claude Code is Anthropic's command-line tool that lets you work with Claude on code and docs right from your terminal. And it has this neat extension point called "skills" — basically custom workflow scripts. On this blog, there's a skill called `post`, and it does one job: take a conversation or a pile of notes and turn it into a clean, structured Markdown article. Under the hood, a script handles the messy parts — stripping out secrets, calling a language model to pull out the metadata, and writing out a finished article file.
Here's the one-liner version: you run a command, point it at your conversation file, and out comes a structured technical article. The script redacts secrets, extracts the title and tags, and drops everything into a debug-article template. The result is about eighty percent done. The last twenty percent? That's human work — explaining the actual root cause, and writing down what you learned.
Now let's talk about why this is hard. Picture a real debugging thread. Alice says her D1 query keeps timing out. Bob asks for the error log. Alice pastes something about SQLITE_BUSY. She mentions she already tried adding a retry, didn't help. Bob asks — are you using batch? Alice says no, should I be? Bob says yeah, try it, got burned by this last time. And eventually — batch works, but inserts still die occasionally.
For the people in that conversation, it's a satisfying little puzzle. For anyone reading it later? It's pure noise. You can't just paste that onto a blog — it's unreadable. And there's a nastier problem: conversations like this are full of secrets. API tokens, internal URLs, staging database IDs. Publishing it raw isn't just ugly, it's a security incident waiting to happen.
So the goal is to take that mess and turn it into a proper article — call it "The D1 SQLITE_BUSY error and the batch fix" — without hand-rewriting the whole thing. And this matters because it's not a one-off. Almost every engineering team is sitting on a goldmine of debug knowledge locked away in chat logs that nobody ever organizes.
Now, how do you actually solve it? The obvious first move is to throw the whole conversation at Claude and say "summarize this, list the steps." And that breaks down fast. The model drifts into free-flowing prose — you get an essay, not a structured post. Run it twice and you get two different formats. Sometimes the sections come out in a different order, sometimes a key error message just vanishes.
So, second attempt: give Claude a template up front. Spell out the fields you want — background, problem, attempts, solution, lessons — and the formatting rules. Now the structure holds together. But there's still a gap: the model describes *what* happened, but not *why*. The "why it happens" section still needs a human.
And the secrets problem stuck around too. Just telling the model "please remove these terms" in the prompt was unreliable — sometimes it ignored the instruction, sometimes it rewrote a token into something that quietly meant something different. What you really need is a filter that runs *before* the prompt ever reaches the model.
So the final design splits everything into two stages. Stage one: an automated script handles secret redaction and pulls out the basic metadata. Stage two: a human fills in the judgment-heavy parts — why it happens, and what I learned. Automation does the mechanical grunt work; humans keep the parts that need actual thinking.
Let's walk through it. First, you prep the input. Copy the conversation into a plain text file, strip out the platform clutter — the Slack reactions, the read receipts — but keep the timestamps and who said what. Save it. And here's the nice part: you don't need to scrub the secrets yourself. The script's filter handles that. Just make sure it's readable plain text.
Then you run the ingest command, and it produces a fresh Markdown file, automatically named from the title it extracted. The script does three things in sequence. One: it scans for and redacts secrets — API tokens, internal URL patterns, database ID formats. Two: it calls a smaller language model to pull out the title, tags, summary, and description, and writes them as proper frontmatter. Three: it applies the section template for a debugging article and tells the model to emit the full structure.
And the heart of all this is the prompt itself. It tells the model: you're a technical blog editor, here's an engineering conversation, output a well-structured article in this exact format. Then it hands over a blank skeleton — TL;DR, Context, Problem, Attempts, Solution, Why it happens, What I learned — with strict rules attached. Use proper fenced code blocks with a language tag. The secrets are already redacted, so don't try to reconstruct them. The "why it happens" section has to explain the actual root cause, not just restate the symptoms. And every section needs at least a couple of real sentences — no lazy one-line bullets.
Then comes the human pass. The draft is usually eighty percent there. What needs a person? The technical depth of "why it happens" — the model loves to describe symptoms instead of digging to the root. Pulling in the specific error messages — it sometimes skips the key stack trace. And the judgment in the conclusion — when *should* you reach for batch, and when shouldn't you? That review takes maybe ten to fifteen minutes. Compare that to writing a thousand-word technical article from scratch — sixty to ninety minutes. That's roughly a seventy-five percent time saving.
And it's worth seeing the transformation concretely. The input is three sparse lines: Alice posts the SQLITE_BUSY error, Bob says try batch, Alice says batch works. The output is a full Solution section — it explains that you switch to a batch call to merge multiple statements into a single transaction, shows clean example code, and then explains *why*: wrapping the operations into one atomic transaction avoids lock contention when you've got concurrent writes. Three throwaway lines become a section someone can actually learn from.
So what came of all this? Once the workflow was smooth, the knowledge-leakage problem genuinely got better. Each week's debug sessions throw off three or four cases worth recording. That knowledge used to evaporate after the next sprint — now each case takes about twenty minutes to become a searchable article. Over a few months the library grew from nothing to forty-plus posts. And every single one is a real problem the team actually hit. Not a textbook example — the real thing.
Now let me pull out the lessons, because these generalize way beyond this one tool.
First: be ruthlessly explicit about output format. Just saying "give me Markdown" is not enough. You have to specify the section headings, the code formatting rules, which fields are mandatory. The more explicit you are, the less cleanup you do afterward. And here's a great trick — paste a blank template into the prompt, headings only, no content, and let the model fill in the blanks instead of free-styling.
Second: automation is an accelerator, not a replacement. The script handles the mechanical stuff — formatting, redaction, metadata. The judgment work — explaining the root cause, nailing the conclusion — still needs a human. Treat the tool as a draft generator, not a one-click publish button, and the quality jumps.
Third: keep a link back to the original conversation. Don't delete that source file after you publish. Months later, the original timestamps and the back-and-forth often hold context that never made it into the polished article.
Fourth: batch your processing. If the conversation is long — say, over two hundred lines — split it into a "problem" chunk and a "solution" chunk, run them separately, then merge by hand. The model catches key details much more reliably inside a shorter context than when you dump everything on it at once.
And finally, the actual technical root cause, since we kept dancing around it. D1 — that's Cloudflare's SQLite — is prone to SQLITE_BUSY under concurrent writes. Why? Because SQLite's write lock is database-wide. Not a row lock, not a table lock — the whole database. So when several writes pile up, they collide. The batch call wraps multiple statements into a single transaction, which slashes the contention: instead of N separate lock requests fighting each other, you get one atomic operation.
So let me leave you with the three things that really matter here. One: the value isn't in the AI summarizing — it's in the strict template plus a pre-filter for secrets. Structure and safety are what make raw conversation publishable. Two: aim for an eighty percent draft, not a hundred percent article. Let the machine do the mechanical work and keep the human on the judgment — the why, and the lessons. And three: that recurring debug knowledge your team keeps losing? It's worth maybe twenty minutes a case to make it permanent and searchable. Do that consistently, and a few months from now you've got a library of hard-won, real-world fixes instead of a graveyard of forgotten chat logs.
🇹🇼 中文
你有沒有遇過這種情況:一場跨越四十分鐘的 debug 對話,當下解謎解得很爽,可是過幾個月再回頭看,根本是一團亂碼。今天要聊的就是,怎麼用 Claude Code 把這種對話直接變成一篇可以發佈的技術文章。
先講工具。Claude Code 是 Anthropic 的命令列工具,讓你在終端機裡直接跟 Claude 一起寫程式、寫文件。它有個叫做 skill 的擴充點,可以自訂工作流程。這個網站用的是一個叫 post 的 skill,把對話或筆記轉成結構化的 Markdown 文章;底層則是一個 ingest 腳本,負責偵測敏感資訊、呼叫 LLM 萃取 metadata,最後吐出一篇帶完整 frontmatter 的文章。
一句話總結整個流程就是:跑一個指令 make ingest,丟進去一個對話檔,它就自動幫你遮蔽敏感資訊、萃取標題標籤摘要、套上 debug 文章的範本。輸出大概有八成可用,剩下兩成你再人工補上技術根因跟結論。
我們先看問題在哪。多人協作 debug 的時候,對話通常長這樣:Alice 說我的 D1 查詢一直 timeout,Bob 說貼一下 error log,然後是一串 SQLITE_BUSY 的錯誤,Alice 說我加了 retry 還是不行,Bob 問你有沒有用 batch,Alice 說沒有,試完之後說 batch 可以了,但 insert 偶爾還是掛。
對當事人來說這是解謎過程,但對後來的讀者來說,這就是一堆雜訊。直接貼到部落格?完全不可讀。更麻煩的是,這種對話常常夾帶 API token、內部 URL、staging 環境的 database ID,直接公開就是安全問題。
所以目標很明確:把對話變成一篇完整的技術文章,而且不要人工重寫全文。這不是個案,幾乎每個工程團隊都有大量有價值的 debug 知識,埋在聊天紀錄裡從來沒被整理過。
那解法是怎麼一步步試出來的?
最直覺的做法,就是把整個對話丟給 Claude,叫它做摘要、列步驟。結果很快踩雷:模型傾向自由發揮,吐出來的是一段散文,不是可以直接貼進部落格的結構化文章。而且同一個對話跑兩次,格式還不一致,段落順序會變,關鍵錯誤訊息有時候還被略掉。
第二次嘗試,改用範本輸出。在 prompt 前面加上明確的欄位要求——背景、問題、嘗試、解法、教訓——還有格式規則,像是 Markdown、程式碼要用 fenced block、列出敏感詞清單。這次結構穩定了,但還是有問題:模型只描述了「發生了什麼」,沒有解釋「為什麼」。那個「為什麼會這樣」的段落還是得人工補。
另外敏感詞也很難搞。你在 prompt 裡寫「請移除以下詞彙」效果很不穩,模型有時候忽略,有時候改寫成意思完全不同的東西。所以這裡的關鍵領悟是:你需要一個在 prompt 進模型「之前」就先過濾的機制,而不是拜託模型自己處理。
最終的做法是分兩段。先用 ingest 腳本自動處理敏感詞遮蔽跟基礎 metadata,再人工補上「為什麼會這樣」跟「學到的事」這兩段。自動化負責機械性的工作,人工保留需要判斷的部分。
實作上分三步。第一步,整理輸入檔案,把對話複製成純文字,去掉平台的 reaction、已讀標記這些雜訊,但保留時間戳跟發言者。重點是你不需要預先清理敏感內容,腳本會處理,你只要確保它是可讀的純文字。
第二步,跑 make ingest。腳本會做三件事:掃描並遮蔽敏感資訊、呼叫一個小模型萃取標題標籤摘要寫成 frontmatter、然後根據文章類型套用段落範本。這裡有個很值得學的細節——它的 prompt 核心,是直接在裡面貼上一個「空白範本」,只有段落標題沒有內容,等於是叫模型去「填空」而不是「自由發揮」。範本裡還明確要求:程式碼要標語言、敏感資訊不要還原、「為什麼會這樣」必須解釋根本原因、每段至少兩三句話、不接受單行列點。規則越死,後處理越少。
第三步,人工審查。輸出通常八成可用,你要補的是三個地方:技術根因的深度,因為模型傾向描述現象而不是根因;特定錯誤訊息的引用,模型有時候會略過關鍵的 stack trace;還有結論的判斷,比如什麼時候該用 batch、什麼時候不該用。審查時間大概十到十五分鐘,對比從零開始寫一篇一千字的技術文章要六十到九十分鐘,省了大概七成五的時間。
那實際效果如何?工作流跑順之後,每週的 debug session 大概有三到四個值得記錄的案例。以前這些知識下一個 sprint 之後就完全蒸發了,現在每個案例平均只要二十分鐘就能變成一篇可搜尋的文章。幾個月下來,文章庫從零累積到超過四十篇,而且每一篇都是真實遇到的問題,不是教科書範例。
最後講幾個踩出來的心得。
第一,prompt 的輸出格式一定要明確指定。光說「輸出 Markdown」根本不夠,你得指定段落標題、程式碼格式、哪些欄位必填。最實用的招就是直接貼空白範本讓模型填空。
第二,自動化是加速器,不是替代品。把它當成「草稿生成器」,而不是「一鍵發佈」,品質會好很多。機械性的格式化、遮蔽、萃取交給腳本,判斷性的根因解釋跟結論正確性,還是人來確認。
第三,記得保留原始對話。輸出文章之後別刪掉原始檔,幾個月後回頭,那些時間戳跟對話脈絡常常能補上文章裡沒有的上下文。還有一個小技巧,如果對話超過兩百行,把它切成「問題段」跟「解法段」分別 ingest 再合併,模型在比較短的 context 下更容易抓到關鍵細節。
那順帶把這次的技術根因講清楚:D1 是 Cloudflare 的 SQLite,它在並發寫入時容易觸發 SQLITE_BUSY,因為 SQLite 的寫入鎖是「整個資料庫層級」的,不是行鎖也不是表鎖。而 batch 把多個 statement 包成單一原子交易,等於把原本 N 次個別鎖請求,壓縮成一次操作,鎖競爭自然就大幅下降。
好,收個尾。今天三個重點:第一,把 debug 對話變文章的關鍵,是用「填空式範本」逼模型輸出結構化內容,而不是讓它自由發揮;第二,敏感詞要在進模型前就先過濾,別指望模型自律;第三,把自動化定位成草稿生成器,機械工作交給它,判斷工作留給人,這樣每篇文章二十分鐘就能搞定,知識才不會繼續埋在聊天紀錄裡爛掉。
Tags
Related Articles
MCP in Claude Code: How Model Context Protocol Connects AI to Your Tool Ecosystem
MCP (Model Context Protocol) is an open protocol designed by Anthropic that lets Claude Code call external tools and data sources through a standardized interface. Since its November 2024 release, it has rapidly become the de facto standard for AI agent tool integration, adopted by Cursor, Windsurf, and 40+ other editors.
RAG's Five Stages: From Pipeline to Reasoning Retrieval, and the Naive RAG on My Own Site
Over the past two years RAG evolved from a 'linear pipeline' to 'loop-based reasoning'. It maps cleanly to five stages: Naive, Advanced, Modular, Graph, Agentic. The real inflection point is control moving from pipeline to agent — a System 1 → System 2 shift. Looking back at engineer-news's own RAG stack, it's stuck at the Naive edge — so this post also lays out what to fix next.
Building a Real RAG: 5 Infra Lessons from InfiniFlow's 2024 Year-in-Review
The previous post zoomed out for a five-stage panorama of RAG. This one zooms in on the five infra lessons any real RAG has to face: document ingestion, contextualized chunking, three-lane hybrid search, tensor reranker, and GraphRAG's semantic gap. Each lesson is checked against engineer-news's current stack, ending with a priority list for a personal site.