-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathkanban.json
More file actions
7486 lines (7486 loc) · 451 KB
/
kanban.json
File metadata and controls
7486 lines (7486 loc) · 451 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
"version": 2,
"metadata": {
"instance_id": "ec78b6c1-e4f5-414e-8b52-3555727ade48",
"saved_at": "2026-02-17T22:29:27.849338110Z"
},
"data": {
"archived_cards": [
{
"archived_at": "2025-11-26T23:43:54.876964Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 136,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-11-20T17:40:47.339315Z",
"description": "Just a small adjustment to the `README.md`\n",
"due_date": null,
"id": "7304c349-5eeb-419a-bf9a-a935624054f0",
"points": 1,
"position": 51,
"priority": "Low",
"sprint_id": null,
"sprint_logs": [],
"status": "Todo",
"title": "update README.md",
"updated_at": "2025-11-20T17:44:16.403707Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 51
},
{
"archived_at": "2025-12-19T23:41:37.542339Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 126,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-11-03T17:06:55.650586Z",
"description": "filtering by any option should not include unassigned cards unless explictly specified\n",
"due_date": null,
"id": "08176c32-e5fe-4753-a3b5-6eff5434862a",
"points": null,
"position": 32,
"priority": "Medium",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [
{
"ended_at": "2025-12-15T20:35:57.264769Z",
"sprint_id": "98901eb9-91ab-43aa-8f82-b2a0b42c03cd",
"sprint_name": "full-bowling",
"sprint_number": 9,
"started_at": "2025-12-04T21:30:07.245016Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264769Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "when filtering by sprint x, dont include unassigned cards.",
"updated_at": "2025-12-15T20:35:57.264770Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 32
},
{
"archived_at": "2025-12-19T23:43:18.062364Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 137,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-11-27T21:19:19.195159Z",
"description": "it would be cool with an indicator of what page I am on out of how many \n\n(1/3)\n\n(45/99)\n",
"due_date": null,
"id": "777f9c87-af5c-408d-b796-cedca10475c7",
"points": 1,
"position": 44,
"priority": "Low",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [
{
"ended_at": "2025-12-15T20:35:57.264712Z",
"sprint_id": "98901eb9-91ab-43aa-8f82-b2a0b42c03cd",
"sprint_name": "full-bowling",
"sprint_number": 9,
"started_at": "2025-12-04T21:30:07.245017Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264713Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "page indicator",
"updated_at": "2025-12-19T23:41:29.819676Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 44
},
{
"archived_at": "2025-12-20T00:11:06.895107Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 37,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-10-12T14:49:41.587356Z",
"description": "The current ui is a bit unintuitive when wanting to navigate to the task list.\n\nI first have to select the board (enter/space) and then I can change to the task list using 2.\n\nIt would probably feel more natural to just have the activation when hitting 2 and activating the currently highlighted board.\n\n2025-10-31 18:49:26\n---\n\nDon't know if we should pursue this card.\n\n2025-12-20 00:41:00\n---\n\nDropping this until further notice\n",
"due_date": null,
"id": "3b771805-ec2a-4b6a-bc3d-57b1528e3742",
"points": 1,
"position": 7,
"priority": "Low",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [
{
"ended_at": "2025-12-15T20:35:57.264750Z",
"sprint_id": "98901eb9-91ab-43aa-8f82-b2a0b42c03cd",
"sprint_name": "full-bowling",
"sprint_number": 9,
"started_at": "2025-12-04T21:30:07.245014Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264750Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "hitting 2 should activate the highlighted board",
"updated_at": "2025-12-19T23:41:14.460414Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 7
},
{
"archived_at": "2025-12-23T00:33:14.088487Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 162,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-12-23T00:30:55.018557Z",
"description": "Currently have flake.nix, shell.nix, default.nix, AND .envrc. This is tooling sprawl. \nPick one canonical approach (recommend flake.nix only) and document it clearly. \nRemove redundant files or document why all are needed.\n",
"due_date": null,
"id": "26fc7b1c-643a-4281-9795-1ebc0eddfc98",
"points": null,
"position": 43,
"priority": "Medium",
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_name": "a-new-year",
"sprint_number": 11,
"started_at": "2025-12-23T00:31:51.248420Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "Consolidate Nix tooling",
"updated_at": "2025-12-23T00:33:05.998566Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 43
},
{
"archived_at": "2026-01-10T00:51:33.548251Z",
"card": {
"assigned_prefix": "KAN",
"card_number": 169,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-12-23T00:35:10.326255Z",
"description": "Currently have flake.nix, shell.nix, default.nix, AND .envrc. This is tooling sprawl. Pick one canonical approach (recommend flake.nix only) and document it clearly. Remove redundant files or document why all are needed.",
"due_date": null,
"id": "d1f52601-6f95-48f3-8a04-7b711870ed7c",
"points": 3,
"position": 49,
"priority": "Medium",
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_name": "a-new-year",
"sprint_number": 11,
"started_at": "2025-12-23T00:35:15.812859Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "Consolidate Nix tooling",
"updated_at": "2025-12-23T00:35:15.812860Z"
},
"original_column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"original_position": 49
}
],
"boards": [
{
"active_sprint_id": null,
"card_prefix": "KAN",
"completion_column_id": null,
"created_at": "2025-10-10T08:47:44.779097Z",
"description": "Management of the **Kanban** project\n",
"id": "e119c091-e1fa-4596-9bc7-038ceab6adec",
"name": "Kanban",
"next_sprint_number": 8,
"prefix_counters": {
"KAN": 211
},
"sprint_counters": {
"KAN": 13
},
"sprint_duration_days": 7,
"sprint_name_used_count": 8,
"sprint_names": [
"an-eventful-october",
"an-experience-to-remember",
"a-hallowed-week",
"progression-is-progress",
"full-bowling",
"a-merry-christmas",
"a-new-year",
"fresh-air"
],
"sprint_prefix": "KAN",
"task_list_view": "ColumnView",
"task_sort_field": "CreatedAt",
"task_sort_order": "Descending",
"updated_at": "2026-02-17T22:22:00.293789192Z"
}
],
"cards": [
{
"assigned_prefix": null,
"card_number": 0,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-21T21:20:21.085162Z",
"created_at": "2025-10-10T08:47:57.834239Z",
"description": "2025-10-20 17:35:49\n---\n\nThe ci woes continue\n\nThe current progress is that of \n\n---\n\n New Idempotent Release Pipeline\n\n Two new minimal scripts:\n - aggregate-changesets.sh - Finds highest priority bump type (major > minor > patch)\n - update-changelog.sh - Merges changesets into CHANGELOG.md with version header\n\n Updated workflows:\n\n 1. aggregate-changesets.yml (on develop PR close):\n - Aggregates all changesets into CHANGELOG.md\n - Single version bump per release cycle\n - Cleans up changesets\n - Commits to develop\n 2. release.yml (on master PR close):\n - Compares Cargo.toml version vs last git tag\n - If version changed → publish & tag\n - If version unchanged → skip (idempotent!)\n - No changeset checking, no pushing back to master\n\n Benefits:\n - ✅ Idempotent: Version is source of truth, safe to re-run\n - ✅ Maintainable: Simple version comparison logic\n - ✅ Single version bump: One bump per release cycle\n - ✅ Changelog preserved: Full history in CHANGELOG.md\n - ✅ Zero race conditions: No pushing to trigger branch\n\n---\n\nMoving cards to and from columns also seem broken\n",
"due_date": null,
"id": "243615fe-fbcd-4d80-a88d-8173ec4b9573",
"points": 1,
"position": 24,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155904Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Fix the CI",
"updated_at": "2025-10-21T21:20:21.085162Z"
},
{
"assigned_prefix": null,
"card_number": 4,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:45.523148Z",
"created_at": "2025-10-10T08:47:57.834239Z",
"description": "The application is exporting boards using two different formats.\n\n```JSON\n{ board: { ... } }\n```\n\nand\n\n```JSON\n{ boards: [{ ... }] }\n```\n\nLet us make import and export only expect the latter.\n",
"due_date": null,
"id": "143615fe-fbcd-4d80-a88d-8173ec4b9573",
"points": 1,
"position": 2,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155905Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Unify import and export",
"updated_at": "2025-10-18T21:08:32.067931Z"
},
{
"assigned_prefix": null,
"card_number": 5,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:46.170907Z",
"created_at": "2025-10-10T09:08:28.861766Z",
"description": "To better align with the kanban domain let us change the export field `tasks` into `cards`\n",
"due_date": null,
"id": "959a092f-61de-41de-ac4a-729d6e1fcadf",
"points": 1,
"position": 3,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155905Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Rename tasks of exports to cards",
"updated_at": "2025-10-16T21:02:46.170907Z"
},
{
"assigned_prefix": null,
"card_number": 1,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:46.691481Z",
"created_at": "2025-10-10T21:58:56.145240Z",
"description": "We want to be able to create git branches and tie them to specific tasks so that we can trace the history.\n\nBe able to specify a prefix for use in the project\n\n- Set prefix in Board settings\n- Assign prefix + autoincremented number to card\n- From a card. Copy branch name\n- From a card. Copy `git checkout -b prefix+id`\n\nShould the id be autoincrement or uuid?\n\nReadability says it should be autoincremented. So we go with that for now.\n\n- If no prefix fallback to a default.\n- Customize default. Requires application configuration file feature?\n\n╭────────────────────────────────────────────────────────────────╮\n│ Git Branch Integration Implementation Plan │\n│ │\n│ Core Design Decisions │\n│ │\n│ - Branch ID: Auto-incremented u32 per board (decided: │\n│ readability over UUIDs) │\n│ - Prefix Hierarchy: Board prefix → App config default → \"task\" │\n│ fallback │\n│ - ID Assignment: Lazy (on first request) to avoid assigning │\n│ IDs to cards that never need branches │\n│ - Branch Format: {prefix}-{id} (e.g., feature-42, task-17) │\n│ │\n│ --- │\n│ 1. Domain Layer (kanban-domain) │\n│ │\n│ Board model (board.rs): │\n│ - Add branch_prefix: Option<String> field │\n│ - Add next_card_id: u32 field (default: 1) │\n│ - Add update_branch_prefix(&mut self, prefix: Option<String>) │\n│ method │\n│ - Add allocate_card_id(&mut self) -> u32 method │\n│ │\n│ Card model (card.rs): │\n│ - Add card_id: Option<u32> field │\n│ - Add ensure_card_id(&mut self, board: &mut Board) method │\n│ - Add branch_name(&self, board: &Board, default_prefix: &str) │\n│ -> Option<String> method │\n│ - Add git_checkout_command(&self, board: &Board, │\n│ default_prefix: &str) -> Option<String> method │\n│ - Add prefix validation (no spaces, git-safe characters) │\n│ │\n│ --- │\n│ 2. Application Configuration (kanban-core) │\n│ │\n│ Create new config.rs module: │\n│ - Define AppConfig struct with default_branch_prefix: │\n│ Option<String> │\n│ - Use config crate or simple TOML parsing │\n│ - Config file location: ~/.config/kanban/config.toml │\n│ - Load config on app startup in kanban-cli │\n│ - Thread through to TUI via App struct │\n│ │\n│ --- │\n│ 3. Clipboard Integration (kanban-tui) │\n│ │\n│ Add dependencies to kanban-tui/Cargo.toml: │\n│ - arboard = \"3.4\" (cross-platform clipboard) │\n│ │\n│ Create new clipboard.rs module: │\n│ - copy_to_clipboard(text: &str) -> io::Result<()> │\n│ - Handle errors gracefully (e.g., headless environments) │\n│ │\n│ --- │\n│ 4. TUI Layer (kanban-tui) │\n│ │\n│ App struct (app.rs): │\n│ - Add app_config: AppConfig field │\n│ - Add clipboard operations to card detail mode │\n│ │\n│ Board settings (new mode AppMode::BoardSettings): │\n│ - Add focus state for navigating board settings │\n│ - Edit branch prefix with external editor or inline input │\n│ - Show current prefix, effective prefix (with fallback chain) │\n│ │\n│ Card detail view (ui.rs): │\n│ - Show branch ID (if assigned) │\n│ - Show computed branch name │\n│ - Show git checkout command │\n│ │\n│ Key bindings (in CardDetail mode): │\n│ - b - Assign branch ID (if not already assigned) │\n│ - y - Copy branch name to clipboard │\n│ - Y - Copy git checkout -b {branch} to clipboard │\n│ - Add to help text │\n│ │\n│ New AppMode variants: │\n│ - BoardSettings - for editing board-level settings │\n│ - SetBranchPrefix - dialog for setting prefix │\n│ │\n│ --- │\n│ 5. Persistence │\n│ │\n│ Update serialization/deserialization: │\n│ - Board export/import already uses serde │\n│ - New fields automatically included │\n│ - Migration: existing boards get branch_prefix: None, │\n│ next_card_id: 1 │\n│ - Existing cards get card_id: None │\n│ │\n│ --- │\n│ 6. Testing Strategy │\n│ │\n│ Unit tests: │\n│ - Branch ID allocation (sequential, no gaps) │\n│ - Branch name generation with different prefix scenarios │\n│ - Prefix validation (reject invalid git characters) │\n│ - Clipboard operations (mock if needed) │\n│ │\n│ Integration tests: │\n│ - Round-trip serialization with new fields │\n│ - Multiple cards getting sequential IDs │\n│ │\n│ --- │\n│ 7. Implementation Order │\n│ │\n│ 1. Add AppConfig in kanban-core │\n│ 2. Update Board domain model with prefix + next_card_id │\n│ 3. Update Card domain model with card_id + methods │\n│ 4. Add clipboard module to kanban-tui │\n│ 5. Update App struct to hold config │\n│ 6. Add board settings mode in TUI │\n│ 7. Add card detail branch operations │\n│ 8. Update help text and key bindings │\n│ 9. Add tests │\n│ 10. Update CLAUDE.md with new features │\n│ │\n│ --- │\n│ 8. Edge Cases & Validation │\n│ │\n│ - Validate prefix: alphanumeric + hyphens only, no │\n│ leading/trailing hyphens │\n│ - Max branch name length: git supports ~255 chars, warn if │\n│ prefix + ID exceeds reasonable length │\n│ - Clipboard unavailable: show friendly error, fallback to │\n│ displaying text │\n│ - Board without prefix: use app config default or \"task\" │\n│ - Cards created before feature: assign IDs on demand │\n│ │\n│ --- │\n│ Example Usage Flow │\n│ │\n│ 1. User opens board settings (s in projects view) │\n│ 2. Sets prefix to \"feat\" │\n│ 3. Navigates to card, presses b to assign branch ID → card │\n│ gets card_id=1 │\n│ 4. Board's next_card_id increments to 2 │\n│ 5. User presses y → copies \"feat-1\" to clipboard │\n│ 6. User presses Y → copies \"git checkout -b feat-1\" to │\n│ clipboard │\n│ 7. Next card gets card_id=2, branch name \"feat-2\" │\n│ │\n│ --- │\n│ Dependencies to add: │\n│ - arboard = \"3.4\" (clipboard) │\n│ - config = \"0.14\" (optional, for TOML config parsing) │\n│ - OR use simple std::fs::read_to_string + toml = \"0.8\" for │\n│ lightweight config \n",
"due_date": null,
"id": "292e3f49-089b-40fb-8ea7-c693603904b2",
"points": 5,
"position": 4,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155905Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Add git branches to card meta data",
"updated_at": "2025-10-16T21:02:46.691481Z"
},
{
"assigned_prefix": null,
"card_number": 2,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-11-17T22:43:16.414879Z",
"created_at": "2025-10-10T22:03:42.988557Z",
"description": "Be able to have references to other cards.\n\n[like this](DEV-0001) Should be able to follow that link and open the card in Task Description interface.\n\n",
"due_date": null,
"id": "f1a80abd-338c-4593-92d6-20c2db7dc093",
"points": 4,
"position": 63,
"priority": "Medium",
"sprint_id": "737b04e6-52e2-4a40-bbaa-e2c9bcc84249",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "737b04e6-52e2-4a40-bbaa-e2c9bcc84249",
"sprint_name": "an-experience-to-remember",
"sprint_number": 6,
"started_at": "2025-11-01T09:30:06.155905Z",
"status": "Active"
}
],
"status": "Done",
"title": "Links to cards",
"updated_at": "2025-11-17T22:43:16.414879Z"
},
{
"assigned_prefix": "KAN",
"card_number": 6,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2026-01-09T01:11:28.387265Z",
"created_at": "2025-10-10T22:05:04.607714Z",
"description": "Introduce two features\n\n# Block\n\n- Add blocked by card\n- Add create blocker. Creating the new card that would be the blocker.\n\n# Grouping of cards under one card\n\nI would like to keep things flat but I would like card meta data where I can group cards by a top level card.\n\nThe interface would present the grouping as nested to begin with and we see how it behaves?\n\nExample\n\n```\nImplement Card Grouping ...\n```\n\nWhich would when hit enter or space on expand into\n\n```\nImplement Card Grouping\n Create ui\n Add metadata\n```\n\n# Duplicate cards\n\nWhen two cards propose changes that are two sides of the same coin. Be able to add them as siblings. \n\nBut when two cards propose changes that are the same side of the same coin, we want to merge them together. Creating a new card by editing the description and title in a git conflict type way\n\nThe merging will ofcourse be handled by opening EDITOR and injecting the content into their respective section.\n\nExample\n---\n\nTitle\n\n```\n<<<<<<< KAN-0001\nCard Background\n=======\nCard Background Color\n>>>>>>> KAN-0002\n```\n\nDescription\n\n```\nWe want to introduce a new feature that\n<<<<<<< KAN-0001\nMakes card background purple\n=======\nMakes card background green\n>>>>>>> KAN-0002\n```\n",
"due_date": null,
"id": "e96684a5-4645-443e-8e2c-6bb646e248c6",
"points": 5,
"position": 96,
"priority": "High",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [
{
"ended_at": "2025-11-02T16:56:39.448929Z",
"sprint_id": "737b04e6-52e2-4a40-bbaa-e2c9bcc84249",
"sprint_name": "an-experience-to-remember",
"sprint_number": 6,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Active"
},
{
"ended_at": "2025-11-17T22:41:06.526717Z",
"sprint_id": "ce752be4-efce-40fe-bfa7-d0b313497feb",
"sprint_name": "a-hallowed-week",
"sprint_number": 7,
"started_at": "2025-11-02T16:56:39.448929Z",
"status": "Planning"
},
{
"ended_at": "2025-12-15T20:35:57.264743Z",
"sprint_id": "a61d4a55-5870-4493-9015-9854d8f583cf",
"sprint_name": "progression-is-progress",
"sprint_number": 8,
"started_at": "2025-11-17T22:41:06.526722Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264743Z",
"status": "Planning"
}
],
"status": "Done",
"title": "Card dependencies",
"updated_at": "2026-01-09T01:11:28.387265Z"
},
{
"assigned_prefix": null,
"card_number": 7,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-21T21:47:36.557987Z",
"created_at": "2025-10-10T22:11:25.809290Z",
"description": "# At the board level. Be able to define columns\n\nDefault columns when creating a new board\n---\n- TODO\n- Doing\n- Complete\n\nFeatures\n---\n- Be able to change the name of columns\n- Be able to change order of columns\n- Be able to add new columns\n- Be able to remove columns\n- Be able to add tasks to column\n- Using 'c' is equivalent to moving a card to the last column. If there is only one column in a board, we can only set complete manually like the current implementation does.\n\nUI\n---\n- hitting `c` in Board Details opens up Columns. In columns we see one panel listing the columns in order.\n- In the \n\n### Task list\n\n- Be able to view tasks in column view grouped by columns\n- Be able to view tasks in list view grouped by columns\n- Be able to view the task list without columns (current behavior)\n- Extend the task list to have column task list\n- Extend the task list to have list grouped by columns\n- Choose which view is active in the task list panel by opening a list dialogue with the choices with `v`. The current view is saved to the board and loaded on startup.\n\nDupe of `KAN-31/update-status-of-a-card-board-columns`\n\nImplementation\n---\nImplementation Plan: Column Definitions at Board Level │ │\n │ │\nOverview │ │\n │ │\nAdd full column management capabilities to the kanban board, allowing users to define, edit, reorder, and manage columns at the │ │\nboard level. This includes default columns on board creation, a dedicated column management UI, and integration with task completion │ │\n workflows. │ │\n │ │\nArchitecture Changes │ │\n │ │\n1. Domain Layer (kanban-domain) │ │\n │ │\nFile: crates/kanban-domain/src/column.rs │ │\n- Add update_name(&mut self, name: String) method │ │\n- Add validation for column names (non-empty, unique within board) │ │\n │ │\nFile: crates/kanban-domain/src/board.rs │ │\n- Add default_column_definitions: Vec<String> field (default: [\"TODO\", \"Doing\", \"Complete\"]) │ │\n- Add task_list_view: TaskListView enum field (default: TaskListView::Flat) │ │\n- Add methods for column management at board level │ │\n │ │\nNew: crates/kanban-domain/src/task_list_view.rs │ │\n- Define TaskListView enum: Flat, GroupedByColumn, ColumnView │ │\n │ │\n2. Application State (kanban-tui/src/app.rs) │ │\n │ │\nAdd new AppMode variants: │ │\n- ColumnManagement - Main column management view │ │\n- CreateColumn - Dialog for creating new column │ │\n- RenameColumn - Dialog for renaming column │ │\n- DeleteColumnConfirm - Confirmation dialog for column deletion │ │\n- SelectTaskListView - Popup for selecting task list view type │ │\n │ │\nAdd new Focus enum: │ │\n- ColumnFocus with variants: ColumnList │ │\n │ │\nAdd to App struct: │ │\n- column_selection: SelectionState - For navigating columns │ │\n- active_column_index: Option<usize> - Currently selected column │ │\n- task_list_view_selection: SelectionState - For view selection popup │ │\n │ │\n3. UI Layer (kanban-tui/src/ui.rs) │ │\n │ │\nAdd rendering functions: │ │\n- render_column_management_view() - Main column management interface │ │\n- render_create_column_popup() - Column creation dialog │ │\n- render_rename_column_popup() - Column rename dialog │ │\n- render_delete_column_confirm_popup() - Deletion confirmation │ │\n- render_select_task_list_view_popup() - View selection popup │ │\n- Update render_tasks_panel() to support three view modes │ │\n │ │\n4. Handlers │ │\n │ │\nNew: crates/kanban-tui/src/handlers/column_handlers.rs │ │\n- handle_column_management_key() - Navigate and manage columns │ │\n- handle_create_column_key() - Initiate column creation │ │\n- handle_rename_column_key() - Initiate column renaming │ │\n- handle_delete_column_key() - Delete column with confirmation │ │\n- handle_move_column_up() - Reorder columns (decrease position) │ │\n- handle_move_column_down() - Reorder columns (increase position) │ │\n- handle_toggle_task_list_view() - Switch between view modes │ │\n- create_column() - Logic to add column to board │ │\n- rename_column() - Logic to update column name │ │\n- delete_column() - Logic to remove column (move cards to first column) │ │\n- reorder_columns() - Update position fields │ │\n │ │\nUpdate: crates/kanban-tui/src/handlers/detail_view_handlers.rs │ │\n- Add KeyCode::Char('5') handler for BoardFocus::Columns in BoardDetail │ │\n- Add BoardFocus::Columns to handle_board_detail_key() │ │\n │ │\nUpdate: crates/kanban-tui/src/handlers/card_handlers.rs │ │\n- Modify handle_toggle_card_completion() to move card to last column if columns exist │ │\n- Add fallback to current behavior if only one column exists │ │\n │ │\n5. Export/Import (kanban-tui/src/export/) │ │\n │ │\nUpdate: models.rs and exporter.rs / importer.rs │ │\n- Ensure columns are properly serialized/deserialized │ │\n- Handle migration from boards without column definitions │ │\n │ │\n6. Board Initialization │ │\n │ │\nUpdate: crates/kanban-tui/src/handlers/board_handlers.rs │ │\n- When creating board with create_board(), automatically create default columns: │ │\n - \"TODO\" (position 0) │ │\n - \"Doing\" (position 1) │ │\n - \"Complete\" (position 2) │ │\n │ │\nKey Bindings │ │\n │ │\nBoard Detail View │ │\n │ │\n- 5 - Open Column Management (new BoardFocus::Columns) │ │\n- e - Edit columns (when focused on Columns panel) │ │\n │ │\nColumn Management View │ │\n │ │\n- ESC - Return to Board Detail │ │\n- n - Create new column │ │\n- r - Rename selected column │ │\n- d - Delete selected column (with confirmation) │ │\n- j/k or ↓/↑ - Navigate columns │ │\n- J or Ctrl+Down - Move column down in order │ │\n- K or Ctrl+Up - Move column up in order │ │\n │ │\nTask List View │ │\n │ │\n- v - Open view selection dialog (Flat / Grouped by Column / Column View) │ │\n- View preference saved per board │ │\n │ │\nImplementation Sequence │ │\n │ │\n1. Domain models - Update Board, Column, add TaskListView enum │ │\n2. Board initialization - Auto-create default columns on board creation │ │\n3. App state - Add new modes, focus types, selections │ │\n4. Column handlers - Implement all column management logic │ │\n5. UI rendering - Add column management view and popups │ │\n6. Task list views - Implement three view modes (Flat, Grouped, Column) │ │\n7. Card completion - Update to move to last column │ │\n8. Export/Import - Update serialization to handle new fields │ │\n9. Testing - Verify column CRUD, reordering, view switching, card movement │ │\n │ │\nSuggestions for Future Enhancements │ │\n │ │\n- WIP limits per column (already in Column model, just needs UI) │ │\n- Customizable column colors │ │\n- Archive columns instead of delete │ │\n- Column templates for new boards │ │\n\nMove Card\n---\n\n1. Manual Card Column Movement (H/L keys):\n - H (Shift+h) - Moves selected card to the previous (left) column\n - L (Shift+l) - Moves selected card to the next (right) column\n - Focus tracking: The card selection stays on the moved card after movement\n - Edge case handling: Warns if already at first/last column\n 2. Updated Card Completion Behavior (c key):\n - When pressing c on a card:\n - If the board has multiple columns: Moves card to the last column AND marks it as Done\n - If the board has only 1 column: Just toggles the status (fallback behavior)\n - Works for both single card completion and multi-select completion\n - Cards moved to the last column are automatically marked as complete\n 3. UI Updates:\n - Footer help text now shows: H/L: move card\n - All functionality is ready to use\n\n How It Works:\n\n The implementation intelligently:\n - Sorts columns by position to determine left/right\n - Calculates the new position in the target column (appends to end)\n - Updates the card's column_id and position\n - Re-sorts the card list and finds the moved card's new index\n - Updates the selection to track the moved card\n\n The code compiles successfully and is ready for testing! 🎉\n \n Three interfaces\n ---\n Implement task list view modes rendering: │ │\n\n1. Refactor render_main function to check view mode: │ │\n - For Flat and Grouped views: Keep current 2-panel layout (Projects 30% | Tasks 70%) │ │\n - For ColumnView/Kanban: Hide projects panel, use full width for kanban board │ │\n - Check board.task_list_view to determine layout │ │\n2. Refactor render_tasks_panel function to route based on view: │ │\n - Match on board.task_list_view enum │ │\n - Call appropriate rendering function: │ │\n - Flat → existing flat list logic │ │\n - GroupedByColumn → new grouped rendering │ │\n - ColumnView → new kanban rendering │ │\n3. Implement Grouped by Column view (render_tasks_grouped_by_column): │ │\n - Get sorted columns for the board │ │\n - For each column: │ │\n - Render column name as a header (styled/bold, e.g., \"TODO (3 tasks)\") │ │\n - Filter cards by column_id │ │\n - Render cards under that header │ │\n - Handle card selection across all groups (track global index) │ │\n4. Implement Column/Kanban view (render_tasks_kanban_view): │ │\n - Takes full area (no projects panel) │ │\n - Split area into N vertical panels (one per column) │ │\n - Calculate equal-width constraints dynamically based on column count │ │\n - For each column panel: │ │\n - Render as bordered panel with column name as title │ │\n - Filter cards by column_id │ │\n - Render cards in that panel │ │\n - Handle card selection (track which card across all columns in order) │ │\n - Navigation: j/k moves through all cards left-to-right, top-to-bottom │ │\n5. Update card selection tracking: │ │\n - Keep card_selection as simple index into flat sorted list │ │\n - All views use same underlying sorted card list │ │\n - Just render differently based on view mode │ │\n6. Handle edge cases: │ │\n - Empty columns in grouped/kanban view (show placeholder text) │ │\n - Selection persistence when switching views │ │\n - Proper focus indicators in all modes │ │\n - Projects panel focus: disabled in kanban mode (only Cards focus active) │ │\n─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n\n2025-10-18 12:24:25\n---\n\nOpening the view selection dialogue should not be possible unless a board has been activated.\n\n\n",
"due_date": null,
"id": "90f8121a-045f-47fd-b8fe-bb0e5b7662b1",
"points": 2,
"position": 29,
"priority": "Critical",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Add column definitions",
"updated_at": "2025-10-21T21:47:36.557991Z"
},
{
"assigned_prefix": "KAN",
"card_number": 8,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-10-10T22:13:04.048521Z",
"description": "Need to try the application to see how it behaves with different editors\n\n- VSCode\n- vim\n- vi\n- emacs\n\nWindows\n\n- notepad\n- notepad++\n",
"due_date": null,
"id": "86c1b649-03bc-4da8-87c3-e20a2c56a640",
"points": 1,
"position": 36,
"priority": "Medium",
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_logs": [
{
"ended_at": "2026-02-01T19:39:40.784516Z",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Completed"
},
{
"ended_at": "2026-02-17T22:20:49.521456764Z",
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_name": "a-new-year",
"sprint_number": 11,
"started_at": "2026-02-01T19:39:40.784516Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_name": "fresh-air",
"sprint_number": 12,
"started_at": "2026-02-17T22:20:49.521457005Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "Test the application with another editor",
"updated_at": "2026-02-17T22:20:49.521457597Z"
},
{
"assigned_prefix": null,
"card_number": 9,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T20:47:08.170395Z",
"created_at": "2025-10-10T22:14:31.293267Z",
"description": "- Be able to change the priority of a task\n\n",
"due_date": null,
"id": "eceed2ce-de5e-4166-95e7-7fb9c7e65b20",
"points": 1,
"position": 0,
"priority": "Critical",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Change priority",
"updated_at": "2025-10-16T20:47:08.170397Z"
},
{
"assigned_prefix": null,
"card_number": 10,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:48.075303Z",
"created_at": "2025-10-10T22:15:09.199792Z",
"description": "On the board level. \n\nBe able to configure priorities and change their names. \n\nWe should start with only medium?\n",
"due_date": null,
"id": "7cd165b0-6671-46d6-b837-4e73ada26333",
"points": 1,
"position": 5,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Add or update priorities",
"updated_at": "2025-10-16T21:02:48.075303Z"
},
{
"assigned_prefix": "KAN",
"card_number": 11,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-10-10T22:53:19.579898Z",
"description": "Should be pretty self explantory. For cards with a longer description.\n\nWe want to be able to scroll so see more.\n\nPriority low as we can open `$EDITOR`\n",
"due_date": null,
"id": "454144d2-27ac-4abf-8632-407908fa8f80",
"points": 1,
"position": 3,
"priority": "Medium",
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_logs": [
{
"ended_at": "2025-11-04T00:22:28.640552Z",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155906Z",
"status": "Completed"
},
{
"ended_at": "2025-11-17T22:41:08.233357Z",
"sprint_id": "ce752be4-efce-40fe-bfa7-d0b313497feb",
"sprint_name": "a-hallowed-week",
"sprint_number": 7,
"started_at": "2025-11-04T00:22:28.640559Z",
"status": "Active"
},
{
"ended_at": "2025-12-15T20:35:57.264686Z",
"sprint_id": "a61d4a55-5870-4493-9015-9854d8f583cf",
"sprint_name": "progression-is-progress",
"sprint_number": 8,
"started_at": "2025-11-17T22:41:08.233362Z",
"status": "Planning"
},
{
"ended_at": "2026-01-10T15:31:10.374923Z",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264687Z",
"status": "Planning"
},
{
"ended_at": "2026-02-01T19:39:40.784588Z",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2026-01-10T15:31:10.374925Z",
"status": "Completed"
},
{
"ended_at": "2026-02-17T22:20:49.521481024Z",
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_name": "a-new-year",
"sprint_number": 11,
"started_at": "2026-02-01T19:39:40.784588Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_name": "fresh-air",
"sprint_number": 12,
"started_at": "2026-02-17T22:20:49.521481262Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "Be able to scroll in the card description",
"updated_at": "2026-02-17T22:20:49.521482639Z"
},
{
"assigned_prefix": "KAN",
"card_number": 12,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2026-01-10T00:49:29.134517Z",
"created_at": "2025-10-10T22:56:04.200355Z",
"description": "# Steps to Add Your Project to Nixpkgs\n\n## 1. Add Yourself as a Maintainer\n\nFirst, you'll need to add yourself to the maintainer list in maintainers/maintainer-list.nix with a separate commit titled \"maintainers: add <your-handle>\".\n\n## 2. Create the Package Expression\n\nSince your Kanban is a Rust project, you'll use buildRustPackage from rustPlatform nixpkgs/doc/languages-frameworks/rust.section.md at master · NixOS/nixpkgs\n\n[default.nix](./default.nix) \n\n## 3. Determine Package Location\n\nDecide which pkgs/ subfolder your expression should live in, then add your expression under a directory like pkgs/applications/misc/kanban/default.nix or similar\n\n## 4. Add to Package Index\n\nAdd an entry in pkgs/top-level/all-packages.nix so nixpkgs can find your package \n\n## 5. Fork and Test\n\nFork the nixpkgs repository, make your changes in a local branch, then build from the root of nixpkgs with nix-build -A kanban Nixpkgs/Contributing - NixOS Wiki\n\n## 6. Handle the Hash Values\n\nFor the cargoHash, you can initially use an empty string or fake hash, and Nix will tell you the correct hash when you try to build nixpkgs/doc/languages-frameworks/rust.section.md at master · NixOS/nixpkgs\n\n## 7. Submit a Pull Request\n\nOnce your package builds successfully, format your code with a Nix formatter like alejandra, then open a PR on GitHub with the title format \"kanban: init at <version>\" \n\n## 8. Wait for Review\n\nBe patient - it's normal for PRs to sit for weeks. If you need help, you can reach out on NixOS Discourse or the Matrix channels\n\n## Important Considerations\n\nRead the commit conventions specific to pkgs and include relevant information in your commit messages so others can understand why changes were made \n\nThe maintainers field is for you as the Nix expression maintainer, not the project maintainer. By submitting this package, you're taking responsibility for ongoing maintenance like version bumps \n\n2025-10-31 18:44:57\n---\n\nI tried packaging my application in nixpkgs and submitting the pull request.\n\nThere are resources in the stash documenting the process I followed\n\nTLDR; I need more users to the repo.\n\nAs a repo with just a couple of users is not enough to justify the package being published into nixpkgs.\n\nIt was a good learning experience. I got some feedback on my pull request regarding the packaging.\n\nTODO\n---\n\nCorrect the packaging job in my nixpkgs fork\n\nGet more users to the application\n\nHow can we make this product worthy?\n\nImplementation of cli possibly\n\nMake an api to manage the app\n\n2025-12-20 23:47:18\n---\n\nTry to get this through again, and see if we can make a new release of the app\n\n Suggestions for Improvement\n\n 1. Consider adding changelog to meta:\n changelog = \"https://github.com/fulsomenko/kanban/releases/tag/v${finalAttrs.version}\";\n\n 2. Platform specificity: The package uses lib.platforms.all, but since it's a TUI application using crossterm, it may only work on Unix-like systems. Consider testing on Windows or using lib.platforms.unix if Windows support is uncertain.\n\n Second point is valid. Wonder if this will show up in an eventual review\n\n2026-01-10 01:46\n---\n\nWe did it ! Hi nixpkgs. glad to be here. Hope to help out if I can.\n[Merged](https://github.com/NixOS/nixpkgs/pull/456042)\n",
"due_date": null,
"id": "d48f9085-57ff-42c5-9f42-3cbb3cafde93",
"points": 1,
"position": 97,
"priority": "Critical",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [
{
"ended_at": "2025-12-15T20:35:57.264752Z",
"sprint_id": "98901eb9-91ab-43aa-8f82-b2a0b42c03cd",
"sprint_name": "full-bowling",
"sprint_number": 9,
"started_at": "2025-12-04T21:30:07.245013Z",
"status": "Planning"
},
{
"ended_at": "2025-12-15T20:57:38.672030Z",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264752Z",
"status": "Planning"
},
{
"ended_at": "2025-12-15T20:58:58.965814Z",
"sprint_id": "98901eb9-91ab-43aa-8f82-b2a0b42c03cd",
"sprint_name": "full-bowling",
"sprint_number": 9,
"started_at": "2025-12-15T20:57:38.672034Z",
"status": "Completed"
},
{
"ended_at": null,
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:58:58.965818Z",
"status": "Planning"
}
],
"status": "Done",
"title": "Publish on nixpkgs",
"updated_at": "2026-01-10T00:49:29.134518Z"
},
{
"assigned_prefix": null,
"card_number": 13,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:49.028474Z",
"created_at": "2025-10-10T23:01:18.553586Z",
"description": "Order tasks by\n\n- Points\n- Priority\n- Date created\n- Date due\n- Date updated\n- Status\n\nMVP\n---\n\nI think in the tasks panel. hit o (for order or organize) and then are presented with a dialogue where can choose one of the above options.\n\nThe dialogue should use the same selection mechanism as the projects and tasks lists do.\n\nAfter one of the options have been selected. The task list should be ordered according to the chosen option.\n\nFor now we leave out Date due from selection as we have not added this feature yet.\n\nOrder can be descending or ascending. a for ascending and d for descending after selecting an item. Taking suggestions on UX here..\n\n---\n\nFor the future. Keep in mind that I would also like to order the projects panel in the same way as the tasks panel but along a shorter list of options\n\n- Name\n- Date created\n- Date updated\n",
"due_date": null,
"id": "6a0cbe1e-a05e-4aca-bd53-f0057215d671",
"points": 2,
"position": 6,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155907Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Add order tasks lists by",
"updated_at": "2025-10-16T21:02:49.028474Z"
},
{
"assigned_prefix": null,
"card_number": 14,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-11-26T23:26:45.563647Z",
"created_at": "2025-10-11T18:27:29.674825Z",
"description": "Using VHS record a demo so that we can post a gif of the tui to our github.\n\n```\n Complete Demo Script: Building a Web API Project\n\n Scene 1: Launch & Create Board (0-15s)\n\n $ cargo run\n [TUI loads]\n Press 'n'\n Type: \"Web API Project\"\n Press Enter\n Narration: \"Creating a new project for our web API development\"\n\n Scene 2: Add Cards with Different Priorities (15-50s)\n\n Press '2' (switch to Tasks panel)\n Press 'n'\n Type: \"Setup development environment\"\n Press Enter\n\n Press 'n'\n Type: \"Design database schema\"\n Press Enter\n\n Press 'n'\n Type: \"Implement authentication API\"\n Press Enter\n\n Press 'n'\n Type: \"Write integration tests\"\n Press Enter\n Narration: \"Adding tasks to our backlog\"\n\n Scene 3: Edit Card Details - Full Metadata (50-90s)\n\n Press 'k' (move up to first card)\n Press Enter (open card detail)\n\n [Show Title panel highlighted]\n Press 'e'\n [External editor opens]\n Type: \"Setup development environment\\nInstall Rust, PostgreSQL, Docker\"\n Save & exit\n\n Press '2' (switch to Metadata panel)\n Press 'e'\n Type: \"3\" (for 3 story points - Yellow badge)\n Press Enter\n\n Press 's' (change status)\n Type: \"InProgress\"\n Press Enter\n\n Press 'p' (change priority)\n Type: \"High\"\n Press Enter\n\n Press '3' (switch to Description panel)\n Press 'e'\n [External editor opens]\n Type: \"Dependencies:\\n- Rust 1.70+\\n- PostgreSQL 15\\n- Docker & docker-compose\\n\\nSetup steps:\\n1. Install Rust toolchain\\n2.\n Configure database\\n3. Run initial migrations\"\n Save & exit\n\n Press Escape (back to main view)\n Narration: \"Setting story points, priority, status, and detailed description\"\n\n Scene 4: Mark Card Complete & Copy Branch Name (90-110s)\n\n Press 'c' (toggle completion)\n [Card shows checkmark ✓]\n\n Press 'j' (move down)\n Press Enter (open second card)\n Press 'b' (copy branch name to clipboard)\n [Shows: \"Copied branch name to clipboard\"]\n\n Press 'y' (copy card ID)\n [Shows: \"Copied card ID to clipboard\"]\n\n Press Escape\n Narration: \"Marking tasks complete and copying branch names for git workflow\"\n\n Scene 5: Board Settings with Branch Prefix (110-130s)\n\n Press '1' (switch to Projects)\n Press 's' (open board settings)\n Press 'p' (set branch prefix)\n Type: \"feat\"\n Press Enter\n [Shows: \"Branch prefix set to: feat\"]\n Press Escape\n Narration: \"Configuring git branch prefixes for consistent naming\"\n\n Scene 6: Edit Board Details (130-150s)\n\n Press 'e' (edit board)\n [BoardDetail view opens]\n Press '2' (Description panel)\n Press 'e'\n [External editor]\n Type: \"RESTful API for user management\\n\\nTech Stack:\\n- Rust + Actix-web\\n- PostgreSQL\\n- JWT authentication\"\n Save & exit\n Press Escape\n Narration: \"Adding project documentation\"\n\n Scene 7: Export Board (150-165s)\n\n Press 'x' (export current board)\n Type: \"web-api-project.json\"\n Press Enter\n [Shows: \"Board exported successfully\"]\n Narration: \"Exporting project data to JSON\"\n\n Scene 8: Complete More Tasks (165-185s)\n\n Press '2' (Tasks panel)\n Press 'j' (move down to third card)\n Press Enter\n Press '2' (Metadata)\n Press 'e'\n Type: \"5\" (5 story points - Red badge for critical)\n Press Enter\n Press 's'\n Type: \"Blocked\"\n Press Enter\n Press Escape\n\n Press 'j' (fourth card)\n Press 'c' (mark complete)\n Narration: \"Managing task progress - marking some blocked, others complete\"\n\n Scene 9: Import Demo (185-205s)\n\n Press 'i' (import)\n [File list shows: web-api-project.json, example.json]\n Press 'j'\n Press Enter\n [Shows: \"1 board(s) imported successfully\"]\n Narration: \"Importing previously exported projects\"\n\n Scene 10: Final Overview & Quit (205-215s)\n\n Press '1' (Projects)\n [Show list of boards]\n Press '2' (Tasks)\n [Show cards with different colors for story points, completion marks]\n\n Press 'q'\n [Auto-save message]\n [Exit to shell]\n Narration: \"All changes are auto-saved on exit\"\n\n ---\n Key Features Showcased:\n\n ✅ Board creation (n)✅ Card creation (n)✅ External editor integration (title & description with e)✅ Story points with color coding\n (1-5: e in Metadata)✅ Status management (s: Todo/InProgress/Blocked/Done)✅ Priority levels (p: Low/Medium/High/Critical)✅ Task\n completion (c)✅ Branch name copying (b)✅ Card ID copying (y)✅ Board settings (s)✅ Branch prefix configuration (p)✅ Board details\n editing (e from Projects)✅ Export single board (x)✅ Import boards (i)✅ Auto-save on quit✅ Panel navigation (1/2/3)✅ Vim-style\n navigation (j/k)\n\n Total runtime: ~3:35 minutes\n ```\n",
"due_date": null,
"id": "9e8f918f-3fde-4847-aca8-0ab7646256a8",
"points": 5,
"position": 64,
"priority": "Critical",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155907Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Record demo",
"updated_at": "2025-11-26T23:26:45.563647Z"
},
{
"assigned_prefix": "KAN",
"card_number": 15,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-12-04T21:59:09.071230Z",
"created_at": "2025-10-11T18:29:10.434614Z",
"description": "If for whatever reason two instances of Kanban are editing the same JSON.\n\nImplement a procedure to listen for changes \n\nProgressive saving and not all at the end?\n\n2025-11-17 23:08:46\n---\n\nAfter a couple of data losses. I don't want to loose data anymore. lets implement progressive data saving so that we can support multiple instances of the tui and other applications. TLDR; enable us to use the cli while using the tui.\n",
"due_date": null,
"id": "1e257c13-f9ee-40e4-86b5-704a9649ea4e",
"points": 3,
"position": 70,
"priority": "High",
"sprint_id": "a61d4a55-5870-4493-9015-9854d8f583cf",
"sprint_logs": [
{
"ended_at": "2025-11-02T16:56:39.448946Z",
"sprint_id": "737b04e6-52e2-4a40-bbaa-e2c9bcc84249",
"sprint_name": "an-experience-to-remember",
"sprint_number": 6,
"started_at": "2025-11-01T09:30:06.155907Z",
"status": "Active"
},
{
"ended_at": "2025-11-17T22:39:49.578053Z",
"sprint_id": "ce752be4-efce-40fe-bfa7-d0b313497feb",
"sprint_name": "a-hallowed-week",
"sprint_number": 7,
"started_at": "2025-11-02T16:56:39.448946Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "a61d4a55-5870-4493-9015-9854d8f583cf",
"sprint_name": "progression-is-progress",
"sprint_number": 8,
"started_at": "2025-11-17T22:39:49.578056Z",
"status": "Planning"
}
],
"status": "Done",
"title": "Progressive saving / Detect changes to current json",
"updated_at": "2025-12-04T21:59:09.071233Z"
},
{
"assigned_prefix": null,
"card_number": 3,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:50.357039Z",
"created_at": "2025-10-11T18:36:43.972262Z",
"description": "It would be nice to be able to view the version of the program\n\nAlso add the version to the `--help` output\n",
"due_date": null,
"id": "9273b022-f7f8-4975-b9c5-7a57c55eba30",
"points": 1,
"position": 7,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155907Z",
"status": "Completed"
}
],
"status": "Done",
"title": "add -v flag",
"updated_at": "2025-10-16T21:02:50.357039Z"
},
{
"assigned_prefix": null,
"card_number": 16,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:51.185668Z",
"created_at": "2025-10-11T18:38:07.855746Z",
"description": "The \"Kanban Board\" header of the interface can go away.\n",
"due_date": null,
"id": "a189ef1a-903c-4c43-924e-2194562237f6",
"points": 1,
"position": 8,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155907Z",
"status": "Completed"
}
],
"status": "Done",
"title": "drop the Header in the UI",
"updated_at": "2025-10-16T22:13:07.759257Z"
},
{
"assigned_prefix": null,
"card_number": 17,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:51.760550Z",
"created_at": "2025-10-11T18:39:14.248330Z",
"description": "No need to explicitly generate IDS for cards.\n\nWe just give them IDs immediately\n",
"due_date": null,
"id": "ed0ead2b-6da9-4889-83b7-e4c60ab11032",
"points": 3,
"position": 9,
"priority": "High",
"sprint_id": null,
"sprint_logs": [],
"status": "Done",
"title": "Give cards ID from the beginning",
"updated_at": "2025-10-16T22:12:30.986301Z"
},
{
"assigned_prefix": null,
"card_number": 18,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-10-16T21:02:52.370276Z",
"created_at": "2025-10-11T18:42:39.943129Z",
"description": "Be able to group cards by sprints. \nSet sprint duration and automatic starting of new sprints\n\nMVP\n\nBoard level settings defines sprint duration.\n\nIf duration is set, sprint init is possible.\n\nAdd Sprint, set start date, assign cards to it.\n\nWhen end date is passed. Start up a new sprint and uncompleted cards over.\n\nSprint will have sprint names the same way tasks do. `prefix-<auto-increment>/sprint-name`\n\nPrefix will be set by sprint settings.\n\nSprint names are derived from user supplied list or explicitly set per sprint (sprint details)\n\nIf a list of sprint names are supplied new sprint will consume each element in order\n\n---\n\nThere should be a UI panel for sprints. On selected Board hit 's' to see sprint details\n\nDiscussion\n---\n\nLooking at this feature comprehensively, here are the potential gaps and clarifications \n needed:\n\n 🔴 Critical Issues\n\n 1. Keybinding conflict: 's' is already used for board settings. Need a different key for\n sprint panel (maybe 't' for sPrinTs, or nested under settings?)\n 2. Sprint rollover trigger: When end date passes, what triggers the new sprint creation?\n - On app startup check?\n - On any board action?\n - Background timer (would need tokio task)?\n - Manual user action only?\n 3. Active sprint ambiguity:\n - Can only one sprint be \"active\" at a time?\n - Can you create future sprints in advance?\n - What's the status model? (Planning, Active, Completed, Cancelled?)\n\n 🟡 Domain Model Gaps\n\n 4. Sprint entity needs:\n struct Sprint {\n id: Uuid,\n board_id: Uuid,\n sprint_number: u32,\n name: String, // format: prefix-<number>/sprint-name\n start_date: DateTime<Utc>,\n end_date: DateTime<Utc>,\n status: SprintStatus, // Active, Completed, Planned?\n created_at: DateTime<Utc>,\n updated_at: DateTime<Utc>,\n }\n 5. Board additions:\n - sprint_duration_days: Option<u32>\n - sprint_prefix: Option<String>\n - sprint_names: Vec<String> // consumed in order\n - next_sprint_number: u32\n - current_sprint_id: Option<Uuid> // which sprint is active?\n 6. Card additions:\n - sprint_id: Option<Uuid> // which sprint is this card in?\n\n 🟡 Unclear Behaviors\n\n 7. Sprint name list:\n - Once consumed, is name removed from list or just marked as used?\n - What happens when list is empty? Fall back to just sprint number?\n - Can user replenish the list?\n 8. Card assignment:\n - How do users assign cards to sprints? (UI interaction)\n - Can cards exist without a sprint (backlog)?\n - What defines \"uncompleted\" for rollover? (status != Done?)\n 9. Sprint settings UI:\n - Where do users configure duration, prefix, name list?\n - Part of board settings menu?\n - Separate sprint settings panel?\n 10. Sprint completion:\n - Is there a manual \"complete sprint early\" action?\n - Or only automatic when end date passes?\n\n 🟢 Architecture Questions\n\n 11. UI Navigation:\n - Is sprint panel a new Focus mode (like Boards/Cards)?\n - Or a separate view that replaces card list?\n - How to switch between sprint view and card view?\n 12. Sprint panel content:\n - List all sprints for board?\n - Show only current/active sprint?\n - Show sprint stats (cards total, completed, remaining)?\n - Burndown visualization?\n 13. Card filtering:\n - Should main card list optionally filter by sprint?\n - Show sprint name/info on cards in list?\n - Visual indication of which cards are in which sprint?\n 14. Time zone handling:\n - All dates in UTC internally?\n - Display in local time?\n - Sprint rollover happens at midnight in which timezone?\n\n 📋 Implementation Order Concerns\n\n 15. Data migration:\n - Existing boards need default values for new sprint fields\n - Existing cards get sprint_id: None (backlog)\n 16. MVP boundaries:\n - MVP: Basic CRUD for sprints, card assignment, viewing\n - Future: Burndown, velocity, sprint reports, analytics\n - Which features are MVP vs future?\n\n ❓ Questions for You\n\n Before I can plan implementation:\n\n 1. Keybinding: What key should open sprint panel? (s is taken)\n 2. Rollover: Manual or automatic? If automatic, when checked?\n 3. Sprint status: Can multiple sprints exist? What are the states?\n 4. Card backlog: Can cards exist without sprint assignment?\n 5. MVP scope: Sprint stats/burndown in MVP or future?\n 6. Sprint panel: Separate focus mode or replaces card list?\n\n Should I proceed with assumptions and note them in the plan, or do you want to clarify these\n first?\n\n> 1. We move board settings into the edit project ui. \n> 2. alert the user on app start up with a dialogue that start new sprint (y/n). \n> 3. multiple sprints should be allowed in parallel to allow for focused work on distinct parts of a project. yes we want to be able to plan sprints. picking tasks from the card list. The card list will be filtered to the active sprint. Planning, Active, Completed, Cancelled - yes. \n> 6. I wonder if we would need the bi directional relationship between sprint and cards? \n> 7. marked as used. for now. no list leaves the user to have to manually input a name. until a name is available the sprint name is `prefix+<auto-increment>` when a name is available it is like specified before. Yes the users supplies the list and it can be topped off whenever. \n> 8. When a sprint is initially created it is marked as in planning. then we in the sprint details interface can choose from a list of non completed tasks to include in the sprint. Yes cards can exist without a sprint as the plain list they are currently. uncompleted is only if the user makes the explicit choice not to start a new sprint after the rollover date. in such case manual creation of the subsequent sprint must occur. The user defines uncompleted. \n> 9 having a board selected. we press e to open edit project. in there we will have moved the current s, project settings. in the project settings panel we configure. duration, name list and prefix. We will replace the s with sprint details where the sprint itself can provide per sprint overrides for name list and prefix but not duration. Yes there is manual complete action. in the sprint details UI. \n> 11. The cards for the sprint can be toggled to replace the task list. for now we will use the key `t` to toggle between active sprint and the main card list. \n> 12. Tasks list will be toggled to show sprint tasks or not. Sprint details ui will handle all the rest. show list of sprints, select active sprint. stats and burndown we leave for the next iteration. \n> 12 For now, if sprint tasks are toggled just show sprint name next to the Tasks header, like Tasks - <sprint-name>. in unfiltered task list show the sprint name next to the task name, greyed out. \n> 14. All dates are in UTC internally. Display in local time. rollover in UTC. \n> 15. Yes.\n> 16. MVP: Basic CRUD for sprints, card assignment, viewing and all specified. think hard and keep planning with me\n\n---\n\nExcellent clarifications! Let me think through the complete user flows and identify remaining\n questions:\n\n 🤔 Critical Flows to Clarify\n\n Flow 1: Sprint Lifecycle Timing\n\n User creates sprint:\n - Status starts as Planning\n - Start date: set at creation OR when activating (Planning → Active)?\n - End date: calculated when? (start_date + duration)\n\n My assumption: Start date set when activating, not at creation. Makes sense for planning\n sprints in advance.\n\n Flow 2: Active Sprint Semantics\n\n You said \"multiple sprints allowed in parallel\" but also mentioned active_sprint_id\n (singular). I think I understand now:\n - Multiple sprints can have status Active (parallel work streams)\n - But ONE sprint is \"selected active\" for filtering with t key\n - active_sprint_id = currently selected sprint for filtering\n - Need action to \"switch active sprint\" in sprint details UI\n\n Confirm?\n\n Flow 3: Sprint Rollover Details\n\n On startup dialog: \"Sprint X ended. Start new sprint? (y/n)\"\n - If YES: Create new sprint with status Active (not Planning), auto-rollover cards where\n status != Done\n - If NO: Those cards stay in ended sprint, user must manually move later\n\n But: Multiple ended sprints? Show one dialog per sprint sequentially? Or one dialog listing\n all?\n\n Flow 4: Sprint Name Consumption\n\n Sprint name list = [\"Cobra Kai\", \"Miyagi-Do\", \"Eagle Fang\"]\n - Track usage with sprint_name_used_count: usize\n - When creating sprint: consume next name from list\n - If sprint_name_used_count >= sprint_names.len(): no name available, user must manually name\n OR fallback to prefix-<number>\n\n Question: Can user explicitly override auto-name even when names available?\n\n Flow 5: Sprint Details UI Structure\n\n Press s → SprintDetails mode:\n ┌─ Sprint Details ───────────────────────┐\n │ > sprint-1/Cobra Kai [Active] │\n │ sprint-2/Miyagi-Do [Planning] │\n │ sprint-3 [Completed] │\n └────────────────────────────────────────┘\n\n Press Enter on selected sprint → SprintDetail mode:\n ┌─ Sprint: sprint-1/Cobra Kai ───────────┐\n │ Status: Active │\n │ Dates: 2025-10-12 → 2025-10-26 │\n │ │\n │ Cards (3): │\n │ ☐ Implement search │\n │ ☐ Add vim motions │\n │ ☑ Fix ordering bug │\n │ │\n │ Actions: │\n │ e: edit name | c: complete | x: cancel │\n │ a: add cards | r: remove cards │\n │ s: set as active | ESC: back │\n └────────────────────────────────────────┘\n\n Questions:\n - Add cards (a): show list of all unassigned cards, select with j/k/space?\n - Remove cards (r): show cards in sprint, select to remove?\n - Set as active (s): updates active_sprint_id, affects t toggle?\n\n Flow 6: Board Settings Panel\n\n Edit project (e) → navigate to panel 3 (settings):\n ┌─ Project Settings [3] ─────────────────┐\n │ Branch Prefix: task- │\n │ │\n │ Sprint Duration: 14 days │\n │ Sprint Prefix: sprint │\n │ │\n │ Sprint Names: │\n │ > Cobra Kai │\n │ Miyagi-Do │\n │ Eagle Fang │\n │ │\n │ n: add name | d: delete name │\n │ e: edit field │\n └────────────────────────────────────────┘\n\n For MVP: Keep sprint names simple:\n - Store as Vec<String>\n - Edit via external JSON? OR\n - Simple dialog: \"Add sprint name:\" (one at a time)\n - Delete selected name with d\n\n My suggestion: For MVP, manually edit JSON. Add TUI editor in next iteration.\n\n Flow 7: Card List Filtering\n\n Unfiltered (default):\n Tasks\n ☐ Fix ordering bug (sprint-1)\n ☐ Add search (sprint-2)\n ☐ Implement vim motions\n\n Filtered to active sprint (t pressed, active = sprint-1):\n Tasks - sprint-1/Cobra Kai\n ☐ Fix ordering bug\n ☐ Add another task\n\n Question: Toggle state per board or global? Probably per board.\n\n Flow 8: Manual Sprint Completion\n\n In SprintDetail mode, press c:\n ┌─ Complete Sprint: sprint-1 ────────────┐\n │ Select cards to rollover (incomplete): │\n │ │\n │ ☑ Fix ordering bug │\n │ ☑ Implement search │\n │ ☐ Add vim motions (Done) │\n │ │\n │ Space: toggle | ENTER: complete │\n └────────────────────────────────────────┘\n\n Selected cards → stay in this sprint or move to new sprint?\n\n My assumption:\n - Cards NOT selected = considered complete (status updated to Done automatically?)\n - Cards selected = stay in completed sprint, user manually moves later OR\n - Cards selected = auto-move to active sprint?\n\n Need clarification here.\n\n 📐 Proposed Domain Model\n\n #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\n pub enum SprintStatus {\n Planning,\n Active,\n Completed,\n Cancelled,\n }\n\n pub struct Sprint {\n pub id: Uuid,\n pub board_id: Uuid,\n pub sprint_number: u32,\n pub name: Option<String>,\n pub prefix_override: Option<String>,\n pub status: SprintStatus,\n pub start_date: Option<DateTime<Utc>>,\n pub end_date: Option<DateTime<Utc>>,\n pub created_at: DateTime<Utc>,\n pub updated_at: DateTime<Utc>,\n }\n\n // Board additions\n pub sprint_duration_days: Option<u32>,\n pub sprint_prefix: Option<String>,\n pub sprint_names: Vec<String>,\n pub sprint_name_used_count: usize,\n pub next_sprint_number: u32,\n pub active_sprint_id: Option<Uuid>,\n\n // Card addition\n pub sprint_id: Option<Uuid>,\n\n 🏗️ Implementation Phases\n\n Phase 1: Board Settings Migration (smaller scope)\n\n 1. Add Settings panel to BoardFocus (1/2/3 navigation)\n 2. Move branch prefix setting to settings panel\n 3. Add sprint fields to Board domain\n 4. UI for editing sprint duration, prefix\n 5. Sprint names: JSON-only for MVP\n\n Phase 2: Sprint Domain & Basic CRUD\n\n 1. Add Sprint struct, SprintStatus enum\n 2. Add sprints to App state\n 3. Add sprint_id to Card\n 4. Create sprint action (from SprintDetails UI)\n 5. List sprints (SprintDetails mode, press s)\n 6. View sprint (SprintDetail mode, Enter on sprint)\n 7. Edit sprint name (dialog)\n 8. Delete/cancel sprint\n\n Phase 3: Card Assignment\n\n 1. Add cards to sprint (UI with card selection)\n 2. Remove cards from sprint\n 3. View cards in sprint detail\n 4. Show sprint name next to cards in main list\n\n Phase 4: Sprint Activation & Toggle\n\n 1. Activate sprint (Planning → Active, set start/end dates)\n 2. Set active sprint (for filtering)\n 3. Toggle card filtering with t\n 4. Update card list rendering (filtered vs all)\n 5. Update header (show sprint name when filtered)\n\n Phase 5: Sprint Lifecycle & Rollover\n\n 1. Check ended sprints on startup\n 2. Dialog per ended sprint\n 3. Auto-create new sprint with rollover\n 4. Manual complete sprint action\n 5. Choose cards to rollover in completion UI\n\n ❓ Questions Before Final Plan\n\n 1. Start date: Set at creation or activation? (I assume activation)\n 2. Active sprint: One \"active\" per board for filtering, but multiple can have Active status?\n 3. Multiple ended sprints: One dialog each, shown sequentially?\n 4. Name consumption: Auto-consume next name, but allow manual override?\n 5. Sprint completion: What happens to cards user selects as \"incomplete\"? Stay in sprint? Move\n to active sprint? User moves manually later?\n 6. Sprint names list: JSON editing only for MVP, or simple add/delete UI?\n\n Please clarify these and I'll present the final implementation plan!\n\n ---\nSprint Management Implementation Plan\n\nOverview\n\nAdd sprint functionality to kanban with MVP features: sprint CRUD, card assignment,\nfiltering, lifecycle management, and automatic rollover.\n\nPhase 1: Domain Model & Board Settings\n\nGoal: Add sprint data structures and move settings to Edit Project UI\n\n1.1 Domain Model\n\n- Add Sprint struct to kanban-domain/src/sprint.rs:\npub struct Sprint {\n pub id: Uuid,\n pub board_id: Uuid,\n pub sprint_number: u32,\n pub name: Option<String>,\n pub prefix_override: Option<String>,\n pub status: SprintStatus,\n pub start_date: Option<DateTime<Utc>>,\n pub end_date: Option<DateTime<Utc>>,\n pub created_at: DateTime<Utc>,\n pub updated_at: DateTime<Utc>,\n}\n- Add SprintStatus enum: Planning, Active, Completed, Cancelled\n- Export from kanban-domain/src/lib.rs\n\n1.2 Update Board Model\n\nAdd to Board struct:\n- sprint_duration_days: Option<u32>\n- sprint_prefix: Option<String>\n- sprint_names: Vec<String>\n- sprint_name_used_count: usize\n- next_sprint_number: u32\n- active_sprint_id: Option<Uuid>\n\nAdd serde defaults for deserialization.\n\n1.3 Update Card Model\n\nAdd to Card: sprint_id: Option<Uuid>\n\n1.4 Restructure Board Settings UI\n\n- Change BoardFocus enum: Name, Description, Settings\n- Move from BoardSettings mode → third panel in BoardDetail mode\n- Press 3 to navigate to settings panel\n- Move branch prefix setting to this panel\n- Add sprint duration, sprint prefix fields (editable with e)\n- Sprint names: JSON-only for MVP\n\n1.5 Remove Old Board Settings\n\n- Remove AppMode::BoardSettings\n- Remove AppMode::SetBranchPrefix\n- Consolidate into BoardDetail with 3 panels\n\nPhase 2: Sprint CRUD\n\nGoal: Create, list, view, edit, delete sprints\n\n2.1 App State\n\nAdd to App:\n- sprints: Vec<Sprint>\n- sprint_selection: SelectionState\n- active_sprint_index: Option<usize>\n- show_sprint_cards_only: bool (toggle state per board)\n\n2.2 App Modes\n\nAdd:\n- AppMode::SprintList (press s in normal mode, board selected)\n- AppMode::SprintDetail (Enter on sprint in list)\n- AppMode::CreateSprint (press n in SprintList)\n- AppMode::EditSprintName (press e in SprintDetail)\n\n2.3 Sprint List UI (s key)\n\n- Render list of sprints for active board\n- Show: sprint_number/name [Status]\n- j/k navigation, Enter to open detail\n- n: create new sprint (Planning status)\n- ESC: back to normal\n\n2.4 Sprint Detail UI (Enter on sprint)\n\n- Show sprint name, status, dates, card count\n- Actions:\n - e: edit name (dialog)\n - a: activate (Planning → Active, set dates)\n - c: complete sprint\n - x: cancel sprint\n - d: delete sprint (only if Planning)\n - s: set as active sprint (for filtering)\n - +: add cards\n - -: remove cards\n - ESC: back to sprint list\n\n2.5 Create Sprint\n\n- Status: Planning\n- Name: auto-consume from sprint_names[sprint_name_used_count] if available\n- If no names: fallback to {prefix}-{sprint_number}\n- Increment next_sprint_number and sprint_name_used_count\n\nPhase 3: Card Assignment\n\nGoal: Assign cards to sprints\n\n3.1 Add Cards to Sprint (+ in SprintDetail)\n\n- Show list of cards where sprint_id == None\n- j/k navigation, Space to multi-select\n- Enter: assign selected cards to current sprint\n- Update card sprint_id and updated_at\n\n3.2 Remove Cards from Sprint (- in SprintDetail)\n\n- Show cards in current sprint\n- j/k navigation, Space to multi-select\n- Enter: unassign (set sprint_id = None)\n\n3.3 Display Cards in Sprint Detail\n\n- In SprintDetail view, show list of cards\n- Format: ☐ Card title [points]\n\nPhase 4: Card Filtering & Display\n\nGoal: Toggle between all cards and active sprint cards\n\n4.1 Toggle Filtering (t key in Normal mode)\n\n- Toggle show_sprint_cards_only boolean\n- Only when active board and active_sprint_id is set\n- Persist toggle state per board (store in Board? or App state?)\n\n4.2 Update Card List Rendering\n\n- When filtered: get_sorted_board_cards() filters by card.sprint_id == active_sprint_id\n- When unfiltered: show all cards\n\n4.3 Update Header\n\n- Filtered: \"Tasks - {sprint_name}\" \n- Unfiltered: \"Tasks\"\n\n4.4 Show Sprint Name on Cards (unfiltered view) \n\n- When sprint_id.is_some(), append sprint name in gray \n- Format: ☐ Card title (sprint-1/Cobra Kai) \n\nPhase 5: Sprint Lifecycle & Rollover \n\nGoal: Activate, complete, automatic rollover \n\n5.1 Activate Sprint (a in SprintDetail, status = Planning)\n\n- Change status: Planning → Active\n- Set start_date = Utc::now()\n- Calculate end_date = start_date + sprint_duration_days\n- Only ONE sprint can be Active at a time (check and prevent if another active exists)\n\n5.2 Startup Check for Ended Sprints\n\n- On app start, find sprints where status == Active && end_date < Utc::now()\n- If any found, show dialog:\n┌─ Sprints Ended ────────────────┐\n│ ☑ sprint-1/Cobra Kai │\n│ ☑ sprint-2/Miyagi-Do │\n│ │\n│ Start new sprints? (y/n) │\n└────────────────────────────────┘\n- j/k navigation, Space to toggle checkboxes\n- y/Enter: create new sprints for selected\n- n/ESC: no action, user handles manually later\n\n5.3 Automatic Rollover (if \"yes\" to dialog)\n\n- For each selected sprint:\n - Mark as Completed\n - Get cards where sprint_id == ended_sprint.id && status != Done\n - Create new sprint (status = Active, auto-name)\n - Move incomplete cards to new sprint\n - Set new sprint dates\n\n5.4 Manual Complete Sprint (c in SprintDetail)\n\n- Show list of cards in sprint with status != Done\n- Checkboxes for each card (default: all selected)\n- Space: toggle, Enter: complete\n- Selected cards: move to next Active sprint (or create new if none)\n- Unselected cards: stay in sprint, user moves manually later\n- Mark sprint as Completed\n\nPhase 6: Polish & Integration\n\nGoal: Help text, validation, error handling\n\n6.1 Help Text Updates\n\n- Add help for all new modes\n- Update keybindings in footer\n\n6.2 Validation\n\n- Prevent activating sprint if duration not set\n- Prevent multiple Active sprints\n- Validate dates\n\n6.3 Auto-save\n\n- Sprints auto-save to JSON along with boards/cards\n\nImplementation Order\n\n1. Phase 1 (domain + settings UI migration)\n2. Phase 2 (sprint CRUD, basic UI)\n3. Phase 3 (card assignment)\n4. Phase 4 (filtering, display)\n5. Phase 5 (lifecycle, rollover)\n6. Phase 6 (polish)\n\nKey Decisions Confirmed\n\n- ✅ One Active sprint at a time (for filtering)\n- ✅ Start dates set on activation, not creation\n- ✅ Sprint name auto-consumed from list, manual override possible\n- ✅ Rollover dialog shows all ended sprints with checkboxes\n- ✅ Manual completion: selected cards move to next sprint\n- ✅ Sprint names list: JSON editing for MVP\n- ✅ Settings moved to Edit Project UI panel 3\n\n2025-10-12 14:58\n---\n\nWe are starting a new section of this on a new day\n\nWhat I feel is missing at this point is a nice way to assign tasks to a sprint.\n\nI want to be able in the task list to select multiple tasks and then assign them to a sprint\n",
"due_date": null,
"id": "1c0b1752-53f1-4240-be75-0706662156df",
"points": 4,
"position": 10,
"priority": "Medium",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_logs": [
{
"ended_at": null,
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2025-11-01T09:30:06.155908Z",
"status": "Completed"
}
],
"status": "Done",
"title": "Add sprints",
"updated_at": "2025-10-16T21:02:52.370276Z"
},
{
"assigned_prefix": "KAN",
"card_number": 19,
"card_prefix": null,
"column_id": "591afb61-7289-46bf-ba07-21758afd44fe",
"completed_at": null,
"created_at": "2025-10-11T18:45:47.488818Z",
"description": "If a pull request is open for a card. Open a link to that pull\n\nshow if a card has a pull request open (or in draft in the task list)\n\nExamples\n\nPR opem\n```\nA task name - [PR] (green)\n```\n\nDraft\n```\nA task name - [PR] (greyed out)\n```\n---\n\nI am getting the feeling that this would depend on having configuration for collaborative version control per board\n\n---\n\nGit integration\n",
"due_date": null,
"id": "f4c31d5d-7f78-451c-b7db-f26e4931fae4",
"points": 3,
"position": 19,
"priority": "Medium",
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_logs": [
{
"ended_at": "2025-11-02T16:56:39.448943Z",
"sprint_id": "737b04e6-52e2-4a40-bbaa-e2c9bcc84249",
"sprint_name": "an-experience-to-remember",
"sprint_number": 6,
"started_at": "2025-11-01T09:30:06.155908Z",
"status": "Active"
},
{
"ended_at": "2025-11-17T22:41:09.863739Z",
"sprint_id": "ce752be4-efce-40fe-bfa7-d0b313497feb",
"sprint_name": "a-hallowed-week",
"sprint_number": 7,
"started_at": "2025-11-02T16:56:39.448944Z",
"status": "Planning"
},
{
"ended_at": "2025-12-15T20:35:57.264721Z",
"sprint_id": "a61d4a55-5870-4493-9015-9854d8f583cf",
"sprint_name": "progression-is-progress",
"sprint_number": 8,
"started_at": "2025-11-17T22:41:09.863743Z",
"status": "Planning"
},
{
"ended_at": "2026-01-10T15:31:10.374920Z",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_name": "a-merry-christmas",
"sprint_number": 10,
"started_at": "2025-12-15T20:35:57.264721Z",
"status": "Planning"
},
{
"ended_at": "2026-02-01T19:39:40.784539Z",
"sprint_id": "5fffb1a7-facb-4461-b0f1-13ec3e4d3f3a",
"sprint_name": "an-eventful-october",
"sprint_number": 1,
"started_at": "2026-01-10T15:31:10.374921Z",
"status": "Completed"
},
{
"ended_at": "2026-02-17T22:20:49.521391358Z",
"sprint_id": "3b98dc57-7c28-4724-9b80-e4dcb3421acd",
"sprint_name": "a-new-year",
"sprint_number": 11,
"started_at": "2026-02-01T19:39:40.784539Z",
"status": "Planning"
},
{
"ended_at": null,
"sprint_id": "c2712055-8490-43fd-be83-5331c49f6c75",
"sprint_name": "fresh-air",
"sprint_number": 12,
"started_at": "2026-02-17T22:20:49.521391596Z",
"status": "Planning"
}
],
"status": "Todo",
"title": "Link to pull request",
"updated_at": "2026-02-17T22:20:49.521392929Z"
},
{
"assigned_prefix": "KAN",
"card_number": 20,
"card_prefix": null,
"column_id": "289b85d8-a199-49fa-9d77-5ad892d87ef8",
"completed_at": "2025-12-19T23:42:20.064240Z",
"created_at": "2025-10-11T19:23:32.612472Z",
"description": "Be able to drop a card \n",
"due_date": null,
"id": "8ecc1849-bde7-4f97-99f7-44b7d9158bbf",
"points": 2,
"position": 90,
"priority": "Low",
"sprint_id": "63d7a91a-cd0f-42fa-8fac-73ae015431f8",
"sprint_logs": [