Skip to content

Commit c4f2fe8

Browse files
authored
Merge pull request #160 from fulsomenko/develop
v0.2.0: Card Dependencies, Undo/Redo & Domain Architecture Refactor ## Summary This releas introduces card dependency management, undo/redo support, and a major architectural refactoring that extracts business logic from the TUI into the domain layer — laying the groundwork for non-TUI consumers (API server, CLI commands, etc.). ## Features ### Card Dependencies (KAN-6) - **Dependency graph** with three edge types: `ParentOf`, `Blocks`, `RelatesTo` - Generic `Graph<E>` data structure in `kanban-core` with **cycle detection** to enforce DAG constraints - Full TUI for managing parent-child card relationships - Dependency graph integrated into persistence (import/export) ### Undo / Redo (KAN-134) - Snapshot-based undo/redo with two-stack approach - `u` to undo, `U` to redo - Automatic state capture before every command execution - Bounded history (capped at 100 entries) to prevent unbounded memory growth ### Cascade Cleanup on Delete (KAN-170) - Deleting a card now removes all its dependency edges - Archiving a card preserves (archives) its edges; restoring unarchives them - Column deletion validates that no active or archived cards remain - Sprint deletion unassigns all cards before removal - Comprehensive integration test suite for data integrity ### Parent/Child Relationship UI (KAN-177) - Parent and child boxes displayed **side-by-side** in card detail view - Interactive `j`/`k` navigation within relationship lists - `Enter` to drill into a related card, `Backspace`/`h` to navigate back (breadcrumb history) - Scrolling with "X items above/below" indicators for long lists - Full wrap-around navigation cycle through all card detail sections ### Position Sort Field (#158) - New `Position` sort option for cards reordered via MCP `move_card` - Fix: sort field now syncs from board after file reload (no more reverting to Task Number) ## Architecture: TUI-to-Domain Refactoring (KAN-178) Extracts business logic from `kanban-tui` into `kanban-domain` and `kanban-core`: ### kanban-core - `InputState` — cursor-aware text buffer with correct multi-byte UTF-8 handling - `SelectionState` — generic single-item list selection - `PageInfo` — viewport pagination with scroll indicators ### kanban-domain - `sort`, `filter`, `search`, `query` modules with `CardQueryBuilder` fluent API - `card_lifecycle` — card movement, completion toggle, archival, position compaction - `HistoryManager` — bounded undo/redo (100 entries) - `export`/`import` with `BoardExporter` and `BoardImporter` - `Snapshot` serialization directly on the domain type - Replaced `dyn` dispatch with enum dispatch in search and sort ### kanban-tui - Removed re-export wrappers and thin delegation layers - Replaced inline business logic with `card_lifecycle` calls - Replaced duplicated filter/sort service with `CardQueryBuilder` - Net reduction of ~265 lines of duplicated logic ## What's Changed * chore/changelog-duplication-and-build-target (#150) * Kan 6/card dependencies (#152) * KAN-134/undo action (#154) * KAN-170/cascade cleanup delete operations (#155) * KAN-177: Display parent/child relationship boxes side-by-side (#156) * Add Position as a sort field option (#158) * KAN-178/tui to domain refactoring migration (#159) **Full Changelog**: v0.1.16...develop
2 parents f819831 + 615674f commit c4f2fe8

File tree

103 files changed

+9861
-2484
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+9861
-2484
lines changed

.github/workflows/aggregate-changesets.yml

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,58 @@ jobs:
1515
aggregate:
1616
name: Aggregate Changesets
1717
runs-on: ubuntu-latest
18+
permissions:
19+
contents: write
1820
if: github.event.pull_request.merged == true
1921
steps:
2022
- uses: actions/checkout@v4
2123
with:
2224
fetch-depth: 0
2325
ref: develop
2426

