Skip to content

Relationships

dart-query supports five relationship types for modeling task dependencies. Each has a canonical (industry-standard) field name and a legacy _ids-suffixed alias:

TypeCanonical FieldLegacy FieldMeaning
Subtaskssubtaskssubtask_idsChild tasks that compose a parent task
Blockersblocked_byblocker_idsTasks that must complete before this task can proceed
Blockingblocksblocking_idsTasks that this task is blocking
Duplicatesduplicatesduplicate_idsTasks that duplicate this task
Relatedrelatedrelated_idsTasks with a logical connection but no dependency

Set relationships at creation time by passing arrays of dart_id values:

create_task({
title: "Deploy OAuth2 to production",
dartboard: "Engineering/backend",
priority: 4,
blocked_by: ["duid_oauth_impl"]
})
create_task({
title: "Update user documentation",
dartboard: "Documentation",
related: ["duid_oauth_impl", "duid_api_docs"],
blocks: ["duid_release_v2"]
})

Use update_task to modify relationships on existing tasks.

Full replacement semantics: providing a relationship array replaces all existing values for that type. Omitting a field leaves it unchanged.

// Set blockers (replaces any existing blockers)
update_task({
dart_id: "duid_deploy_task",
blocked_by: ["duid_testing", "duid_code_review"]
})
// Clear all blockers
update_task({
dart_id: "duid_deploy_task",
blocked_by: []
})
// Link related tasks (does not affect blockers or other types)
update_task({
dart_id: "duid_feature_a",
related: ["duid_feature_b", "duid_feature_c"]
})

update_task also accepts add_to and remove_from objects that merge into existing relationship arrays instead of replacing them — see Task Management for that shortcut. To add a relationship manually without losing existing ones, read the current state first:

// Step 1: Get current relationships
const task = get_task({ dart_id: "duid_deploy_task" })
// task.blocker_ids = ["duid_testing"]
// Step 2: Append and update
update_task({
dart_id: "duid_deploy_task",
blocked_by: [...task.blocker_ids, "duid_code_review"]
})
// blocker_ids is now ["duid_testing", "duid_code_review"]

link_tasks writes both sides of a relationship in one call, so the graph never ends up one-sided (a parent set on the child without a matching entry in the parent’s subtasks, for example). Prefer it over hand-editing relationship arrays on two separate update_task calls.

// Anchor task gets a parent; the parent's subtasks array is updated to match
link_tasks({
type: "parent",
from: "duid_oauth_subtask",
to: ["duid_oauth_epic"]
})
// Anchor task blocks one or more others; each target's blocked_by is updated to match
link_tasks({
type: "blocks",
from: "duid_migration",
to: ["duid_deploy_prod", "duid_release_notes"]
})

type accepts parent, subtasks, blocks, blocked_by, duplicates, or related. For type: "parent", to must contain exactly one dart_id; the other types accept any number of targets. The response includes mirror_applied (which inverse-side tasks were patched) and mirror_warnings (any inverse-side writes that failed).

get_task returns relationship IDs by default. Use expand_relationships: true to include titles:

get_task({ dart_id: "duid_task123", expand_relationships: true })
// {
// blocker_ids: ["duid_design_review"],
// blocking_ids: ["duid_deploy_prod"],
// expanded_relationships: {
// blockers: [{ dart_id: "duid_design_review", title: "Design review meeting" }],
// blocking: [{ dart_id: "duid_deploy_prod", title: "Deploy to production" }]
// },
// relationship_counts: { subtasks: 0, blockers: 1, blocking: 1, duplicates: 0, related: 0 }
// }

Use include_relationships: false to exclude relationship data entirely and save tokens.

Relationship columns in CSV use comma-separated dart_id values:

title,priority,blocker_ids,related_ids,tags
"Deploy to production",critical,"duid_testing,duid_code_review",,deployment
"Write unit tests",high,,"duid_feature_impl","testing,quality"
"Code review",medium,"duid_feature_impl",,review

Wrap values in quotes when they contain commas. Column names are flexible: blockers, blocked_by, and blocker_ids all map to the same field.

Every get_task response includes relationship_counts as a compact summary:

{
"relationship_counts": {
"subtasks": 3,
"blockers": 1,
"blocking": 2,
"duplicates": 0,
"related": 1,
"total": 7
}
}

This lets agents check for dependencies without loading the full relationship arrays.