CDF Data Modeling query-vs-list expert skill. Use for graph-native reads with instances.query, traversal payload design, failure debugging, pagination/dedupe semantics, and regression-proof tests (including Node.js/TypeScript parity checks).
69
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Ship correct, maintainable, graph-native CDF Data Model reads.
This skill turns query/list ambiguity into a deterministic workflow:
instances/query payloads.instances/query vs instances/listUse instances/query when any is true:
direction, through, from).Use instances/list when all are true:
Heuristic:
When user intent is discovery/ranking (for example free-text name matching), prefer:
instances.search to find/rank candidate anchors.instances.query to hydrate graph-related details for those anchors.Why:
code/validate-query-parity.cjs for payload validation in all normal cases.This skill is the source of truth for graph query correctness (traversal shape, refs, relation direction, and payload guardrails).
For runtime limits and throughput controls, see dm-limits-and-best-practices (concurrency budgets, semaphore/task-runner usage, retry policy details, and write batching limits).
Practical rule:
dm-limits-and-best-practices.with.<step>.limitwith.<step>.nodes.limit, with.<step>.edges.limitselect.sources requires propertiesIf a step uses select.<step>.sources, each source entry includes explicit properties.
For node start steps, include:
['node','space'])hasData for expected view where relevantIn traversal-step filters, use:
[space, 'ViewExternalId/version', property]Paginate per step with:
nextCursor.<step> -> cursors: { <step>: ... }When combining multi-step outputs:
explicit > fallback, max, latest, etc.)Start with strict server-side constraints (space, exact filters, scoped predicates).
Only broaden filters when needed, and keep fallback stages explicit and ordered.
When a use case asks for graph relationships plus the latest numeric value, use a two-phase read:
instances.query to traverse and collect the relevant time-series node IDs.datapoints.retrieveLatest on those IDs in batches.Why:
instances.query is best for relationship traversal and filtering.retrieveLatest is the efficient API for last-value reads.Efficiency guardrails:
ignoreUnknownIds: true to tolerate stale references.Reference shape:
const nodeIds = queryResult.items.ptts.map((n) => ({ instanceId: { space: n.space, externalId: n.externalId } }));
const latest = await client.datapoints.retrieveLatest(nodeIds, { ignoreUnknownIds: true });Use this pattern when relationship edges carry business data (for example risk, confidence, allocation, ownership, status, weight).
Mental model:
select.<edgeStep>.sources.Why this matters:
Generic example:
const result = await client.instances.query({
with: {
start: {
nodes: {
filter: {
and: [
{ equals: { property: ["node", "space"], value: "my_space" } },
{ hasData: [{ type: "view", space: "my_space", externalId: "PrimaryEntity", version: "v1" }] }
]
}
},
limit: 1000
},
links: {
edges: {
from: "start",
direction: "outwards",
filter: {
equals: {
property: ["edge", "type"],
value: { space: "my_space", externalId: "PrimaryToSecondaryLink" }
}
}
},
limit: 1000
},
secondary: {
nodes: {
from: "links",
direction: "outwards"
},
limit: 1000
}
},
select: {
start: { sources: [{ source: { type: "view", space: "my_space", externalId: "PrimaryEntity", version: "v1" }, properties: ["name"] }] },
links: { sources: [{ source: { type: "view", space: "my_space", externalId: "PrimaryToSecondaryLink", version: "v1" }, properties: ["weight", "status"] }] },
secondary: { sources: [{ source: { type: "view", space: "my_space", externalId: "SecondaryEntity", version: "v1" }, properties: ["name"] }] }
}
});Edge aggregation guidance:
max(weight), latest timestamp, explicit-over-derived, etc.).| Error / Symptom | Likely Cause | Fix |
|---|---|---|
Unexpected field - nodes.limit | limit nested under nodes | move to with.<step>.limit |
Unexpected field - edges.limit | limit nested under edges | move to with.<step>.limit |
properties must not be null | sources without properties | add explicit properties: [...] |
| Unexpectedly slow latest-value endpoint | trying to read latest values via traversal-only flow | split into instances.query + batched retrieveLatest |
| Query path intermittently fails with 429/5xx/timeout | missing transient failure handling | add bounded retries with exponential backoff + jitter |
| Edge properties missing in output | traversed edges but did not project edge properties | add explicit select.<edgeStep>.sources[*].properties for edge view |
| Aggregates inflated after edge traversal | multiple edges per endpoint without dedupe policy | dedupe by stable key and apply explicit tie-break rule |
| Traversal step returns empty, no error | non-versioned traversal ref | use View/version in property refs |
Cannot traverse lists of direct relations inwards. | inwards traversal through list direct relation | traverse from owning node with outwards, or remodel as edge |
| Traversal step empty despite data | missing hasData or wrong direction/identifier | add hasData; verify direction + through.identifier |
| First page works, later missing | cursor loop not step-scoped | iterate nextCursor.<step> |
| Inflated totals | dedupe policy missing | dedupe and apply explicit tie-break |
initiatives, featureLinks, commitments, customerArr).hasData).For every critical helper, tests must assert payload shape (not only mapped output):
with.<step>.limit existsnodes.limit / edges.limit absentselect.<step>.sources[*].properties presenthasData present on constrained start stepsinstances/list is absent (if query-only design)Example assertion style:
const call = (client.instances.query as ReturnType<typeof vi.fn>).mock.calls[0]?.[0];
expect(call?.with?.initiatives?.limit).toBe(1000);
expect(call?.with?.initiatives?.nodes?.limit).toBeUndefined();
expect(call?.select?.initiatives?.sources?.[0]?.properties).toContain('title');Use the TypeScript SDK to validate query shape and traversal semantics directly in frontend/backend JavaScript tooling:
const query = {
with: {
cycles: {
nodes: {
filter: {
and: [
{ equals: { property: ["node", "space"], value: "product_portfolio" } },
{
hasData: [{ type: "view", space: "product_portfolio", externalId: "PortfolioReviewCycle", version: "v1" }]
}
]
}
},
limit: 200
}
},
select: {
cycles: {
sources: [
{
source: { type: "view", space: "product_portfolio", externalId: "PortfolioReviewCycle", version: "v1" },
properties: ["key", "displayName", "periodStart", "periodEnd", "status"]
}
]
}
}
};
await client.dataModeling.instances.query(query);Parity checks:
properties shape are valid.node skills/dm-graph-traversal/code/validate-query-parity.cjs --query <path-to-query.json> --check all --expect passnode skills/dm-graph-traversal/code/validate-query-parity.cjs --query <path-to-query.json> --check all --expect fail--schema-hints <path-to-schema-hints.json> when running schema-aware relation checks.sources-properties, limit-placement, start-step-hasdata, versioned-traversal-refs, cursor-shape, inwards-list-direct-relations, allinstances.query IDs -> batched retrieveLatest) instead of forcing latest reads into traversal payloads."*").properties: ["*"]) in production query paths.references/query-vs-list.mdcode/validate-query-parity.cjsc87160a
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.