@basementuniverse/kanbn 1.1.1 → 2.1.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 (182) hide show
  1. package/README.md +3 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/coverage-summary.json +31 -0
  5. package/coverage/favicon.png +0 -0
  6. package/coverage/index.html +146 -0
  7. package/coverage/kanbn/index.html +116 -0
  8. package/coverage/kanbn/index.js.html +217 -0
  9. package/coverage/kanbn/src/board.js.html +646 -0
  10. package/coverage/kanbn/src/controller/add.js.html +1729 -0
  11. package/coverage/kanbn/src/controller/archive.js.html +277 -0
  12. package/coverage/kanbn/src/controller/board.js.html +217 -0
  13. package/coverage/kanbn/src/controller/burndown.js.html +607 -0
  14. package/coverage/kanbn/src/controller/comment.js.html +397 -0
  15. package/coverage/kanbn/src/controller/edit.js.html +2920 -0
  16. package/coverage/kanbn/src/controller/find.js.html +1597 -0
  17. package/coverage/kanbn/src/controller/gantt.js.html +1342 -0
  18. package/coverage/kanbn/src/controller/help.js.html +115 -0
  19. package/coverage/kanbn/src/controller/history.js.html +400 -0
  20. package/coverage/kanbn/src/controller/index.html +431 -0
  21. package/coverage/kanbn/src/controller/init.js.html +511 -0
  22. package/coverage/kanbn/src/controller/move.js.html +514 -0
  23. package/coverage/kanbn/src/controller/remove-all.js.html +226 -0
  24. package/coverage/kanbn/src/controller/remove.js.html +307 -0
  25. package/coverage/kanbn/src/controller/rename.js.html +397 -0
  26. package/coverage/kanbn/src/controller/restore.js.html +268 -0
  27. package/coverage/kanbn/src/controller/sort.js.html +1318 -0
  28. package/coverage/kanbn/src/controller/sprint.js.html +361 -0
  29. package/coverage/kanbn/src/controller/status.js.html +310 -0
  30. package/coverage/kanbn/src/controller/task.js.html +244 -0
  31. package/coverage/kanbn/src/controller/validate.js.html +367 -0
  32. package/coverage/kanbn/src/controller/version.js.html +100 -0
  33. package/coverage/kanbn/src/index.html +191 -0
  34. package/coverage/kanbn/src/main.js.html +9787 -0
  35. package/coverage/kanbn/src/parse-index.js.html +1063 -0
  36. package/coverage/kanbn/src/parse-markdown.js.html +202 -0
  37. package/coverage/kanbn/src/parse-task.js.html +2191 -0
  38. package/coverage/kanbn/src/utility.js.html +616 -0
  39. package/coverage/lcov.info +11837 -0
  40. package/coverage/prettify.css +1 -0
  41. package/coverage/prettify.js +2 -0
  42. package/coverage/sort-arrow-sprite.png +0 -0
  43. package/coverage/sorter.js +210 -0
  44. package/coverage/tmp/coverage-214292-1787777191526-0.json +1 -0
  45. package/coverage/tmp/coverage-214293-1787777184569-0.json +1 -0
  46. package/docs/advanced-configuration.md +25 -0
  47. package/docs/commands/add.txt +19 -0
  48. package/docs/commands/archive.txt +5 -0
  49. package/docs/commands/board.txt +8 -1
  50. package/docs/commands/boards.txt +34 -0
  51. package/docs/commands/burndown.txt +8 -4
  52. package/docs/commands/comment.txt +5 -0
  53. package/docs/commands/edit.txt +27 -0
  54. package/docs/commands/find.txt +56 -1
  55. package/docs/commands/gantt.txt +5 -0
  56. package/docs/commands/help.txt +2 -0
  57. package/docs/commands/history.txt +5 -0
  58. package/docs/commands/init.txt +13 -0
  59. package/docs/commands/move.txt +10 -1
  60. package/docs/commands/remove.txt +12 -1
  61. package/docs/commands/rename.txt +5 -0
  62. package/docs/commands/restore.txt +6 -0
  63. package/docs/commands/sort.txt +18 -2
  64. package/docs/commands/sprint.txt +9 -0
  65. package/docs/commands/status.txt +10 -1
  66. package/docs/commands/task.txt +5 -0
  67. package/docs/commands/validate.txt +27 -0
  68. package/docs/filtering-and-sorting.md +218 -0
  69. package/docs/index-structure.md +94 -19
  70. package/docs/index.md +28 -2
  71. package/docs/migration-2.0.md +92 -0
  72. package/docs/multiple-boards.md +258 -0
  73. package/docs/quick-start.md +57 -1
  74. package/docs/task-structure.md +33 -3
  75. package/docs/views.md +229 -0
  76. package/example/README.md +93 -0
  77. package/example/advanced/.kanbn/index.md +26 -0
  78. package/example/advanced/.kanbn/tasks/add-rate-limiting.md +18 -0
  79. package/example/advanced/.kanbn/tasks/add-sso-support.md +15 -0
  80. package/example/advanced/.kanbn/tasks/migrate-to-new-cdn.md +14 -0
  81. package/example/advanced/.kanbn/tasks/refresh-marketing-site.md +14 -0
  82. package/example/advanced/.kanbn/tasks/rotate-api-keys.md +15 -0
  83. package/example/advanced/.kanbn/tasks/split-billing-service.md +17 -0
  84. package/example/advanced/.kanbn/tasks/upgrade-node-runtime.md +19 -0
  85. package/example/advanced/kanbn.yml +59 -0
  86. package/example/boards/.kanbn/design.md +31 -0
  87. package/example/boards/.kanbn/index.md +44 -0
  88. package/example/boards/.kanbn/tasks/add-usage-alert-emails.md +19 -0
  89. package/example/boards/.kanbn/tasks/build-tenant-settings-page.md +43 -0
  90. package/example/boards/.kanbn/tasks/create-organization-switcher.md +44 -0
  91. package/example/boards/.kanbn/tasks/design-onboarding-checklist.md +22 -0
  92. package/example/boards/.kanbn/tasks/refresh-marketing-site.md +28 -0
  93. package/example/boards/.kanbn/tasks/ship-billing-portal.md +29 -0
  94. package/example/views/.kanbn/index.md +140 -0
  95. package/example/views/.kanbn/tasks/add-audit-log-export.md +18 -0
  96. package/example/views/.kanbn/tasks/add-dark-mode-toggle.md +16 -0
  97. package/example/views/.kanbn/tasks/build-webhook-retry-queue.md +15 -0
  98. package/example/views/.kanbn/tasks/fix-avatar-upload-crash.md +13 -0
  99. package/example/views/.kanbn/tasks/fix-expired-session-redirect.md +15 -0
  100. package/example/views/.kanbn/tasks/investigate-offline-mode.md +12 -0
  101. package/example/views/.kanbn/tasks/rewrite-search-indexer.md +12 -0
  102. package/example/views/.kanbn/tasks/tidy-up-settings-copy.md +12 -0
  103. package/package.json +12 -9
  104. package/routes/add.json +35 -11
  105. package/routes/archive.json +10 -2
  106. package/routes/board.json +11 -3
  107. package/routes/boards.json +30 -0
  108. package/routes/burndown.json +23 -7
  109. package/routes/comment.json +14 -4
  110. package/routes/edit.json +33 -10
  111. package/routes/find.json +37 -12
  112. package/routes/gantt.json +20 -6
  113. package/routes/history.json +39 -25
  114. package/routes/init.json +3 -1
  115. package/routes/move.json +22 -6
  116. package/routes/remove.json +15 -4
  117. package/routes/rename.json +11 -3
  118. package/routes/restore.json +8 -2
  119. package/routes/sort.json +35 -11
  120. package/routes/sprint.json +14 -4
  121. package/routes/status.json +23 -7
  122. package/routes/task.json +10 -2
  123. package/routes/validate.json +19 -4
  124. package/skills/kanbn-plan/SKILL.md +10 -1
  125. package/skills/kanbn-plan/references/index-structure.md +1 -0
  126. package/skills/kanbn-plan/references/task-structure.md +2 -0
  127. package/skills/kanbn-replan/SKILL.md +6 -1
  128. package/src/board.js +2 -1
  129. package/src/controller/add.js +52 -46
  130. package/src/controller/archive.js +8 -4
  131. package/src/controller/board.js +9 -4
  132. package/src/controller/boards.js +140 -0
  133. package/src/controller/burndown.js +9 -5
  134. package/src/controller/comment.js +9 -5
  135. package/src/controller/edit.js +74 -17
  136. package/src/controller/find.js +12 -9
  137. package/src/controller/gantt.js +8 -4
  138. package/src/controller/history.js +8 -4
  139. package/src/controller/init.js +39 -4
  140. package/src/controller/move.js +69 -15
  141. package/src/controller/remove.js +34 -11
  142. package/src/controller/rename.js +8 -4
  143. package/src/controller/restore.js +23 -8
  144. package/src/controller/sort.js +19 -3
  145. package/src/controller/sprint.js +31 -7
  146. package/src/controller/status.js +20 -4
  147. package/src/controller/task.js +22 -9
  148. package/src/controller/validate.js +139 -23
  149. package/src/git-user-name.js +19 -0
  150. package/src/main.d.ts +184 -6
  151. package/src/main.js +1642 -221
  152. package/src/parse-index.js +17 -0
  153. package/src/parse-markdown.js +3 -1
  154. package/src/parse-task.js +44 -1
  155. package/src/utility.js +166 -1
  156. package/PLANS.md +0 -9
  157. /package/example/{.kanbn → basic/.kanbn}/index.md +0 -0
  158. /package/example/{.kanbn → basic/.kanbn}/tasks/add-basic-activity-feed.md +0 -0
  159. /package/example/{.kanbn → basic/.kanbn}/tasks/add-passwordless-login-option.md +0 -0
  160. /package/example/{.kanbn → basic/.kanbn}/tasks/add-usage-alert-email-thresholds.md +0 -0
  161. /package/example/{.kanbn → basic/.kanbn}/tasks/build-email-template-system.md +0 -0
  162. /package/example/{.kanbn → basic/.kanbn}/tasks/build-invoice-download-endpoint.md +0 -0
  163. /package/example/{.kanbn → basic/.kanbn}/tasks/build-tenant-settings-page.md +0 -0
  164. /package/example/{.kanbn → basic/.kanbn}/tasks/create-organization-switcher.md +0 -0
  165. /package/example/{.kanbn → basic/.kanbn}/tasks/create-sandbox-environment-provisioner.md +0 -0
  166. /package/example/{.kanbn → basic/.kanbn}/tasks/create-self-serve-cancellation-flow.md +0 -0
  167. /package/example/{.kanbn → basic/.kanbn}/tasks/define-product-pricing-strategy.md +0 -0
  168. /package/example/{.kanbn → basic/.kanbn}/tasks/design-onboarding-checklist.md +0 -0
  169. /package/example/{.kanbn → basic/.kanbn}/tasks/design-team-invite-expiry-flow.md +0 -0
  170. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-data-retention-policy-jobs.md +0 -0
  171. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-feature-flags-foundation.md +0 -0
  172. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-project-creation-wizard.md +0 -0
  173. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-stripe-webhook-signature-check.md +0 -0
  174. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-team-permissions-ui.md +0 -0
  175. /package/example/{.kanbn → basic/.kanbn}/tasks/implement-user-signup-and-login.md +0 -0
  176. /package/example/{.kanbn → basic/.kanbn}/tasks/integrate-crm-lead-sync.md +0 -0
  177. /package/example/{.kanbn → basic/.kanbn}/tasks/legal-review-terms-and-privacy.md +0 -0
  178. /package/example/{.kanbn → basic/.kanbn}/tasks/migrate-legacy-events-to-new-schema.md +0 -0
  179. /package/example/{.kanbn → basic/.kanbn}/tasks/optimize-dashboard-first-load.md +0 -0
  180. /package/example/{.kanbn → basic/.kanbn}/tasks/prototype-report-export-scheduler.md +0 -0
  181. /package/example/{.kanbn → basic/.kanbn}/tasks/publish-internal-qa-checklist.md +0 -0
  182. /package/example/{.kanbn → basic/.kanbn}/tasks/setup-ci-pipeline.md +0 -0
