Skip to content

rfc: the GQ 3 grammar, one break and the slots it freezes - #1202

Open
ragnorc wants to merge 11 commits into
ModernRelay:mainfrom
ragnorc:claude/query-language-evaluation-5a029b
Open

ragnorc wants to merge 11 commits into
ModernRelay:mainfrom
ragnorc:claude/query-language-evaluation-5a029b

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Why

GQ has grown one capability at a time: a logic word reserved when a filter needed it, ranking placed in order because that is where a score was first wanted, search functions added as keywords, the operator vocabulary amended in place, an explain version per feature, a deprecation shim per rename. Each change was right on its own terms and none was asked to fit the next five, so the language now carries decisions justified only by arrival order, and four open RFCs each schedule a break of their own. A whole-language review against SQL, GQL, Cypher, GraphQL, Datalog and the Elasticsearch, LanceDB and Vespa query languages found the semantics sound and the accretion the problem: every expressiveness gap is a position the grammar never reserved.

This RFC opens a revision of the language as a whole. The goal is a foundation that is evolvable (every later capability has a reserved position), maintainable (one grammar, one function registry, one annotation form, one diagnostic contract), expressive (slots for derived relations, optional patterns, block aggregates as values, retrieval as typed columns, post-group filters, pattern-scoped writes) and extensible (registry rows and declarations an agent can author without a compiler release). It does the breaking work first, once, so everything after it is additive.

Summary

Adds docs/rfcs/2026-10-28-gq-3-grammar.md (maintainer track) and its registry row, and commits the evaluation it is drawn from as docs/rfcs/assets/2026-10-28-gq-3-grammar-evaluation.md (17 axes against SQL, GQL, Cypher, GraphQL, Datalog, Elasticsearch, LanceDB and Vespa; literature; the GQ 3 redesign; the RFC review; staging; TypeDB; direction; the one-way rule). The RFC links it from the Motivation and the decision log.

The RFC decides that GQ makes one incompatible change, edition 3, and then stops making incompatible changes. The release that ships it lands every syntactic decision that would otherwise force a later break; forms whose semantics arrive in later RFCs land as slots the parser accepts and the type checker refuses with a stable code naming the owning RFC.

The freeze list: mutation for writes; the triple traversal kept unchanged ($p worksAt $c, source on the left; <e> for either orientation), with every arrow form a parse error whose fix prints the triple; a retrieve { } clause binding typed score columns, with order sorting values and having filtering them; every function a generic call over a registry, with nothing left in the grammar; the reserved set closed at the six logic words; limit $k; generic @name(args) annotations; reserved declaration kinds (view, batch), blocks (optional { }, block aggregates as values), a view binding with arguments, returning, the meta-fields @type_id/@distance/@score, the schema-catalogue type names, namespaced type references a::b::C, and the schema roles Vector(n, distance=…), @analyzed, @valid; an optional gq 3; edition line.

A rewriter, omnigraph queries migrate, turns edition-2 sources into edition 3 by byte-range edits with an IR-equality proof; the release that ships edition 3 rewrites an edition-2 stored source at boot with a warning instead of quarantining its graph, and the release after removes the edition-2 grammar.

Owners of the slots

