Manually change a task node in the TODO.md tree. Use for /todo-update <ID> [--status in-progress|blocked|ready|drop|revert] [--note "<message>"], where ID may be dotted like FEAT-002.1. Changes markers, drops a node with its subtree after confirmation, and edits Title, Done when, Ref, Depends, From, State, Complexity, Priority or Lane in place using only text the user supplied.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Is that skill safe to install?
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
Are you the author of todo-update?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/bassemzohdy-todo-update)
---
name: todo-update
description: Manually change a task node in the TODO.md tree. Use for /todo-update <ID> [--status in-progress|blocked|ready|drop|revert] [--note "<message>"], where ID may be dotted like FEAT-002.1. Changes markers, drops a node with its subtree after confirmation, and edits Title, Done when, Ref, Depends, From, State, Complexity, Priority or Lane in place using only text the user supplied.
argument-hint: <ID> [--status in-progress|blocked|ready|drop|revert] [--note "<message>"]
---
# /todo-update
First load the `todo` core skill (tree format, complexity, invariants, guardrails). For `--status in-progress` also read `../todo-continue/SKILL.md` (steps 3-5), and `reference/git-flow.md` and `reference/verification.md` from the core's folder; for `--status ready` on a claimed root, `reference/git-flow.md`. If the core is not available as a skill, read them under `../todo/`.
Check git (core, "Git mode"); in a non-git folder print the core's warning and continue. Run the invariant check first. With neither `--status` nor `--note`, show the node's lines and change nothing. If the ID is in CHANGELOG.md, refuse to reopen it and suggest `/todo-add "<description>" --type fix --from <ID>` on the base. Otherwise, if `<ID>` is not in TODO.md, list the IDs that are and change nothing.
## Status changes
- `--status in-progress`: refuse if another task is `[/]` and suggest `ready` for that one first. Refuse if the node is not workable (open children, unmet `Depends:`, blocked ancestor, tree claimed by another branch) or its `State:` says `next: decompose` (suggest `/todo-continue`, which breaks it down), and say why. Otherwise do `/todo-continue` steps 3-5 for it (ready check, branches with the tree claim, `[/]`, `State:`, start commit). It builds nothing; `/todo-continue` resumes from there.
- `ready`, `blocked` and `revert` act where the node's state lives: `[/]`, `[!]` and `[x]` live on the tree's branches, while the base shows the node as `[ ]`. Except for a claim release (below), which runs from any branch: for a node in a claimed tree, if you are not on the branch that shows its state (its own `todo/<ID>-…` branch when one exists, else its parent's), name that branch and stop; do not switch. A node of an unclaimed tree is changed only on the base. Judge the marker in the checked-out `TODO.md`.
- `--status ready`, by the node's marker:
- **`[ ]` root whose `State:` in `git show <base>:TODO.md` names a branch** (a tree claim): release it. Check whether it is stale: does the branch still exist in this clone (local, or remote-tracking refs already fetched)? If it is **live**, say so with the branch's last commit date, require an explicit yes, and warn that the owner's branch keeps its `[/]` and `State:` and will conflict if merged later. Require a clean tree; on a `todo/` branch with uncommitted backlog edits, first commit them there as `chore(backlog): pending edits <IDs>` (backlog files only, by path). Switch to the base (in the main checkout, core "Git mode"), delete the root's whole `State:` line (it holds only the claim), commit `TODO.md` alone as `chore(backlog): release <ID>` (a direct base commit, like the tree claim; other uncommitted backlog edits on the base: ask the user to commit them with a claim or stash them first, and stop), and switch back. The branch is kept unless the user asks to delete it. Nothing else changes.
- **`[/]` or `[!]`:** set `[ ]` (a `[/]` abandons the attempt; its branch is kept) and delete `Blocked:` (its gist goes in your reply, not the file). Write `State: attempts 0/3; branch <the node's own todo/ branch> from <its parent's branch, or the base for a root>; next: resume` (no branch item when it has none; no git: `attempts 0/3; no git; next: resume`, plus a root's `files:`/`docs:` kept in its `Blocked:` action part). On a `todo/` branch commit `TODO.md` as `chore(backlog): ready <ID>`; on the base leave it uncommitted (a backlog edit). `/todo-continue` then reuses that branch (git-flow "Start commit").
- **Any other `[ ]` node:** say it is already ready and change nothing.
- `--status blocked`: set `[!]`, replace `State:` (or add as the last field line) with `Blocked:` in the core format, built from `--note` and the last failing command. For a manual block with no failing command, write `n/a (manual)` as the middle part. If `--note` gives only a reason, the action is `resolve this, then run /todo-update <ID> --status ready` (written without quotes); ask only when even the reason is missing. In no-git mode a root's rolled-up `files:`/`docs:` go into the action part as `(files: …; docs: …)`. On a `todo/` branch commit it as `chore(backlog): block <ID>` (on a `[/]` node, together with its changed files by explicit path, as `/todo-continue` "Test failures" does); on the base leave it uncommitted (a backlog edit); on any other branch, name the base and stop. Then **stop autonomous execution** and do not pick up another node.
- `--status drop`: refuse on `[/]` (set it `ready` first), on `[x]` (use `revert`), on a claimed root (release the claim first), and when any descendant is not `[ ]` (name them and what must happen first); a node inside a claimed tree is dropped on its parent's branch (its nearest ancestor branch that exists; from elsewhere, name that branch and stop); if the node has its own `todo/` branch, list its commits and print `git branch -D <branch>` for the user (the skill does not run it). Require a reason in `--note`, otherwise ask. List the node, its whole subtree, and every task that names any dropped ID in `Depends:` or `From:` with your proposal to remove or re-point each reference, and ask for confirmation. Then delete the node and its subtree, apply the reference changes, update any ancestor sizes given, and lint (a parent left without children becomes a leaf: check its complexity and any size still fit, and ask if not). So the ID is never reused (core "IDs"): when `docs/plans/PROJECT.md` exists, append `- <date +%F> · <reason> · dropped <IDs>` to its `## Revisions`; otherwise offer to create `docs/plans/PROJECT.md` holding only `## Revisions` and that line (a backlog file; ask first when `docs/` is missing), telling the user that without it the ID could be reused. In git mode on a `todo/` branch, commit the change as `chore(backlog): drop <ID> - <reason>` (backlog files only); on the base leave it uncommitted (a backlog edit). With an RTM (`../todo/reference/rtm.md`) remove each dropped ID from its Tasks cell in the same change. Never write dropped tasks to CHANGELOG.
- `--status revert`: only for an `[x]` node whose parent is `[ ]` (roots never are `[x]`); if the parent is `[/]`, refuse and suggest `/todo-update <PARENT-ID> --status ready` first. Set the node and every descendant back to `[ ]`, each with `State: attempts 0/3; next: redo after revert`, so the work is redone from the leaves up; on a `todo/` branch commit it as `chore(backlog): revert <ID>`. In git mode, to undo the code too, print for the user (the skill does not run it): `git revert --no-commit -m 1 <merge commit>`, `git checkout HEAD -- TODO.md CHANGELOG.md` (keep the backlog as it is now), `git commit -m "Revert <ID>"`.
## Field edits
`--note` with `ready`, `in-progress` or `revert` is refused. `--note` without a status: if the note starts with `Title:`, `Done when:`, `Ref:`, `Depends:`, `From:`, `State:`, `Complexity:`, `Size:`, `Priority:` or `Lane:`, replace that part with the user's text verbatim. Otherwise tell the user the note was not applied; notes have no field of their own.
- `Done when:` must keep at least one backticked non-vacuous command and must not contain a forbidden command (I3, I10).
- `Lane:` only on roots (name: lowercase letters, digits, dashes).
- `Complexity:` is `low`, `mid` or `high`. `Size:` (optional, `-` removes it) must match the tree (core). `Priority:` only on roots.
- A new `Ref:` path must exist, and `Depends:` / `From:` IDs must exist, otherwise ask. A `Depends:` that would create a cycle is refused.
- A user edit of `State:` is one way to reset the attempt counter (`--status ready` is the other).
Field edits are refused on `[x]` nodes and, from outside the tree's branch, on nodes of a claimed tree. Only text the user supplied may change a field. Never rewrite Done when on your own. Re-run lint after any edit and reply with the updated task lines (or a one-line confirmation for a drop). Do not start work.