@basementuniverse/kanbn 2.0.0 → 2.5.0

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.
Files changed (100) hide show
  1. package/README.md +3 -1
  2. package/coverage/tmp/coverage-916017-1788028598821-0.json +1 -0
  3. package/coverage/tmp/{coverage-214293-1787777184569-0.json → coverage-916018-1788028597179-0.json} +1 -1
  4. package/coverage/tmp/coverage-916036-1788028598796-0.json +1 -0
  5. package/docs/actions.md +337 -0
  6. package/docs/advanced-configuration.md +55 -0
  7. package/docs/commands/add.txt +17 -1
  8. package/docs/commands/archive.txt +11 -0
  9. package/docs/commands/board.txt +9 -0
  10. package/docs/commands/boards.txt +34 -0
  11. package/docs/commands/burndown.txt +7 -0
  12. package/docs/commands/comment.txt +13 -1
  13. package/docs/commands/contributors.txt +58 -0
  14. package/docs/commands/edit.txt +18 -1
  15. package/docs/commands/find.txt +39 -1
  16. package/docs/commands/gantt.txt +6 -0
  17. package/docs/commands/help.txt +2 -0
  18. package/docs/commands/history.txt +6 -0
  19. package/docs/commands/init.txt +13 -0
  20. package/docs/commands/move.txt +26 -0
  21. package/docs/commands/remove.txt +22 -1
  22. package/docs/commands/rename.txt +5 -0
  23. package/docs/commands/restore.txt +12 -0
  24. package/docs/commands/sort.txt +23 -0
  25. package/docs/commands/sprint.txt +9 -0
  26. package/docs/commands/status.txt +10 -1
  27. package/docs/commands/task.txt +9 -0
  28. package/docs/commands/validate.txt +29 -1
  29. package/docs/contributors.md +145 -0
  30. package/docs/filtering-and-sorting.md +60 -3
  31. package/docs/index-structure.md +145 -11
  32. package/docs/index.md +6 -2
  33. package/docs/multiple-boards.md +259 -0
  34. package/docs/quick-start.md +21 -1
  35. package/docs/task-structure.md +31 -3
  36. package/example/README.md +23 -0
  37. package/example/advanced/kanbn.yml +65 -0
  38. package/example/boards/.kanbn/design.md +31 -0
  39. package/example/boards/.kanbn/index.md +44 -0
  40. package/example/boards/.kanbn/tasks/add-usage-alert-emails.md +19 -0
  41. package/example/boards/.kanbn/tasks/build-tenant-settings-page.md +43 -0
  42. package/example/boards/.kanbn/tasks/create-organization-switcher.md +44 -0
  43. package/example/boards/.kanbn/tasks/design-onboarding-checklist.md +22 -0
  44. package/example/boards/.kanbn/tasks/refresh-marketing-site.md +28 -0
  45. package/example/boards/.kanbn/tasks/ship-billing-portal.md +29 -0
  46. package/package.json +9 -7
  47. package/routes/add.json +40 -12
  48. package/routes/archive.json +14 -2
  49. package/routes/board.json +11 -3
  50. package/routes/boards.json +30 -0
  51. package/routes/burndown.json +23 -7
  52. package/routes/comment.json +19 -5
  53. package/routes/contributors.json +18 -0
  54. package/routes/edit.json +37 -11
  55. package/routes/find.json +37 -12
  56. package/routes/gantt.json +20 -6
  57. package/routes/history.json +39 -25
  58. package/routes/init.json +3 -1
  59. package/routes/move.json +24 -6
  60. package/routes/remove.json +18 -3
  61. package/routes/rename.json +11 -3
  62. package/routes/restore.json +14 -2
  63. package/routes/sort.json +40 -11
  64. package/routes/sprint.json +14 -4
  65. package/routes/status.json +23 -7
  66. package/routes/task.json +10 -2
  67. package/routes/validate.json +18 -5
  68. package/skills/kanbn-plan/SKILL.md +10 -1
  69. package/skills/kanbn-replan/SKILL.md +6 -1
  70. package/src/actions.js +904 -0
  71. package/src/board.js +25 -2
  72. package/src/controller/add.js +72 -55
  73. package/src/controller/archive.js +9 -4
  74. package/src/controller/board.js +21 -13
  75. package/src/controller/boards.js +140 -0
  76. package/src/controller/burndown.js +14 -7
  77. package/src/controller/comment.js +13 -6
  78. package/src/controller/contributors.js +166 -0
  79. package/src/controller/edit.js +48 -18
  80. package/src/controller/find.js +87 -13
  81. package/src/controller/gantt.js +13 -6
  82. package/src/controller/history.js +13 -6
  83. package/src/controller/init.js +39 -4
  84. package/src/controller/move.js +153 -25
  85. package/src/controller/remove.js +73 -13
  86. package/src/controller/rename.js +8 -4
  87. package/src/controller/restore.js +24 -8
  88. package/src/controller/sort.js +59 -3
  89. package/src/controller/sprint.js +31 -7
  90. package/src/controller/status.js +8 -4
  91. package/src/controller/task.js +34 -10
  92. package/src/controller/validate.js +197 -7
  93. package/src/git-user-name.js +9 -0
  94. package/src/git-user.js +55 -0
  95. package/src/main.d.ts +387 -9
  96. package/src/main.js +2757 -113
  97. package/src/parse-index.js +219 -17
  98. package/src/parse-task.js +16 -0
  99. package/src/utility.js +274 -0
  100. package/coverage/tmp/coverage-214292-1787777191526-0.json +0 -1
