tackboard 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tackboard-0.1.0/LICENSE +21 -0
- tackboard-0.1.0/PKG-INFO +497 -0
- tackboard-0.1.0/README.md +478 -0
- tackboard-0.1.0/pyproject.toml +89 -0
- tackboard-0.1.0/pyproject.toml.orig +71 -0
- tackboard-0.1.0/src/tackboard/__init__.py +13 -0
- tackboard-0.1.0/src/tackboard/__main__.py +8 -0
- tackboard-0.1.0/src/tackboard/claims.py +264 -0
- tackboard-0.1.0/src/tackboard/cli.py +1240 -0
- tackboard-0.1.0/src/tackboard/config.py +121 -0
- tackboard-0.1.0/src/tackboard/db.py +110 -0
- tackboard-0.1.0/src/tackboard/dependencies.py +414 -0
- tackboard-0.1.0/src/tackboard/errors.py +138 -0
- tackboard-0.1.0/src/tackboard/history.py +130 -0
- tackboard-0.1.0/src/tackboard/models.py +218 -0
- tackboard-0.1.0/src/tackboard/schema.py +606 -0
- tackboard-0.1.0/src/tackboard/service.py +1295 -0
- tackboard-0.1.0/src/tackboard/static/README +19 -0
- tackboard-0.1.0/src/tackboard/static/app.css +190 -0
- tackboard-0.1.0/src/tackboard/static/graph.js +283 -0
- tackboard-0.1.0/src/tackboard/static/refresh.js +31 -0
- tackboard-0.1.0/src/tackboard/templates/base.html +31 -0
- tackboard-0.1.0/src/tackboard/templates/board.html +13 -0
- tackboard-0.1.0/src/tackboard/templates/card.html +11 -0
- tackboard-0.1.0/src/tackboard/templates/column.html +10 -0
- tackboard-0.1.0/src/tackboard/templates/dep_line.html +1 -0
- tackboard-0.1.0/src/tackboard/templates/error.html +5 -0
- tackboard-0.1.0/src/tackboard/templates/filters.html +33 -0
- tackboard-0.1.0/src/tackboard/templates/graph.html +29 -0
- tackboard-0.1.0/src/tackboard/templates/history.html +1 -0
- tackboard-0.1.0/src/tackboard/templates/note.html +1 -0
- tackboard-0.1.0/src/tackboard/templates/show_all.html +1 -0
- tackboard-0.1.0/src/tackboard/templates/task.html +34 -0
- tackboard-0.1.0/src/tackboard/views.py +140 -0
- tackboard-0.1.0/src/tackboard/web.py +902 -0
tackboard-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sangingeum
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tackboard-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tackboard
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Project-scoped task board for coordinating multiple AI agents: CLI-first, SQLite-backed, with a read-only board and dependency-DAG view
|
|
5
|
+
Keywords: task-board,agents,sqlite,cli,kanban,dependency-graph
|
|
6
|
+
Author: sangingeum
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Office/Business :: Groupware
|
|
15
|
+
Requires-Python: >=3.12, <3.13
|
|
16
|
+
Project-URL: Homepage, https://github.com/sangingeum/tackboard
|
|
17
|
+
Project-URL: Repository, https://github.com/sangingeum/tackboard
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# tackboard
|
|
21
|
+
|
|
22
|
+
A small, reliable, project-scoped task board for coordinating multiple AI
|
|
23
|
+
agents — one SQLite database per project, a predictable CLI (`tack`) as the
|
|
24
|
+
primary interface, and a read-only web view for the human operator.
|
|
25
|
+
|
|
26
|
+
A codebase with several agents working at once needs three
|
|
27
|
+
things that a chat transcript cannot give it: a durable record of what is
|
|
28
|
+
still open, a way for one agent to hand work to another without stepping on
|
|
29
|
+
it, and a machine-readable answer to "what should I do next?". `tackboard`
|
|
30
|
+
provides exactly those, and nothing more:
|
|
31
|
+
|
|
32
|
+
- **One authoritative database per project.** A project is a SQLite file. Two
|
|
33
|
+
projects never share a task-ID namespace and dependencies never cross files.
|
|
34
|
+
- **The CLI is an API.** Every command can emit a versioned JSON document, and
|
|
35
|
+
exit codes are a stable contract, so an agent never has to scrape a table.
|
|
36
|
+
- **Tasks form a dependency DAG.** A task declares its prerequisites; readiness
|
|
37
|
+
is *derived* from the graph, so a reopened prerequisite immediately changes
|
|
38
|
+
who can start what.
|
|
39
|
+
- **Claims prevent collisions.** A claim is a short-lived lock held by one
|
|
40
|
+
agent on one task; taking the next task and locking it happen in one
|
|
41
|
+
transaction, so two agents racing for work never get the same task.
|
|
42
|
+
- **A human can watch.** A read-only board and dependency-graph view serves the
|
|
43
|
+
operator while agents work.
|
|
44
|
+
|
|
45
|
+
Installation, the CLI contract, the semantics, and troubleshooting are all
|
|
46
|
+
below; `DESIGN.md` describes the architecture for contributors.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
Install the `tack` command from PyPI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv tool install tackboard # install `tack` for your user
|
|
54
|
+
tack --help
|
|
55
|
+
|
|
56
|
+
uvx tackboard --help # or run it once without installing
|
|
57
|
+
pip install tackboard # or with pip
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`tack` is the console entry point (`tackboard.cli:main`). `python -m tackboard`
|
|
61
|
+
runs the same CLI, so an environment that cannot install scripts can still be
|
|
62
|
+
driven by a module invocation. Python 3.12; the only runtime dependency is the
|
|
63
|
+
standard library.
|
|
64
|
+
|
|
65
|
+
### Install from source
|
|
66
|
+
|
|
67
|
+
For development, work from a checkout:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv sync # development install into .venv (includes test tools)
|
|
71
|
+
uv run tack --help # the CLI, from the checkout
|
|
72
|
+
|
|
73
|
+
uv tool install . # or install the `tack` command from the checkout
|
|
74
|
+
tack --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Create a board
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
tack init --db .tackboard/project.sqlite3 --json
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`init` creates the parent directories, writes the schema, records the project
|
|
84
|
+
label and creation time, and enables WAL. It is idempotent: running it against
|
|
85
|
+
a board that already exists never erases tasks. Against a non-empty SQLite file
|
|
86
|
+
that is not a board it fails with exit 6 rather than touching the file.
|
|
87
|
+
|
|
88
|
+
The command is the only one that creates a database. Every other command
|
|
89
|
+
refuses to run against a missing file instead of silently creating one.
|
|
90
|
+
|
|
91
|
+
## Choosing which board a command acts on
|
|
92
|
+
|
|
93
|
+
Every command that touches project data resolves the database in this order:
|
|
94
|
+
|
|
95
|
+
1. `--db PATH`
|
|
96
|
+
2. the `TACKBOARD_DB` environment variable
|
|
97
|
+
3. the nearest `.tackboard.toml`, searched from the current directory upwards
|
|
98
|
+
|
|
99
|
+
Relative paths given to `--db` and `TACKBOARD_DB` resolve against the current
|
|
100
|
+
working directory; paths inside `.tackboard.toml` resolve against that file's
|
|
101
|
+
directory. If nothing can be resolved, the command fails with exit 3 and says
|
|
102
|
+
what to do.
|
|
103
|
+
|
|
104
|
+
Two workflows are supported.
|
|
105
|
+
|
|
106
|
+
**A — commit the configuration, ignore the database.** Check in
|
|
107
|
+
`.tackboard.toml`:
|
|
108
|
+
|
|
109
|
+
```toml
|
|
110
|
+
[tackboard]
|
|
111
|
+
db = ".tackboard/project.sqlite3"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
and ignore the database and its sidecars in `.gitignore`:
|
|
115
|
+
|
|
116
|
+
```gitignore
|
|
117
|
+
.tackboard/
|
|
118
|
+
*.sqlite3
|
|
119
|
+
*.sqlite3-wal
|
|
120
|
+
*.sqlite3-shm
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Any command run from inside the project then finds the board with no flags.
|
|
124
|
+
|
|
125
|
+
**B — commit nothing, use the environment.**
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
export TACKBOARD_DB=/somewhere/shared/project.sqlite3
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This suits a board that is deliberately outside the repository, or several
|
|
132
|
+
boards selected per shell.
|
|
133
|
+
|
|
134
|
+
> **Do not commit a live SQLite file.** The board is written to while agents
|
|
135
|
+
> work, and a committed copy goes stale the moment anyone claims a task; git
|
|
136
|
+
> will also conflict on the `-wal`/`-shm` sidecars, which are not mergeable.
|
|
137
|
+
> Commit `.tackboard.toml` (workflow A) or nothing at all (workflow B), never
|
|
138
|
+
> the database. To hand a board to someone, use `tack backup` — it takes a
|
|
139
|
+
> consistent snapshot even while agents are writing.
|
|
140
|
+
|
|
141
|
+
## Commands
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
tack init --db PATH
|
|
145
|
+
tack migrate
|
|
146
|
+
tack backup PATH
|
|
147
|
+
tack board [filters/options]
|
|
148
|
+
tack list [filters/options]
|
|
149
|
+
tack show TASK_ID
|
|
150
|
+
tack create TITLE [options]
|
|
151
|
+
tack update TASK_ID [options]
|
|
152
|
+
tack status TASK_ID STATUS [mutation options]
|
|
153
|
+
tack complete TASK_ID [mutation options]
|
|
154
|
+
tack reopen TASK_ID [mutation options]
|
|
155
|
+
tack cancel TASK_ID [mutation options]
|
|
156
|
+
tack restore TASK_ID [mutation options]
|
|
157
|
+
tack note TASK_ID TEXT [--agent ID]
|
|
158
|
+
tack claim TASK_ID [--agent ID] [--force --reason TEXT] [--if-version N]
|
|
159
|
+
tack release TASK_ID [--agent ID] [--force --reason TEXT] [--if-version N]
|
|
160
|
+
tack claims [--agent ID] [--older-than DURATION]
|
|
161
|
+
tack next [--agent ID] [--claim] [--stage work|review] [--force --reason TEXT] [filters]
|
|
162
|
+
tack depend add TASK_ID --on PREREQUISITE_ID [--agent ID] [--if-version N] [--force --reason TEXT]
|
|
163
|
+
tack depend remove TASK_ID --on PREREQUISITE_ID [--agent ID] [--if-version N]
|
|
164
|
+
tack deps TASK_ID
|
|
165
|
+
tack dependents TASK_ID
|
|
166
|
+
tack graph [TASK_ID] [--format json|dot]
|
|
167
|
+
tack history [TASK_ID] [--limit N]
|
|
168
|
+
tack doctor
|
|
169
|
+
tack serve [--host H] [--port P] [--allow-remote] [--allowed-host HOST ...]
|
|
170
|
+
tack guide
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Global options work before or after the subcommand: `--db PATH`, `--json`,
|
|
174
|
+
`--quiet`, `--verbose`, `--version`, `--help`. `--quiet` suppresses
|
|
175
|
+
informational headers only — never a command's result, never an error.
|
|
176
|
+
|
|
177
|
+
Examples:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
tack create "Implement discovery" --priority high --label networking \
|
|
181
|
+
--depends-on T-000100 --agent cpp-dev --json
|
|
182
|
+
|
|
183
|
+
tack list --eligible --json
|
|
184
|
+
tack board --brief
|
|
185
|
+
tack board --count --assignee cpp-dev
|
|
186
|
+
tack next --claim --json
|
|
187
|
+
tack depend add T-000123 --on T-000100 --agent cpp-dev
|
|
188
|
+
tack graph --format dot > graph.dot
|
|
189
|
+
tack history T-000123 --limit 20 --json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Titles work with spaces when quoted, and a bare `--` ends option parsing, so
|
|
193
|
+
`tack create -- "--weird title"` is unambiguous.
|
|
194
|
+
|
|
195
|
+
## Board views
|
|
196
|
+
|
|
197
|
+
`board` groups tasks by stored status; `list` prints a flat list. Both take the
|
|
198
|
+
same filters: `--status`, `--assignee`, `--label`, `--priority`, `--blocked`,
|
|
199
|
+
`--eligible` (`--ready` is an accepted alias), `--limit N`, `--sort FIELD`
|
|
200
|
+
(`priority`, `status`, `created_at`, `updated_at`, `id`, `title`), `--reverse`,
|
|
201
|
+
and `--full`.
|
|
202
|
+
|
|
203
|
+
Default ordering is priority descending, then oldest `created_at`, then lowest
|
|
204
|
+
numeric sequence — priority sorts by rank, never alphabetically. `done` and
|
|
205
|
+
`cancelled` groups show their ten most recently updated tasks plus a count of
|
|
206
|
+
the rest, unless `--status` selects the group or `--limit` is given.
|
|
207
|
+
|
|
208
|
+
Three forms answer "how much is there?":
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
$ tack board --brief
|
|
212
|
+
tasks=18 backlog=5 ready=4 in_progress=3 review=2 done=3 cancelled=1 eligible=2 reviewable=1 blocked=4 claimed=2
|
|
213
|
+
|
|
214
|
+
$ tack board --count --assignee cpp-dev
|
|
215
|
+
tasks=3
|
|
216
|
+
backlog=1
|
|
217
|
+
...
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Filters apply to both, and every count is computed over the filtered set.
|
|
221
|
+
`ready` is the stored status; `eligible` is derived and usually differs.
|
|
222
|
+
Combining `--brief` with `--count` is a usage error (exit 2). Under `--json`
|
|
223
|
+
both return the same document — only the human formatting differs.
|
|
224
|
+
|
|
225
|
+
JSON omits descriptions to save agent tokens; pass `--full` to include them.
|
|
226
|
+
`show` and mutation results always include the description.
|
|
227
|
+
|
|
228
|
+
## Tasks
|
|
229
|
+
|
|
230
|
+
`create` accepts `--description TEXT` / `--description-file PATH` (`-` reads
|
|
231
|
+
stdin), `--priority`, `--assignee`, `--label` (repeatable), `--depends-on`
|
|
232
|
+
(repeatable, committed atomically with the task), and `--status`. A task may be
|
|
233
|
+
created as `ready`; creating one as `in_progress`, `review`, or `done` is
|
|
234
|
+
rejected, because those states are reached by claiming and transitioning.
|
|
235
|
+
|
|
236
|
+
`update` changes `--title`, `--description`, `--priority`, `--assignee`
|
|
237
|
+
(`--assignee ""` clears it), `--blocked-reason TEXT` /
|
|
238
|
+
`--clear-blocked-reason`, and labels — either `--label` (replace the whole set)
|
|
239
|
+
or `--add-label` / `--remove-label`.
|
|
240
|
+
|
|
241
|
+
`note TASK_ID TEXT` appends an untitled, uneditable note for handoff context.
|
|
242
|
+
Notes appear in `show` (the latest five) and in `history`; they never change a
|
|
243
|
+
task's fields, version, or `updated_at`.
|
|
244
|
+
|
|
245
|
+
Mutation results are uniform:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{"schema_version": 1, "changed": true, "task": {"id": "T-000123", ...}}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
An operation that would change nothing reports `"changed": false` and writes no
|
|
252
|
+
history event and no version increment.
|
|
253
|
+
|
|
254
|
+
## Status, readiness and claims
|
|
255
|
+
|
|
256
|
+
Status is stored; readiness is derived. Nothing about readiness is ever
|
|
257
|
+
trusted from a caller.
|
|
258
|
+
|
|
259
|
+
- **Statuses**: `backlog`, `ready`, `in_progress`, `review`, `done`,
|
|
260
|
+
`cancelled`. Allowed transitions are enforced in one place, and a task cannot
|
|
261
|
+
jump from `ready` straight to `done`.
|
|
262
|
+
- **dependency_ready**: every direct prerequisite is `done`.
|
|
263
|
+
- **eligible**: status is `ready`, dependencies are ready, there is no explicit
|
|
264
|
+
blocked reason, and nobody holds a claim. This is the work an agent may take.
|
|
265
|
+
- **reviewable**: status is `review`, unclaimed, unblocked, dependencies ready.
|
|
266
|
+
- **blocked**: a non-terminal task with an unfinished or cancelled
|
|
267
|
+
prerequisite, or an explicit blocked reason. Done and cancelled tasks are
|
|
268
|
+
never blocked.
|
|
269
|
+
|
|
270
|
+
A claim is a short-lived lock: `tack claim T-000123 --agent my-agent` records
|
|
271
|
+
the holder and moves a `ready` task to `in_progress` in the same transaction.
|
|
272
|
+
`tack release` gives it back. `tack next --claim` selects the highest-priority
|
|
273
|
+
eligible task and claims it in **one** transaction, so concurrent agents never
|
|
274
|
+
receive the same task; without `--claim` it only reports what is available.
|
|
275
|
+
|
|
276
|
+
An agent's identity is `--agent AGENT_ID`, falling back to `TACKBOARD_AGENT`.
|
|
277
|
+
Every mutating command needs one; without either, it exits 2. Identifiers are
|
|
278
|
+
advisory labels and are not authenticated — the board prevents accidents, not
|
|
279
|
+
malice.
|
|
280
|
+
|
|
281
|
+
### `--force` is a short, audited list
|
|
282
|
+
|
|
283
|
+
`--force --reason TEXT` is accepted only for these cases, and each use is
|
|
284
|
+
recorded as a history event carrying the reason:
|
|
285
|
+
|
|
286
|
+
1. releasing or taking over a claim held by another agent;
|
|
287
|
+
2. starting a `ready` task whose only eligibility failure is an unfinished or
|
|
288
|
+
cancelled prerequisite;
|
|
289
|
+
3. adding an unfinished prerequisite to a task already in progress or review;
|
|
290
|
+
4. claiming a task assigned to another agent (the assignment transfers, and the
|
|
291
|
+
previous and new assignee are both recorded);
|
|
292
|
+
5. recovering an `in_progress` task that has lost its claim.
|
|
293
|
+
|
|
294
|
+
It is not a general bypass. Modifying a task claimed by another agent still
|
|
295
|
+
fails with exit 5 — take the claim over with `claim --force --reason` or clear
|
|
296
|
+
it with `release --force --reason` first. `--reason` is required and must not be
|
|
297
|
+
empty.
|
|
298
|
+
|
|
299
|
+
## Dependencies
|
|
300
|
+
|
|
301
|
+
An edge `A -> B` means B depends on A. `depend add`, `depend remove`, `deps`,
|
|
302
|
+
`dependents`, and `graph` build and read the graph. Adding an edge that would
|
|
303
|
+
create a cycle fails with exit 4, names the cycle, and leaves the database
|
|
304
|
+
unchanged; adding an edge that already exists is an idempotent success.
|
|
305
|
+
Completing a task never completes its dependents, and cancelling a prerequisite
|
|
306
|
+
leaves its dependents blocked until the edge is removed or the prerequisite is
|
|
307
|
+
restored and finished.
|
|
308
|
+
|
|
309
|
+
`graph --format json` emits `{"schema_version": 1, "nodes": [...], "edges":
|
|
310
|
+
[...]}` with `source` = prerequisite and `target` = dependent. `graph --format
|
|
311
|
+
dot` emits Graphviz text (identifiers and labels are quoted and escaped);
|
|
312
|
+
combining it with `--json` is a usage error.
|
|
313
|
+
|
|
314
|
+
## JSON output
|
|
315
|
+
|
|
316
|
+
Every document on stdout is a single JSON object carrying
|
|
317
|
+
`"schema_version": 1`, and stdout carries nothing else. Under `--json` errors
|
|
318
|
+
are also JSON — on **stdout**, matching:
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{"schema_version": 1, "error": {"code": "claim_conflict", "message": "Cannot claim T-000123: already claimed by agent qa.", "exit_code": 5, "retryable": false, "details": {"claimed_by": "qa"}}}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Error codes are `usage`, `not_found`, `validation`, `invalid_transition`,
|
|
325
|
+
`cycle`, `claim_conflict`, `stale_version`, `locked`, `schema`, `internal`.
|
|
326
|
+
Without `--json`, errors go to stderr as `error: <message>`. Tracebacks appear
|
|
327
|
+
only with `--verbose`, and output is UTF-8 with no color codes.
|
|
328
|
+
|
|
329
|
+
## Agent quickstart
|
|
330
|
+
|
|
331
|
+
The text below is the output of `tack guide`. Paste it into an agent's
|
|
332
|
+
instructions.
|
|
333
|
+
|
|
334
|
+
```text
|
|
335
|
+
tack — a project-scoped task board for coordinating agents
|
|
336
|
+
|
|
337
|
+
Setup
|
|
338
|
+
tack init --db PATH create the board (creates parent dirs)
|
|
339
|
+
export TACKBOARD_DB=PATH or pass --db PATH on every command
|
|
340
|
+
export TACKBOARD_AGENT=my-agent every mutating command needs an actor
|
|
341
|
+
tack guide print this summary
|
|
342
|
+
|
|
343
|
+
The work loop
|
|
344
|
+
tack next --claim --json take the top eligible task, atomically
|
|
345
|
+
tack next --stage review --claim take a task waiting for review
|
|
346
|
+
tack show T-000001 --json read one task in full
|
|
347
|
+
tack note T-000001 "progress note" append a handoff note
|
|
348
|
+
tack complete T-000001 --json finish it
|
|
349
|
+
tack reopen T-000001 --json put it back to ready
|
|
350
|
+
|
|
351
|
+
Reading the board
|
|
352
|
+
tack board --json every task, for machines
|
|
353
|
+
tack board --brief one line of counts
|
|
354
|
+
tack board --count counts, one key=value per line
|
|
355
|
+
tack board --eligible --limit 5 only work an agent may claim now
|
|
356
|
+
tack list --assignee my-agent filter by assignee
|
|
357
|
+
tack list --label core --full --full adds the description
|
|
358
|
+
tack list --blocked only tasks with derived blockers
|
|
359
|
+
|
|
360
|
+
Dependencies
|
|
361
|
+
tack create "Ship the parser" --depends-on T-000001
|
|
362
|
+
tack depend add T-000002 --on T-000001
|
|
363
|
+
tack deps T-000002 direct prerequisites
|
|
364
|
+
tack dependents T-000001 direct dependents
|
|
365
|
+
tack graph --format dot whole graph; --format json for machines
|
|
366
|
+
|
|
367
|
+
JSON
|
|
368
|
+
--json works before or after the subcommand and prints exactly one JSON
|
|
369
|
+
document on stdout, always carrying "schema_version": 1. --full adds
|
|
370
|
+
descriptions to board, list and next. With --json, errors are printed to
|
|
371
|
+
stdout as {"schema_version":1,"error":{...}} and stderr stays empty unless
|
|
372
|
+
--verbose is given; without --json, errors go to stderr.
|
|
373
|
+
|
|
374
|
+
Exit codes
|
|
375
|
+
0 success, including no-ops and an empty next
|
|
376
|
+
1 internal error 4 validation, invalid transition, or cycle
|
|
377
|
+
2 usage error, bad option 5 concurrency conflict: claim held by another
|
|
378
|
+
combination, or no actor agent, stale version, lock timeout
|
|
379
|
+
3 task, project, or database 6 database, schema, or migration error
|
|
380
|
+
not found / not configured
|
|
381
|
+
|
|
382
|
+
Status and readiness
|
|
383
|
+
Statuses: backlog, ready, in_progress, review, done, cancelled.
|
|
384
|
+
Readiness is derived, never stored: a task is eligible when its status is
|
|
385
|
+
ready, every prerequisite is done, it has no explicit blocked reason, and
|
|
386
|
+
nobody holds a claim. --force --reason TEXT is allowed only for the audited
|
|
387
|
+
cases listed in the specification — exhaustive, not a general bypass.
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Web view
|
|
391
|
+
|
|
392
|
+
`tack serve` starts a **read-only** browser view of one board:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
tack serve # 127.0.0.1:8765, the selected database
|
|
396
|
+
tack serve --port 8080
|
|
397
|
+
tack serve --json # exactly one startup record on stdout
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
It serves the same database as every other command (`--db`, then
|
|
401
|
+
`TACKBOARD_DB`, then the nearest `.tackboard.toml`) through the same service
|
|
402
|
+
layer, and shows the project name and database path on every page so the
|
|
403
|
+
operator can confirm which board is on screen.
|
|
404
|
+
|
|
405
|
+
| Path | What it shows |
|
|
406
|
+
|---|---|
|
|
407
|
+
| `/board` | Kanban columns per stored status, with filters (status, assignee, priority, label, blocked, eligible) and the same counts as `board --count`. `done` and `cancelled` show their 20 most recently updated cards behind a "show all" link, and an optional 5-second auto-refresh can be paused. |
|
|
408
|
+
| `/graph` | The dependency DAG — edge `A -> B` means B depends on A — with zoom, pan, fit-to-screen and a one-task neighbourhood. Nodes show ID, title, status, priority and assignee. Done, eligible, in-progress, review, blocked and cancelled are distinguished by shape and label as well as colour, and a server-rendered indented outline sits underneath the canvas, so the dependency structure is readable with JavaScript disabled or if the drawing fails. |
|
|
409
|
+
| `/task/<id>` | Description, status, priority, assignee, labels, the claim holder and its age, prerequisites and dependents with their statuses, derived blockers and readiness, timestamps, notes and recent history. |
|
|
410
|
+
|
|
411
|
+
The JSON API publishes the CLI's own documents — same `schema_version`, same
|
|
412
|
+
task projection, same counts object:
|
|
413
|
+
|
|
414
|
+
| Endpoint | Equivalent CLI document |
|
|
415
|
+
|---|---|
|
|
416
|
+
| `/api/board` | `tack board --json` |
|
|
417
|
+
| `/api/graph` (add `?task=T-000123` for one neighbourhood) | `tack graph --json` |
|
|
418
|
+
| `/api/tasks/<id>` | the `tack show --json` task, plus its claim, prerequisites, dependents, notes and history |
|
|
419
|
+
|
|
420
|
+
### Read-only, loopback-only, no authentication
|
|
421
|
+
|
|
422
|
+
- **Read-only.** The server accepts `GET` and `HEAD` and answers **405** to
|
|
423
|
+
everything else, including any attempt to mutate. Every change goes through
|
|
424
|
+
the CLI.
|
|
425
|
+
- **Loopback by default.** `--host` defaults to `127.0.0.1`.
|
|
426
|
+
- **Host validation.** The hostname *and* port from the `Host` header are
|
|
427
|
+
checked against an allowlist — `localhost`, `127.0.0.1`, `::1` and the bind
|
|
428
|
+
host for a loopback bind; only the explicitly configured values for a remote
|
|
429
|
+
one. This is the DNS-rebinding guard.
|
|
430
|
+
- **Headers.** Every response carries
|
|
431
|
+
`Content-Security-Policy: default-src 'self'` and
|
|
432
|
+
`X-Content-Type-Options: nosniff`. There are no inline scripts or styles, the
|
|
433
|
+
page never requests another host, and the JavaScript is committed as-is
|
|
434
|
+
(hand-written SVG for the graph, no vendored library, no build step).
|
|
435
|
+
- **Selected database only.** Connections are opened per request with
|
|
436
|
+
`PRAGMA query_only=ON`, and every page renders task text escaped.
|
|
437
|
+
|
|
438
|
+
> **There is no authentication.** Anyone who can reach the port can read the
|
|
439
|
+
> whole board. Keep it on loopback unless you accept that. Exposing it
|
|
440
|
+
> deliberately takes both options:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
tack serve --host 100.64.0.10 --allow-remote --allowed-host 100.64.0.10
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
A non-loopback `--host` without `--allow-remote` *and* at least one
|
|
447
|
+
`--allowed-host` is refused with exit 2, and a warning that the board has no
|
|
448
|
+
authentication is printed when remote access is enabled. `--json` writes
|
|
449
|
+
exactly one startup record to stdout (so a supervisor can parse the first
|
|
450
|
+
line) while the banner and every ongoing server log go to stderr; `--quiet`
|
|
451
|
+
silences the banner but never the record.
|
|
452
|
+
|
|
453
|
+
## Backup, migration and diagnostics
|
|
454
|
+
|
|
455
|
+
- `tack backup PATH` writes a consistent snapshot using SQLite's online backup
|
|
456
|
+
API, verifies the copy, and refuses to overwrite an existing file. The
|
|
457
|
+
destination directory must already exist; only `init` creates directories.
|
|
458
|
+
Never copy a live database with `cp`.
|
|
459
|
+
- `tack migrate` backs the database up first
|
|
460
|
+
(`<database>.pre-migrate-v<old>-to-v<new>-<timestamp>.sqlite3`), then applies
|
|
461
|
+
migrations transactionally. It never overwrites a backup, and it aborts
|
|
462
|
+
without touching the database if a backup cannot be written. No other command
|
|
463
|
+
migrates; opening an older schema fails with exit 6 and tells you to run it.
|
|
464
|
+
- `tack doctor` checks readability, the application id, the schema version,
|
|
465
|
+
foreign-key consistency, dangling dependencies, cycles, claims on inactive
|
|
466
|
+
tasks, `in_progress` tasks without a claim, and metadata integrity. Errors
|
|
467
|
+
exit 6; warnings are listed and exit 0.
|
|
468
|
+
|
|
469
|
+
## Troubleshooting
|
|
470
|
+
|
|
471
|
+
| Message | Exit | What to do |
|
|
472
|
+
|---|---|---|
|
|
473
|
+
| `No project database configured. Run 'tack init --db PATH' or set TACKBOARD_DB.` | 3 | Pass `--db`, export `TACKBOARD_DB`, or add a `.tackboard.toml` |
|
|
474
|
+
| `Database <path> not found.` | 3 | Run `tack init --db <path>` |
|
|
475
|
+
| `Task T-000123 not found in project.sqlite3.` | 3 | Check the ID, or list the board |
|
|
476
|
+
| `No actor. Pass --agent or set TACKBOARD_AGENT.` | 2 | Identify the agent making the change |
|
|
477
|
+
| `Cannot claim T-000123: already claimed by agent qa.` | 5 | Wait, or take over with `claim --force --reason` |
|
|
478
|
+
| `Cannot claim T-000123: assigned to agent qa (use --force --reason to transfer assignment).` | 4 | Transfer deliberately, or leave it alone |
|
|
479
|
+
| `Cannot start T-000123: prerequisite T-000100 is not done.` | 4 | Finish the prerequisite, or start with `--force --reason` |
|
|
480
|
+
| `Cannot add dependency: this would create a cycle (T-000123 -> T-000145 -> T-000123).` | 4 | Break the other edge first |
|
|
481
|
+
| `Stale version for T-000123: expected 4, current 5.` | 5 | Re-read the task, then retry with the current `--if-version` |
|
|
482
|
+
| `Database is locked after retries; retry the command.` | 5 | Retry — the error is marked `retryable: true` |
|
|
483
|
+
| `<file> is not a tackboard database (application_id ...).` | 6 | Point `--db` at the right file; `init` never overwrites one |
|
|
484
|
+
| `<file> uses schema version N, older than the supported version M.` | 6 | Run `tack migrate` |
|
|
485
|
+
|
|
486
|
+
## Development
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
uv sync
|
|
490
|
+
uv run pytest -q # the whole suite
|
|
491
|
+
uvx ruff check src tests # lint
|
|
492
|
+
uv run mypy src # types
|
|
493
|
+
uv run pytest --cov # coverage (the configured floor is in pyproject.toml)
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
`AGENT.md` is the short orientation brief for agents working in this
|
|
497
|
+
repository; `DESIGN.md` is the architecture document for contributors.
|