25-
- name: Aggregate changesets
27+
- name: Check for changesets
28+
id: check
2629
run: |
27-
bash scripts/aggregate-changesets.sh
28-
echo "✅ Changesets validated and aggregated"
30+
CHANGESETS=$(find .changeset -maxdepth 1 -name "*.md" ! -name "README.md" 2>/dev/null || true)
31+
if [ -z "$CHANGESETS" ]; then
32+
echo "has_changesets=false" >> "$GITHUB_OUTPUT"
33+
echo "No changesets found, skipping version bump"
34+
else
35+
echo "has_changesets=true" >> "$GITHUB_OUTPUT"
36+
echo "Found changesets:"
37+
echo "$CHANGESETS"
38+
fi
39+
40+
- uses: cachix/install-nix-action@v27
41+
if: steps.check.outputs.has_changesets == 'true'
42+
with:
43+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
44+
extra_nix_config: |
45+
experimental-features = nix-command flakes
46+
47+
- name: Setup SSH deploy key
48+
if: steps.check.outputs.has_changesets == 'true'
49+
run: |
50+
mkdir -p ~/.ssh
51+
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/deploy_key
52+
chmod 600 ~/.ssh/deploy_key
53+
ssh-keyscan github.com >> ~/.ssh/known_hosts
54+
55+
- name: Configure Git
56+
if: steps.check.outputs.has_changesets == 'true'
57+
run: |
58+
git config user.name "github-actions[bot]"
59+
git config user.email "github-actions[bot]@users.noreply.github.com"
60+
git remote set-url origin git@github.com:fulsomenko/kanban.git
61+
62+
- name: Bump version from changesets
63+
if: steps.check.outputs.has_changesets == 'true'
64+
run: |
65+
nix develop --command bash scripts/bump-version.sh "${{ github.event.pull_request.number }}"
66+
67+
- name: Push version bump
68+
if: steps.check.outputs.has_changesets == 'true'
69+
env:
70+
GIT_SSH_COMMAND: "ssh -i ~/.ssh/deploy_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"
71+
run: |
72+
git push origin develop

CHANGELOG.md

Lines changed: 51 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
1+
## [0.2.0] - 2026-02-01
2+
3+
- - feat(tui): register undo/redo keybindings in CardList provider
4+
- feat(tui): register undo/redo keybindings in BoardDetail provider
5+
- feat(tui): register undo/redo keybindings in CardDetail provider
6+
- feat(tui): register undo/redo keybindings in NormalMode providers
7+
- feat(tui): add Undo and Redo KeybindingAction variants
8+
- feat(tui): add undo() and redo() methods to App
9+
- feat(tui): capture snapshots before command execution for undo history
10+
- feat(tui): integrate HistoryManager into StateManager
11+
- feat(tui): create HistoryManager module for undo/redo support
12+
- - test: add cycle detection tests for dependency graph
13+
- test: add integration tests for cascade cleanup operations
14+
- feat(domain): unassign cards when deleting sprints
15+
- feat(domain): add validation to DeleteColumn command
16+
- feat(domain): implement cascade cleanup in card deletion and archival
17+
- feat(domain): add cascade cleanup methods to DependencyGraph trait
18+
- - feat(tui): implement backward wrap-around navigation from title to children
19+
- feat(tui): add scrolling support to parent/child relationship boxes
20+
- feat(tui): Implement interactive navigation for parent/child relationship boxes
21+
- feat(tui): add infrastructure for parent/child relationship navigation
22+
- feat(tui): Display parent/child relationship boxes side-by-side with increased height
23+
- Extract business logic from kanban-tui into kanban-domain and kanban-core, establishing a clean layered architecture.
24+
### kanban-core
25+
- Add `InputState`, `SelectionState`, and `PageInfo` modules for reusable UI-agnostic state primitives
26+
### kanban-domain
27+
- Add `sort`, `filter`, `search`, and `query` modules for card filtering/sorting pipeline
28+
- Add `CardQueryBuilder` with fluent API for composing card queries
29+
- Add `card_lifecycle` module for card movement, completion toggling, and archival logic
30+
- Add `HistoryManager` for bounded undo/redo (capped at 100 entries)
31+
- Add `export`/`import` modules with `BoardExporter` and `BoardImporter`
32+
- Add `Snapshot` serialization (`to_json_bytes`/`from_json_bytes`) directly on the domain type
33+
- Add sprint query functions and `CardFilters` struct
34+
- Replace dyn dispatch with enum dispatch in search and sort
35+
### kanban-tui
36+
- Remove re-export wrappers and thin delegation layers that proxied domain logic
37+
- Replace inline business logic in handlers with `card_lifecycle` calls
38+
- Replace duplicated filter/sort service with `CardQueryBuilder`
39+
- Fix multi-byte UTF-8 cursor handling via core `InputState`
40+
- - feat(tui): Add TUI for managing parent-child card relationships
41+
- feat(domain): Add commands for parent-child card relationships
42+
- feat(domain): Add ParentOf edge type for hierarchical card grouping
43+
- feat(tui,cli): integrate dependency graph into persistence
44+
- feat(domain): add dependency management commands
45+
- feat(domain): add card dependency graph types
46+
- feat(core): add graph-related error variants
47+
- feat(core): add graph cycle detection algorithms
48+
- feat(core): add generic Graph<E> data structure
49+
- feat(core): add graph module with edge types and GraphNode trait
50+
51+
152
## [0.1.16] - 2025-12-21
253

