SignalQL v0.4 specification — the Operational Graph
Status: Draft with reference implementation. v0.4 adds graph-native querying and reasoning lineage on top of v0.3. The reference compiler implements the v0.4-core surfaces (graph patterns,
TRACE,CONTEXT FOR, bitemporalAS OF) as parameterized SQL plus an in-memory demo graph evaluator. Vision and rationale live in the v0.4 roadmap.
SignalQL v0.4 extends evidence retrieval into graph-native querying (patterns, reachability, paths, absence) and reasoning lineage — answering why do we believe this?, not just what happened?. The motivating consumer is an AspectGraph-style system of record for organizational reasoning, but the core stays domain-agnostic.
Preserved core principles (unchanged)
- Retrieval and shaping only; no mutation.
- No interpretation labels in core. States like
at_risk/blocked/staleare derivedsignal(...)values, never operators.WITHOUT relis structural absence, not a judgment. - Domain relationship vocabularies (
supports,depends_on,derived_from, …) live in external packs (see domain-packs.md), not core grammar. - SignalQL returns lineage; it renders no verdict on whether the reasoning was good.
Graph data model
v0.4 binds two logical sources in addition to v0.1–v0.3 sources:
- entities —
(entity_id, kind, properties, version, superseded, updated_at) - edges —
(from_entity, relationship, to_entity, properties, observed_at, version)
Provenance edges (derived_from, plus source/confidence on entities/edges) form the lineage substrate. Physical tables resolve through the source map (entities, edges keys); defaults are entities and edges. See data model.
Grammar (EBNF, additive over v0.3)
graph_query ::= "MATCH" node_pattern ( edge_pattern node_pattern )*
( "WITHOUT" identifier )* as_of_clause?
"RETURN" return_field ( "," return_field )* limit_clause?
reach_query ::= "FIND" node_pattern "CONNECTED" "TO" node_pattern
( "VIA" identifier )? depth_clause? as_of_clause?
"RETURN" return_field ( "," return_field )* limit_clause?
path_query ::= "PATH" "FROM" node_pattern "TO" node_pattern
( "VIA" identifier )? as_of_clause?
trace_query ::= "TRACE" node_pattern ( "VIA" identifier )? depth_clause? as_of_clause?
context_query ::= "CONTEXT" "FOR" node_pattern depth_clause?
( "INCLUDE" include_item ( "," include_item )* )? as_of_clause?
node_pattern ::= "entity" "(" identifier ")" ( "WHERE" predicate_expr )?
edge_pattern ::= "-[" identifier "]->" (* outgoing *)
| "<-[" identifier "]-" (* incoming *)
include_item ::= "entities" | "relationships" | "decisions" | "evidence" | "signals"
depth_clause ::= "DEPTH" "<=" integer
as_of_clause ::= "AS" "OF" timestamp_literal (* bitemporal: entity-version time *)predicate_expr, return_field, and timestamp_literal are as defined in v0.3. Node predicates resolve fields as: entity_id, kind, or a properties key (bare names and properties.x both map to properties). Supported node predicate operators: =, !=, <, >, <=, >=, IN, BETWEEN, IS [NOT] NULL, CONTAINS (compiles to a parameterized ILIKE), plus AND/OR/NOT. signal()/probability()/semantic_match() field refs in graph predicates are v0.4-extended (guarded). Aggregate/count returns inside graph queries are v0.4-extended (guarded); use FROM events … RETURN count() for aggregation.
Execution model
- Parse to AST; reject unknown constructs.
- Bind entity/edge logical sources to physical tables via the source map.
- Compile to parameterized SQL:
- MATCH → joins over the edge store;
WITHOUT rel→NOT EXISTSanti-join. - FIND … CONNECTED TO →
WITH RECURSIVEbounded traversal; returns find-nodes from which a target is reachable. - PATH → recursive path accumulation with a visited-set cycle guard.
- TRACE → recursive lineage walk over provenance edges, returning an ordered chain.
- CONTEXT FOR → recursive bounded subgraph; runtime assembles the bundle.
- MATCH → joins over the edge store;
- Enforce traversal bounds:
DEPTH≤ 16 and a visited-set cycle guard are enforced in the compiled SQL. The node budget is reported as advisory metadata (meta.graph.node_budget) for the runtime to enforce — the reference compiler does not cap row counts itself. - Caller permissions are applied by the runtime, not the reference compiler or demo evaluator (consistent with v0.2/v0.3, which also delegate permission filtering to execution).
- Return rows / lineage / bundle plus execution metadata (depth, truncation).
AS OF is bitemporal: it filters entities/edges to the version effective at the timestamp (updated_at/observed_at ≤ as-of), reconstructing the graph as it was believed then.
Examples (v0.4-core)
Absence pattern — assumptions lacking evidence (structural, not "at risk"):
MATCH entity(assumption) WITHOUT validated_by RETURN entity_idReachability:
FIND entity(decision) CONNECTED TO entity(experiment) WHERE name = "onboarding-v2" VIA supports RETURN entity_idPath:
PATH FROM entity(assumption) TO entity(release) VIA derived_fromReasoning lineage:
TRACE entity(ticket) WHERE id = "123" VIA derived_from DEPTH <= 6returns an ordered chain ticket → decision → experiment → metric → assumption → interview, each step { entity_id, kind, relationship, confidence, observed_at, depth }.
Context bundle:
CONTEXT FOR entity(roadmap) WHERE quarter = "Q3" DEPTH <= 2 INCLUDE entities, relationships, evidenceTime-travel:
TRACE entity(ticket) WHERE id = "123" VIA derived_from AS OF "2026-03-01T00:00:00Z"Result shapes
| Query | Result |
|---|---|
MATCH … RETURN … | rows of the start-node return fields |
FIND … CONNECTED TO … | rows of reachable find-node ids |
PATH … | ordered node-id sequence |
TRACE … | ordered LineageStep[] + metadata |
CONTEXT FOR … | ContextBundle { root, entities, relationships, evidence, metadata } |
Determinism
- Generated SQL is parameterized; entity kinds, relationships, and predicate values are never interpolated.
- Recursive traversals carry a visited-set/path cycle guard and a depth bound.
- Lineage steps are ordered by derivation distance.
AS OFmakes graph reads reproducible at a point in time.
Non-goals (v0.4 core)
- Interpretive state operators (
AT RISK,BLOCKED) — derive viasignal(...). - Standard relationship vocabulary — ships as
aspectgraph-work-pack, not core. - Mutation / write-back — remains out of core.
- Judging reasoning quality — SignalQL returns the chain; the AI layer evaluates it.
Residual limitations
- MATCH edges are single-hop; use
DEPTHon reachability/trace/context for multi-hop. CONTEXT FORcompiles to the entity-set walk; full bundle assembly (relationships, evidence sections) is performed by the runtime/demo evaluator.signal()/probability()/semantic_match()in graph predicates are v0.4-extended.- The demo evaluator coerces JSON values best-effort and targets fixtures, not warehouses.
Normative references
- Extends: v0.3 spec, v0.2 spec
- Vision: v0.4 roadmap
- Relationship vocab: domain-packs.md
- Version index & support matrix: spec versions