Relationships
Create a link
xpo link <source> <target> -t blocks
Relationship types
| Kind | Inverse | Meaning |
|---|---|---|
blocks | blocked_by | Source blocks target from starting |
depends_on | dependency_of | Source depends on target being done |
duplicates | duplicated_by | Source duplicates target |
relates_to | relates_to | Symmetric, informational |
Types are case-insensitive, and blocked-by or BlockedBy work as well as blocked_by.
Links are bidirectional
A link is stored once, on the issue you created it from, and read from both sides: A blocks B also means B blocked_by A. Showing B lists the inverse as a derived link, so you never need to create both directions.
One link per pair
Two issues can have at most one link between them. Linking a pair that's already linked fails and names the existing link:
Error linking issues: myapp-a1b2c3 is already linked to myapp-b2c3d4 (blocks); set overwrite to replace it with relates_to
To change the kind, pass --overwrite. It replaces the existing link, whichever issue stores it, and says what it replaced:
xpo link myapp-a1b2c3 myapp-b2c3d4 -t relates_to --overwrite
# Linked myapp-a1b2c3 relates_to myapp-b2c3d4 (replaced blocks)
In JSON and MCP, set "overwrite": true; the result lists what was replaced under replaced. Creating the exact same link again also fails (already exists). add and update reject a links set with two entries for the same issue. Pairs that were linked more than once before xpo 1.4.0 keep working until a write changes them.
In xpo show --json and the MCP show tool, links appear under dependencies, and inverse links stored on the other issue carry "derived": true:
"dependencies": [
{"source_id": "myapp-b2c3d4", "target_id": "myapp-a1b2c3", "kind": "blocked_by", "derived": true}
]
Remove a link
xpo unlink <source> <target> -t blocks
unlink removes one relationship, whichever side stores it — xpo unlink B A -t blocked_by also removes A blocks B stored on A. Links to deleted issues can be removed by the deleted issue's ID.
Enforcement
Only blocked_by gates work: an issue can't move to DOING until every blocker, stored on either side, is DONE. CANCELED and DUPLICATE blockers still block until you remove the link or point it at another issue. If you try to xpo start a blocked issue, xpo tells you which issue is blocking it and to remove the link. On the board, Start Work is disabled, and its tooltip lists the blockers with a Remove link button for canceled and duplicate ones. All other kinds are informational. See Status for the full issue lifecycle.
From the board
In an issue's sidebar, the Relationships button (flag icon) shows how many relations the issue has — "No relations", "1 Relationship", "3 Relationships" — counting its parent as one. Click it to add a relation:
- Remove parent — first in the menu, when the issue has a parent
- Parent of…, Sub-issue of… — set this issue as the parent of another, or pick its parent
- Related to…, Blocked by…, Blocking…, Duplicate of…
- More — Depends on…, Dependency of…, Duplicated by…
Picking a kind opens an issue search. Sub-issue of… only offers open, top-level issues outside this issue's own subtree.
The Relations card lists the links in sections worded from the issue you're viewing — Related, Blocked by, Blocking, Depends on, Dependency of, Duplicate of, Duplicated by. Each linked issue is a chip:
- Click opens the issue; hover for a moment to preview its ID, assignee, title, status, and priority.
- × (on hover) removes the relation.
- Right-click offers Change to (switch the link's kind in place), Remove relation, and Copy link (the linked issue's URL).
The waypoints button in the card's corner opens the issue in the dependency graph.
View dependencies
xpo show <id>
Dependencies are listed in the issue's detail view, including inverse links stored on the other issue. The web board also has a dedicated Dependencies view that visualizes the full graph of blocking relationships across all issues.
Set links in a payload
Add links at creation time:
xpo add --json <<'EOF'
{
"title": "Subtask",
"links": [{"target": "myapp-a1b2c3", "type": "blocked_by"}]
}
EOF
When updating, links is the issue's full relationship set, including derived links (as returned by show). A link you leave out is removed from whichever side stores it, and "links": [] removes them all. Omit links to leave them unchanged.
echo '{"links": []}' | xpo update <id> --json