Graph metadata and discovery
Attach metadata to definePipeline or any step constructor to describe ownership,
domains, tags, and application annotations. Metadata does not change execution,
selection, dependencies, ordering, dry runs, or failure handling. Stable IDs remain
the execution identity; use declared targets to select operational goals.
See the executable metadata recipe.
import { createSteps, definePipeline, querySteps } from "tubeless";
const { step } = createSteps();
const summarize = step("summarize", {
metadata: {
owner: "analytics",
domain: "customers",
tags: ["sensitive", "aggregate"],
annotations: { retentionDays: 30 },
},
run: () => ({ count: 12 }),
});
const pipeline = definePipeline({
id: "customer-summary",
metadata: { owner: "data-platform" },
steps: [summarize],
});
const matches = querySteps(pipeline.plan(), { tags: ["sensitive"], owner: "analytics" });
const diagram = pipeline.toMermaid({ query: { domain: "customers" }, includeMetadata: true });
PipelineMetadata has four optional fields: tags, owner, domain, and
annotations. Put application fields inside annotations; unknown top-level fields
are rejected. Pipeline metadata and each step’s metadata remain separate. Nothing
inherits or merges, including across child pipeline wrappers. Child metadata is
available in the child’s own plan and recorded definition.
Compilation copies and deeply freezes metadata, including nested annotations.
Later edits to the author’s objects cannot change plans, diagrams, or recorded
definitions. Values must be plain JSON: null, booleans, finite numbers, strings,
dense arrays, and plain objects. Undefined values, functions, symbols, accessors,
class instances, cycles, and sparse arrays are rejected without invoking getters
or toJSON. Each metadata object is limited to 16,384 UTF-8 bytes, 256 visited
values, and nesting depth 8 (the root is depth 0). Keys and strings also consume a
conservative JSON encoding budget. Tags allow at most 64 nonblank strings; tags,
owners, and domains have a 256-character limit. Omit secrets: metadata is recorded
and displayed as supplied.
Query and diagram semantics
querySteps(plan, query) returns matching plan steps in execution order. Filters
combine with AND, every requested tag must match, and comparisons are exact and
case-sensitive. Empty filters match every step. Missing metadata does not match a
specified owner, domain, or tag. Queries leave the plan and its selection state
unchanged, so a match may be an unselected step. Prerequisites are not added.
Applications can check the returned metadata against their own conventions.
toMermaid({ query, includeMetadata: true }) renders matching steps and only edges
whose endpoints are both visible. An empty match produces just the flowchart
header. Metadata labels are escaped like other Mermaid labels. The complete
pipeline remains available for ordinary planning and execution.
bunx tubeless inspect --tag sensitive --owner analytics --json ./pipeline.ts
bunx tubeless graph --domain customers --metadata ./pipeline.ts
Repeat --tag to require several tags. inspect filters stepIds and plan.steps;
the definition snapshot and declared target IDs remain complete context. Text
inspection displays metadata. graph --metadata includes it in node labels.
These filters belong to inspect and graph; run and plan continue using
execution controls such as --target and --step.
Studio plan previews and observed definitions show metadata. Expand Explore steps to search IDs and metadata (case-insensitive substring search), or group matches by owner or domain. Grouping preserves the order of first occurrence; steps without the selected field appear in a separate Unassigned group marked No owner specified or No domain specified, even when a supplied value is also named “Unassigned.” These controls do not affect launches.
Recording and compatibility
Plans expose step metadata and the complete definition snapshot. Pipeline objects
also expose their pipeline metadata. Trace pipeline.started events carry both
pipeline and step metadata in definitionSnapshot, allowing application-owned
telemetry exporters to enrich their output without parsing descriptions.
Metadata contributes to the structural fingerprint and definition ID, so Studio
retains and compares metadata edits as different definitions. Object key order
does not affect identity; array order, including tag order, does. Definitions with
metadata, and their ancestors, use definition identity version 3. Definitions
without metadata retain their existing version 1 or 2 hashes. New readers continue
to accept those older definitions. Older readers do not understand identity
version 3. Handler equivalence still requires an application-supplied
implementationVersion; a fingerprint does not prove equal code.
The existing overall trace snapshot limit still applies: oversized definitions retain their identity but omit the complete snapshot, so metadata browsing and comparison are unavailable for those recordings.
This release supports graph metadata in TypeScript definitions. Declarative
pipeline document version 1 continues to reject metadata on pipelines and
steps, and arbitrary unknown fields remain errors. Its existing document-level
metadata (name, description, authors, date) is unchanged and does not
inherit into compiled pipelines. Graph metadata in declarative documents requires
a future explicit document version and matching JSON Schema.