@@ -29,7 +29,57 @@ The project description should appear below the title. The description can be of
29
29
 
30
30
  Below the description there should be one or more level-2 headings. The 'Options' name is reserved for project options.
31
31
 
32
- All level-2 headings (except for 'Options') will be treated as columns. Columns should only contain a list of links to task markdown files.
32
+ All level-2 headings (except for 'Options') will be treated as columns.
33
+
34
+ ## Column contents
35
+
36
+ A column tracks a task when it contains a **markdown link** to that task's file:
37
+
38
+ ```markdown
39
+ ## Todo
40
+
41
+ - [my-task](tasks/my-task.md)
42
+ ```
43
+
44
+ That link is the only thing kanbn reads as a task. Any other line in a column - a bare title, a checkbox item, a note, a paragraph - is left exactly as you wrote it and ignored by every command:
45
+
46
+ ```markdown
47
+ ## Todo
48
+
49
+ - [my-task](tasks/my-task.md)
50
+ - remember to check the staging deploy
51
+ - [ ] buy milk
52
+ ```
53
+
54
+ These lines are **simple tasks**. They have no id, no metadata and no dates, they can't collide with a task of the same name, and they count towards nothing: not `kanbn status`, not workload, burndown, gantt or sprints. They are preserved when kanbn saves the board file, and stay in the column they were written in - though a line isn't anchored to the task above it, so sorting a column can move tasks around it.
55
+
56
+ They are shown on the board in the default layout, dimmed, at the end of their column. Views leave them out: a view filters and sorts tasks, and a simple task has no fields to filter or sort on. They are left out of `kanbn board --json` for the same reason.
57
+
58
+ ### Working with simple tasks
59
+
60
+ Four commands accept a simple task, addressed by its title - exactly, ignoring case, or slugified, so `"Buy milk"` and `buy-milk` both work. **A real task always wins**: kanbn only looks for a simple task when no task has that id, so a line whose title matches an existing task can only be edited by hand.
61
+
62
+ | Command | Effect |
63
+ | --- | --- |
64
+ | `kanbn move "Buy milk" -c Done` | Moves the line to another column. `-b` moves it to another board |
65
+ | `kanbn edit "Buy milk" ...` | **Promotes** it to a real task file, then edits it as normal |
66
+ | `kanbn remove "Buy milk"` | Deletes the line. There is no file to delete and nothing to archive |
67
+ | `kanbn task "Buy milk"` | Says which column it's in, and how to promote it |
68
+
69
+ Promotion is one-way. The new task gets a file, an id, a `created` date as of the promotion and a `created` history event naming the column the line was in. There is no way back: a task carries metadata, comments and history that a line can't hold.
70
+
71
+ Two lines with the same title on one board are two different lines, so a command naming one of them reports the ambiguity rather than guessing. Two lines with the same title on *different* boards are unrelated - a simple task belongs to the board file it's written in, and nothing else.
72
+
73
+ Anything that isn't a single line of a list - a paragraph, a quote, a code block - is preserved just the same, but isn't a simple task: it isn't shown on the board, and no command will move, promote or remove it.
74
+
75
+ `kanbn validate` lists them, so they can't hide. It reports two cases more loudly, because both mean a task silently isn't being tracked:
76
+
77
+ - a line that looks like a task link with a typo in it, e.g. a missing closing bracket;
78
+ - a line naming a task file that exists but isn't written as a link, e.g. `- my-task`.
79
+
80
+ _Note: before version 2.2.0 a bare task id in a column (`- my-task`) was treated as a task reference. It no longer is - only links are. Run `kanbn validate` to find any, and write them as links._
81
+
82
+ If a column references a task file that doesn't exist, kanbn skips that task and carries on rather than failing - this happens routinely when a board file and a task file arrive in different commits. `kanbn board` says how many tasks were skipped, and `kanbn validate` names them.
33
83
 
34
84
  ## Project options
35
85
 
@@ -37,7 +87,7 @@ The 'Options' heading should be followed by a code block containing YAML. The 'y
37
87
 
38
88
  Project options will be merged with YAML front-matter if any is present.
39
89
 
40
- *Note: when the index is saved, all project options will be placed into YAML front-matter. The 'Options' heading is still supported for backwards-compatibility.*
90
+ _Note: when the index is saved, all project options will be placed into YAML front-matter. The 'Options' heading is still supported for backwards-compatibility._
41
91
 
42
92
  ```yaml
43
93
  hiddenColumns:
@@ -89,6 +139,23 @@ customFields:
89
139
  type: 'date'
90
140
  {customFieldName}Columns:
91
141
  - {Column name}
142
+ contributors:
143
+ - gordon
144
+ - name: dave
145
+ displayName: Dave Smith
146
+ email: dave@example.com
147
+ aliases:
148
+ - Dave Smith
149
+ colour: '#7c5cff'
150
+ actions:
151
+ - name: start-work
152
+ on: task.moved
153
+ when:
154
+ toColumn: In Progress
155
+ then:
156
+ - assign: '@me'
157
+ - addTag: active
158
+ actionsFile: ""
92
159
  ```
