You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
id: design: ID query language/shape v1 (from #5675) #5816
Follow-on design ticket from #5675 — its decomposition sketch names this as a separate design, and the parent's acceptance requires these tickets to be filed and linked. Filed in the 2026-11-04 gap pass (WS-F) so the design work exists and can be prioritised on its own merits; implementation timing is a separate decision, possibly months out.
What this design must settle
The query language and shape for v1, with the local + remote execution model and the local/remote parity rule: same query, same meaning, wherever it runs.
Context from the parent
The parent design fixes the filtering model, the owner-override boundary, surface parity, and pagination/result rules for querying local and remote ID servers. The parent issue carries the surrounding decisions this ticket must not re-decide; where this ticket's answer would change a parent decision, record that explicitly rather than silently forking it.
Decision record expected
Close this design with a recorded decision (on this issue or in a doc/ record linked from it): the chosen shape, the alternatives rejected and why, and the invariants implementation must hold. If the design splits further, file the splits and link them here rather than growing this issue.
Relations
Parent: #5675. Programme: epic #2056. Filed by WS-F (2026-11-04 ID design gap & expansion pass).
Triage outcome — task 10219 (2026-11-05, lane code-p)
Verdict: CONFIRMED as a valid standalone design ticket — parent bounds verified, no design record exists yet, design work routed. No blocker; no Drew decision needed to start the design.
Fresh verification (2026-11-05): issue #5816 OPEN, labels task/triage + queue/queued + app/id — no hard-stop labels. Parent #5675 (id: design: Querying local and remote ID servers v1) is OPEN and was itself triaged today (task 10108, lane code-s): its body fixes the filtering model, the owner-override boundary, surface parity, and pagination/result rules, and the WS-F gap pass filed all seven decomposition children — #5816 (this ticket, query language/shape), #5817 (classification model), #5818 (filtered remote execution), #5819 (owner override mechanics), #5820 (surfaces parity), #5821 (result shaping & pagination), #5822 (mutating separation) — all re-verified OPEN today. Programme epic #2056 is OPEN.
Nothing pre-empts this design: no doc/ record covers the ID query language (the doc/ id records cover packaging, sync, crate split, concurrent sessions — not query shape), and search returns only the #5675 family. pkgs/id has an existing commands/find.rs CLI surface but no designed query language — the design must treat find as an existing surface to subsume or align with, not a greenfield.
Scope discipline for the design (from the ticket + parent):
Settle only the v1 query language/shape: table selection, filters, projections, ordering, joins or explicitly no joins — plus the local + remote execution model and the parity invariant (same query, same meaning, wherever it runs).
Do NOT re-decide parent-fixed items (filtering model, owner-override boundary, surface parity, pagination/result rules). Where this design's answer would change a parent decision, record that explicitly on id: design: Querying local and remote ID servers v1 #5675 rather than silently forking it.
No dependency blocks starting: the shape does not need id: design: sensitivity classification model (from #5675) #5817's classification levels (the parent fixes how filtering composes with queries); the siblings proceed in parallel. If the design splits further, file the splits and link them here.
Deliverable is this ticket's own decision record: chosen shape, alternatives rejected and why, and the invariants implementation must hold — on this issue or in a doc/ record linked from it. Implementation timing is a separate later decision (the ticket says possibly months out); the follow-up task is the DESIGN only.
Routing: follow-up design task enqueued.
agent-note:v1
{"kind": "triage", "task": 10219, "issue": 5816, "state": "done", "outcome": "confirmed_design_ticket_routed", "evidence": "parent #5675 triaged today (task 10108), all 7 decomposition children OPEN; no doc/ query-language record; pkgs/id has find.rs surface but no designed query language"}
Decision record — ID query language / shape v1 (issue #5816)
Decision
v1 queries are a structured query object — data, not a text language. One canonical object is the query on every surface: the human CLI builds it from flags, the scripting/API surface passes it as JSON, agents pass the same JSON. There is no v1 query string to parse, inject into, or dialect-drift.
A v1 query object has exactly these fields:
Field
v1 shape
from
One table/collection name. Single-table only.
where
A conjunction (AND) of predicates. Each predicate is field op literal — no expressions, no functions, no subqueries, no OR groups in v1.
select
A list of field names, or *. Plus a count mode returning only the cardinality.
order
A list of (field, asc|desc); the executor appends the table's primary ID as final tiebreak, always.
limit + cursor
Page size (server-capped) and an opaque continuation cursor. No offset pagination.
Operators in v1: eq, ne, lt, le, gt, ge (ordered scalar fields), prefix, contains (text fields), in (literal set membership). That set is deliberately closed: an operator not on this list is a validation error, not a fallback.
Joins: explicitly NO joins in v1. Cross-table questions are answered by composing single-table queries (client-side, or by following ID references between results). This is a v1 scope cut, not a claim that joins are never needed; a join design, if wanted, is a follow-on ticket against this record, not a silent extension of the predicate grammar.
Read-only by construction. The shape has no verb that can mutate; there is nothing to "accidentally" write with. (Whether any mutating surface exists at all is issue #5822's decision — this record neither creates one nor pre-empts it.)
Execution model and the parity invariant
The query object is executed by the server that holds the data: in-process/embedded for a local target, sent over ID transport for a remote target (the existing MetaRequest pattern in pkgs/id is the transport precedent). Never "dump the table and filter client-side."
Parity invariant: the same query object, against the same data, produces the same validation result, the same predicate semantics, the same ordering (including the ID tiebreak), and the same error codes on local and remote executors. The ONLY permitted differences are the ones parent issue id: design: Querying local and remote ID servers v1 #5675 already fixes: the target, and the filtering outcome under the requester's clearance / owner override. Any other local-vs-remote behavioural difference is a bug against this record.
The executing server validates before executing: unknown table, unknown field, operator/field-type mismatch, and over-cap limit are structured errors with stable codes, identical on both executors.
Oracle for parity (testing doctrine): the reference semantics are this record's predicate/ordering rules; conformance is a shared query corpus executed against BOTH the local embedded executor and the remote executor, asserting identical results and error codes modulo the parent-fixed filtering differences. A parity claim without that corpus run is unverified.
Relation to the current surface (pkgs/id/src/commands/find.rs, current main)
The existing surface is blob pattern search, not table querying: find/search match name-or-hash patterns (exact → prefix → contains), with --first N / --last N / --count / --exclude PATTERN, formats tag/group/union, local or remote via --node. The v1 shape subsumes it as a special case, and the existing flags become sugar that compiles to a v1 query object:
pattern match over the blobs table → where on name/hash with eq/prefix/contains (the existing match-kind priority order is preserved as the sugar's expansion order);
--first N / --last N → limit + order (last = reversed order, same limit);
--exclude PATTERN → negated contains predicates (v1 expresses this as ne-style exclusion predicates in the sugar expansion; the core grammar stays conjunctive);
--count → count mode.
show/view/peek/get are point reads by ID, not queries, and stay outside this shape. Nothing in the current CLI is removed by this record; implementation maps it onto the shape.
What this record does NOT decide (parent-fixed or sibling-owned)
Cross-checked against parent issue #5675's body — no parent decision is re-decided here, so no conflict note on issue #5675 is required:
Grants-vs-classification composition (parent open question) stays open on issue id: design: Querying local and remote ID servers v1 #5675; the v1 shape assumes deny-wins composition only as an execution input, and records that assumption here rather than deciding it.
Alternatives rejected
SQL subset as the v1 shape (parent open question): rejected as the primary shape — a text grammar invites injection-shaped mistakes, per-server dialect drift that breaks parity, and cost control that fights the parser. Recorded instead: a SQL front-end MAY later compile to this object; the object is the semantics, SQL would be sugar.
GraphQL-style query documents: rejected — resolver/schema machinery and unbounded nesting are a DoS and complexity surface a remote executor must not carry in v1; the flat predicate list covers the v1 use cases.
General expression language / arbitrary predicates (incl. regex, functions): rejected — unpriceable cost, un-auditable remotely, and unnecessary for v1's table lookups.
Status quo only (keep find pattern search as "the query language"): rejected — it cannot express field predicates, projections, or deterministic ordering over tables, which is the gap parent issue id: design: Querying local and remote ID servers v1 #5675 exists to close.
Client-side filtering over replicated data as the remote model: rejected — contradicts parent issue id: design: Querying local and remote ID servers v1 #5675's server-side filtering and leaks exactly what classification exists to withhold.
Invariants implementation must hold
One canonical query object; every surface (CLI flags, API, agent) compiles to it — no surface has a private query semantics.
Parity: identical validation, semantics, ordering + ID tiebreak, and error codes, local and remote; differences ONLY in target and parent-fixed filtering outcome.
Single-table, conjunctive field predicates from the closed operator set; no joins, expressions, functions, or subqueries in v1.
Read-only by construction — the shape cannot mutate.
Execution is server-side, always; filtering under the requester's clearance happens on the executing server before results leave it.
Deterministic ordering: stated order fields plus the mandatory primary-ID tiebreak; cursor continuation, never offsets.
Structured, stable error codes for validation and cost refusals, identical across executors.
The find/search sugar expansion above is the only compatibility promise for the legacy flags; new capability is added to the object, not to new flag dialects.
Activity
developing-today commentedon Nov 5, 2026
CODE-1499
dezren39 commentedon Nov 5, 2026
Triage outcome — task 10219 (2026-11-05, lane code-p)
Verdict: CONFIRMED as a valid standalone design ticket — parent bounds verified, no design record exists yet, design work routed. No blocker; no Drew decision needed to start the design.
Fresh verification (2026-11-05): issue #5816 OPEN, labels task/triage + queue/queued + app/id — no hard-stop labels. Parent #5675 (id: design: Querying local and remote ID servers v1) is OPEN and was itself triaged today (task 10108, lane code-s): its body fixes the filtering model, the owner-override boundary, surface parity, and pagination/result rules, and the WS-F gap pass filed all seven decomposition children — #5816 (this ticket, query language/shape), #5817 (classification model), #5818 (filtered remote execution), #5819 (owner override mechanics), #5820 (surfaces parity), #5821 (result shaping & pagination), #5822 (mutating separation) — all re-verified OPEN today. Programme epic #2056 is OPEN.
Nothing pre-empts this design: no
doc/record covers the ID query language (the doc/ id records cover packaging, sync, crate split, concurrent sessions — not query shape), and search returns only the #5675 family.pkgs/idhas an existingcommands/find.rsCLI surface but no designed query language — the design must treatfindas an existing surface to subsume or align with, not a greenfield.Scope discipline for the design (from the ticket + parent):
doc/record linked from it. Implementation timing is a separate later decision (the ticket says possibly months out); the follow-up task is the DESIGN only.Routing: follow-up design task enqueued.
agent-note:v1
{"kind": "triage", "task": 10219, "issue": 5816, "state": "done", "outcome": "confirmed_design_ticket_routed", "evidence": "parent #5675 triaged today (task 10108), all 7 decomposition children OPEN; no doc/ query-language record; pkgs/id has find.rs surface but no designed query language"}
dezren39 commentedon Nov 5, 2026
🤖 Agent note · picked up
pkgs/id/src/commands/find.rssurface.dezren39 commentedon Nov 5, 2026
🤖 Agent note · done
Decision record — ID query language / shape v1 (issue #5816)
Decision
v1 queries are a structured query object — data, not a text language. One canonical object is the query on every surface: the human CLI builds it from flags, the scripting/API surface passes it as JSON, agents pass the same JSON. There is no v1 query string to parse, inject into, or dialect-drift.
A v1 query object has exactly these fields:
fromwherefield op literal— no expressions, no functions, no subqueries, no OR groups in v1.select*. Plus acountmode returning only the cardinality.order(field, asc|desc); the executor appends the table's primary ID as final tiebreak, always.limit+cursorOperators in v1:
eq,ne,lt,le,gt,ge(ordered scalar fields),prefix,contains(text fields),in(literal set membership). That set is deliberately closed: an operator not on this list is a validation error, not a fallback.Joins: explicitly NO joins in v1. Cross-table questions are answered by composing single-table queries (client-side, or by following ID references between results). This is a v1 scope cut, not a claim that joins are never needed; a join design, if wanted, is a follow-on ticket against this record, not a silent extension of the predicate grammar.
Read-only by construction. The shape has no verb that can mutate; there is nothing to "accidentally" write with. (Whether any mutating surface exists at all is issue #5822's decision — this record neither creates one nor pre-empts it.)
Execution model and the parity invariant
MetaRequestpattern inpkgs/idis the transport precedent). Never "dump the table and filter client-side."limitare structured errors with stable codes, identical on both executors.limit/cursorso id: design: result shaping & cursor pagination (from #5675) #5821 has what it needs).Oracle for parity (testing doctrine): the reference semantics are this record's predicate/ordering rules; conformance is a shared query corpus executed against BOTH the local embedded executor and the remote executor, asserting identical results and error codes modulo the parent-fixed filtering differences. A parity claim without that corpus run is unverified.
Relation to the current surface (
pkgs/id/src/commands/find.rs, current main)The existing surface is blob pattern search, not table querying:
find/searchmatch name-or-hash patterns (exact → prefix → contains), with--first N/--last N/--count/--exclude PATTERN, formatstag/group/union, local or remote via--node. The v1 shape subsumes it as a special case, and the existing flags become sugar that compiles to a v1 query object:whereon name/hash witheq/prefix/contains(the existing match-kind priority order is preserved as the sugar's expansion order);--first N/--last N→limit+order(last = reversed order, same limit);--exclude PATTERN→ negatedcontainspredicates (v1 expresses this asne-style exclusion predicates in the sugar expansion; the core grammar stays conjunctive);--count→countmode.show/view/peek/getare point reads by ID, not queries, and stay outside this shape. Nothing in the current CLI is removed by this record; implementation maps it onto the shape.What this record does NOT decide (parent-fixed or sibling-owned)
Cross-checked against parent issue #5675's body — no parent decision is re-decided here, so no conflict note on issue #5675 is required:
Alternatives rejected
findpattern search as "the query language"): rejected — it cannot express field predicates, projections, or deterministic ordering over tables, which is the gap parent issue id: design: Querying local and remote ID servers v1 #5675 exists to close.Invariants implementation must hold
orderfields plus the mandatory primary-ID tiebreak; cursor continuation, never offsets.find/searchsugar expansion above is the only compatibility promise for the legacy flags; new capability is added to the object, not to new flag dialects.