@@ -67,23 +67,23 @@ taskTemplate: ""
67
67
  dateFormat: ""
68
68
  views:
69
69
  - name: My view 1
70
+ filters:
71
+ tag: Bug
70
72
  columns:
71
- "Column 1":
72
- - hidden: false
73
- filters:
74
- name: /test/
75
- workload:
76
- - 1
77
- - 5
78
- sorters:
79
- - field: name
80
- filter: ""
81
- order: ascending
82
- lanes:
83
- - name: "Lane 1"
84
- filter:
73
+ - name: Column 1
74
+ filters:
75
+ name: test
76
+ workload:
77
+ - 1
78
+ - 5
79
+ sorters:
85
80
  - field: name
86
81
  filter: ""
82
+ order: ascending
83
+ lanes:
84
+ - name: Lane 1
85
+ filters:
86
+ assigned: username
87
87
  customFields:
88
88
  - name: 'myCustomField'
89
89
  type: 'date'
@@ -95,18 +95,71 @@ customFields:
95
95
 
96
96
  A list of column names. These columns will be hidden from the kanbn board.
97
97
 
98
+ This only affects the default board layout. Tasks in hidden columns are still tracked, still show up in `kanbn find` and `kanbn status`, and are still shown by any [view](views.md) that defines its own `columns`.
99
+
98
100
  ### `startedColumns`
