Conversation
Chunking was one hard-coded branch that recovered line numbers by searching for a chunk's own text inside the file. That produced wrong citations two ways: `full_text.find(piece, offset)` locks onto the wrong occurrence in files with repeated blocks, and stripping the source before counting newlines shifted every line number in a file starting with blank lines. Chunkers now only choose byte cut points; a single shared `finalize` turns a span into content plus a line range, so the range is correct by construction. `chunkers/` imports nothing from `config`, keeping the span math testable without a workspace.yaml. The `chunking:` config surface ships with it — profiles, extension rules and validation — with markdown and code routed to the text strategy until those chunkers land. Existing workspaces need no config edit. INDEX_VERSION 1.1.0 -> 1.2.0, since corrected line numbers rotate chunk ids.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First feature PR of Phase 2. Lands the seam every later chunking change plugs into, and fixes two live citation bugs on the way.
Bugs fixed
_line_window_for_piecerecovered a chunk's position by searching for the chunk's own text:full_text.find(piece, offset_hint)locks onto the wrong occurrence in files with repeated blocks — JSON, XML,.csproj, generated code._chunk_plaindid"\n".join(text.splitlines()).strip()and then passedbase_line=1, so every line number in a file starting with blank lines was shifted.Reproduced on a 200-line JSON file with three leading blank lines. Old code reports the first chunk as lines
1-38; the content actually lives at4-41, and assertingcontent in "\n".join(lines[0:38])fails. New code reports4-41and the assertion holds.Both disappear once lines derive from byte offsets.
The contract
ChunkDraftis three fields:start_byte,end_byte,section. A single sharedfinalizesnaps both offsets to UTF-8 codepoint boundaries, trims whitespace, drops whitespace-only spans, and computesline_end = line_of(end - 1)so a span ending at column 0 reports the previous line — the off-by-one tree-sitter's end-exclusiveend_pointwould otherwise introduce later.chunkers/*import nothing fromconfig.py, so they are unit-testable without aworkspace.yaml(config.py:20callsload_config()at import).CRLF is normalised once, at read. Lone
\rand\x0bdeliberately are not:splitlines()treating them as breaks is exactly the divergence being removed.Behaviour held constant
step = max(1, target - overlap), same loop), so.json/.yaml/.xmlboundaries do not move._build_chunkis untouched — same stable string, samesha1[:16], sametokens_est.Chunkstays at nine fields, sochunk_types.py,keyword_index.py,vector_index.py,storage.pyandapp.pyare zero-diff. Cost: no filtering by strategy.ordinalis now file-global rather than per-section, closing a chunk-ID collision when two sections share a heading and line window.Config surface
chunking:inworkspace.yaml, entirely optional — existing workspaces need no edit. Profiles merge per-name (setting onlytarget_sizeoncodekeeps its defaultedstrategy);rules:replaces wholesale;hard_max_sizedefaults to2 * target_size. Rules flatten toby_extensionat load, so routing is one dict lookup.markdownandcodestrategies route totextuntil their chunkers land. The surface ships first; the strategies fill in behind it._coerce_chunkingrejects: non-mapping sections, unknownstrategy(catchestree_sitterpasted from the design doc), non-positive sizes,hard_max_size < target_size,overlap >= target_size(today masked into a chunk explosion bymax(1, target - overlap)), unknown profile references, duplicate extensions, acoderule withoutlanguage,languageon a non-code rule, and unknown keys in a profile body.Not validated at load: whether
languageis supported by the parser pack. That would dragtree_sitter_language_packinto a moduleapp.py,cli.pyandsetup.shall import — validate it lazily when the code chunker lands.INDEX_VERSION1.1.0 -> 1.2.0: corrected line numbers rotate chunk IDs, so existing indexes must rebuild. A real chunking fingerprint replaces this blunt instrument next.Tests
79 passing. Two new modules, both runnable without a
workspace.yaml:test_chunk_spans.py—test_line_numbers_exact(for every chunk, the source lines it claims contain its content) plus leading blank lines, repeated blocks, UTF-8 multibyte with noU+FFFD, CRLF vs LF, no trailing newline, span ending at column 0,line_ofboundaries,snap_forwardnever splitting a codepoint.test_chunking_config.py— defaults, partial profile override, wholesale rule replacement, and each validation error.Verified
make testgreen.CLAUDE.md,index.py,cli.py,workspace.example.yamland a synthetic repeated-block JSON file throughchunk_file: 35 chunks, every reportedpath:line_start-line_endconfirmed to contain its content, no duplicate chunk IDs._schema_invalidation_reason({"index_version": "1.1.0"})returnsindex_version 1.1.0 -> 1.2.0.Not verified: a full
make buildend to end — this checkout has no cloned repos and no embeddings endpoint running.