SignalQL v0.4 roadmap — the Operational Graph (draft)
Status: Roadmap / not specified. This document captures direction for a v0.4 release, not ratified grammar. Nothing here is implemented. It exists to frame the next layer — graph-native querying and reasoning lineage — and to keep that layer faithful to SignalQL's core principles. The motivating consumer is an AspectGraph-style system of record for organizational reasoning, but every construct below stays domain-agnostic; product vocabularies live in domain packs.
Premise
v0.1–v0.3 made SignalQL a credible evidence-retrieval and shaping language: events, entities, relationships, signals, probabilities, provenance, versioning, aggregation, sufficient statistics, governed metrics, catalog introspection, and a typed result envelope. The events/aggregation path executes as direct Postgres SQL; the entity/graph path parses and compiles to execution plans.
The next layer is not more aggregation. It is graph-native querying and, above it, reasoning lineage — the ability to answer why do we believe this?, not just what happened?. That is the layer AI systems actually need, and the one with the most strategic upside.
Already shipped in v0.3 (not v0.4 work)
To avoid re-proposing what exists, the following are done and only need incremental extension, not new foundations:
- Governed metrics —
FROM metric(name)binding with versioned definitions (name, version, source, measure, owner, unit, dimensions). v0.4 extension: treat a metric as a queryable entity with history (DESCRIBE metric(...)). - Catalog introspection —
DESCRIBE entities/events/signals/metrics/relationships. v0.4 extension: aDESCRIBE CAPABILITIESsummary for cross-tool portability. AS OFanchoring — reproducible window anchoring for retrieve/funnel. v0.4 extension: bitemporalAS OFacross entity versions (see below).context_bundleis already a core-safe entity type; the typed result envelope is the first step toward context-shaped output.
Core principles carried forward (non-negotiable)
v0.4 does not relax the v0.2/v0.3 contract:
- No interpretation labels in core. States like
at_risk,blocked,priority,staleare not operators or keywords. They are derivedsignal(...)values or domain-pack interpretations. A query asksWHERE signal(risk_score) > 0.8, never… AT RISK. - No mutation. Retrieval and shaping only; writes remain out of core.
- Domain vocab is external. Typed relationship names (
supports,blocks,depends_on,validates,derived_from,ships_in, …) live in packs such asaspectgraph-work-pack, not in core grammar.
1. Graph pattern operators
Today's traversal is intentionally low-level:
FROM entity(decision) TRAVERSE references DEPTH <= 2 RETURN entity_idv0.4 adds higher-level, still-non-interpretive pattern operators so agents can express connection and absence directly:
graph_query ::= "MATCH" node_pattern ( edge_pattern node_pattern )*
where_clause? select_clause
edge_pattern ::= "-[" relationship ( "*" depth_bound )? "]->"
| "<-[" relationship "]-"
absence_pred ::= "WITHOUT" relationship (* anti-join / negation *)
reach_query ::= "FIND" node_pattern "CONNECTED TO" entity_ref ( "VIA" relationship )?
path_query ::= "PATH" "FROM" entity_ref "TO" entity_ref ( "VIA" relationship )?Examples (all structural, no business opinion):
FIND entity(decision) CONNECTED TO entity(experiment) WHERE name = "onboarding-v2"MATCH entity(assumption) WITHOUT validated_by RETURN entity_idPATH FROM entity(assumption) TO entity(release) VIA derived_fromNote WITHOUT validated_by is the structural "assumptions lacking evidence" query — it is an absence pattern, not an at_risk label. The relationship name validated_by comes from a domain pack.
2. Reasoning lineage (the headline feature)
The primitives already exist — provenance fields (source, created_by, derived_from, confidence, observed_at) and traversal. What is missing is a lineage-shaped query and result: walk the provenance/derivation edges and return the ordered chain, not a flat set.
A dedicated verb (not EXPLAIN, which is reserved for execution-plan explainability):
trace_query ::= "TRACE" entity_ref ( "VIA" relationship )? ( "DEPTH" "<=" integer )?TRACE entity(ticket) WHERE id = "123" VIA derived_fromreturns the lineage as an ordered, typed chain:
customer_interview → activation_drop_signal → experiment_result → decision → ticket → releaseResult shape: an ordered list of { entity_id, kind, relationship, confidence, observed_at } steps, so an agent can render why this exists and an auditor can verify the chain. SignalQL returns the lineage; it renders no judgment about whether the reasoning was good.
3. Context bundles (graph-shaped output)
SELECT returns rows. RETURN returns evidence. v0.4 adds context: a single operator that assembles a bounded subgraph optimized for LLM consumption.
context_query ::= "CONTEXT" "FOR" entity_ref ( "DEPTH" "<=" integer )?
( "INCLUDE" include_list )?
include_list ::= include_item ( "," include_item )*
include_item ::= "entities" | "relationships" | "decisions" | "evidence" | "signals"CONTEXT FOR entity(roadmap) WHERE quarter = "Q3" DEPTH <= 2returns a typed bundle (an extension of the v0.3 result envelope):
{
"root": { "entity_id": "...", "kind": "roadmap" },
"entities": [ /* ... */ ],
"relationships": [ /* {from, to, kind} */ ],
"evidence": [ /* provenance-bearing records */ ],
"metadata": { "as_of": "...", "depth": 2, "truncated": false }
}This is the "SignalQL returns context, not rows" idea made concrete, and it builds directly on the existing context_bundle entity and typed envelope.
4. Bitemporal AS OF (time-travel graph)
v0.3 AS OF anchors the event-time window. v0.4 generalizes it to entity version time, using the existing version / superseded / updated_at fields, so a query reconstructs the graph as it was believed at a point in time:
CONTEXT FOR entity(roadmap) WHERE quarter = "Q3" AS OF "2026-03-01"TRACE entity(decision) WHERE id = "d-42" AS OF "2026-03-01"Answers "what did we believe then / what evidence existed then / why did we prioritize this then" — high-value for audit and for AI that must reason about past states, not just the present.
5. Graph execution engine (the real lift)
The honest dependency: today the entity/graph surface parses and plans but does not execute as direct SQL (it emits JSON execution plans for an unimplemented runtime; semantic_match is capability-gated on vector infra). Graph patterns, TRACE, CONTEXT FOR, and bitemporal AS OF all require a real graph/recursive execution backend (recursive CTEs for bounded traversal, an edge store, optional vector index). This is the largest piece of v0.4 and gates the rest — the grammar is the easy part.
Out of scope for v0.4 core
- Interpretive state operators (
AT RISK,BLOCKED,STALE) — derive viasignal(...)or domain packs. - Standard relationship vocabularies — ship as
aspectgraph-work-packand peers. - Mutation / write-back — remains a separate protocol decision.
- Judging reasoning quality — SignalQL returns the chain; the AI layer evaluates it.
Sequencing
- Graph execution engine (recursive traversal backend) — unblocks everything.
- Graph pattern operators + absence patterns.
TRACElineage query + ordered-chain result.CONTEXT FORbundle output.- Bitemporal
AS OFacross entity versions. aspectgraph-work-pack(relationship vocab, lineage conventions) as the first consumer that proves the core stayed domain-agnostic.
Normative references
- Carries the contract of: v0.3 spec, v0.2 spec, scope
- Domain packs (where vocab lives): domain-packs.md
- Version index: spec versions