99
101
 
100
102
  A list of column names. When a task is created in or dragged into one of these columns, the task's `started` date will be set to the current time, unless the task already has a 'started' date.
101
103
 
102
104
  ### `completedColumns`
103
105
 
104
- 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. If the task already has a `completed` date, this date will be updated.
106
+ 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
+
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.
109
+
110
+ ### `startedField`
111
+
112
+ The name of the metadata field that holds a task's started date. Defaults to `started`.
113
+
114
+ ### `completedField`
115
+
116
+ The name of the metadata field that holds a task's completed date. Defaults to `completed`.
117
+
118
+ Pointing these at custom date fields lets a board track its own started/completed state without touching the shared `started` and `completed` fields:
119
+
120
+ ```yaml
121
+ completedField: designSignedOffAt
122
+ completedColumns:
123
+ - Signed Off
124
+ customFields:
125
+ - name: designSignedOffAt
126
+ type: date
127
+ ```
128
+
129
+ ### `verbose`
130
+
131
+ 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
+
133
+ ### `defaultBoard`
134
+
135
+ 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.
136
+
137
+ See [multiple boards](multiple-boards.md).
138
+
139
+ ### `boards`
140
+
141
+ 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.
142
+
143
+ ```yaml
144
+ boards:
145
+ exclude:
146
+ - notes.md
147
+ order:
148
+ - index
149
+ - design
150
+ design:
151
+ hiddenColumns:
152
+ - Ideas
153
+ ```
154
+
155
+ Workspace-scoped. See [multiple boards](multiple-boards.md).
105
156
 
