kanban-core
Foundation crate for the kanban workspace. Provides shared types, error handling, configuration, state primitives, and the dependency graph used by all other crates.
Modules
Error Types
CoreError
pub type CoreResult<T> = ;
AppConfig
Application configuration loaded from a TOML or JSON config file.
| Field | Type | Default | Description |
|---|---|---|---|
configuration_format |
Option<String> |
"toml" |
Config file format ("json" or "toml") |
configuration_location |
Option<String> |
system default | Path to the config file |
default_card_prefix |
Option<String> |
"task" |
Default card identifier prefix (e.g. "KAN" → KAN-1) |
default_sprint_prefix |
Option<String> |
"sprint" |
Default sprint identifier prefix |
editing_format |
Option<String> |
"json" |
Format used for external editor ("json" or "toml") |
storage_backend |
Option<String> |
"json" |
Storage backend ("json" or "sqlite") |
storage_location |
Option<String> |
"boards.json" / "boards.sqlite" |
Path to the data file |
Effective-value getters (return the value or its default):
config.effective_default_card_prefix // → "task"
config.effective_default_sprint_prefix // → "sprint"
config.effective_storage_backend // → "json"
config.effective_editing_format // → "json"
config.effective_configuration_format // → "toml"
config.effective_storage_location // → "boards.json"
Validation: config.validate_values() returns CoreError::Validation if any field is out of range.
Branch prefix validation: validate_branch_prefix(prefix: &str) -> bool — non-empty, alphanumeric + hyphens/underscores, must start and end with an alphanumeric character.
PaginatedList<T>
Serialized pagination envelope used by CLI and MCP list responses.
PaginatedList::paginate(items, page, page_size)— slicesitemsand returns the envelope. ReturnsCoreError::Validationifpage_size > MAX_PAGE_SIZE(500) orpage_size == 0.resolve_page_params(page: Option<u32>, page_size: Option<u32>) -> CoreResult<(usize, usize)>— applies defaults (page=1,page_size=50) and validates.- Constants:
DEFAULT_PAGE = 1,DEFAULT_PAGE_SIZE = 50,MAX_PAGE_SIZE = 500.
Page / PageInfo
TUI viewport pagination — manages which items are visible in a terminal viewport given a scroll offset. Pure in-memory state — lives only in the TUI process.
Page computes a PageInfo for a given item count, viewport height, and scroll offset.
InputState
UTF-8–aware text input with cursor management. Used for all text entry dialogs in the TUI.
Methods: insert_char, delete_char_before, delete_char_after, move_left, move_right, jump_to_start, jump_to_end, clear.
The cursor tracks byte offsets that always fall on UTF-8 character boundaries.
SelectionState
Cursor navigation for a fixed-size list.
Methods: next(), prev(), clamp() — wraps around at boundaries.
Editable<T> trait
Implemented by domain types to support round-trip serialization through an external editor.
Graph machinery
Reusable graph primitives for relating domain entities (used by kanban-domain for card dependency tracking). Direction is a property of the container, not the edge — DagGraph<E> carries directed edges, UndirectedGraph<E> carries undirected ones, and the type system prevents calling directed vocabulary on an undirected graph.
The machinery is fully generic over Edge::NodeId. The kanban domain keys on Uuid today, but the algorithms only require Copy + Eq + Hash, so external callers can pick any node identity (e.g. u32, or a discriminated (EntityKind, Uuid) newtype for heterogeneous-entity graphs).
Trait taxonomy
| Trait | Purpose |
|---|---|
GraphNode |
Implemented by domain entities (Card, Sprint, ...) — provides node_id() -> Uuid. |
Graph |
Direction-agnostic core: add_edge, remove_edge, contains_edge. Object-safe with an explicit NodeId binding (e.g. &dyn Graph<NodeId = Uuid>). |
Directed: Graph |
Adds outgoing(node) / incoming(node) — distinct successor / predecessor sets. Only directed containers implement this. |
Undirected: Graph |
Adds neighbors(node) — a single direction-less neighbour set. Only undirected containers implement this. |
Cascadable: Graph |
Mutating node-keyed cascade: archive_node, unarchive_node, remove_node. |
EdgeSet: Graph |
Read-only set vocabulary aligned with HashSet / BTreeSet: len, active_len, is_empty, contains, contains_archived. contains returns the active view; contains_archived is the explicit escape hatch for history. |
Splitting Cascadable (mutating) from EdgeSet (read-only) keeps each trait's name accurate to its single purpose and lets a generic consumer ask for only the surface it actually uses.
The Edge trait and EdgeBase<N>
Concrete edge kinds (e.g. SpawnsEdge, BlocksEdge, RelatesEdge in kanban-domain) embed EdgeBase<N> for the common fields and implement the Edge trait so the graph containers can operate on them uniformly:
EdgeBase<N> itself implements Edge for N: Copy + Eq + Hash, so it works as a standalone edge when no per-kind metadata is needed.
Concrete containers
DagGraph<E: Edge>— directed acyclic graph. Rejects self-references, active duplicates, and any edge whose insertion would create a cycle in the active subgraph. ImplementsGraph + Directed + Cascadable + EdgeSet. Providesdescendants/ancestorsfor transitive traversal.Deserializere-runs the DAG invariants so a corrupted file fails to load up front.UndirectedGraph<E: Edge>— undirected graph. Rejects self-references and active duplicates (either ordering of endpoints counts as the same edge). Cycles are permitted. ImplementsGraph + Undirected + Cascadable + EdgeSet.
Both are backed by a crate-internal EdgeStore<E> that holds active + archived edges in a flat list. Archived edges survive remove operations as history; the active view ignores them.
GraphError
Plugging in a custom edge struct
use ;
use ;
use Uuid;
let mut graph: = new;
graph.add_edge_with_metadata?;
Use Graph::add_edge(from, to) when default metadata is fine (it calls Edge::from_endpoints under the hood); use add_edge_with_metadata(edge) when the caller needs to set kind-specific fields explicitly.
Algorithms
The cycle, path, and reachability primitives live in kanban_core::graph::algorithms (would_create_cycle, has_cycle, reachable_from). They operate on a generic HashMap<N, Vec<N>> adjacency list and are reused both by the containers and by EdgeStore::adjacency_list for ad-hoc analysis.
LogEntry / Loggable trait
Structured logging support for domain events.
Dependencies
| Crate | Purpose |
|---|---|
serde + serde_json |
Serialization |
uuid |
Uuid type |
thiserror |
Error derivation |
chrono |
Timestamps |