Repository navigation
Conversation
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.
…-evaluation-5a029b # Conflicts: # docs/rfcs/README.md
…-evaluation-5a029b # Conflicts: # docs/rfcs/README.md
|
Recommendation: revise. This review covers head WhyEach GQ feature has reserved a word, added a call, or advanced an explain version. Three open drafts still schedule a later major. #805 schedules What this RFC decidesEdition 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. The latest commit keeps one directed spelling, Claims that match the codeChecked at the RFC pin
Blocking1. The retrieve column records the wrong type, and a score
|
…hema queryable at any size
…e-spelling rule removes
|
Recommendation: revise. This review covers head WhyEach 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 changesThe triple stays. 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
Optional items from that comment:
Blocking1. An incoming triple is legal edition-2 text, and the swap fails the equality proof
This RFC makes that triple The swap also fails the IR proof when the rewriter does apply it. The original lowers to The compatibility sentence says every edition-2 text other than
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 2. The evaluation still specifies the arrow rewrite this delta withdrewThe 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.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 3. A catalogue
|
Throwaway prototype of the GQ 3 grammar: an edition-3 front end selected by `gq 3;`, every freeze-list row and slot, a function registry, `retrieve`, `having` and `limit $k` through the planner onto Lance, the edition-2 rewriter with its IR-equality proof, and tests that stress the one-break claim against the corpus, generated queries and the open RFCs' forms. Findings: docs/spikes/gq3-1202.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spike report: the GQ 3 grammar (this RFC at
|
| Frozen decision or claim | Result | Evidence |
|---|---|---|
| One incompatible change, then only additive ones | Holds with a change (A3, A4, and the score contracts A1/A2) | later_tests.rs: of 60 later forms, 0 change the meaning of an accepted well-typed query. #793's refusals and #792's removals are the remaining scheduled breaks (rfc_0048_refusals_against_edition_3: all three accepted). |
| Every later capability has a reserved slot | Does not hold (wording) | 24 of 60 need a new production (later.tsv: pattern-scoped update/delete, invalidate, unions, is T, Person($id), $x: *, qualified edges, use, namespace, arithmetic, k 200, rerank stage, partial objects, limit 3 per, at $commit, nulls last, offset, multivector). Each starts at a token edition 3 refuses (Q001), so each is additive. One ($p: Person at date(…)) already parses as a binding plus two bare filters and is refused only by the type checker. Arithmetic $x * $y in match meets the wildcard traversal $a * $b and needs a lookahead (arithmetic_meets_the_wildcard_traversal). |
mutation for writes; query with a write body refused with the fix |
Holds | mutation_keyword_and_the_wrong_keyword_refusal; engine mutation_keyword_writes_and_query_keyword_refuses_a_write (T62 with a machine-applicable edit). |
Triple traversal unchanged; <e> for either orientation |
Holds with a change (G3, A5) | the_triple_is_unchanged_and_arrows_print_the_triple; the RFC rule refuses <a | b> (rfc_undirected_rule_drops_the_unparenthesised_alternation). <owns | ownedBy> is still T22; the RFC's T22 narrowing is a semantic change I did not implement. |
| Arrow forms are parse errors whose fix prints the triple | Holds (with G8) | Q008 for -e->, <-e-, -e-, with binding and bounds; the fix is computed without the schema and preserves meaning. |
A triple written against the schema is T5 with the swapped fix |
Holds with a change (A5) | an_incoming_triple_is_refused_in_edition_3. Corpus: 4 edition-2 declarations become refusals. The swap keeps typed IR equal (swapping_an_incoming_triple_keeps_the_typed_ir, with a same-type negative control showing the comparison detects a real swap) and rows equal on Lance (an_incoming_triple_and_its_swap_return_the_same_rows). |
retrieve { } binds typed score columns |
Holds with a change (A1, A2, G7) | Engine retrieve_matches_the_edition_2_ranking_on_lance_indexes: nearest, bm25, rrf(k: 60), a match-scoped and a traversal-scoped ranking return the same rows as edition 2. arm_columns_keep_todays_score_types_and_names: non-null Float32, named d._distance/d._score, not F64?. Under rrf an arm is not a value (T33). |
order sorts values; leads with an arm or rrf over arms |
Holds | T65 for a ranking written in order or an arm order does not read; T54 and T17 unchanged. |
having filters values |
Holds with a change (A2) | having_filters_groups_and_scores: groups by alias or aggregate; a score threshold returns the top limit passing rows through the existing ladder (a lower bound > 0.01 returns d2, d3, d5, not the 2 a post-window filter would give). Group key beside an aggregate refused (T58 inside T67). Explain: Filter above Sort, below Limit (explain_*.json). |
| Functions are generic calls over one registry | Holds with a change (G1, G4, G5, A4) | calls_resolve_against_the_registry, a_new_function_is_a_registry_row_not_a_grammar_rule (a row added in a test resolves, no grammar change); fuzzy's third positional must become max_edits:, a row the rewriter table lacks. |
| Reserved set closed at the six logic words | Holds | every_contextual_keyword_stays_an_identifier: 54 keywords legal as a property after a dot, an edge name, a return alias and a bare mutation property; the six refused as aliases. |
limit $k |
Holds | limit_binds_a_parameter (k = 1, 2, 4 on Lance; negative refused at bind); explain shows Nearest k: 2 from $k. Plan acceptance needed a change (it compares the written limit). |
null only as an assignment value |
Holds with a change | null_is_legal_only_as_an_assignment_value. The RFC also admits null in prop_match, which is the read binding ($p: Person { age: null }), contradicting "no literal in a read"; the spike refuses it. |
Generic @name(args) annotations; @description, @instruction, @example, @if_commit typed and working |
Holds with a change (G6) | annotations_are_generic. @example and @if_commit have no mechanism in the compiler or engine; the spike makes them slots. |
Reserved declaration kinds view, batch |
Holds | every_slot_parses_and_refuses_with_its_owner (T60, owner in fix). |
optional { }, block aggregates as values |
Holds | Same test. |
View binding $x: Name($arg) |
Holds with a change (G2) | rfc_binding_rule_swallows_a_parenthesised_filter. |
returning |
Holds as a slot | Same test. |
Meta-fields @type_id, @distance, @score |
Holds for @type_id; with a change for the other two (G7) |
Same test. |
Catalogue names and the @ edge selector |
Holds as slots, with G8 | The review is right that retrieve over @Type contradicts the accepted catalogue RFC; the spike refuses it as a slot. |
Namespaced type references a::b::C |
Holds for types | #1015 also needs qualified edge names, use and namespace blocks (new productions). RFC type_ref admits crm::@Operator, which #1015 makes a syntax error. |
Schema roles Vector(n, distance=…), @analyzed, @valid |
Holds with a change (G6) | later_schema_forms_against_the_one_grammar: all three parse with the spike's annotation_args; none parses with today's schema.pest rules. |
| One grammar for schema and queries | Holds | 432 GQT schemas and the 5 repository .pg files parse under the one file rule. The RFC does not say what a query source containing a schema declaration means; the spike refuses it as a slot. |
file keeps today's statements |
Holds with a change (review finding 4) | The spike keeps show/branch/commit/load/explain, settings and missing_param_list (Q002 with the same fix). |
Optional gq 3; edition line |
Holds | edition_line_selects_the_grammar. |
| Rewriter: byte-range edits, IR-equality proof, idempotent | Holds with a change: the arm reference must resolve to its call before IR, the table needs the fuzzy and swap rows, and the gate must allow expected-code updates |
Corpus (2,609 sources: GQT query/mutate sections, user/dev guide and skill code blocks, .gq files): 1,818 declarations IR-equal, 0 differing, 611 statements unchanged, 43 doc blocks parse-only (no schema), 2 renamed result columns. Of 132 expect-error steps, 129 keep their code and 3 change (T33→T65 twice, T32→T65), so "zero hand edits" fails on expectations unless the gate allows them. Fuzz: 2,968 generated reads IR-equal after migration, all idempotent. |
Plan acceptance gains the arm and having |
Holds, and it is required work | Without it the validator refuses having (predicate origin, order, row cut) and limit $k (lowering). RFC 0047's exact fragment, a closed and versioned contract, also classifies a having/limit $k read as a member and then finds no derivation (the server test hit this); the spike excludes both forms from the fragment. Its version must move, or the fragment must exclude them, in edition 3. The spike's exemptions are stubs. |
| Planner maps onto Lance, filters and limits pushed down | Holds | explain_retrieve_having.json: topic = "db" in the Lance scan filter, Nearest k: 2 bound from $k, the scan and search fused physically, having as a typed Filter with the threshold cast to F32. |
| HTTP contract unchanged | Holds for reads | spike_gq3_edition_3_stored_read_boots_and_serves: an edition-3 stored read with retrieve, having and limit $k boots and returns today's envelope, including ranking.score_columns. The CLI runs edition-3 text unchanged and prints the new diagnostics with code, position and fix (target/spike-gq3/cli_demo.txt). |
| Parse and type-check cost | Not a design concern | Parse, check and lower over 1,808 sources × 20 rounds (debug build): edition 2 5.95 s, edition 3 8.11 s (1.36×). The spike type-checks having twice and re-parses settings; this is not a measure of an optimised implementation. |
Lance primitive table
Lance 13.0.0 (all lance* crates in Cargo.lock), DataFusion 54.0.0; paths under lance-13.0.0/src/ unless noted. Exposed: the frozen grammar reaches it. Row/slot: a later release reaches it with a registry row, call option, slot or annotation argument. Plan: a planner change behind existing syntax. No slot: a new (additive) production.
| Primitive | Engine uses it | GQ 3 | How |
|---|---|---|---|
Scanner::filter/filter_expr (scalar-index routing) dataset/scanner.rs:1705,1776 |
yes | exposed | match filters, binding matches |
Scanner::project :1564 |
yes | exposed | return |
Scanner::limit(limit, offset) :1956 |
internal | exposed (limit n, limit $k); offset: no slot |
offset is refused by design |
Scanner::nearest :1990 |
yes | exposed | nearest arm; k = limit, widened by the ladder |
Scanner::prefilter :1622 |
yes | plan | eligibility placement |
Scanner::distance_range :2117 |
no | plan | having over a lone nearest arm already has its semantics (A2); a pushdown would remove the ladder's re-search |
nprobes/minimum_nprobes/maximum_nprobes :2133-2182 |
max only (set ann_nprobes) |
row | options on nearest |
ef, refine, fast_search, distance_metric, approx_mode :2191-2275 |
no | row | options |
use_index(false) :2263 |
yes (ladder) | row | exact: true |
full_text_search (MatchQuery, fuzzy) :1746; lance-index scalar.rs:116,127 |
yes | exposed | search, match_text, fuzzy(…, max_edits:), bm25 arm |
FTS BooleanQuery should/must_not |
must only | row | query-valued functions; needs a query value type in the registry |
PhraseQuery/slop, BoostQuery, MultiMatchQuery/BM25F, Operator::And, prefix length, max expansions, wand_factor |
no | row | phrase(…, slop:), options, variadic rows |
BM25 K1/B lance-index scalar/inverted/scorer.rs:73-74 |
constants | n/a | not configurable in Lance |
nearest and FTS in one scan |
refused by Lance :3354 |
exposed as rrf |
fused in the engine |
minhash_search :1757 |
no | row + annotation | an arm plus an index argument |
Scanner::aggregate :1785 |
no | plan | behind today's aggregates |
count_rows(filter) dataset.rs:1705 |
unfiltered only | plan | behind count |
Scanner::order_by with nulls placement :2241 |
internal | exposed (asc/desc); nulls: no slot |
nulls last is a new production |
take_rows/take_blobs/read_blobs |
yes | n/a | Blob API |
| BTree | yes | exposed | @index, @key |
Bitmap, LabelList (array_has_any/all), NGram, ZoneMap, Bloom, FM index |
no | annotation + row | @index(kind="…") parses only with G6's annotation_args (today's .pg constraint rule refuses it); has_any, regex_match are rows |
Inverted index parameters (tokenizer, stem, stop words, positions, folding) scalar/inverted/tokenizer.rs:105 |
default() |
slot (@analyzed) |
needs G6; list-valued options need lists in annotation arguments; positions change the RFC 0043 certificate |
RTree/st_*, JSON index |
no (feature off) | no slot | new base_type productions |
| IVF_FLAT | yes (L2) | exposed | @index on Vector(n) |
| IVF_PQ/RQ/SQ, IVF_HNSW_* | no | annotation | @index(kind=…, m=…) with G6 |
DistanceType L2/Cosine/Dot/Hamming lance-linalg distance.rs:392 |
L2 | slot for L2/Cosine/Dot (Vector(n, distance=…)); Hamming: no slot |
needs a binary vector type |
| Multivector | no | no slot | [Vector(n)] is a parse error |
merge_insert UpdateAll/Fail/InsertAll dataset/write/merge_insert.rs |
yes | exposed | keyed insert, update, load mode merge |
WhenMatched::DoNothing/UpdateIf/Delete, WhenNotMatchedBySource, dedupe FirstSeen |
no | no slot | insert-if-absent, conditional upsert, sync loads need a statement modifier or load mode; @on_conflict(…) fits per declaration only |
UpdateBuilder::set(col, sql) dataset/write/update.rs:151 |
no | no slot | computed assignment needs arithmetic and a relaxed T45 |
DeleteBuilder, InsertBuilder |
yes | exposed | delete … where, load modes |
add/alter/drop_columns |
no (own Project/Merge) | n/a | schema apply |
checkout_version |
yes (pins) | slot (Commit parameter); at $c clause: no slot |
request --snapshot today |
| Branches | __manifest |
exposed | branch statements |
Tags, restore, compaction, optimize_indices |
engine/CLI | no slot | new statement keywords (additive); Restore and cleanup_old_versions are deny-listed for graph tables |
index_statistics/describe_indices |
partly | slot (@Index) |
catalogue |
delta() |
no (own feed) | exposed | branch diff |
Dataset::sql, LanceTableProvider |
no | n/a | string-built semantics are deny-listed |
What I could not verify
- The competence instrument (the RFC's first gate). Its task set is outside the repository; I did not run it. The parser it needs is on the branch:
gq 3;sources parse and refuse slots. - The edition-2 boot rewrite,
cluster planoutput, the release-after refusal, envelope fields, explain/bound-plan version bumps, named score columns in explain, and parameter defaults. None was built. - The fleet's stored queries. The corpus is the repository: GQT cases, the guides, the skill and
.gqfixtures. 43 doc blocks have no schema, so they were checked for parse only. - Each of the 24 new productions. I showed each form is refused today, so adding it at its first novel token is additive. I did not implement them. Arithmetic needs a lookahead against
$a * $b. - The RFC's T22 narrowing (
<owns | ownedBy>per member). I did not implement it. - ANN behaviour of
havingunder a real IVF index at scale. The Lance test graph is 6 rows, where the ladder's search is exact. - S3 and Azure, and release-build timing. Everything ran on local files and a debug build.
- Workspace-wide test suites. I ran the spike tests plus the full compiler and planner suites (green). I did not run the canonical workspace test graph, GQT, clippy or DST. The
QueryIR/QueryDeclfield additions compile across the workspace.
Branch
Prototype, tests and evidence: spike/gq3-1202 (commit f63e4393, from main f728f01e). The repository's fork-only rule refused a branch on ModernRelay/omnigraph, so it is on the fork. The report is docs/spikes/gq3-1202.md. The run's artifacts are in docs/spikes/gq3-1202/. The code is throwaway; the edition-3 front end is crates/omnigraph-compiler/src/gq3/. Its "What was built" section lists what is real and what is a stub.
…-evaluation-5a029b # Conflicts: # docs/rfcs/README.md
…ore columns, having semantics, both triple orders, registry rows kept, grammar fixes
|
Recommendation: revise. This review covers head WhyOne grammar break is cheaper than the majors the open drafts still schedule, when the frozen forms match the engine that already exists. This delta takes the contracts the last review and the spike said were wrong, and it writes them into the freeze list. What this delta changesThe score column is the non-null How the text is organizedThe RFC is the decision. The evaluation is the record of the comparison. The spike is the trial implementation. A slot parses and refuses with the owner RFC in ImpactSeven of the eight blocking findings from the review on Earlier findingsBlocking items the review on
Optional items from that review:
The compatibility citation from the review on Blocking1. The fused score has no legal spelling, and the rewriter table drops live projections
The table does rewrite
The sentence after the table says everything not in the table is unchanged text. Unchanged ranking calls are 2.
|
… read, except under rrf
|
Recommendation: revise. This review covers head WhyA score has to stay on its binding after the ranked scan. What this commit changes
How the sentence fitsThe sentence is in "The retrieve clause in this release". Clause order stays ImpactThis commit resolves none of the six findings from the review on Earlier findingsBlocking findings that stay open:
Optional findings that stay open:
BlockingNo new blocking defect. OptionalNo new optional note. Tradeoffs and liabilityThe non- The Five more sentences of this shape stay cheap when each one names that same column. The open liability is the rewriter table, the two widths of A filter in Checked against the codeThis head contains
PR 1213's stage text agrees with the Validation and limitations
Limits: I did not run |
|
Revised at Blocking findings from the first review
Optional items: the shared-expression quote, bump base Blocking findings from the second review
Spike amendmentsA1 to A5 as above ( One choice to flag: the lexical rows stay rather than being removed in edition 3, because |
|
Added "The syntax by example" (b48c9a5): one schema, then one worked example per form of edition 3 (file and schema, bindings and every filter operator, every traversal spelling, blocks, return and aggregates, |
|
Recommendation: revise. This review covers head WhyThe new section is the text a later parser test will paste. Several of those pastes do not parse, or they do not typecheck, under the rules this RFC already states. The six findings from the review of What this commit changes
How the section is organizedThe introduction splits forms into works, slot, and refused. The formatter table is a fourth class. Two of its rows wait on later RFCs ( ImpactThe six earlier findings stay open. Six new defects are in the examples. They block the use of those examples as the parser-test fixtures the Evidence section names. The bare-node sentence matches the compiler. A large part of the traversal example typechecks once two lines are removed. Earlier findingsBlocking findings that stay open:
Optional findings that stay open:
Blocking
Optional
What holdsThese checks passed on the current compiler. The compiler files named below are identical at
Tradeoffs and liabilityWorked examples are the right addition. A reader can see one schema and one form. The Evidence section already asks for one parser test per freeze-list row. The examples can be those tests. The cost is a third inventory. The freeze list, the grammar sketch, and this section can drift. This commit is the drift. The introduction makes the examples the grammar. Five more sections of this shape would leave the freeze list as the text nobody pastes, and the examples as the text the parser is held to. One list should own the forms. The examples should illustrate that list, and each example should parse under it. The older liability is unchanged. The rewriter table, the two widths of The non- Keeping the triple, and refusing catalogue rank search, remain the right cuts. They are not changed here. PR 792, PR 793, PR 805, and PR 884 are still open at Validation and limitations
This pull request changes documentation only. It changes no query result, error, or plan. A GQT regression does not apply. The probe is the check that the worked examples match today's grammar where the RFC says the form is unchanged, and match the RFC's own rules where the form is new. Limits: I did not re-run |
Why
GQ has grown one capability at a time: a logic word reserved when a filter needed it, ranking placed in
orderbecause 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 asdocs/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:
mutationfor 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; aretrieve { }clause binding typed score columns, withordersorting values andhavingfiltering 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 referencesa::b::C, and the schema rolesVector(n, distance=…),@analyzed,@valid; an optionalgq 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)Checks
python3 scripts/check-docs.py(425 files OK) andtypospass.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- $ais 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 isT5with a machine-applicable swapped clause, the deterministic correction the Cypher ecosystem bolts on after generation (LangChain'sCypherQueryCorrector; 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), andT22narrows 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-valuedgivenparameters 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 namedpropertyorfrom; 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 narrowedT22, a triple written against the schema isT5with 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.