93
160
 
94
161
  ### `hiddenColumns`
@@ -105,7 +172,7 @@ A list of column names. When a task is created in or dragged into one of these c
105
172
 
106
173
  A list of column names. When a task is created in or dragged into one of these columns, the task's `completed` date will be set to the current time, unless the task already has a `completed` date.
107
174
 
108
- Note that these columns control when the date is *written*. They don't decide whether a task counts as complete - that comes from the date itself. See [Task state](#task-state) below.
175
+ Note that these columns control when the date is _written_. They don't decide whether a task counts as complete - that comes from the date itself. See [Task state](#task-state) below.
109
176
 
110
177
  ### `startedField`
111
178
 
@@ -130,10 +197,36 @@ customFields:
130
197
 
131
198
  Set to `false` to suppress hints and notices printed alongside normal output, such as the reminder shown when tasks have dates that don't match their column. Defaults to `true`. Hints are never shown when using `--json`.
132
199
 
200
+ ### `defaultBoard`
201
+
202
+ The board that commands target when `-b` isn't given and `KANBN_BOARD` isn't set. Workspace-scoped: it belongs in the config file, or in the main board's front matter when there is no config file.
203
+
204
+ See [multiple boards](multiple-boards.md).
205
+
206
+ ### `boards`
207
+
208
+ Board discovery settings. `exclude` lists markdown files in the kanbn folder that aren't boards, `order` sets the display order for `kanbn boards`, and any other key is a board slug whose value is that board's options.
209
+
210
+ ```yaml
211
+ boards:
212
+ exclude:
213
+ - notes.md
214
+ order:
215
+ - index
216
+ - design
217
+ design:
218
+ hiddenColumns:
219
+ - Ideas
220
+ ```
221
+
222
+ Workspace-scoped. See [multiple boards](multiple-boards.md).
223
+
133
224
  ### `sprints`
134
225
 
135
226
  A list of sprints. Each sprint will have `start`, `name` and `description` properties.
136
227
 
