fix!: actionable fql errors and anchor-seeded recall - #218
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #218 +/- ##
====================================
+ Coverage 86% 87% +1%
====================================
Files 56 56
Lines 3461 3567 +106
====================================
+ Hits 2980 3107 +127
+ Misses 403 389 -14
+ Partials 78 71 -7
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This was referenced Aug 22, 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.
Pull Request
Type of change
Description of the changes
A recall is now seeded by a term, an anchor or a vector instead of requiring a text term, and every FQL rejection carries a repair instruction rather than a report. The shapes that used to be answered wrongly (a repeated modifier, a second line, an empty quoted value) are now errors. With support for upper case, a warning is issued for an upper case letter in a keyword.
Motivation for the changes
An agent can only fix a query the error tells it how to fix, and it can only notice a wrong answer if it gets one:
Expected colon, but found ""names an empty literal the caller never wrote, anddepth:2 depth:5silently ran a differently-scoped query. Closes #160 —recall topic:billing, the SDK's own documented seed, was unreachable, so callers padded it with a keyword they did not mean.Breaking change?
If yes, fill this in
What breaks: four query shapes that returned 200 now return 400, three that
returned 400 now return 200, and several error strings changed wording. The FQL
surface is public API, so this is a breaking release.
Before → after:
Migration: drop the repeat from any query giving
top:/depth:/since:/until:/vec:twice (repeatedtopic:/entity:is unaffected — anchors are a list by design); send one command per request, a trailing newline is still fine; and update anything matching on the old error strings (exceeds max,Error while parsing,Expected colon, but found ""). Positions and theparse error at column N:prefix are unchanged, and the newly-accepted shapes need nothing —recall("zzznomatch", vector=...)still works, it just no longer needs the placeholder keyword.How it was tested
Against a locally-built server on
tests/fraise.config.toml(the config compose mounts), each suite on a fresh instance since they claim overlapping graphs:The skip is
test_vector_search_with_real_embeddings— needstransformers,and was skipped before this change too.
tests/e2e/parser_test.pyis the new adversarial surface: 208 cases over rawHTTP, all passing. It pins the error a caller can repair rather than the one the
parser happens to emit, and pins that queries which only look dangerous
(specials inside quotes, reserved words in value position) still succeed.
Checklist
Notes for reviewers
Two calls worth a second opinion:
:is now that clause. ReadingDEPTH:5as the depth clause is what letsdepth:2 DEPTH:5be a duplicate error rather than a casing one — blaming the casing names the shallower problem, and an agent that lower-cased it would get a silently rescoped query back. The cost:recall x Depth:2now parses, relaxing "keywords are matched exactly" and changing six pinned cases. Away from a colon casing still matters.gatherSeedsguards onlen(keywords) > 0, so with no term and no vector there are no seeds. Making anchors actually seed retrieval is a graph-layer change with scoring consequences, so this stops at making the question askable.Also:
sdk/python/pyproject.tomlgainsreadme = "README.md"— unrelated to the grammar, but it is why the PyPI page has no description. Happy to split it out.