Milestone 4 of 8

Find exact phrase matches with complete traces

Intersect ordered positions under query offsets, retain overlapping and repeated-term matches, and show which candidate starts survive each step.

A phrase is an intersection of positions, not a substring search. The trace should show how possible starts survive each query-term offset.

Goal

Find every exact phrase match using adjacent searchable-term positions and record a complete trace of candidates and surviving starts.

Inputs

Use the positional index and analyzed phrase queries. For query terms q[0] ... q[m-1], a match starts at indexed position s only when every q[i] occurs at s + i in the same document. Phrase matching uses indexed_position, not raw character adjacency or full-token positions.

Deliverables

Implement src/phrases.py. Produce output/phrase_matches.jsonl and matching trace records in output/match_traces.jsonl. Each phrase match must retain query ID, document ID, start and end indexed positions, every matched term, every indexed and full-token position, and a deterministic ordinal.

Each trace records candidate documents and the surviving starts after offset zero, after each later term, and after the final intersection. A missing-term, invalid, or zero-candidate query receives an explicit no-match reason rather than a fabricated match.

Checks

Check a a in a a a and preserve both overlapping matches. Check repeated terms, phrases longer than a document, shared prefixes, one match after a failed candidate, exact case, non-ASCII terms, underscores, and punctuation between searchable terms. Verify every recorded position belongs to the same document and term occurrence, and every end position is the final matched indexed position.

Check that the trace's surviving starts exactly predict the emitted matches. Return every match, including overlaps; do not add scores, top_k, or zero-match fillers. Keep query-file order and defer the complete cross-query result order to the later results milestone.

Workspace

Keep offset intersection and phrase trace construction in src/phrases.py. Read the positional index and query analysis without changing either. Save phrase matches and traces in deterministic JSONL form.

Hints

HintStart with the first term
For each candidate document, begin with the starts of q[0]. For offset i, retain a start only if s + i is a position of q[i].
HintA set is not the final evidence
Sets can make intersection easy, but retain the ordered positions needed to explain each surviving start and reconstruct the match record.
HintOverlap is expected
Do not remove a start merely because the previous phrase used some of the same occurrences. Overlapping phrases are distinct evidence.

Review

Trace a a in a a a by hand. Which starts survive after the second offset? What would the trace reveal if the implementation accidentally treated a phrase as raw-character equality?

How to check your work

Checks compare phrase records and offset traces with the tiny repeated-term fixture. The supplied fixture uses exact adjacent searchable positions and retains every overlap; it does not infer meaning from the phrase.

LLM PrimerFind exact phrase matches with complete traceshttps://llmprimer.com/python/projects/build-a-phrase-and-proximity-search-engine/find-exact-phrase-matches-with-complete-traces© 2026 LLM Primer