Boards and Sub-Boards
Relevant source files
The following files were used as context for generating this wiki page:
- TODO/cards/cross-workspace-boards.md
- TODO/cards/tauri-backend-module-split.md
- TODO/todo.md
- docs/plans/2026-03-10-markdown-kanban-design.md
- docs/plans/2026-03-11-example-workspace-refresh-design.md
- docs/plans/2026-03-12-cross-workspace-boards-design.md
- src/types/workspace.ts
- src/utils/boardMarkdown.test.ts
- src/utils/kanbanPath.ts
This document explains the structure and organization of Kanban boards in KanStack, including columns, sections, sub-board hierarchies, and the discovery mechanism. For information about the workspace-level TODO/ directory structure, see Workspaces and TODO/ Structure. For details on individual card structure and content, see Cards. For the markdown syntax conventions used in board files, see Markdown Format.
Overview
A board in KanStack is represented by a single markdown file (todo.md) within a TODO/ directory. Each board organizes cards into columns and optional sections, and can reference child boards to create hierarchical project structures. Boards are identified by their filesystem path rather than their title, allowing boards to be renamed without breaking relationships.
Board File Structure
File Location and Identity
Every board exists as a TODO/todo.md file within its own project directory. The board's identity is derived from the normalized path to its TODO/ directory, not from its filename or title.
project-name/
├── TODO/
│ ├── todo.md # Board definition file
│ ├── cards/ # Local card files
│ └── README.md # Optional board notes
└── ...Sources: docs/plans/2026-03-12-cross-workspace-boards-design.md:8-13, docs/plans/2026-03-10-markdown-kanban-design.md:8-12
Board Identity Functions
The boardIdFromBoardPath function converts a board file path to its stable identity:
The boardTodoPathFromBoardPath function extracts the TODO/ directory path:
Sources: src/utils/kanbanPath.ts:84-90
Board Structure Components
Columns
Columns are the primary organizational units of a board, defined using ## headings in the markdown file. Each column can contain cards directly or organize them into sections.
## Backlog
## In Progress
## Review
## DoneThe parser represents columns using the KanbanBoardColumn type, which includes a name, slug, and array of sections:
interface KanbanBoardColumn {
name: string
slug: string
sections: KanbanBoardSection[]
}Sources: docs/plans/2026-03-10-markdown-kanban-design.md:15-20, TODO/todo.md:25-41
Sections
Sections are optional subdivisions within columns, defined using ### headings. They provide additional organization for grouping related cards within a column.
Board Markdown Structure Diagram
Sources: docs/plans/2026-03-10-markdown-kanban-design.md:26-32, src/utils/boardMarkdown.test.ts:18-40
Example with sections:
## Todo
### High Priority
- [[cards/critical-bug]]
### Routine
- [[cards/minor-fix]]The parser preserves sections throughout operations. The DEFAULT_SECTION_KEY constant ("__default__") is used internally for cards not under an explicit section:
Sources: src/utils/kanbanPath.ts:7, src/utils/boardMarkdown.test.ts:42-82
Archive Column
Boards can have a special Archive column for storing completed or deprioritized cards. The archive column is automatically created when archiving a card if it doesn't exist, and is always placed last in column order:
src/utils/boardMarkdown.test.ts:84-105
Sources: src/utils/boardMarkdown.test.ts:84-105, src/utils/boardMarkdown.test.ts:180-194
Settings Block
Boards can include a settings block to store preferences and view state. Settings are stored in a special comment block using Obsidian-style syntax with a JSON payload:
%% kanban:settings
```json
{
"sort-order": "manual",
"group-by": "none",
"show-empty-columns": true,
"card-preview": "metadata",
"show-sub-boards": false,
"show-archive-column": false
}%%
**Sources:** [docs/plans/2026-03-10-markdown-kanban-design.md:34-40](../docs/plans/2026-03-10-markdown-kanban-design.md), [TODO/todo.md:5-23](../TODO/todo.md)
## Board Data Types
The `KanbanBoardDocument` type represents a fully parsed board:
```typescript
interface KanbanBoardDocument {
slug: string // Path-based identity
path: string // File path
title: string // Display name
frontmatter: Record<string, unknown>
columns: KanbanBoardColumn[]
subBoards: NormalizedWikiTarget[]
settings?: Record<string, unknown>
}Board Data Flow Diagram
Sources: src/types/workspace.ts:31-40
The LoadedWorkspace provides indexed access to boards:
Sources: src/types/workspace.ts:31-40
Sub-Boards
Sub-Board Hierarchy Model
Sub-boards enable hierarchical project organization. A parent board can reference child boards, which can themselves reference their own child boards, creating a tree structure. Each sub-board maintains its own TODO/ directory with local cards and configuration.
Sub-Board Hierarchy Structure
Sources: docs/plans/2026-03-10-markdown-kanban-design.md:51-56, docs/plans/2026-03-12-cross-workspace-boards-design.md:8-13
Sub-Board Markdown Representation
Sub-boards are declared in a special ## Sub Boards section of the parent board. Each sub-board is represented as a bulleted wikilink pointing to the child board's TODO/ directory:
## Sub Boards
- [[services/api/TODO|API Service]]
- [[services/worker/TODO|Worker Service]]The wikilink target points to the child's TODO/ directory, not directly to todo.md. The optional display text after the | provides a human-readable title.
Sources: docs/plans/2026-03-10-markdown-kanban-design.md:21-23, docs/plans/2026-03-12-cross-workspace-boards-design.md:27-30
Sub-Board Discovery Mechanism
Sub-board discovery is a manual, user-triggered process rather than automatic scanning. The "Find Sub Boards" action scans the filesystem tree for descendant TODO/ directories and persists discovered paths to markdown.
Discovery Process:
- User triggers "Find Sub Boards" from system menu
- Scanning starts from parent directory of current board's
TODO/folder - Scanner recursively finds descendant directories named
TODO - Current board's own
TODO/is excluded from results - Only nearest
TODO/folders are selected (no double-counting nested boards) - Discovered paths are written to
## Sub Boardsas relative wikilinks - Workspace reloads from updated markdown
Sub-Board Discovery Flow
Sources: docs/plans/2026-03-12-cross-workspace-boards-design.md:15-23, TODO/cards/cross-workspace-boards.md:32-34
Sub-Board Path Resolution
Sub-board paths are stored as relative paths from the parent board's project directory. The resolveBoardTargetPath function resolves these relative paths to absolute board identities:
src/utils/kanbanPath.ts:111-119
The buildSubBoardTarget function constructs the relative path from parent to child:
src/utils/kanbanPath.ts:127-130
Sources: src/utils/kanbanPath.ts:111-130
Sub-Board Loading Model
When opening a board, the loader:
- Loads the selected board's
todo.mdand localcards/*.md - Resolves
## Sub Boardslinks relative to the board's project root - Recursively loads child boards and their cards
- Builds indexed maps keyed by path-based slugs
Missing child board paths remain visible as unresolved links rather than being silently removed.
Sources: docs/plans/2026-03-12-cross-workspace-boards-design.md:40-45
Board Operations
Column Management
Boards support several column operations, each serialized to markdown and persisted:
| Operation | Function | Description |
|---|---|---|
| Add Column | addBoardColumnMarkdown() | Appends new column before archive |
| Rename Column | renameBoardColumnMarkdown() | Updates column heading and slug |
| Delete Column | deleteBoardColumnMarkdown() | Removes column, orphaning cards |
| Reorder Columns | reorderBoardColumnsMarkdown() | Rearranges columns in specified order |
Sources: src/utils/boardMarkdown.test.ts:157-178
Creating Sub-Boards
Creating a sub-board involves two operations:
- Add sub-board link to parent using
addSubBoardMarkdown():
src/utils/boardMarkdown.test.ts:120-124
- Create child board file using
createBoardMarkdown(), which inherits parent column structure:
src/utils/boardMarkdown.test.ts:125
The child board inherits the parent's column structure but starts with empty columns:
src/utils/boardMarkdown.test.ts:130-139
Sources: src/utils/boardMarkdown.test.ts:107-139
Board Renaming
Renaming a board updates only the title field in frontmatter without changing the board's identity or path:
src/utils/boardMarkdown.test.ts:141-155
Sources: src/utils/boardMarkdown.test.ts:141-155
Board Constraints and Rules
Structural Rules
- Column Headings: Must use
##for columns, never###or other levels - Section Headings: Must use
###for sections, only inside columns - Card Links: Must be bulleted wikilinks, one per line
- Sub-Board Section:
## Sub Boardsis reserved and must not contain###sections - Archive Position: Archive column always appears last if present
Sources: docs/plans/2026-03-10-markdown-kanban-design.md:14-32
Sub-Board Constraints
- Tree Structure: Sub-boards only exist under parent board's filesystem tree
- No External Links: Cannot link to arbitrary boards outside the tree
- Manual Discovery: Discovery is user-triggered, not automatic
- Path-Based Identity: Board identity derives from
TODO/path, not title - Local Cards: Each board's cards live in its own
TODO/cards/directory
Sources: docs/plans/2026-03-12-cross-workspace-boards-design.md:15-23, docs/plans/2026-03-12-cross-workspace-boards-design.md:34-37
Summary Table: Board vs Sub-Board
| Aspect | Board | Sub-Board |
|---|---|---|
| File Location | TODO/todo.md | child-dir/TODO/todo.md |
| Identity | Path to TODO/ directory | Path to child's TODO/ directory |
| Cards | TODO/cards/*.md | child-dir/TODO/cards/*.md |
| Parent Reference | None | Listed in parent's ## Sub Boards |
| Column Structure | Defined by ## headings | Inherits parent columns on creation |
| Discovery | Root board opened by user | Found via "Find Sub Boards" scan |
Sources: docs/plans/2026-03-12-cross-workspace-boards-design.md:8-61
