Specifications
This page defines the behavioral/data contract for taskdb.
Filestructure Layout
The tasks for the project are stored by convention under .tasks/ in the current working directory. This is overridable using either the --project=... or TASKDB_PROJECT_PATH environment variable to specify a different path.
Within this directory are two mandatory subdirectories: all & complete.
The all directory holds all of the task files within set of zero-padded subdirectories. Each task file gets an auto-incrementing/unique integer, which is zero-padded to retain ordering under normal lexicographical conditions (e.g. 0001 -> 0009, then 0010).
Note: This structure of nested subdirectories was chosen due to common filesystem limitations, preventing more than ~32,768 files in a single directory. By computing the integer, splitting up the numbering across multiple subdirectories (
<NNNNN>/<NNNNN>-<slug>.md, e.g.00000/00001-initial-setup.md), this allows for32768 * 32768, or over 1 BILLION tasks.If you need more than 1B tasks for a single project, you may want to investigate a different solution…
The complete directory holds symlinks to all of the completed task files. It mirrors the all layout/filepaths.
The filestructure layout ends up looking like:
.tasks/
| # Required, where all the task files actually live
├── all/
| └── <NNNNN>/
| ├── <NNNNN-task-slug>.md
| ├── <NNNNN-task-slug>.md
| └── <NNNNN-task-slug>.md
| # Required, symlinks to all the completed tasks
├── complete/
| └── <NNNNN>/
| | # ... & are just symlinks back to `all/`.
| └── <NNNNN-task-slug>.md
| # Task statuses are sibling subdirectories, ...
├── needs-definition/
| └── <NNNNN>/
| | # ... & are just symlinks back to `all/`.
| └── <NNNNN-task-slug>.md
├── ready/
├── in-progress/
├── on-hold/
├── done/
└── wontfix/
Other subdirectories live alongside all/complete, and make up task statuses. These status subdirectories have an identical internal structure to the all/ directory. Inside each are symlinks back to the task file’s original location in all/.
Status directory names are arbitrary. You can rename these directories, remove them, or add others. In this way, taskdb adapts to your desired set of statuses without code changes/configuration.
Status Model
Status is not stored in task frontmatter.
A task’s current status is determined by which status directory contains its symlink. Status transitions remove the old symlink and create a new one in the target status directory.
Task File Format
Each task is stored as a Markdown file, with YAML frontmatter fields for structured metadata.
Required frontmatter fields:
id- The task id. Integer, but expressed as a zero-padded string, for easy splitting. Auto-incrementing & unique.slug- The slug is computed from thetitleone-time (at creation). It’s immutable, & is non-unique.title- The human-readable task title. What needs to be done.labels- A YAML array of string labels/tags (e.g.['feat'], or['chore', 'easy']). Can be empty. Labels are arbitrary.created- An RFC 3339-formatted datetime string of when the task was created. Computed one-time (at creation) & immutable.updated- An RFC 3339-formatted datetime string of when the task was last updated. Computed every time there’s an update made to the task.
The Markdown body contains:
## Descriptionsection (free-form task details)- horizontal rule (
---) ## Task Commentstable with columnsCommented AtandComment
Example Task File
For example, the first task of a project (e.g. “Initial Setup”) would live at .tasks/all/00000/00001-initial-setup.md, & might look like:
---
id: "0000000001"
slug: "initial-setup"
title: "Initial Setup"
labels: ["feat"]
created: "2026-05-14T18:26:13.246-05:00"
updated: "2026-05-14T18:28:54.123-05:00"
---
## Description
Perform the initial setup steps on the codebase. This includes scaffolding out a `src/` directory, a `tests/` directory, creating all the project files, adding a `.gitignore` & a `README.md`, etc. Also run `git init .`.
---
## Task Comments
| Commented At | Comment |
| ----------------------------- | ---------------------------------------- |
| 2026-05-14T18:27:10.246-05:00 | Status changed from ready to in-progress |
Slug Generation
Task slugs are generated from the task title at creation time, then treated as immutable.
Rules (in order):
- Trim surrounding whitespace.
- Lowercase the string.
- Remove any character that is not
a-z,0-9, whitespace, or-. - Replace runs of whitespace with a single
-. - Collapse repeated hyphens (
---→-). - Remove leading/trailing hyphens.
Examples:
"My Cool Project!"→"my-cool-project"" Hello World "→"hello-world"
Notes:
- Slugs are not unique; task identity comes from the numeric
id. - Updating a task title does not change its slug or filename.
Task Identifiers
Commands that accept <task-identifier> support:
- integer ID (
1or00001) - full basename (
00001-my-task) - path fragment (
00000/00001-my-task.md), with or without status/project prefix