fix(rag): include heading_path in the prompt context blocks - #175
Merged
Conversation
build_prompt read only source and chunk_index off each context and never touched context["metadata"], so heading_path reached the API sources list but never the model. The structural chunker computes heading_path precisely so a chunk carries its position in the document hierarchy; discarding it at the prompt boundary threw away the signal that distinguishes a passage about long-term risk from one about immediate effects. Each context block header now carries a section= segment when heading_path is populated, and is unchanged when it is not, so text_block and code_block chunks keep the previous format. Heading paths are bounded by MAX_SECTION_CHARS. The bound is derived rather than picked: compression budgets measure only chunk body text and reserve nothing for the block scaffolding, so worst-case headers have to fit inside reserved_prompt_tokens. A test pins that invariant, since a looser bound silently pushes the prompt past what any budget counted. Refs #172 Claude-Session: https://claude.ai/code/session_01PhFVTFLaxhmP8icwzmcm5Q
This was referenced Aug 7, 2026
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.
Closes #172
Problem
build_promptread onlysourceandchunk_indexoff each context and never touchedcontext["metadata"], soheading_pathreached the APIsourceslist (engine.py:214) but never reached the model.The structural chunker computes
heading_pathprecisely so a chunk carries its position in the document hierarchy. Discarding it at the prompt boundary threw away the one signal distinguishing a passage about long-term epidemiological risk from one about immediate effects — which is how a query about a single short night got answered with chronic-exposure statistics.The metadata was already there: the compressor copies the whole dict (
compressor.py:105, popping onlyexpanded_text), so this is a missing read, not missing plumbing.Change
Context block headers gain a
section=segment whenheading_pathis populated:The segment is omitted entirely when
heading_pathis empty, missing, or metadata is absent, sotext_block/code_blockchunks and theollama.pycall site (which passes no metadata) keep the exact previous header.On the truncation bound
The issue flagged token cost as worth checking. It was: compression budgets measure only chunk body text and reserve nothing for block scaffolding, so an unbounded heading path grows the prompt past what any budget counted.
reserved_prompt_tokens(512) is the reservation covering everything outside chunk bodies. Measured worst case —max_contextsheadings of single-character words, one whitespace token per two characters — comes to 518 tokens at a 200-char bound and 262 at 120.MAX_SECTION_CHARSis therefore 120, and a test pins the invariant rather than leaving it as a comment that drifts.Verification
section=present on all 4,043 chunks carryingheading_path, absent on the 172 without.Notes for the reviewer
Two pre-existing issues surfaced during verification, both out of scope here:
Taskfile.yml:127—LOCALRAG_BUILD_SHA="$$(git rev-parse HEAD)"does not expand outside Task's own escaping, so/build-inforeports the literal string99848(git rev-parse HEAD)andtask docker-checkcan never pass..env, so 26 tests fail on a machine that has one. Moving.envaside takes the suite from 26 failed to 0.https://claude.ai/code/session_01PhFVTFLaxhmP8icwzmcm5Q