The engine's reference value — deliberately NOT a lake reference, and
spelled differently on purpose (id/type, never _ref/_id):
id is a URI, not a bare doc id, because workflow references cross
resources (another dataset, a media library) and a bare _id is only
meaningful inside one dataset. Everything resource-aware — client
routing, spawn discovery, guard target resolution — reads the resource
out of this URI. It is stripped to a bare lake id at exactly one
boundary: paramsForLake (core/params.ts), when a query actually runs
against a lake.
type carries the target's schema type so consumers don't need a
fetch — as stamped by the ref's constructor, never verified against the
target. Engine-built refs stamp it from real data (a projection row's
_type, the releaseRef constructor); a caller-supplied ref's type
is a trusted claim. Three advisory surfaces read it: the declared-subject-type
contract (an entry's types: [...] accepts/rejects incoming refs —
refTypeIssues at start initialFields and spawn with), start
applicability ("which definitions accept a doc of this _type" —
acceptsDocumentType), and guard match.types inference from resolved
targets (resolveMatchTypes). All must work when the target lives in a
resource the reader can't (or shouldn't) fetch from.
In rendered GROQ this shape is what a reference-held value exposes
($fields.<entry>.id / .type) — whereas _id/_type belong to
documents you hold (lake rows, $row, a dereferenced singular doc.ref).
The spelling is not a namespace guarantee: a content document can carry
its own id/type fields — what you're reading follows what the entry's
declared kind puts in your hand, never the key's spelling.
TType preserves the type literal the constructors (refDataset,
gdrRef, …) were called with, so a constructed ref satisfies
literal-typed targets (e.g. a release.ref value's "system.release").
The engine's reference value — deliberately NOT a lake reference, and spelled differently on purpose (
id/type, never_ref/_id):idis a URI, not a bare doc id, because workflow references cross resources (another dataset, a media library) and a bare_idis only meaningful inside one dataset. Everything resource-aware — client routing, spawn discovery, guard target resolution — reads the resource out of this URI. It is stripped to a bare lake id at exactly one boundary:paramsForLake(core/params.ts), when a query actually runs against a lake.typecarries the target's schema type so consumers don't need a fetch — as stamped by the ref's constructor, never verified against the target. Engine-built refs stamp it from real data (a projection row's_type, thereleaseRefconstructor); a caller-supplied ref'stypeis a trusted claim. Three advisory surfaces read it: the declared-subject-type contract (an entry'stypes: [...]accepts/rejects incoming refs —refTypeIssuesat startinitialFieldsand spawnwith), start applicability ("which definitions accept a doc of this_type" —acceptsDocumentType), and guardmatch.typesinference from resolved targets (resolveMatchTypes). All must work when the target lives in a resource the reader can't (or shouldn't) fetch from.In rendered GROQ this shape is what a reference-held value exposes (
$fields.<entry>.id/.type) — whereas_id/_typebelong to documents you hold (lake rows,$row, a dereferenced singulardoc.ref). The spelling is not a namespace guarantee: a content document can carry its ownid/typefields — what you're reading follows what the entry's declared kind puts in your hand, never the key's spelling.TTypepreserves thetypeliteral the constructors (refDataset, gdrRef, …) were called with, so a constructed ref satisfies literal-typed targets (e.g. arelease.refvalue's"system.release").