354
- chore: bump version to 16
@@ -92,184 +143,6 @@ view in the background instead of hardcoded destinations.
92143
- Two-phase rendering: base view first, then dialog overlay
93144
- Converted all push_mode(AppMode::X) calls to open_dialog(DialogMode::X)
94145

95-
## [0.1.15] - 2025-12-21
96-
97-
- feat(cli): include git commit hash in version output
98-
- fix(tui): skip empty sprints section when navigating board details
99-
- fix: filter out completed and cancelled sprints from assign list
100-
- fix: navigate to last sprint when scrolling up from columns in board settings
101-
- fix: preserve navigation mode during auto-reload from external changes
102-
- chore: cargo fmt
103-
- fix(tui): fix gg/G vim navigation in grouped-by-column view
104-
- chore: remove wip file
105-
- fix: prevent premature column switching in handle_navigation_down
106-
- fix: Centralize file watcher pause/resume in StateManager
107-
- feat: add kanban-mcp server
108-
- feat(mcp): add McpTools trait for compile-time parity with KanbanOperations
109-
- docs(mcp): add subprocess architecture documentation and Nix wrapper
110-
- feat(mcp): add CLI executor for subprocess-based operations
111-
- feat(mcp): enhance card operations and add delete/archive functionality
112-
- feat: add kanban-mcp: Model Context Protocol server implementation
113-
- fix: batch card creation with optional status update
114-
- fix: batch card movements with conditional status updates
115-
- fix: batch sprint activation and completion with board updates
116-
- fix: batch column position swaps
117-
- fix: batch card unassignment from sprint
118-
- fix: batch card completion toggles
119-
- fix: batch card moves when deleting column
120-
- fix: batch default column creation to prevent conflict dialog on new board
121-
- refactor: use batch command execution in sprint assignment handlers
122-
- feat: add execute_commands_batch for race-free command execution
123-
- fix: enhance AssignCardToSprint to handle sprint log transitions
124-
- fix: batch card archive and delete operations in animation completion
125-
- feat(persistence): create kanban-persistence crate structure
126-
- feat(state): create Command trait and StateManager
127-
- feat(domain): add CreateBoard command
128-
- feat(domain): add active_sprint_id field to BoardUpdate
129-
- feat(state): add debouncing to StateManager::save_if_needed()
130-
- feat(persistence): add automatic V1→V2 migration on load
131-
- feat(core,persistence): add conflict detection for multi-instance saves
132-
- feat(persistence): detect file conflicts before save
133-
- feat(state): propagate conflict errors in StateManager
134-
- feat(tui): Implement conflict resolution dialog and event loop integration
135-
- feat(tui): Integrate FileWatcher with App event loop
136-
- feat(state): Add view refresh tracking to StateManager
137-
- feat(tui): Add ExternalChangeDetected dialog
138-
- feat(tui): add user-visible error display banner
139-
- feat(app): prevent quit with pending saves
140-
- feat(app): add save completion receiver to App struct
141-
- feat(state): add bidirectional save completion channel
142-
- feat: Add migration verification and automatic backup cleanup
143-
- fix: Add instance ID check to file watcher to prevent false positives
144-
- fix: Remove redundant version fields from PersistenceMetadata
145-
- fix(tui): restoring restoring cards
146-
- fix(cli): restoring to a non existing column
147-
- docs: add CLI quick start section to root README
148-
- docs: update CLI README with command documentation
149-
- fix: use get_selected_card_in_context for points dialog
150-
- feat: add TuiContext struct with KanbanOperations implementation
151-
- feat: implement KanbanOperations trait for TUI App
152-
- test: update CLI tests for positional ID arguments
153-
- feat: make ID positional argument for single-resource commands
154-
- fix: return descriptive errors for invalid priority and status values
155-
- feat: add API version to CLI output and document never type
156-
- feat: simplify CLI file argument and add shell completions
157-
- fix: CLI context bugs and improve error messages
158-
- fix: Support positional file argument for TUI mode
159-
- test: Add comprehensive integration tests for CLI
160-
- feat: Implement full CLI with subcommand interface
161-
- feat: Add KanbanOperations trait for TUI/CLI feature parity
162-
- feat(mcp): omit description and sprint_logs from card list responses
163-
- feat(cli): include git commit hash in version output (#132)
164-
- fix: stabilize release pipeline for v0.1.15
165-
Jumping cards
166-
- fix: jump by actual visible cards count from render_info, not cards_to_show
167-
- feat: add vim jump motions to normal mode keybinding display
168-
- feat: add vim jump motions to card list keybinding display
169-
- feat: wire up vim jump motions to keybinding handlers
170-
- feat: add jump motion handlers
171-
- feat: add jump methods to CardList
172-
- feat: add jump_to_first and jump_to_last methods to SelectionState
173-
- feat: add jump action variants to KeybindingAction enum
174-
- feat: add pending_key field to App struct for multi-key sequences
175-
Refactored dialog mode handling to use nested AppMode::Dialog(DialogMode) enum
176-
for type-safe dialog management. Dialogs now correctly display their parent
177-
view in the background instead of hardcoded destinations.
178-
- Added DialogMode enum with all 23 dialog variants
179-
- Simplified is_dialog_mode() to matches!(self.mode, AppMode::Dialog(_))
180-
- Added get_base_mode() to determine parent view from mode_stack
181-
- Two-phase rendering: base view first, then dialog overlay
182-
- Converted all push_mode(AppMode::X) calls to open_dialog(DialogMode::X)
183-
184-
## [0.1.15] - 2025-12-21
185-
186-
- feat(cli): include git commit hash in version output
187-
- fix(tui): skip empty sprints section when navigating board details
188-
- fix: filter out completed and cancelled sprints from assign list
189-
- fix: navigate to last sprint when scrolling up from columns in board settings
190-
- fix: preserve navigation mode during auto-reload from external changes
191-
- chore: cargo fmt
192-
- fix(tui): fix gg/G vim navigation in grouped-by-column view
193-
- chore: remove wip file
194-
- fix: prevent premature column switching in handle_navigation_down
195-
- fix: Centralize file watcher pause/resume in StateManager
196-
- feat: add kanban-mcp server
197-
- feat(mcp): add McpTools trait for compile-time parity with KanbanOperations
198-
- docs(mcp): add subprocess architecture documentation and Nix wrapper
199-
- feat(mcp): add CLI executor for subprocess-based operations
200-
- feat(mcp): enhance card operations and add delete/archive functionality
201-
- feat: add kanban-mcp: Model Context Protocol server implementation
202-
- fix: batch card creation with optional status update
203-
- fix: batch card movements with conditional status updates
204-
- fix: batch sprint activation and completion with board updates
205-
- fix: batch column position swaps
206-
- fix: batch card unassignment from sprint
207-
- fix: batch card completion toggles
208-
- fix: batch card moves when deleting column
209-
- fix: batch default column creation to prevent conflict dialog on new board
210-
- refactor: use batch command execution in sprint assignment handlers
211-
- feat: add execute_commands_batch for race-free command execution
212-
- fix: enhance AssignCardToSprint to handle sprint log transitions
213-
- fix: batch card archive and delete operations in animation completion
214-
- feat(persistence): create kanban-persistence crate structure
215-
- feat(state): create Command trait and StateManager
216-
- feat(domain): add CreateBoard command
217-
- feat(domain): add active_sprint_id field to BoardUpdate
218-
- feat(state): add debouncing to StateManager::save_if_needed()
219-
- feat(persistence): add automatic V1→V2 migration on load
220-
- feat(core,persistence): add conflict detection for multi-instance saves
221-
- feat(persistence): detect file conflicts before save
222-
- feat(state): propagate conflict errors in StateManager
223-
- feat(tui): Implement conflict resolution dialog and event loop integration
224-
- feat(tui): Integrate FileWatcher with App event loop
225-
- feat(state): Add view refresh tracking to StateManager
226-
- feat(tui): Add ExternalChangeDetected dialog
227-
- feat(tui): add user-visible error display banner
228-
- feat(app): prevent quit with pending saves
229-
- feat(app): add save completion receiver to App struct
230-
- feat(state): add bidirectional save completion channel
231-
- feat: Add migration verification and automatic backup cleanup
232-
- fix: Add instance ID check to file watcher to prevent false positives
233-
- fix: Remove redundant version fields from PersistenceMetadata
234-
- fix(tui): restoring restoring cards
235-
- fix(cli): restoring to a non existing column
236-
- docs: add CLI quick start section to root README
237-
- docs: update CLI README with command documentation
238-
- fix: use get_selected_card_in_context for points dialog
239-
- feat: add TuiContext struct with KanbanOperations implementation
240-
- feat: implement KanbanOperations trait for TUI App
241-
- test: update CLI tests for positional ID arguments
242-
- feat: make ID positional argument for single-resource commands
243-
- fix: return descriptive errors for invalid priority and status values
244-
- feat: add API version to CLI output and document never type
245-
- feat: simplify CLI file argument and add shell completions
246-
- fix: CLI context bugs and improve error messages
247-
- fix: Support positional file argument for TUI mode
248-
- test: Add comprehensive integration tests for CLI
249-
- feat: Implement full CLI with subcommand interface
250-
- feat: Add KanbanOperations trait for TUI/CLI feature parity
251-
- feat(mcp): omit description and sprint_logs from card list responses
252-
- feat(cli): include git commit hash in version output (#132)
253-
- fix: stabilize release pipeline for v0.1.15
254-
Jumping cards
255-
- fix: jump by actual visible cards count from render_info, not cards_to_show
256-
- feat: add vim jump motions to normal mode keybinding display
257-
- feat: add vim jump motions to card list keybinding display
258-
- feat: wire up vim jump motions to keybinding handlers
259-
- feat: add jump motion handlers
260-
- feat: add jump methods to CardList
261-
- feat: add jump_to_first and jump_to_last methods to SelectionState
262-
- feat: add jump action variants to KeybindingAction enum
263-
- feat: add pending_key field to App struct for multi-key sequences
264-
Refactored dialog mode handling to use nested AppMode::Dialog(DialogMode) enum
265-
for type-safe dialog management. Dialogs now correctly display their parent
266-
view in the background instead of hardcoded destinations.
267-
- Added DialogMode enum with all 23 dialog variants
268-
- Simplified is_dialog_mode() to matches!(self.mode, AppMode::Dialog(_))
269-
- Added get_base_mode() to determine parent view from mode_stack
270-
- Two-phase rendering: base view first, then dialog overlay
271-
- Converted all push_mode(AppMode::X) calls to open_dialog(DialogMode::X)
272-
273146
## [0.1.14] - 2025-11-17 ([#patch](https://github.com/fulsomenko/kanban/pull/patch))
274147

275148
- - refactor: integrate card list into keybinding registry

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ members = [
1010
]
1111

1212
[workspace.package]
13-
version = "0.1.16"
13+
version = "0.2.0"
1414
edition = "2021"
1515
authors = ["Max Emil Yoon Blomstervall"]
1616
license = "Apache-2.0"

0 commit comments

Comments
 (0)