SignalQL v0.5 — derivation, dependency state, and lineage
Status: Draft with reference implementation. Part of the v0.5 specification. Builds on the object model.
Derivation
A derivation records that one object revision was produced from another:
sales.csv → analysis → forecast → board-summaryforecast DERIVED FROM sales.csv is a stronger statement than forecast RELATED TO sales.csv. v0.5 defines five derivation relations:
| Relation | Meaning |
|---|---|
derived_from | generic derivation |
generated_from | produced by a model or program from the source |
extracted_from | a part or claim lifted out of the source |
summarized_from | a condensed form of the source |
transformed_from | a format or structure change of the source |
All five share one model: a link from a derived revision to a source revision.
(from_object, from_rev) —relation→ (to_object, to_rev)The link is pinned: it names the exact source revision that was read. This is what makes staleness computable.
These relation names are part of the core vocabulary, unlike domain relationship names, which stay in packs. They are written in snake_case, matching the derived_from edge already used for v0.4 lineage; the camelCase spellings (derivedFrom, …) are accepted on input and normalized. Because derivations are projected as graph edges, v0.4 TRACE … VIA derived_from works over objects as well.
A link MAY carry metadata such as a locator (the paragraph, timestamp, or cell range the derived content came from).
Dependency state
Every object revision has a dependency state, one of:
CURRENT POSSIBLY_STALE STALE INVALIDState is a computed fact, not an interpretation. It is a pure function of revision facts as of the query time T — no clocks, thresholds, or domain meaning — so the same facts always give the same state. This is why it does not conflict with the v0.4 rule that stale is not an operator: there is still no STALE operator. state is a field, read through ordinary predicates. Interpretive staleness ("no activity in 3 days") remains a signal(...).
Link state
For a link pinned to (source, n), with latest(source, T) the source's latest revision as of T:
| Link state | Condition |
|---|---|
invalid | revision n of the source does not exist as of T, or the source is tombstoned at or before T |
stale | otherwise, latest(source, T) > n |
current | otherwise |
Object state
For revision r of object X, let its direct links be the derivations recorded from (X, r) at or before T, and its pinned lineage be the links reached by following each source at its pinned revision, transitively. The first matching rule wins:
invalid— a direct link isinvalid.stale— a direct link isstale.possibly_stale— a link deeper in the pinned lineage isstaleorinvalid.current— otherwise, including objects with no derivations.
The traversal MUST guard against cycles on (object, revision) and MUST be bounded; the reference bound is 16 levels.
This lets SignalQL answer a question conventional retrieval cannot:
Which generated knowledge may no longer be valid because its sources changed?
Grammar (EBNF, additive)
find_object ::= "FIND" "OBJECT" ( "(" name ")" )?
( "WHERE" object_predicate )? as_of_clause? limit_clause?
find_dependents ::= "FIND" "DEPENDENTS" "OF" object_ref
via_derivation? depth_clause? ( "WHERE" object_predicate )?
as_of_clause? limit_clause?
trace_object ::= "TRACE" ( object_ref | "CLAIM" ( object_uri | name ) )
via_derivation? depth_clause? as_of_clause?
via_derivation ::= "VIA" derivation_rel ( "," derivation_rel )*
derivation_rel ::= "derived_from" | "generated_from" | "extracted_from"
| "summarized_from" | "transformed_from"
depth_clause ::= "DEPTH" "<=" integerobject_predicate is the v0.3 predicate_expr over object fields.
Object fields
| Field | Meaning | Operators |
|---|---|---|
id (object_id) | object id | all |
type, title, project | object attributes | all |
rev (revision) | selected revision number | all |
created_at | object creation time | all |
origin, trust, validation | revision metadata | =, !=, IN |
role | revision roles; = means "has this role" | =, !=, IN |
state | dependency state | =, !=, IN |
any other name, or metadata.<key> | a metadata key | all |
Values of the closed vocabularies (state, origin, trust, validation, role) MAY be written as bare words or as strings, in any case: STATE = STALE and state = "stale" are the same query. An unknown value is a parse error.
FIND OBJECT
Returns live (non-tombstoned) objects at their latest revision as of T, ordered by id.
FIND OBJECT WHERE STATE = STALE
FIND OBJECT(document) WHERE trust IN (verified, authoritative) AND project = "SignalQL"Columns: object_id, type, title, project, revision, state, origin, trust, validation, roles, revised_at.
FIND DEPENDENTS
Returns the objects whose current revision derives, directly or transitively, from the given object. Links recorded from a superseded revision of a dependent are history and do not count. With @n, only objects derived from that revision are seeds.
FIND DEPENDENTS OF OBJECT source_data
FIND DEPENDENTS OF OBJECT object://source_data WHERE state != currentOne row per dependent, at its nearest depth; ties resolve to the lowest (source_object, relation). Ordered by depth, then id. Default depth 6, maximum 16.
Columns: object_id, type, title, project, revision, depth, relation, source_object, pinned_revision, state.
TRACE OBJECT and TRACE CLAIM
TRACE OBJECT returns the upstream lineage of one object revision: the target, then every derivation link reachable by following pinned source revisions.
TRACE OBJECT board_summaryboard_summary@3
generated_from forecast@7 current
derived_from sales_data@18 stale (latest is 19)
derived_from assumptions@4 currentEach step carries object_id, type, revision, relation, from_object, latest_revision, link_state, depth, metadata. Step 0 is the target. Steps are ordered by depth, then from_object, object_id, relation. A source appears at the revision the link is pinned to. metadata.truncated reports that links exist beyond the depth bound.
A claim is an object of type claim, linked to its source by extracted_from. TRACE CLAIM is TRACE OBJECT that requires the target to be a claim, so the result identifies the exact source revision — and, through link metadata, the location — a claim came from:
TRACE CLAIM launch_dateTRACE returns lineage. It renders no verdict on whether the reasoning was good.
Timestamps
AS OF and timestamp comparisons use the literal as written. A literal without a zone is interpreted in the executing engine's session time zone, so for portable queries write an explicit zone ("2026-08-12T00:00:00Z"). Implementations SHOULD execute in UTC.