Derived relations and optional patterns (new), the RFC 0048 rewrite, the analyzed lexical search RFC (#792), a writes RFC (new), polymorphic types (#884), schema namespaces (#1015). No permissions construct is reserved: the permissions model is not accepted, and a file-level declaration keyword can be added later without a break.

Gates (blocked_on)

  • the in-context competence instrument run on the edition-3 spelling through a parser-only build;
  • the GQT corpus rewritten by the tool with zero hand edits, green, with IR equality on every case.

Checks

python3 scripts/check-docs.py (425 files OK) and typos pass.

Update 2026-10-29: TypeDB, direction, one spelling per meaning

  • One way. The maintainer's requirement that there be one way of expressing something is now a stated design rule (Go's "a language needs only one conditional control flow construct"): a respelling with no meaning of its own is absent from the grammar and named by the diagnostics; a form that falls out of a general construct is canonicalised by omnigraph queries fmt (count { } > 0 → exists { }, count { } = 0 → not { }, later $x.@type = "T" → $x is T). The freeze list is checked against the rule in the Design section.

  • Direction. The left arrow $b <-knows- $a is removed as a second spelling of $a -knows-> $b (GQ has no path chains, so it never continues a line); it is a parse error whose fix prints the swap. An arrow written against the schema is T5 with a machine-applicable swapped clause, the deterministic correction the Cypher ecosystem bolts on after generation (LangChain's CypherQueryCorrector; CypherBench counts a reversed relation in about one gpt-4o failure in six) made loud and moved into the compiler. -e- resolves orientation per member from the endpoint types (the issue 659 alternation case), and T22 narrows to the genuinely ambiguous case.

  • TypeDB 3 / TypeQL. Reviewed against the design (pipelines, functions, try, type variables, roles, the 2-to-3 migration, its agent benchmarks). It confirms the slot strategy (TypeQL's grammar carries lists, structs and three annotations its docs call "not yet available") and the rewriter (TypeDB's break had no query-migration tool), and changes no slot: its pipeline closure is the alternative the RFC rejects (text order is plan order, rows cross-product, reads and writes mix) and is now an Alternatives row; its type-variable binding and row-valued given parameters are additive in GQ and noted for the polymorphism and a later RFC; roles are an Alternatives row.

  • Schema queryable at any size. A sixth property in the Motivation: the schema is data in the same language. Catalogue types (@Type, @Property, @Edge, @Interface, @Index, @Function, @View) are node types for every purpose of the grammar, so a thousand-type schema is bound, traversed, searched (retrieve { $t.lex: bm25($t.instruction, $q) }) and counted instead of dumped into a context window. The @-prefixed catalogue edge selector ($t: @Type -@property-> $p: @Property) is reserved so catalogue edges never collide with a user edge named property or from; the engine catalogue RFC's schema extension owns the catalogue's edges and searchable fields.

Update 2026-10-30: no arrow

The arrow spelling is withdrawn. The arrowhead carries no information: when the endpoint types differ the schema fixes the orientation, and when they are the same the position does, in a triple exactly as in an arrow form. Its two justifications did not hold up (a triple reads in source-to-target order; the only in-context measurement GQ has is on the triple, with no direction errors, while CypherBench shows arrows do not prevent reversals), and it was the break's largest rewrite. The triple traversal therefore stays unchanged from edition 2, <e> keeps the per-member orientation rule and the narrowed T22, a triple written against the schema is T5 with the swapped clause as its fix, and -e->, <-e- and -e- are recognised parse errors whose fix prints the triple. Catalogue edges are $t: @Type @property $p: @Property. The rewriter table and the compatibility section lose their traversal rows; the break no longer touches every stored query.

One incompatible change to the GQ grammar, edition 3, after which every
later capability is a registry row, a plan node behind existing syntax or
the filling of a reserved slot. The RFC carries the freeze list (mutation
keyword, arrow traversals, a retrieve clause binding score columns, generic
calls over a function registry, the closed reserved set, limit as a
parameter, generic annotations, and the reserved declaration kinds, blocks,
clauses, meta-fields, catalogue names and namespaced type references), the
rewriter that migrates edition-2 sources with an IR-equality proof, and the
one-release window in which edition 2 is rewritten at boot instead of
quarantined.
@ragnorc ragnorc changed the title docs(rfc): add the GQ 3 grammar RFC rfc: the GQ 3 grammar, one break and the slots it freezes Oct 29, 2026
@ragnorc
ragnorc marked this pull request as ready for review October 29, 2026 19:28

ragnorc commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Recommendation: revise.

This review covers head 6dc6315d3bc73217723a5e916312bd94b4970943. The pull request was ready at aa174ee16e9096ccbcdf895eca4ae6de58978313. One later commit changed only docs/rfcs/2026-10-28-gq-3-grammar.md (+102/−13). I checked that delta against the same compiler facts.

Why

Each GQ feature has reserved a word, added a call, or advanced an explain version. Three open drafts still schedule a later major. #805 schedules (3, 0) and then (4, 0). #792 removes search, match_text, and fuzzy at the next major. #793 removes the positional k of rrf at a later major. One edition that reserves the missing positions costs less, when those positions match the engine that already exists.

What this RFC decides

Edition 3 is the one incompatible break. After it, the grammar accepts more text and keeps every produced shape. The grammar parses each reserved position. The type checker refuses a position whose owner has not landed, with a stable code that names that RFC. omnigraph queries migrate rewrites source by byte range and requires the typed IR to stay equal. Edition 2 compiles for one release. Boot rewrites that source in memory and logs a warning. The following release removes the edition-2 grammar.

The latest commit keeps one directed spelling, $a -knows-> $b, with the source on the left. left_arrow exists so the diagnostic can print the swapped form. $a -knows- $b means the orientation the endpoint types admit, including one direction per alternation or wildcard member. That matches the direction the type checker already stores on each member.

Claims that match the code

Checked at the RFC pin ea895066 and at local main e7d95ae74dc1bf8d42aacef35dc0395c804fe6a4. query.pest, schema.pest, GQ_LANGUAGE_VERSION, and EXPLAIN_VERSION are the same bytes at both commits.

  • GQ_LANGUAGE_VERSION is (2, 3) in crates/omnigraph-compiler/src/lib.rs. EXPLAIN_VERSION is 7 in crates/omnigraph-planner/src/explain.rs.
  • The reserved rule is and, or, not, is, null, and in.
  • The named function productions exist, including rank_expr and nearest_ordering. text_search_clause and match_value are still in query.pest.
  • Query annotations are @description and @instruction. Schema keyword arguments use ident = literal.
  • TypedExpr::score_type returns F32 for nearest and bm25, and F64 for rrf. The comment there says Lance writes F32 scores and distances.
  • IRExpr and IROp derive Eq and Hash. search_node in crates/omnigraph-planner/src/optimizer.rs matches IRExpr::Nearest, Bm25, and Rrf and builds LogicalNode::Nearest, TextSearch, and RankFuse.
  • A stored query that fails to parse quarantines the graph (crates/omnigraph-server/src/settings.rs).
  • Catalogue types are the five names in CATALOGUE_TYPES. Query diagnostic codes allow the prefixes Q, T, and P (crates/omnigraph-compiler/src/query/codes.rs). Schema lint codes use OG-….
  • The quoted pre-stable sentence is in the Summary of docs/rfcs/2026-09-24-shared-expression-model.md. The Compatibility section says the language wins where a better language and an unchanged behavior conflict.
  • python3 scripts/check-docs.py on this head: Documentation OK (435 Markdown files). typos 1.50.1 on the two changed files: exit 0. The title passes scripts/check-pr-title.py.

Blocking

1. The retrieve column records the wrong type, and a score having is a new contract

The freeze table says the nearest column and the bm25 column are F64?, null outside the window or null for a non-match. score_type publishes F32 for both, and F64 only for rrf. A GQT projection of this score is F32. Returned rows are the window, or the match set. A row outside the window is absent. A non-match is absent. Lance 13 distance values are f32.

having { $d.vec < 0.6 } is described as the same Filter the overfetch ladder already applies. The ladder widens candidates so a graph or property filter can still fill limit. A distance predicate after the window can return fewer rows than limit. T32 already refuses a retrieval under an aggregate, so a query that has both is unspecified.

Freeze F32 for the nearest column and the bm25 column, and F64 for rrf. Say that rows outside the window are absent. State how having interacts with limit, or keep a score having as a slot until the ladder is specified to see that predicate. score_type and score_column already own the column type. A later Lance distance-type change stays a language break unless the frozen type remains the type score_type already publishes.

2. IR equality cannot prove the ranking rewrite

The rewriter refuses a write unless the edited text compiles to a typed IR equal to the original. Eq on IRExpr compares Nearest, Bm25, or Rrf with a later property access of the arm name. Those trees differ, so the proof refuses every ranking query. The gate that requires a green corpus and zero hand edits cannot pass.

After both editions lower, compare the logical nodes. Both must be the same Nearest, TextSearch, or RankFuse. Treat the generated arm name as a projection alias. lower.rs and those logical nodes already own this. A second IR would copy that state.

A list contains rewritten as in can stay equal when both lower to the same Binary operator. A string contains() can stay equal the same way. Traversal direction lives on the expand operator, so a rewrite that changes direction fails this proof. That failure is correct. Finding 3 is the rewrite that causes it.

3. The rewriter table disagrees with the direction rule, and it omits live spellings

The traversal section says the fix prints -> with the ends in schema order. The table maps $a knows $b to $a -knows-> $b and leaves the variables in the written order. An incoming pattern ($company worksAt $person when worksAt runs from person to company) changes Direction. The equality proof then fails.

The table also omits $a * $b, $a <*> $b, and $a "in" $b. The wildcard resolver includes an edge in either direction between the two types (EdgeSelector::Wildcard in crates/omnigraph-compiler/src/query/typecheck.rs). The form that keeps both orientations is $a -*- $b. The freeze table lists $a -*-> $b, which keeps one orientation and omits the incoming members. The sentence "Everything not in the table is unchanged text" leaves the omitted spellings unparsed under an arrow-only grammar.

A traversal clause still accepts hop bounds. {1,3} is already in the table. The accurate claim is that one clause is not a path chain, so a left arrow is not required to continue a line.

The compatibility list names the triple traversal. Name every removed spelling: the bare triple, undirected angle brackets, the edge binding, alternation, the wildcard, the quoted edge, and the left arrow (an edition-3 recognizer only).

4. The proposed file rule omits statements the grammar accepts today

query_file accepts show, branch, commit, load, explain, or many query_decls, plus statement_trailer and missing_param_list. The sketch accepts one undefined statement, or declaration*. declaration* also matches an empty file. The summary says branch and commit statements stay. missing_param_list is the diagnostic for a missing (), which is what the competence gate measures (RFC 0047). Extend query_file. Keep missing_param_list.

5. @example and @if_commit are marked working, and annotation_args has no production

The compiler accepts @description("…") and @instruction("…") on a query. @example and @if_commit are absent. --if-commit is a request precondition. Mark those two names as slots, or specify them as new behavior. The later sentence, that the writes RFC must choose one owner for @if_commit and the request field, is the right open question. It conflicts with "the typed four work" in this row.

annotation_args is used and has no production. Schema annotations already use ident = literal (annotation_kwarg in schema.pest). Call options in this RFC use :. Define annotation arguments with =, the production .pg files already use. A grammar that applies : to @embed("text", model="…") refuses text this RFC says a .pg file may still contain.

6. (3, 0) collides with open drafts that still schedule their own majors

The current language version is (2, 3). Explain is 7. #805 (draft, head 49f436ce618b) assigns (3, 0) and (4, 0) to the infix-to-call change. This RFC also assigns (3, 0), to a larger break. #793 (draft, head 5e6e82e102d0) says retrieval adds no clause and removes positional k at a later major. This RFC adds retrieve and rejects positional k in edition 3. #792 (draft, head c5d33c2931a1) removes search, match_text, and fuzzy at the next major. This RFC puts those names in the working registry and then stops incompatible changes. State that those names remain aliases, or allow a registry row to be removed.

#884 (ready, head 24b60eba9e37) allocates a minor and an explain bump. Its base (2.2, explain 4) is stale. main is already (2, 3) and explain 7. That RFC is a minor.

The alternatives line "three open RFCs already schedule four majors" matches #805 (two majors), #792, and #793. The line "Each is a break" also counts #884. Record, in this decision log, the grammar sentences each of those drafts must remove. Leave supersedes empty while they still own the semantics. The RFC process requires that shared dependency to be explicit.

Optional

  1. Cite the Compatibility wording of the shared-expression RFC beside the Summary sentence, or quote the Summary and name that section.
  2. ea895066 is behind current main. The four files above match local e7d95ae. Name the merge base when this RFC is accepted. State the bump base as (2, 3) and explain 7.
  3. State what boot does when the IR proof refuses. A parse failure quarantines the graph today. An in-memory rewrite does not update the cluster bundle, so a warning "once per query" repeats on every boot until an operator runs queries migrate. Say whether the warning is once per process.
  4. L… matches neither the query catalog (Q, T, P) nor schema lint (OG-…). Name the catalog that owns the count { } > 0 and count { } = 0 lint.
  5. The heading "Operators" introduces the boot window. Use a heading that names the window. The engine-catalogue RFC decides five catalogue types. Attribute @Type, @Property, @Edge, @Interface, @Index, @Function, and @View to this RFC's reservation.

Liability

The one break removes repeated majors, a second spelling of one traversal, and one grammar production per function. That reduces long-term liability when later RFCs only fill a slot or add a registry row.

This RFC adds one release with two grammars, a rewriter, a boot rewrite, a function registry, an annotation registry, a working having, one explain and bound-plan advance, and additive envelope fields. Five more breaks of the same shape would rebuild the accretion this RFC is written to stop. The freeze succeeds when #792, #793, and #805 remove the majors they still schedule.

A frozen F64? score, and an equality proof that rejects every ranking query, add liability. They publish a contract the engine does not implement, and they block the evidence gate this RFC requires. The column type, the file rule, schema annotation punctuation, and edge direction already have owners: score_type, query_file, annotation_kwarg, and member direction. New registries are justified for functions and annotations that are still separate grammar productions. They are a second copy if they also own those four contracts.

One grammar is the right priority over a permanent left-arrow spelling and a long deprecation shim. That trade fits a pre-stable language. Keep the published F32 score and a proof the corpus can pass.

Invariant 9 (typed structures) is stronger when a registry and a named score column replace a grammar keyword. It is weaker while the frozen column type is wrong. Invariant 8 requires the rewriter to refuse an unprovable edit, and requires a loud boot outcome when the proof fails. Invariant 13 binds the grammar to the rewritten corpus and to the competence instrument. Those gates are the right evidence. They fail while findings 2 and 3 stand. No deny-list item is added when having is a Filter and the rewriter is a compiler pass. No Lance API is written. The column type still freezes the Lance f32 distance.

Validation

GQT regression proof does not apply. This pull request changes documentation only. It changes no query result, error, or plan.

Limits: the 37 of 45 competence figure is copied from RFC 0047. I did not re-run that instrument. The size of the stored-query fleet is the RFC's figure. I did not measure the boot rewrite. These docs checks are local. I did not use a CI run on this commit as evidence.

How to revise

Revise the freeze table, the rewriter table, the file rule, the annotation row, and the decision log. Each slot should name the owner that already holds the contract. The open drafts should remove the grammar sentences this edition takes, in this decision log, before (3, 0) is the single major.

Inline comments (RIGHT side of docs/rfcs/2026-10-28-gq-3-grammar.md)

Line 273: Blocking. TypedExpr::score_type returns F32 for nearest and bm25, and F64 only for rrf (crates/omnigraph-compiler/src/query/typecheck.rs). The comment there says Lance writes F32 scores and distances. A projected score in crates/omnigraph-gqt/cases/issue_640_search_score_projection.gqt is F32. Returned rows are the window or the match set. A row outside the window is absent. A non-match is absent. Lance 13 distances are f32. Freeze F32 for these two arms and F64 for rrf. score_type and score_column already own this contract. Keep the frozen type equal to the type score_type publishes, so a later Lance distance type is a language break only when that owner changes.

Line 284: Blocking, with the row above. The overfetch ladder widens candidates so a graph or property filter can still fill limit (docs/user/search/index.md). A distance predicate after the window can return fewer rows than limit. That is a new observable contract. T32 already refuses a retrieval under an aggregate, so a query with both is unspecified. State the having and limit interaction, or keep a score having as a slot until the ladder is specified to see the predicate.

Line 299: Blocking. IRExpr derives Eq, and the variants differ. Today the leading order key is IRExpr::Nearest, Bm25, or Rrf, so search_node can build LogicalNode::Nearest, TextSearch, or RankFuse (crates/omnigraph-planner/src/optimizer.rs). After this rewrite the order key is a property access of the arm name. The trees differ, so the proof refuses every ranking query and the corpus gate cannot pass. Compare the logical nodes after both editions lower. Treat the generated arm name as a projection alias. lower.rs and those logical nodes already own this. A list contains rewritten as in, and a string contains(), can stay equal when both lower to the same Binary operator.

Line 307: Blocking. The traversal section says the fix prints -> with the ends in schema order (a swap). This row always emits -> and leaves the variables in the written order. An incoming pattern then changes Direction on the expand operator, and the equality proof in the paragraph above fails. Also add rows for $a * $b, $a <*> $b, and $a "in" $b. EdgeSelector::Wildcard includes an edge in either direction between the two types. The form that keeps both orientations is $a -*- $b. The freeze table's $a -*-> $b keeps one orientation. The sentence at line 319 ("unchanged text") leaves the omitted spellings unparsed. Line 483 says a traversal is one hop. Hop bounds remain ({1,3} is in this table). The accurate claim is that one clause is not a path chain. Line 637 should name every removed spelling: the bare triple, undirected angle brackets, the edge binding, alternation, the wildcard, the quoted edge, and the left arrow.

Line 366: Blocking. query_file today is show, branch, commit, load, explain, or many query_decls, plus statement_trailer and missing_param_list (query.pest). This production accepts one undefined statement, or declaration*, and declaration* also matches an empty file. That omits the statements the summary says stay, and it omits the missing-() diagnostic the competence gate measures. Extend the current query_file rule. Keep missing_param_list.

Line 253: Blocking. The compiler accepts @description and @instruction only (query.pest). @example and @if_commit are absent. --if-commit is a request precondition. Mark those two as slots, or specify them as new behavior. The later note that the writes RFC must choose one owner for @if_commit and the request field is the right open question, and it conflicts with "the typed four work" here. annotation_args (line 558) has no production. Schema annotations already use ident = literal (annotation_kwarg in schema.pest). Call options use :. Define annotation arguments with =. A grammar that applies : to @embed("text", model="…") refuses text a .pg file accepts today.

Line 132: Blocking. #884 allocates a minor and an explain bump. Its pins (2.2, explain 4) are stale: main is (2, 3) and explain 7. It is a minor. The four majors are #805 ((3, 0) then (4, 0)), #792, and #793. This RFC also takes (3, 0). #805 still assigns that number to the infix-to-call change. #793 says retrieval adds no clause. #792 removes search, match_text, and fuzzy at the next major, while this registry keeps those names and then stops incompatible changes. State that those names remain aliases, or allow a registry row to be removed. Record in the decision log the grammar sentences each draft must remove. Leave supersedes empty while those drafts still own the semantics.

ragnorc commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Recommendation: revise.

This review covers head 7c44b23d683921d7ed84104615f3525d0d33b441. It reviews the delta 6dc6315d..7c44b23d only. The earlier comment on 6dc6315d still stands where this delta does not change the text.

Why

Each GQ feature has reserved a word, added a call, or advanced an explain version. Open drafts still schedule their own majors. One edition that reserves the missing positions costs less, when those positions match the engine that already exists.

What this delta changes

The triple stays. $p worksAt $c remains the directed spelling, with the source on the left. <e> remains the undirected spelling. An arrow is a recognised error whose fix prints the triple. The evaluation is committed beside the RFC. Catalogue type names and an @-prefixed catalogue edge are reserved slots, so a large schema can be queried in GQ.

Keeping the triple is the right cut. The arrowhead adds no fact. Position and the schema already carry direction. The rewrite of every traversal was the largest mechanical edit in the break, and this delta removes it.

Earlier findings

  1. Still open. The nearest column and the bm25 column are still F64?, null outside the window or null for a non-match. TypedExpr::score_type returns F32 for both, and F64 only for rrf (crates/omnigraph-compiler/src/query/typecheck.rs). The comment there says Lance writes F32 scores and distances. Lance 13 distance tables are f32 (lance-index 13.0.0 vector/pq/distance.rs). A projected score in crates/omnigraph-gqt/cases/issue_640_search_score_projection.gqt is F32. Returned rows are the window, or the match set. A row outside the window is absent. having { $d.vec < 0.6 } is still described as the same Filter the overfetch ladder already applies. The ladder widens candidates so a graph or property filter can still fill limit (docs/user/search/index.md). A distance predicate after the window can return fewer rows than limit. The evaluation repeats the same F64? null-outside-the-window column (§9.5).

  2. Still open. The rewriter still refuses a write unless the edited text compiles to a typed IR equal to the original. The new lowering paragraph says the leading order key becomes an arm reference, and search_node finds that reference where it found a call. Eq on IRExpr compares Nearest, Bm25, or Rrf with a property access of the arm name. Those trees differ, so the proof refuses every ranking query. Compare the logical nodes after both editions lower. Treat the generated arm name as a projection alias.

  3. Partly resolved. The rewriter table no longer rewrites a triple into an arrow. The freeze list names $a knows $b, $a <knows> $b, $a $e:knows $b, the alternation, $a * $b, and $a "in" $b. Hop bounds stay on traversal. The compatibility list no longer says the break removes the triple. Two holes remain, and they are blocking. See new finding 1.

  4. Still open. file is still edition? ~ setting_stmt* ~ (statement | declaration*). query_file today is show, branch, commit, load, explain, or many query_decls, plus statement_trailer and missing_param_list (query.pest). statement has no production. declaration* matches an empty file. The evaluation's own extension table (§7.2) names those statement kinds as the file-level ordered choice. Extend query_file. Keep missing_param_list.

  5. Still open. The annotations row still says the typed four work. The compiler accepts @description and @instruction only (query.pest). @example and @if_commit are absent. --if-commit is a request precondition. The one-spelling section still leaves @if_commit and the request field to the writes RFC. That sentence and "the typed four work" disagree. annotation_args is still used and has no production. Schema annotations use annotation_arg then ident = literal (schema.pest). Define annotation arguments with =.

  6. Still open. The heads are unchanged: rfc: amend the shared expression model with the operator vocabulary #805 49f436ce618b still assigns (3, 0) and (4, 0); rfc: search contracts on the shared expression model (RFC 0048) #793 5e6e82e102d0 still says retrieval adds no clause; rfc: analyzed lexical search on the shared expression model #792 c5d33c2931a1 still removes search, match_text, and fuzzy at the next major; rfc: polymorphic types with interfaces, unions and polymorphic edges #884 24b60eba9e37 is still a minor whose pins (2.2, explain 4) are stale. main is (2, 3) and explain 7. The decision log does not record the grammar sentences those drafts must remove. supersedes is still empty.

Optional items from that comment:

  1. Resolved. The motivation quotes the Compatibility section of the shared-expression RFC.
  2. Still open. Code references still say main ea895066. Those four files are unchanged from that commit through main 17557824. Name the merge base when the RFC is accepted. State the bump base as (2, 3) and explain 7.
  3. Still open. The text still does not say what boot does when the IR proof refuses. A parse failure quarantines the graph today.
  4. Still open. L… still matches neither the query catalog (Q, T, P) nor schema lint (OG-…).
  5. Partly resolved. The catalogue names and the @ edge selector are now slots, and the engine-catalogue RFC's schema extension is named as their owner. The heading "Operators" still introduces the boot window.

Blocking

1. An incoming triple is legal edition-2 text, and the swap fails the equality proof

$c: Company worksAt $p typechecks today when WorksAt runs from person to company. resolve_member selects Direction::In. crates/omnigraph-gqt/cases/issue_659_compiler_scopes.gqt contains $c worksAt $p and $c (worksAt | employs) $p under edge WorksAt: Person -> Company. The same shape is test_edge_selection_resolution_and_scope_issue_659: one member is Direction::In, the other is Direction::Out.

This RFC makes that triple T5, and the fix swaps the ends. The rewriter table does not contain the swap. "Everything not in the table is unchanged text" therefore leaves the corpus clause in place. Edition 3 then rejects it. The gate that requires a green corpus cannot pass.

The swap also fails the IR proof when the rewriter does apply it. The original lowers to Direction::In with company at the source variable. The swapped text lowers to Direction::Out with person at the source variable. The pairs of nodes are the same. The typed traversal is not. Finding 2's equality check refuses the edit.

The compatibility sentence says every edition-2 text other than query-for-write, the infix string operators, and positional rrf parses unchanged. This T5 is a further break, and the list does not name it.

arrow_traversal is a separate rule. clause tries traversal and does not try arrow_traversal. A pest rule that the start rule never reaches does not recognise $p -worksAt-> $c. Put the recognizer in the clause choice, before traversal, so <knows> stays the undirected form.

<e> over an asymmetric edge is T22 today (test_undirected_traversal_rejected_on_asymmetric_edge). The freeze row says the traversal is unchanged from edition 2, and the evaluation's break table says "today's semantics" (§11.6). The prose then narrows T22 so <owns | ownedBy> resolves per member. That is a semantic change. State it as a change, and add the rewriter or the proof that keeps issue 659's rows.

Keep both written orders for an asymmetric edge. They are one meaning, and the type checker already records which orientation each member takes. The one-spelling rule still removes the arrow, the infix operators, positional k, and query around a write. A later canonical spelling can be a formatter rule once the proof compares resolved members, not raw variable order.

2. The evaluation still specifies the arrow rewrite this delta withdrew

The RFC cites the evaluation as its evidence. These passages still tell an implementer to rewrite triples into arrows, or they keep decisions the RFC has dropped:

  • §9.4's sample file uses -supports->, -about->, and -$e:supports->.
  • §9.9 still lists the arrow spelling as one of the four incompatible items, and the next sentence still rewrites $p worksAt $c to $p -worksAt-> $c.
  • §11.1's diagram still labels the break "(arrows, mutation, retrieve".
  • §11.2 stage X still contains the arrow spelling. The agent outcome is still "direction visible". The withdrawal note in parentheses does not replace that row. The same section says the stage contents stand.
  • §11.6's break paragraph still gives the rewriter $p worksAt $c → $p -worksAt-> $c.
  • §11.6's freeze list still includes contract. This RFC removed that declaration on 2026-10-29. The same row says Branch and Commit parameters bind. The RFC says they are slots. The envelope row bumps the HTTP contract from 0.14 to 0.15. The RFC says the HTTP contract does not change.

§9.3, §13.3, and §14.2 record the withdrawal. The operative rewrite, the sample, and the stage table do not. One source has to win. Make the evaluation match the RFC's freeze list, rewriter table, and compatibility list. A historical sentence can say the first draft chose the arrow. It cannot remain the instruction for queries migrate.

3. A catalogue retrieve is not a query over an ordinary table

The new property says @Type, @Property, @Edge, @Interface, @Index, @Function, and @View are bound, traversed, searched, and counted like user data. The traversal section says match { $t: @Type } retrieve { $t.lex: bm25($t.instruction, $q) } is an ordinary query over an ordinary table, and that the grammar does not distinguish it.

The accepted engine-catalogue RFC says the five catalogue types are derived at query time, never stored, and never part of schema identity. "Writes, loads and text or rank search over catalogue types are refused; catalogue access never calls Lance." The user guide repeats the refusal: catalogue rows have no index (docs/user/queries/catalogue.md). There is no @instruction column on those types. There are no catalogue edges.

Reserving the seven names and the @ edge selector as slots is coherent. Calling the search an ordinary table conflicts with that accepted contract. Rank search needs an index or a scan the catalogue does not have. Five more features of this shape would grow a second schema store beside the derived catalogue.

Say the slots parse and refuse until a catalogue extension amends the rank-search refusal. That extension names the mechanism, and it does not add a Lance dataset for rows the catalogue RFC keeps derived. bm25 stays on user tables, where the index exists.

Optional

  1. RFC 0047 reports 45 of 45 tasks correct and 8 of 45 first attempts refused, all for a missing (). It does not report a direction result. "No direction error" is an inference. CypherBench's figure in the evaluation is 9 of 55 error marks for gpt-4o, not one failure in six. Cite the 0047 sentence as written.
  2. updated is 2026-10-29. The decision log has a 2026-10-30 entry. Frontmatter is the date authority.
  3. < and > are already comparison operators (comp_op in query.pest). The ordered choice of traversal before filter already keeps $a <knows> $b a traversal. The function-registry work does not introduce that operator.

Liability

The triple decision removes a second spelling and the largest rewrite. That reduces long-term liability. The arrow recognizer is a small diagnostic, and it belongs in the existing clause choice.

The delta adds a 4,194-line evidence file. That file is justified as the record of the comparison. It adds liability while its break instructions disagree with the RFC. Two readers can implement two grammars. One file has to be the decision. The RFC is that file. The evaluation should follow it.

Unchanged liability from the earlier comment: a frozen F64? score, an equality proof that rejects every ranking query, a file rule that drops live statements, and (3, 0) shared with drafts that still schedule their own majors. The incoming-triple T5 adds a break the compatibility list hides, and the proof cannot show the swap preserves the plan. Catalogue search described as an ordinary table would duplicate the derived catalogue.

Invariant 9 is stronger when one registry replaces a grammar keyword. It is weaker while the score type and the incoming-triple plan disagree with the type checker. Invariant 8 requires the rewriter to refuse an unprovable edit. The swap is that edit. Invariant 12 and the catalogue RFC keep one derived catalogue. A searchable catalogue table would be a second source. Invariant 13 binds the grammar to the rewritten corpus. That gate fails while findings 2 and 3 from the earlier comment, and finding 1 here, stand. No deny-list item is added by a Filter or a compiler pass. No Lance API is written. The column type still has to freeze the F32 value score_type publishes.

Validation

  • Head reviewed: 7c44b23d683921d7ed84104615f3525d0d33b441. State: open, ready for review. Rechecked immediately before this comment. Reviews and inline comments: none. Issue comments: the earlier review on 6dc6315d only.
  • Related pull requests still open at the heads named above: rfc: amend the shared expression model with the operator vocabulary #805, rfc: analyzed lexical search on the shared expression model #792, rfc: search contracts on the shared expression model (RFC 0048) #793, rfc: polymorphic types with interfaces, unions and polymorphic edges #884.
  • python3 scripts/check-pr-title.py --title "rfc: the GQ 3 grammar, one break and the slots it freezes" → PASS.
  • Detached checkout of this head, after python3 -m pip install --user -r scripts/requirements-docs.txt: python3 scripts/check-docs.py → Documentation OK (436 Markdown files checked). The new asset is outside docs/rfcs/*.md, so the registry check does not treat it as an RFC. Links and the RFC row pass.
  • bash scripts/check-agents-md.sh → AGENTS.md ↔ docs indexes OK (66 links, 62 docs).
  • git diff --check 6dc6315d HEAD → exit 0.
  • typos 1.50.1 on the RFC, the evaluation asset, and .typos.toml → exit 0. EDN and FO occur in the asset as the intended tokens.
  • Code inspection at main 17557824 of query.pest, schema.pest, typecheck.rs, codes.rs, the issue 659 tests and GQT case, issue_640_search_score_projection.gqt, the engine-catalogue RFC, and Lance 13.0.0 lance-index distance tables. query.pest, schema.pest, GQ_LANGUAGE_VERSION, and EXPLAIN_VERSION have no commits between ea895066 and 17557824.

GQT regression proof does not apply. This pull request changes documentation only. It changes no query result, error, or plan.

Limits: these docs checks are local. I did not use a CI run on this commit as evidence. The path classifier's docs/*.md arm does not cover docs/rfcs/ or .typos.toml, so this pull request is outside the docs-only skip. I did not run the workspace test graph. The 37 of 45 figure is copied from RFC 0047. I did not re-run that instrument. I did not re-measure the CypherBench figure from the paper image.

How to revise

Keep the triple. Revise the incoming-triple rule so edition 2's accepted orders still typecheck, or specify a proof that does not require the swapped IR to be equal. Wire arrow_traversal into clause. Make the evaluation's sample, stage table, and rewriter paragraph match the RFC. Describe catalogue search as a slot that waits for an amendment of the rank-search refusal. The earlier open items (score type, IR proof for rankings, file, annotation arguments, and the other drafts' majors) are unchanged by this delta and still block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant