ADR-002: AST-Pure Chunking
**Supersedes:** Previous `extractChunks()` + `splitLargeChunk()` approach
ADR-002: AST-Pure Chunking
Status: Accepted
Date: 2026-03-02
Supersedes: Previous extractChunks() + splitLargeChunk() approach
Related: ADR-001 (chunk size limits)
Problem
The previous chunker had two critical flaws:
1. ~16% of code was orphaned (not in any chunk)
Tested on indexer.ts (1562 lines): 227 non-empty lines were not covered by any chunk.
| Orphan type | Example | Why missed |
|---|---|---|
| File imports | import { foo } from "./bar" | import_statement not in getChunkType() |
| Module constants | const MAX = 100 | lexical_declaration not in getChunkType() |
| Class fields | private field: string | public_field_definition not recognized |
| Static properties | static readonly ITEMS = [...] | Same — field definition |
| JSDoc comments | /** Does something */ | comment nodes separate from method nodes |
| Small methods | clear(): void { ... } | Below MIN_CHUNK_TOKENS threshold |
2. Oversized nodes got blind line-split
splitLargeChunk() cut by line count with zero AST awareness. A 640-line method
became 22 arbitrary 29-line blocks that started/ended mid-statement.
Decision
Replace extractChunks() + splitLargeChunk() with AST-pure chunking:
Every line of source code belongs to exactly one chunk. Every chunk maps to an AST node (or connected group of sibling nodes). No orphans. No blind splits.
New Architecture
Step 1: Top-down AST walk with size-aware decisions (processChildren)
For each child node:
if recognized type && fits MAX_CHUNK_TOKENS → emit as one chunk
if recognized type && too large && container (class/module) → descend into body
if recognized type && too large && leaf (function/method) → split into connected parts
if unrecognized (import, field, comment) → accumulate as gap
Step 2: Connected parts for oversized leaves (splitIntoConnectedParts)
Oversized functions/methods split by line count at MAX_CHUNK_TOKENS boundaries, but each part carries:
- Original
chunkType(not "block") - Name with
(part K/N)suffix partIndex/totalPartsfor downstream consumers
Step 3: Gap absorption (flushGap)
Non-chunk siblings (imports, fields, comments) are handled:
- JSDoc comments immediately before a method → included in that method's chunk
- Field declarations in class body → merged into a "fields" module chunk
- File header (imports + constants before first function) → "preamble" module chunk
- Trivial gaps (< 10 chars) → dropped
Data Model Changes
Added to ParsedChunk and CodeChunk:
partIndex?: number; // 1-based index within the group
totalParts?: number; // total parts in the group
Consequences
Positive
- ~100% code coverage (no orphan lines)
- Every chunk has semantic type (function, method, class, module)
- JSDoc attached to its method (better embeddings)
- Class fields grouped as meaningful chunks
- File preamble (imports/constants) preserved
- Oversized splits preserve original type + are linked via partIndex/totalParts
splitLargeChunk()eliminated (source of "block" type + cross-boundary splits)
Negative
- Chunk count increases ~10-15% (gap chunks for fields/imports)
- Gap chunks ("fields", "preamble") have lower search value but prevent orphans
- Comment attachment is heuristic (JSDoc goes to next sibling — correct 99% of time)
Migration
- Requires
claudemem index --forceto re-index after this change splitLargeChunk()removed, replaced bysplitIntoConnectedParts()extractChunks()+walkTree()removed, replaced byextractChunksAST()+processChildren()fallbackChunk()kept for truly unparseable files only
Related Documents
基于命题分块以增强RAG
命题分块技术(Proposition Chunking)——这是一种通过将文档分解为原子级事实陈述来实现更精准检索的先进方法。与传统仅按字符数分割文本的分块方式不同,命题分块能保持单个事实的语义完整性。
TileMap Chunk Manager
**Category:** Performance - 2D Rendering & Memory Management
🤖 n8n AI Agent Mastery Course 2025
Welcome to the most comprehensive n8n AI Agent course! Build powerful automation workflows and intelligent AI agents using n8n's visual workflow builder.
Document Chunking/Splitting in Langroid
Langroid's [`ParsingConfig`][langroid.parsing.parser.ParsingConfig]