106
157
  ### `sprints`
107
158
 
108
159
  A list of sprints. Each sprint will have `start`, `name` and `description` properties.
109
160
 
161
+ 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).
162
+
110
163
  Run `kanbn sprint --help` for more information.
111
164
 
112
165
  ### `defaultTaskWorkload`
@@ -123,7 +176,9 @@ An associative array of column names and sorters. Each column can have an array
123
176
 
124
177
  The `filter` property can be used to transform values before sorting.
125
178
 
126
- Run `kanbn sort --help` for more information.
179
+ Columns listed here are re-sorted every time Kanbn writes the index, so the order survives `kanbn add`, `kanbn move` and `kanbn validate --save`.
180
+
181
+ See [Filtering and Sorting](filtering-and-sorting.md#sorters), or run `kanbn sort --help`.
127
182
 
128
183
  ### `taskTemplate`
129
184
 
@@ -137,6 +192,7 @@ updated
137
192
  started
138
193
  completed
139
194
  due
195
+ assigned
140
196
  tags
141
197
  subTasks
142
198
  relations
@@ -149,6 +205,8 @@ progress
149
205
  {...customFields}
150
206
  ```
151
207
 
208
+ *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.*
209
+
152
210
  The default task template is `^+^_${overdue ? '^R' : ''}${name}^: ${created ? ('\\n^-^/' + created) : ''}`.
153
211
 
154
212
  This string can contain markup sequences. See [terminal-kit](https://github.com/cronvel/terminal-kit/blob/21607fb51749853dd9193c6aaf205b14c63b2768/doc/markup.md#markup) for markup reference.
@@ -161,11 +219,13 @@ The date format to use for dates on the kanbn board and burndown chart views. Se
161
219
 
162
220
  ### `views`
163
221
 
164
- An array of views that can be used to customize how the kanbn board is displayed.
222
+ An array of views that can be used to customize how the kanbn board is displayed. Select a view with `kanbn board --view "name"`.
223
+
224
+ Each view has a required `name`, an optional root `filters` object, an optional array of `columns` and an optional array of `lanes`. Each column has a required `name` and optional `filters` and `sorters`. Each lane has a required `name` and optional `filters`.
165
225
 
166
- Each view should have a `name` property (run `kanbn board --view "name"` to specify which view to use), a list of columns and a list of lanes.
226
+ If `columns` is omitted, the index columns are used (minus `hiddenColumns`). If `lanes` is omitted, a single lane containing all matching tasks is used.
167
227
 
168
- Each column can be hidden, filtered or sorted. Each lane has a list of filters.
228
+ See [Views](views.md) for a full description and worked examples, and [Filtering and Sorting](filtering-and-sorting.md) for the filter and sorter formats.
169
229
 
170
230
  ### `customFields`
171
231
 
@@ -204,3 +264,18 @@ Here's an example of how this could be used:
204
264
  - Testing
205
265
  ```
206
266
  * 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.
267
+
268
+ ## Task state
269
+
270
+ Whether a task has started or been completed is decided by its metadata dates, and nothing else:
271
+
272
+ * a task has started if its `started` field is set (or whichever field `startedField` names);
273
+ * a task is complete if its `completed` field is set (or whichever field `completedField` names).
274
+
275
+ 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.
276
+
277
+ 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.
278
+
279
+ To clear a date - for example to reopen a task that was completed - use `kanbn edit "task-id" --unset completed`.
280
+
281
+ _Note: before version 2.0.0, a task sitting in a completed column counted as complete even with no `completed` date. See [Migrating to 2.0.0](migration-2.0.md)._
package/docs/index.md CHANGED
@@ -7,6 +7,32 @@ To get started quickly, check out the [Quick Start](quick-start.md) guide.
7
7
  ## Contents
8
8
 
9
9
  - [Quick Start](quick-start.md)
10
- - [Index Structure](index-structure.md)
11
- - [Task Structure](task-structure.md)
10
+ - [Index Structure](index-structure.md) — the board file and all project options
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
+ - [Views](views.md) — custom board layouts, columns and lanes
14
+ - [Filtering and Sorting](filtering-and-sorting.md) — the filter and sorter model shared by `find`, `sort` and views
12
15
  - [Advanced Configuration](advanced-configuration.md)
16
+ - [Migrating to 2.0.0](migration-2.0.md) — separate config files and custom folder locations
17
+
18
+ ## Examples
19
+
20
+ The [`example`](../example) directory contains ready-to-run workspaces:
21
+
22
+ | Example | Demonstrates |
23
+ | --- | --- |
24
+ | [`example/basic`](../example/basic) | A typical board: columns, tags, workload, sprints |
25
+ | [`example/views`](../example/views) | Views, lanes, filters and sorters |
26
+ | [`example/boards`](../example/boards) | Multiple boards over one shared pool of tasks, with per-board state fields |
27
+ | [`example/advanced`](../example/advanced) | A `kanbn.yml` config file, custom fields, column sorting and a custom task template |
28
+
29
+ Run any Kanbn command from inside one of those directories to try it out.
30
+
31
+ ## Command help
32
+
33
+ Every command has built-in help:
34
+
35
+ ```bash
36
+ kanbn help <command>
37
+ kanbn <command> --help
38
+ ```
@@ -0,0 +1,92 @@
1
+ # Migrating to 2.0.0
2
+
3
+ Version 2.0.0 changes how kanbn decides whether a task has started or been completed. Everything else about the file format is unchanged: your `index.md` and task files are read and written exactly as before, and no migration step is required to open an existing board.
4
+
5
+ ## What changed
6
+
7
+ **Before**, a task counted as complete if *either* it had a `completed` date *or* it was sitting in a column listed in `completedColumns`. The same applied to `started` and `startedColumns` when drawing burndown charts.
8
+
9
+ **Now**, only the dates count. A task has started if its `started` date is set, and it is complete if its `completed` date is set.
10
+
11
+ Columns still cause those dates to be written. Moving a task into a completed column through `kanbn move`, `kanbn edit -c`, or the VSCode extension sets its `completed` date exactly as it did before. What's changed is that the column no longer *stands in for* the date.
12
+
13
+ ## Why
14
+
15
+ Inferring state from columns produced results that contradicted themselves and couldn't be corrected:
16
+
17
+ * `kanbn status --due` could report a task as `completed: true` with `completedDate: null` - complete, but with no record of when.
18
+ * Burndown charts substituted a task's **created** date when it had no started date, inventing timings unrelated to when the work happened.
19
+ * State could only ever move forwards. Once a `completed` date was written, moving the task back out of the column didn't clear it, and there was no command that could.
20
+ * A task could be complete on one board and not another purely because of what its columns were called - which becomes a real problem now that a task can appear on several boards.
21
+
22
+ ## What you might notice
23
+
24
+ * **Tasks moved by hand.** If you moved a task between columns by editing `index.md` directly, or a git merge moved it, no date was written. Those tasks will now show as not started or not complete.
25
+ * **Burndown charts will look different** for any board where dates and columns disagreed.
26
+ * **Generated boards.** Tasks written by a tool or an AI assistant may sit in `Done` without a `completed` date.
27
+
28
+ Boards that have only ever been driven through the CLI or the VSCode extension already have the right dates and won't change.
29
+
30
+ ## Fixing up an existing board
31
+
32
+ ```
33
+ kanbn validate
34
+ ```
35
+
36
+ reports every task whose dates and column disagree, in both directions:
37
+
38
+ ```
39
+ 2 tasks have dates that don't match their column:
40
+ bravo: task is in column "In Progress" but has no started date
41
+ alpha: task is in column "Done" but has no completed date
42
+ ```
43
+
44
+ ```
45
+ kanbn validate --fix
46
+ ```
47
+
48
+ fills in the missing dates. Each date is taken from the task's own history, where the move into that column is recorded, so the backfilled dates reflect when the move actually happened:
49
+
50
+ ```
51
+ Filled in 2 missing dates:
52
+ bravo: started set to 2026-08-14T09:12:04.000Z (from history)
53
+ alpha: completed set to 2026-08-19T16:40:11.000Z (from history)
54
+ ```
55
+
56
+ If a task has no history recording the move, the date falls back to the task's `updated` date, then its `created` date. The source is always reported so you can see which dates were guessed.
57
+
58
+ Tasks that have a `completed` date while sitting **outside** a completed column are reported but never changed - the date records something that really happened, and removing it automatically would lose it.
59
+
60
+ `kanbn board` and `kanbn status` print a one-line reminder when a board has drifted. Set `verbose: false` in your project options to suppress it.
61
+
62
+ ## Clearing a date
63
+
64
+ There is now a way to remove a date, which is what makes the dates trustworthy as the single source of truth:
65
+
66
+ ```
67
+ kanbn edit "my-task" --unset completed
68
+ ```
69
+
70
+ This reopens a completed task. It works for `started`, `completed`, `due`, `plannedStart`, `plannedFinish`, `assigned`, `progress`, `tags`, and any custom field. If the same command also moves the task into a completed column, the unset is applied last, so it wins.
71
+
72
+ ## Per-board state fields
73
+
74
+ Two new project options, `startedField` and `completedField`, name the metadata fields a board reads and writes. They default to `started` and `completed`.
75
+
76
+ Pointing them at custom date fields lets one task carry separate state for separate workflows:
77
+
78
+ ```yaml
79
+ completedField: designSignedOffAt
80
+ completedColumns:
81
+ - Signed Off
82
+ customFields:
83
+ - name: designSignedOffAt
84
+ type: date
85
+ ```
86
+
87
+ A board configured this way writes and reads `designSignedOffAt` and leaves the shared `completed` field alone.
88
+
89
+ ## Other changes in 2.0.0
90
+
91
+ * **Task ids ending in a single capital letter are now fully lowercased.** `kanbn add -n "Task A"` previously produced the id `task-A`; it now produces `task-a`. Non-ASCII capitals are lowercased too. Existing task files are not renamed.
92
+ * **Unknown history event types and keys are preserved** rather than rejected, so a task file written by a newer version of kanbn stays readable.
@@ -0,0 +1,258 @@
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
+ | `defaultTaskWorkload`, `taskWorkloadTags` | Workspace — workload is a property of the task, not the board |
170
+ | `dateFormat` | Workspace only — dates should read identically everywhere |
171
+ | `mainFolder`, `indexFile`, `taskFolder`, `archiveFolder`, `defaultBoard`, `boards` | Workspace only — layout, meaningless in a board file |
172
+
173
+ 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.
174
+
175
+ Where options are written:
176
+
177
+ - **Main board** — unchanged. If a config file exists, options go there; otherwise they go into `index.md`'s front matter.
178
+ - **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.
179
+
180
+ 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.
181
+
182
+ **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.
183
+
184
+ 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.
185
+
186
+ ### Per-board state fields
187
+
188
+ 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:
189
+
190
+ ```markdown
191
+ ---
192
+ startedField: designStartedAt
193
+ completedField: designSignedOffAt
194
+ startedColumns:
195
+ - Designing
196
+ completedColumns:
197
+ - Signed Off
198
+ ---
199
+
200
+ # Design Pipeline
201
+ ```
202
+
203
+ 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`.
204
+
205
+ ### Per-board sprints
206
+
207
+ 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.
208
+
209
+ `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.
210
+
211
+ 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.
212
+
213
+ `--sprint` can't be combined with `--all-boards`, because sprint numbers and names are relative to one board's list.
214
+
215
+ `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.
216
+
217
+ 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.
218
+
219
+ ### History and charts are board-scoped
220
+
221
+ `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.
222
+
223
+ `progress` and `archived` events are never filtered: progress is a property of the task, and archiving removes a task from every board.
224
+
225
+ ### Validation
226
+
227
+ `kanbn validate` reports multi-board problems as warnings once a workspace has more than one board:
228
+
229
+ - a workspace-scoped option in a secondary board's front matter, which is ignored;
230
+ - several boards stamping the same shared `completedField`, where whichever board is touched first silently owns that date for every board;
231
+ - sprints out of chronological order, which the "current sprint" logic assumes;
232
+ - a task on no board at all, or referenced by a board with no file behind it;
233
+ - a task in two columns on the same board;
234
+ - history events naming a board that has been deleted;
235
+ - markdown files beside the boards that don't parse as one, and files excluded by `boards.exclude`.
236
+
237
+ `kanbn validate --all-boards` validates every board and its tasks, rather than just the target board.
238
+
239
+ ### Board discovery
240
+
241
+ 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`.
242
+
243
+ Where discovery isn't enough, the `boards` config key takes over:
244
+
245
+ ```yaml
246
+ boards:
247
+ exclude:
248
+ - notes.md # not a board, just a scratch file
249
+ order: # display order for `kanbn boards`
250
+ - index
251
+ - design
252
+ - ops
253
+ design: # options applied to the design board
254
+ hiddenColumns:
255
+ - Ideas
256
+ ```
257
+
258
+ `exclude` and `order` are reserved; every other key is a board slug whose value is that board's options.
@@ -59,4 +59,60 @@ This will add a "Large" tag to the task and modify the task's updated date. If y
59
59
 
60
60
  Run `kanbn edit --help` for more information on editing tasks.
61
61
 
62
- See [index structure](index-structure.md) for more information on project settings, tags and workload calculations.
62
+ ## Find tasks
63
+
64
+ ```
65
+ kanbn find --tag "Large" --column "Todo"
66
+ ```
67
+
68
+ Filters are combined with AND, string filters are case-insensitive regular expressions, and dates and numbers can be given twice to search a range. See [filtering and sorting](filtering-and-sorting.md) for the full model.
69
+
70
+ ## Customise the board
71
+
72
+ Define a view in your project options to re-arrange, filter or group the board without moving any tasks:
73
+
74
+ ```yaml
75
+ views:
76
+ - name: 'by-assignee'
77
+ lanes:
78
+ - name: 'Me'
79
+ filters:
80
+ assigned: '^my-username$'
81
+ - name: 'Everyone else'
82
+ filters:
83
+ assigned: '^(?!my-username$).*'
84
+ ```
85
+
86
+ ```
87
+ kanbn board -v by-assignee
88
+ ```
89
+
90
+ See [views](views.md) for columns, lanes, filters and sorters.
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
+
111
+ ## Next steps
112
+
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
115
+ - [Task structure](task-structure.md) — task metadata, sub-tasks, relations and comments
116
+ - [Views](views.md) and [filtering and sorting](filtering-and-sorting.md)
117
+ - [Advanced configuration](advanced-configuration.md) — separate config files and custom paths
118
+ - The [`example`](../example) directory contains four ready-to-run workspaces you can experiment with