228
+ A board can declare its own `sprints` in its front matter, which **replaces** the workspace list for that board entirely. See [multiple boards](multiple-boards.md#per-board-sprints).
229
+
137
230
  Run `kanbn sprint --help` for more information.
138
231
 
139
232
  ### `defaultTaskWorkload`
@@ -179,13 +272,13 @@ progress
179
272
  {...customFields}
180
273
  ```
181
274
 
182
- *Note: referencing a variable that isn't in the list above (or a custom field that isn't declared in `customFields`) will print "Unable to build task template" instead of rendering the board.*
275
+ _Note: referencing a variable that isn't in the list above (or a custom field that isn't declared in `customFields`) will print "Unable to build task template" instead of rendering the board._
183
276
 
184
277
  The default task template is `^+^_${overdue ? '^R' : ''}${name}^: ${created ? ('\\n^-^/' + created) : ''}`.
185
278
 
186
279
  This string can contain markup sequences. See [terminal-kit](https://github.com/cronvel/terminal-kit/blob/21607fb51749853dd9193c6aaf205b14c63b2768/doc/markup.md#markup) for markup reference.
187
280
 
188
- *Note: custom fields can also be interpolated into the task template, but only if they are defined in `customFields` (see below).*
281
+ _Note: custom fields can also be interpolated into the task template, but only if they are defined in `customFields` (see below)._
189
282
 
190
283
  ### `dateFormat`
191
284
 
@@ -208,6 +301,7 @@ An array of custom metadata fields, where each field should have `name` and `typ
208
301
  Run `kanbn add --help`, `kanbn edit --help`, `kanbn find --help` and `kanbn sort --help` for more information.
209
302
 
210
303
  Valid types are:
304
+
211
305
  - `boolean`
212
306
  - `date`
213
307
  - `number`
@@ -227,8 +321,8 @@ If the custom field has `updateDate` set to `always`, the field will be updated
227
321
 
228
322
  Here's an example of how this could be used:
229
323
 
230
- * Assume we have added a column called `Testing`
231
- * In `index.md` (or `kanbn.json` / `kanbn.yml` if using a separate configuration file), we have:
324
+ - Assume we have added a column called `Testing`
325
+ - In `index.md` (or `kanbn.json` / `kanbn.yml` if using a separate configuration file), we have:
232
326
  ```
233
327
  customFields:
234
328
  - name: testedAt
@@ -237,16 +331,56 @@ Here's an example of how this could be used:
237
331
  testedAtColumns:
238
332
  - Testing
239
333
  ```
240
- * When a task is moved into the `Testing` column and the task doesn't already have a `testedAt` value in its metadata, this value will be automatically populated with the current date/time.
334
+ - When a task is moved into the `Testing` column and the task doesn't already have a `testedAt` value in its metadata, this value will be automatically populated with the current date/time.
335
+
336
+ ### `contributors`
337
+
338
+ An optional list of the people who work on this workspace. Each entry is either a bare name or an object with a required `name` and optional `displayName`, `email`, `aliases` and `colour`.
339
+
340
+ `contributors` is **workspace-scoped**: like `customFields`, it belongs in `kanbn.json` / `kanbn.yml`, or in the main board's front matter when there is no configuration file, and every board inherits it. A secondary board that declares its own is reported by `kanbn validate` and ignored.
341
+
342
+ Contributors are **advisory**. `assigned` and comment `author` remain free text and are never validated against the list, never rejected and never rewritten. What the list buys you is a canonical spelling for each person, an autocomplete in the interactive prompts, `@me` in filters, and `kanbn contributors --usage` to find the spellings already in use.
343
+
344
+ See [Contributors](contributors.md) for the full description, and `kanbn contributors --help`.
345
+
346
+ ### `actions`
347
+
348
+ A list of rules that fire when a task is created, moved, updated, commented on, archived, restored, deleted, started or completed. Each rule names an event (`on`), an optional condition (`when`), an optional set of related tasks to act on (`for`), and the verbs to apply (`then`).
349
+
350
+ ```yaml
351
+ actions:
352
+ - name: start-work
353
+ on: task.moved
354
+ when:
355
+ toColumn: In Progress
356
+ then:
357
+ - assign: '@me'
358
+ - addTag: active
359
+ - removeTag: ready
360
+ ```
361
+
362
+ `actions` is board-scoped: in a configuration file it applies to every board, and in a board's front matter it applies to that board alone. A board that declares its own replaces the inherited list rather than adding to it.
363
+
364
+ Rules only fire during commands that change something, and nothing a rule writes ever fires another rule. See [Actions](actions.md) for the events, the verbs, the substitutions and the failure semantics.
365
+
366
+ ### `actionsFile`
367
+
368
+ A path, relative to the `.kanbn` folder, of a file holding the same list of rules that `actions` would have held. For rule sets that have outgrown a board's front matter.
369
+
370
+ ```yaml
371
+ actionsFile: actions/design.yml
372
+ ```
373
+
374
+ Setting both `actions` and `actionsFile` on the same board is an error, and so is naming a file that doesn't exist or doesn't parse.
241
375
 
242
376
  ## Task state
243
377
 
244
378
  Whether a task has started or been completed is decided by its metadata dates, and nothing else:
245
379
 
246
- * a task has started if its `started` field is set (or whichever field `startedField` names);
247
- * a task is complete if its `completed` field is set (or whichever field `completedField` names).
380
+ - a task has started if its `started` field is set (or whichever field `startedField` names);
381
+ - a task is complete if its `completed` field is set (or whichever field `completedField` names).
248
382
 
249
- The column a task sits in doesn't make it started or complete. Columns listed in `startedColumns` and `completedColumns` cause those dates to be *written* when a task is moved through the CLI or the VSCode extension, but the dates are what everything else reads: `kanbn status`, `kanbn find`, progress, burndown charts and gantt charts.
383
+ The column a task sits in doesn't make it started or complete. Columns listed in `startedColumns` and `completedColumns` cause those dates to be _written_ when a task is moved through the CLI or the VSCode extension, but the dates are what everything else reads: `kanbn status`, `kanbn find`, progress, burndown charts and gantt charts.
250
384
 
251
385
  This matters when a task is moved **outside** of kanbn - by editing the index file by hand, or through a git merge. No date is written in that case, so the task keeps whatever state its metadata already described. `kanbn validate` reports tasks whose dates and columns disagree, and `kanbn validate --fix` fills in the missing dates, taking them from the task's history where possible.
252
386
 
package/docs/index.md CHANGED
@@ -9,6 +9,9 @@ To get started quickly, check out the [Quick Start](quick-start.md) guide.
9
9
  - [Quick Start](quick-start.md)
10
10
  - [Index Structure](index-structure.md) — the board file and all project options
11
11
  - [Task Structure](task-structure.md) — task files, metadata, sub-tasks, relations, comments and history
12
+ - [Multiple Boards](multiple-boards.md) — several boards over one shared pool of tasks
13
+ - [Contributors](contributors.md) — an optional list of who works on a workspace, and who "you" are
14
+ - [Actions](actions.md) — declarative rules that fire when a task is created, moved, updated or finished
12
15
  - [Views](views.md) — custom board layouts, columns and lanes
13
16
  - [Filtering and Sorting](filtering-and-sorting.md) — the filter and sorter model shared by `find`, `sort` and views
14
17
  - [Advanced Configuration](advanced-configuration.md)
@@ -16,13 +19,14 @@ To get started quickly, check out the [Quick Start](quick-start.md) guide.
16
19
 
17
20
  ## Examples
18
21
 
19
- The [`example`](../example) directory contains ready-to-run boards:
22
+ The [`example`](../example) directory contains ready-to-run workspaces:
20
23
 
21
24
  | Example | Demonstrates |
22
25
  | --- | --- |
23
26
  | [`example/basic`](../example/basic) | A typical board: columns, tags, workload, sprints |
24
27
  | [`example/views`](../example/views) | Views, lanes, filters and sorters |
25
- | [`example/advanced`](../example/advanced) | A `kanbn.yml` config file, custom fields, column sorting and a custom task template |
28
+ | [`example/boards`](../example/boards) | Multiple boards over one shared pool of tasks, with per-board state fields |
29
+ | [`example/advanced`](../example/advanced) | A `kanbn.yml` config file, custom fields, contributors, column sorting and a custom task template |
26
30
 
27
31
  Run any Kanbn command from inside one of those directories to try it out.
28
32
 
@@ -0,0 +1,259 @@
1
+ # Multiple Boards
2
+
3
+ A Kanbn workspace can hold more than one board. Every board is a markdown file directly inside the kanbn folder, in exactly the same format as `index.md`, and they all share one `tasks/` folder — so a single task can appear on several boards, in a different column on each.
4
+
5
+ Multi-board support is opt-in. A workspace that never creates a second board behaves, and produces files, exactly as it always has.
6
+
7
+ ## Views or boards?
8
+
9
+ > **A view is a lens; a board is a place.** A view re-arranges, filters and groups the tasks that are already on a board — it is read-only, and nothing you do to a view changes where a task lives. A board is somewhere tasks actually live: a task is added to it, moved between its columns, and removed from it. If you want to *look at* the same work differently, use a view. If you want to *track* the same work in a different workflow, use a board.
10
+
11
+ See [Views](views.md) for the read-only option.
12
+
13
+ ## The main board
14
+
15
+ `.kanbn/index.md` is the **main board**. Its slug is `index`, and `main` and `default` always resolve to it. If you have customised `indexFile` in your config file, the main board's slug is that filename without its extension, and `main` / `default` still resolve to it.
16
+
17
+ The main board is the default target of every command, so nothing changes for existing workspaces or scripts.
18
+
19
+ ## Creating a board
20
+
21
+ ```bash
22
+ kanbn init -b design -n "Design Pipeline" -d "Design work across the product" -c Ideas -c Designing -c "Signed Off"
23
+ ```
24
+
25
+ That writes `.kanbn/design.md`:
26
+
27
+ ```markdown
28
+ # Design Pipeline
29
+
30
+ Design work across the product
31
+
32
+ ## Ideas
33
+
34
+ ## Designing
35
+
36
+ ## Signed Off
37
+ ```
38
+
39
+ Board slugs are generated the same way task ids are, so they must be lowercase and hyphenated. A few slugs are reserved and rejected: `index`, `main`, `default`, and the names of your task and archive folders.
40
+
41
+ Running `kanbn init -b design` again updates the board's name, description and columns, in the same way `kanbn init` does for the main board.
42
+
43
+ A new board only picks up the default `startedColumns: [In Progress]` and `completedColumns: [Done]` if it actually has columns by those names — a board with its own workflow does not silently claim that "In Progress" started work and "Done" finished it.
44
+
45
+ ## Listing boards
46
+
47
+ ```bash
48
+ kanbn boards
49
+ ```
50
+
51
+ ```
52
+ 2 boards:
53
+
54
+ index (main board)
55
+ Main Project
56
+ 4 columns, 12 tasks, 25% complete, last modified 2026-08-27T11:49:17.314Z
57
+
58
+ design
59
+ Design Pipeline
60
+ Design work across the product
61
+ 3 columns, 4 tasks, 50% complete, last modified 2026-08-27T11:50:34.602Z
62
+ ```
63
+
64
+ `kanbn boards --tasks` shows the cross-board picture — every task that appears on more than one board, and the column it occupies on each. Add `--all` to include tasks that are only on one board.
65
+
66
+ `kanbn boards --rename design ux --name "UX Pipeline"` renames a board, and `kanbn boards --delete design` deletes one. Deleting a board never deletes task files, but tasks that no other board references become untracked; if that would happen, they are listed and `-f` is required to go ahead.
67
+
68
+ Both listings support `--json`.
69
+
70
+ ## Targeting a board
71
+
72
+ Every board-scoped command takes `-b` / `--board`:
73
+
74
+ ```bash
75
+ kanbn board -b design
76
+ kanbn status -b design
77
+ kanbn task build-tenant-settings-page -b design
78
+ kanbn find -b design --tag Bug
79
+ kanbn validate -b design
80
+ ```
81
+
82
+ The target board is resolved in this order:
83
+
84
+ 1. `--board` / `-b`
85
+ 2. the `KANBN_BOARD` environment variable
86
+ 3. the `defaultBoard` option in your config file
87
+ 4. the main board
88
+
89
+ There is deliberately no stateful "current board" — everything stays visible in your shell and in git-tracked markdown.
90
+
91
+ ## Tracked, untracked, and tracked elsewhere
92
+
93
+ "Tracked" is workspace-scoped: a task is tracked if **any** board references it.
94
+
95
+ ```bash
96
+ kanbn status -b design --untracked
97
+ ```
98
+
99
+ reports two separate things:
100
+
101
+ - `untrackedTasks` — task files that are on no board at all;
102
+ - `tasksOnOtherBoards` — tasks that other boards track but this one doesn't, naming the board and column each one sits in. This is the useful half: it is the "what could I pull onto this board" list.
103
+
104
+ Once a workspace has more than one board, `kanbn status` also reports a `board` field naming the board it applied to. A single-board workspace produces exactly the output it always has.
105
+
106
+ `kanbn task <task-id>` lists the boards a task appears on, and the column it occupies on each, whenever there is more than one.
107
+
108
+ `kanbn find --all-boards` searches every board, annotating each result with the board and column it occupies. Without the flag, `find` is scoped to the target board as it always has been.
109
+
110
+ ## Working with tasks across boards
111
+
112
+ Every board-scoped command takes `-b`, so a task can be created, moved, commented on and removed on whichever board you mean:
113
+
114
+ ```bash
115
+ kanbn add -n "Task" -b design -c Ideas
116
+ kanbn move build-settings-page -b design -c Designing
117
+ kanbn remove build-settings-page -b design
118
+ kanbn edit build-settings-page -b design --tag Bug
119
+ ```
120
+
121
+ `kanbn add -b` is repeatable, so one task can land on several boards at once. Each `-b` pairs with the `-c` in the same position; boards with no `-c` of their own use the first one given, and a board that hasn't got that column falls back to its first column with a notice:
122
+
123
+ ```bash
124
+ kanbn add -n "Task" -b index -b design -b ops -c Todo -c Designing
125
+ ```
126
+
127
+ With a *single* board an unknown column is still an error — the fallback only applies when several boards are named at once, where a column can legitimately exist on one and not another.
128
+
129
+ ### Adding, moving and removing membership
130
+
131
+ - `kanbn move <task> -b design -c Designing` moves the task on the design board. If it isn't on that board yet, it is **added**, with a notice. `--no-add` turns that back into an error.
132
+ The main board never adds implicitly: moving an untracked task there is an error either way.
133
+ - `kanbn remove <task> -b design --index` removes the task from the design board only. Boards own membership, so the task file and every other board are untouched.
134
+ - Without `--index`, `remove` deletes the task file as well, and that is refused while another board still references it. `--all-boards` removes the task from every board and then deletes the file.
135
+ - `kanbn rename <task>` rewrites the id on **every** board that references it, because the id is the file name.
136
+
137
+ Membership changes are recorded in the task's history as `added` and `removed` events, so a board's history includes the task arriving and leaving. See [Task Structure](task-structure.md#history).
138
+
139
+ ### Archiving and restoring
140
+
141
+ Archiving removes a task from every board and remembers where it was on each:
142
+
143
+ ```yaml
144
+ column: In Progress # the main board, as it always has been
145
+ columns: # only written when the task was on more than one board
146
+ design: Designing
147
+ ops: Backlog
148
+ ```
149
+
150
+ `kanbn restore <task>` puts it back on every board it was on, in the column it occupied on each. A board that has since been deleted, or a column that has since gone, is reported and skipped rather than failing the restore. `kanbn restore <task> -b design` restores to that board only.
151
+
152
+ ## Options and configuration
153
+
154
+ Options resolve in layers when a board is loaded:
155
+
156
+ ```
157
+ defaults < kanbn.yml / kanbn.json < boards.<slug> in config < board file front matter
158
+ ```
159
+
160
+ Array options — `startedColumns`, `completedColumns`, `hiddenColumns`, `sprints`, `views` — **override** rather than merge. Element-wise merging of an ordered array has no predictable semantics.
161
+
162
+ Some options describe the workspace rather than a single board:
163
+
164
+ | Option | Scope |
165
+ | --- | --- |
166
+ | `hiddenColumns`, `startedColumns`, `completedColumns`, `startedField`, `completedField`, `columnSorting`, `views`, `taskTemplate`, `verbose` | Board |
167
+ | `sprints` | Workspace, overridable per board |
168
+ | `customFields` | Workspace — task files are shared, so a field defined on one board must parse on all |
169
+ | `contributors` | Workspace — `assigned: gordon` has to mean the same person on every board that references the task |
170
+ | `defaultTaskWorkload`, `taskWorkloadTags` | Workspace — workload is a property of the task, not the board |
171
+ | `dateFormat` | Workspace only — dates should read identically everywhere |
172
+ | `mainFolder`, `indexFile`, `taskFolder`, `archiveFolder`, `defaultBoard`, `boards` | Workspace only — layout, meaningless in a board file |
173
+
174
+ A workspace-scoped option in a *secondary* board's front matter is ignored, because one shared task file has to parse identically for every board that references it.
175
+
176
+ Where options are written:
177
+
178
+ - **Main board** — unchanged. If a config file exists, options go there; otherwise they go into `index.md`'s front matter.
179
+ - **Secondary boards** — board-scoped options are always written to that board file's front matter. A board-local operation never writes to the workspace config file.
180
+
181
+ If you have no config file, the workspace options live in the main board's front matter, so loading any secondary board also reads `index.md`. That costs one extra file read per process, not per board.
182
+
183
+ **Only workspace-scoped options are inherited from `index.md`'s front matter.** With no config file that file is doing double duty — it holds the workspace options *and* the main board's own board-scoped ones — so `startedColumns`, `completedColumns`, `hiddenColumns`, `views`, custom field column linkages and the rest stay with the main board. A secondary board that happens to share a column name with it does not silently pick up its behaviour; if you want those columns to mean the same thing on another board, declare them in that board's front matter.
184
+
185
+ A `kanbn.yml` / `kanbn.json` config file is different: it is workspace-level by construction, so a board-scoped option declared there is a deliberate statement about every board, and is inherited.
186
+
187
+ ### Per-board state fields
188
+
189
+ Because `started` and `completed` live on the shared task file, a board that needs its own notion of started or completed can point at different metadata fields:
190
+
191
+ ```markdown
192
+ ---
193
+ startedField: designStartedAt
194
+ completedField: designSignedOffAt
195
+ startedColumns:
196
+ - Designing
197
+ completedColumns:
198
+ - Signed Off
199
+ ---
200
+
201
+ # Design Pipeline
202
+ ```
203
+
204
+ Declare `designStartedAt` and `designSignedOffAt` once as workspace-scoped `customFields` of type `date`. The design board then stamps and reads its own fields, and the main board's `completed` can never leak into it. See [Index Structure](index-structure.md) for `customFields`.
205
+
206
+ ### Per-board sprints
207
+
208
+ Sprints are workspace-level by default: a board reads the workspace list unless it declares its own `sprints` in its front matter, in which case that list **replaces** the workspace one entirely.
209
+
210
+ `kanbn sprint -b design` appends to the workspace list, and says so, unless the design board has its own list — in which case it appends there, auto-named `Design Pipeline Sprint 2`. A board-local list is never created implicitly: forking is permanent and invisible in the CLI, so it stays a deliberate front-matter edit.
211
+
212
+ The cost of forking is worth stating plainly: a board with its own list can no longer see workspace sprints at all, so `kanbn status -b design -s "Foundation Sprint"` will fail to find it.
213
+
214
+ `--sprint` can't be combined with `--all-boards`, because sprint numbers and names are relative to one board's list.
215
+
216
+ `kanbn burndown -b <slug>` on a board that declares no `startedColumns` errors rather than drawing an empty chart — a board with no notion of work in progress has nothing to burn down. Gantt charts don't use sprints at all, so they are unaffected.
217
+
218
+ Note that a task on two boards contributes its workload to both boards' charts. That is correct for per-board reporting; it is only double counting if you add the charts together.
219
+
220
+ ### History and charts are board-scoped
221
+
222
+ `kanbn history -b design` shows the events that happened on the design board, plus the ones that aren't board-scoped at all. Burndown datapoints are placed and annotated the same way, so activity on one board never puts markers on another board's chart.
223
+
224
+ `progress` and `archived` events are never filtered: progress is a property of the task, and archiving removes a task from every board.
225
+
226
+ ### Validation
227
+
228
+ `kanbn validate` reports multi-board problems as warnings once a workspace has more than one board:
229
+
230
+ - a workspace-scoped option in a secondary board's front matter, which is ignored;
231
+ - several boards stamping the same shared `completedField`, where whichever board is touched first silently owns that date for every board;
232
+ - sprints out of chronological order, which the "current sprint" logic assumes;
233
+ - a task on no board at all, or referenced by a board with no file behind it;
234
+ - a task in two columns on the same board;
235
+ - history events naming a board that has been deleted;
236
+ - markdown files beside the boards that don't parse as one, and files excluded by `boards.exclude`.
237
+
238
+ `kanbn validate --all-boards` validates every board and its tasks, rather than just the target board.
239
+
240
+ ### Board discovery
241
+
242
+ Boards are discovered by globbing `*.md` directly inside the kanbn folder, so `tasks/` and `archive/` are excluded by construction. A file counts as a board if it parses as an index — that is, if it has an H1. Files that don't parse are ignored during ordinary commands and reported by `kanbn validate`.
243
+
244
+ Where discovery isn't enough, the `boards` config key takes over:
245
+
246
+ ```yaml
247
+ boards:
248
+ exclude:
249
+ - notes.md # not a board, just a scratch file
250
+ order: # display order for `kanbn boards`
251
+ - index
252
+ - design
253
+ - ops
254
+ design: # options applied to the design board
255
+ hiddenColumns:
256
+ - Ideas
257
+ ```
258
+
259
+ `exclude` and `order` are reserved; every other key is a board slug whose value is that board's options.
@@ -89,10 +89,30 @@ kanbn board -v by-assignee
89
89
 
90
90
  See [views](views.md) for columns, lanes, filters and sorters.
91
91
 
92
+ ## Multiple boards
93
+
94
+ A workspace can hold more than one board, all sharing the same pool of tasks:
95
+
96
+ ```
97
+ kanbn init -b design -n "Design Pipeline" -c Ideas -c Designing -c "Signed Off"
98
+ kanbn boards
99
+ ```
100
+
101
+ Every board-scoped command takes `-b`, so a task can sit in `In Progress` on the main board and `Designing` on the design board at the same time:
102
+
103
+ ```
104
+ kanbn move my-new-task -b design -c Designing
105
+ kanbn board -b design
106
+ kanbn boards --tasks
107
+ ```
108
+
109
+ See [multiple boards](multiple-boards.md) for membership, per-board options and per-board sprints.
110
+
92
111
  ## Next steps
93
112
 
94
113
  - [Index structure](index-structure.md) — project settings, tags and workload calculations
114
+ - [Multiple boards](multiple-boards.md) — several boards over one shared pool of tasks
95
115
  - [Task structure](task-structure.md) — task metadata, sub-tasks, relations and comments
96
116
  - [Views](views.md) and [filtering and sorting](filtering-and-sorting.md)
97
117
  - [Advanced configuration](advanced-configuration.md) — separate config files and custom paths
98
- - The [`example`](../example) directory contains three ready-to-run boards you can experiment with
118
+ - The [`example`](../example) directory contains four ready-to-run workspaces you can experiment with
@@ -100,13 +100,17 @@ An array of tags to apply to this task.
100
100
 
101
101
  ### `assigned`
102
102
 
103
- The name of the user this task is assigned to.
103
+ The name of the user this task is assigned to. This is free text: any string is a valid assignee, and Kanbn never rejects or rewrites one.
104
+
105
+ A workspace can declare a list of [contributors](contributors.md), which gives each person a canonical spelling, autocompletes this field in the interactive prompts and lets `kanbn find --assigned @me` work. Contributors are advisory — `assigned` stays free text either way, and a name that isn't in the list is still perfectly valid.
106
+
107
+ A task has one assignee. `assigned` is a single string in the schema, the filters, the board template and the reporting commands, and there is no multiple-assignee form.
104
108
 
105
109
  ### `progress`
106
110
 
107
111
  The amount of progress for this task. This should be between 0 (not started) and 1 (complete).
108
112
 
109
- Progress is not derived from sub-tasks. If the task has no `progress` value it counts as 0, and a task that is considered complete always counts as 1 regardless of this value. A task is considered complete if it has a `completed` date or is in one of the [`completedColumns`](index-structure.md#completedcolumns).
113
+ Progress is not derived from sub-tasks. If the task has no `progress` value it counts as 0, and a task that is considered complete always counts as 1 regardless of this value. A task is considered complete if its [`completedField`](index-structure.md#completedfield) `completed` by default — has a date. The column a task sits in doesn't make it complete; see [Index Structure](index-structure.md#completedcolumns).
110
114
 
111
115
  ### `started`
112
116
 
@@ -150,6 +154,8 @@ The `depends-on` relation type is reserved for gantt scheduling. It means the cu
150
154
 
151
155
  An array of comments, where each comment has an `author` and `date` property, and some text.
152
156
 
157
+ `author` is free text, exactly like [`assigned`](#assigned), and it is optional — a comment added on a machine with no resolvable user has an empty author rather than failing to save. When the workspace declares [contributors](contributors.md), the default author is the canonical name for the current user.
158
+
153
159
  ## History
154
160
 
155
161
  An optional array of structured lifecycle events used for richer timeline reporting (including burndown).
@@ -157,6 +163,8 @@ An optional array of structured lifecycle events used for richer timeline report
157
163
  Supported event types:
158
164
 
159
165
  - `created`: requires `date` and `column`
166
+ - `added`: requires `date` and `column` — the task joined a board
167
+ - `removed`: requires `date` — the task left a board
160
168
  - `moved`: requires `date`, `fromColumn`, `toColumn`
161
169
  - `progress`: requires `date`, `fromProgress`, `toProgress`
162
170
  - `archived`: requires `date`, `fromColumn`
@@ -166,4 +174,24 @@ Notes:
166
174
 
167
175
  - History entries are stored in the reserved `## History` section as list items.
168
176
  - `date` should be an ISO timestamp.
169
- - Additional fields (for example `author`) are preserved if present.
177
+ - Every event carries an optional `author`, naming the [current user](contributors.md#the-current-user) at the time it happened. The key is omitted entirely when there is no resolvable user, so a machine with no git identity writes exactly the history it always has.
178
+ - Any other additional field is preserved if present.
179
+ - An event type this version of Kanbn doesn't recognise is preserved and skipped rather than rejected, so a task file written by a newer version stays readable.
180
+
181
+ ### Board attribution
182
+
183
+ In a workspace with [multiple boards](multiple-boards.md), an event carries an optional `board` key naming the board it happened on:
184
+
185
+ ```markdown
186
+ ## History
187
+
188
+ - type: moved
189
+ date: 2026-07-14T09:12:00.000Z
190
+ fromColumn: Ideas
191
+ toColumn: Designing
192
+ board: design
193
+ ```
194
+
195
+ Events on the **main board carry no `board` key**, so a single-board workspace writes exactly the history it always has, and every existing task file stays valid.
196
+
197
+ `archived` and `progress` events are never board-scoped: archiving removes a task from every board, and progress is a property of the task rather than of a board.
package/example/README.md CHANGED
@@ -48,6 +48,29 @@ kanbn board -v everything
48
48
  kanbn board -v triage --json
49
49
  ```
50
50
 
51
+ ## [`boards`](boards)
52
+
53
+ Several boards over one shared pool of tasks. See [Multiple Boards](../docs/multiple-boards.md).
54
+
55
+ - an engineering board in [`index.md`](boards/.kanbn/index.md) and a design board in
56
+ [`design.md`](boards/.kanbn/design.md), sharing one `tasks/` folder
57
+ - three tasks that sit in **different columns on each board** - including one that design has started
58
+ and engineering hasn't
59
+ - `startedField` / `completedField` on the design board, so design progress is tracked in its own
60
+ metadata fields rather than in the shared `started` / `completed` dates
61
+ - board-scoped options in each board's front matter, and workspace-scoped `customFields` in the main
62
+ board's - where they are inherited by both
63
+
64
+ ```bash
65
+ kanbn boards
66
+ kanbn boards --tasks
67
+ kanbn board -b design
68
+ kanbn status -b design --untracked
69
+ kanbn task build-tenant-settings-page
70
+ kanbn find --all-boards
71
+ kanbn validate --all-boards
72
+ ```
73
+
51
74
  ## [`advanced`](advanced)
52
75
 
53
76
  Project configuration beyond the defaults. See