1---
2name: managing-tasks-with-td
3description: Manages tasks with the td CLI. Use when tracking work items, creating todos, managing task dependencies, or when the user mentions td, tasks, or todos in a project using td. When the project obviously uses something else, or the user doesn't mention td explicitly, do not read.
4---
5
6Don't forget the single quotes around `'EOF'` HEREDOCs; they disable
7shell interpolation, preventing it from messing up Markdown in the
8description.
9
10## Projects and initialisation
11
12Storage is central (`~/.local/share/td/`), not per-directory. Each
13project is a named Loro CRDT document. Directories are bound to projects
14via `td init` or `td use`; the binding is resolved by longest-prefix
15match on the canonical path. You can also override with `--project
16<name>` or the `TD_PROJECT` env var.
17
18For multi-machine setup, initialize only once. On others, bootstrap from
19the first with `td sync` instead of `td init` so both sides share the
20same project identity.
21
22```bash
23td init myproject # create project + bind cwd to it
24td use myproject # bind cwd to an existing project
25td projects # list all known projects
26td sync # machine A: print a wormhole code
27td sync 7-goldfish-soccer # machine B: bootstrap from machine A
28td --project other list # one-off override
29TD_PROJECT=other td list # env override
30```
31
32## General reference
33
34These are not your most-used commands, nor are they the most critical,
35but you still need reference for them. The sections following are more
36important than this one and thus contain more instruction.
37
38Task IDs are 26-character ULIDs. Most commands accept a unique prefix as
39shorthand (e.g. the first 7 characters shown in table output). If the
40prefix is ambiguous, td will tell you.
41
42Deletion is a soft delete (sets `deleted_at` and marks the task closed).
43Deleted tasks are hidden from `list`/`ready`/`next` but still visible to
44`show` and included in `export`.
45
46```bash
47td rm <id> [<id>...] # soft-delete one or many
48td rm --recursive <parent> # required for deleting task trees
49td rm --force <blocker> # suppress dependent-unblocked warnings
50td list # all non-deleted tasks
51td list -s open
52td list -p high
53td list -e low
54td list -l frontend
55td label add <id> frontend
56td label rm <id> backend
57td label list <id>
58td label list-all
59td show <id> # read a task, its metadata, and logs
60td ready # show open tasks with no active blockers
61td search "smtp" # substring match in title and description
62```
63
64## Creating tasks
65
66Flags:
67
68- -p priority: low, medium (default), high
69- -e effort: low, medium (default), high
70- -t type -d desc -l labels (csv)
71
72Titles and bodies should stand on their own a year from now, not
73requiring additional context to understand the nuances but including
74those nuances directly at task creation or by adding them later through
75logs.
76
77```bash
78td create "Panic in token_refresh when OAuth provider returns HTTP 429" \
79 -p high -e medium -t bug -d "$(cat <<'EOF'
80Reproduction:
811. Point OAuth at a rate-limiting provider (or stub with httpbin/status/429)
822. Let the access token expire
833. Trigger any authenticated request
84
85Expected: graceful retry with exponential backoff
86Actual: unwrap() panics on the error response body
87
88auth::refresh_token() assumes every response is valid JSON. Matching on
89the status code before parsing would prevent this.
90
91Relevant: src/auth/refresh.rs:84 (the unwrap), src/auth/client.rs RetryPolicy
92EOF
93)"
94
95td create "Add STARTTLS for outbound SMTP per RFC 3207" \
96 -e high -t feature -d "$(cat <<'EOF'
97smtp::send() opens a plaintext socket and never upgrades. Per RFC 3207,
98send EHLO, check for STARTTLS capability, then upgrade before AUTH.
99
100rustls is already a dependency (used by the HTTP client), so the TLS
101upgrade should be straightforward.
102
103Relevant: src/smtp/transport.rs SmtpTransport::connect(),
104 src/smtp/transport.rs:52 (socket open)
105EOF
106)"
107
108td create "Flaky: test_concurrent_writes times out ~1/5 CI runs" \
109 -p low -e low -t bug -l ci,flaky -d "$(cat <<'EOF'
110Passes locally, times out on CI. Likely a race on the shared tempdir —
111each spawn should use its own database file.
112
113CI: https://builds.example.org/job/1284
114Relevant: tests/db_stress.rs:88, db::open()
115EOF
116)"
117
118td create "Child task" --parent td-a1b2c3 # for splitting tasks into smaller chunks
119```
120
121## Dependencies
122
123These are a core part of td; use them liberally, but appropriately. The
124`next` and `ready` commands both filter tasks with blockers so you have
125an easier time ordering work. Blocker relationships also make obvious
126opportunities for parallel work. When there are opportunities for
127parallelisation, and you have a subagent tool with write capabilities,
128and the tasks are small, ask the user whether they want you to invoke
129subagents for them. If the tasks aren't quite scoped well enough yet,
130ask whether they would like you to interview/question them regarding the
131gaps that need filling. DO NOT parallelise work without EXPLICIT user
132confirmation.
133
134```bash
135td dep add td-child td-blocker # child waits for blocker to close
136td dep rm td-child td-blocker
137td dep tree td-parent # subtask tree
138```
139
140## Updating tasks
141
142_As soon as_ it's obvious you're working on a particular task, mark it `in_progress`. If that was wrong, the user will say so.
143
144```bash
145td update td-a1b2c3 -s in_progress
146td update td-a1b2c3 -p high -e low -t "Revised title" -d "Added context"
147td done td-a1b2c3 td-d4e5f6 # one or many
148td reopen td-a1b2c3 # only on explicit user request
149```
150
151## Logging and looking for work
152
153These, and `show` of course, but that one's self-explanatory, are your
154most-run `td` commands.
155
156The `log`s are your scratchpad from which another agent or human might
157pick up after you. Any time you discover something, like maybe the code
158doesn't actually line up with the spec or some constraint forces you to
159implement something differently from initially described or whatever
160else, this is where you note it down. As you're discussing things with
161the user, they'll communicate preferences or make decisions or tell you
162to do things a certain way. Any time the task doesn't already reflect
163those, this is where you record them. Do not EVER let these signals go
164unrecorded. In case there's a network outage or a power failure or any
165other kind of weird issue, the user needs to be able to hand this ticket
166in whatever form YOU left it to what will effectively be an amensic
167agent. It'll be competent, but like a completely fresh consultant, need
168lots and lots of context to get up to speed. The ticket and the logs you
169attach to it _massively_ help anyone coming after you. While the "what"
170is definitely important, be sure not to skimp on the much more important
171"why", such as "User said ..." or "Discovered that [code symbol]
172actually ...".
173
174```bash
175td log td-a1b2c3 "User asked for logs in show/export/import but not in list --json. Kept list untouched to avoid breaking existing scripts that parse its output."
176td log td-a1b2c3 "User wanted task validation before insert. Added it so 'task not found' is explicit and immediate instead of a less obvious DB failure."
177td log td-a1b2c3 "Kept ON DELETE CASCADE only on task_logs because the user scoped labels/blockers cascade changes to a separate task."
178td log td-a1b2c3 "Stayed with positional message input (no stdin) because that was explicitly requested and is easier to replay from shell history."
179td log td-a1b2c3 "Used timestamp+id ordering so handoff readers get a stable timeline even when two entries land in the same second."
180td log td-a1b2c3 "Import replaces logs for the task to keep repeated imports deterministic and consistent with label/blocker import behavior."
181td log td-a1b2c3 "CI failed only on migration version assertions after adding 0004; updated expected version and re-ran full suite to confirm no behavioral regressions."
182td log td-a1b2c3 "Ran manual round-trip (init -> create -> log -> show -> export/import -> show) to prove logs survive transfer and stay in chronological order."
183```
184
185The `next` command traverses the graph using an algorithm that weighs
186tasks such that the top-scoring task is likely the one you want to start
187with. The algorithm accounts for unblocking other tasks, priority, and
188effort signals. It defaults to sorting by tasks which make the impact.
189Effort mode still accounts for all the signals, buts weighs them such
190that tasks with lower effort values bubble to the top. Only use effort
191mode if explicitly requested. Passing the verbose flag will show the
192equation resulting in the score, if the user is curious why things are
193sorted as they are. Only use the verbose flag if the user is curious
194about sorting/scoring.
195
196```bash
197# What should I work on next?
198td next # top 5 by critical path (default)
199td next --mode effort
200td next --verbose # show scoring breakdown per task
201td next -n 3 # limit to top 3
202td next --mode effort -v # combine flags
203```
204
205Reminder: update the status to "in_progress" before starting and
206_frequently_ attach high-signal, low-noise logs to the task at hand.