Skip to content

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, bitemporal AS 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/stale are derived signal(...) values, never operators. WITHOUT rel is 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)

ebnf
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

  1. Parse to AST; reject unknown constructs.
  2. Bind entity/edge logical sources to physical tables via the source map.
  3. Compile to parameterized SQL:
    • MATCH → joins over the edge store; WITHOUT relNOT EXISTS anti-join.
    • FIND … CONNECTED TOWITH RECURSIVE bounded 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.
  4. 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.
  5. 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).
  6. 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"):

signalql
MATCH entity(assumption) WITHOUT validated_by RETURN entity_id

Reachability:

signalql
FIND entity(decision) CONNECTED TO entity(experiment) WHERE name = "onboarding-v2" VIA supports RETURN entity_id

Path:

signalql
PATH FROM entity(assumption) TO entity(release) VIA derived_from

Reasoning lineage:

signalql
TRACE entity(ticket) WHERE id = "123" VIA derived_from DEPTH <= 6

returns an ordered chain ticket → decision → experiment → metric → assumption → interview, each step { entity_id, kind, relationship, confidence, observed_at, depth }.

Context bundle:

signalql
CONTEXT FOR entity(roadmap) WHERE quarter = "Q3" DEPTH <= 2 INCLUDE entities, relationships, evidence

Time-travel:

signalql
TRACE entity(ticket) WHERE id = "123" VIA derived_from AS OF "2026-03-01T00:00:00Z"

Result shapes

QueryResult
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 OF makes graph reads reproducible at a point in time.

Non-goals (v0.4 core)

  • Interpretive state operators (AT RISK, BLOCKED) — derive via signal(...).
  • 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 DEPTH on reachability/trace/context for multi-hop.
  • CONTEXT FOR compiles 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