Architecture

Deep dive into BackMark's technical architecture, design decisions, and implementation details.

Project Structure

Directory Layout

When you initialize BackMark, it creates this structure:

your-project/
├── backlog/                    # BackMark root directory
│   ├── tasks/                  # Task markdown files
│   │   ├── 1.md
│   │   ├── 2.md
│   │   └── ...
│   ├── templates/              # Custom task templates
│   │   ├── feature.md
│   │   ├── bugfix.md
│   │   └── custom-template.md
│   ├── config.yml              # Project configuration
│   └── .cache/                 # Performance cache (auto-generated)
│       └── tasks-index.db
└── [your project files]

tasks/ Directory

Contains all task files as Markdown with YAML frontmatter.

  • Naming: {id}.md
  • Each file is a complete task
  • Human-readable and editable
  • Perfect for version control

.cache/ Directory

Optional LokiJS database for fast queries.

  • Auto-generated when indexing enabled
  • Syncs with file mtime
  • Safe to delete (regenerates)
  • 50-250x faster than file scanning

Task File Structure

Anatomy of a Task File

Every task file has two parts: YAML frontmatter (metadata) and Markdown body (content).

---
# === Core Metadata ===
id: 42
title: "Implement fuzzy search"
status: "In Progress"
priority: "high"

# === Dates ===
# Manual (user-planned)
start_date: "2024-11-01"
end_date: "2024-11-05"
release_date: "2024-11-10"

# Automatic (system-tracked)
created_date: "2024-11-01T09:00:00Z"
updated_date: "2024-11-06T14:30:00Z"
closed_date: null

# === Organization ===
milestone: "v1.0"
assignees: ["@alice", "Claude"]
labels: ["feature", "search", "performance"]

# === Hierarchy ===
parent_task: null
subtasks: [43, 44, 45]
dependencies: [40, 41]
blocked_by: []

# === Acceptance Criteria ===
acceptance_criteria:
  - text: "Search works with fuzzy matching"
    checked: true
  - text: "Response time under 100ms"
    checked: false

# === Changelog (automatic) ===
changelog:
  - timestamp: "2024-11-01T09:00:00Z"
    action: "created"
    details: "Task created"
    user: "@alice"
  - timestamp: "2024-11-06T10:00:00Z"
    action: "status_changed"
    details: "status: To Do → In Progress"
    user: "Claude"

# === AI Spaces ===
ai_plan: |
  ## Implementation Steps
  1. Install Fuse.js...

ai_notes: |
  **2024-11-06 10:30** - Started implementation...

ai_documentation: |
  ## Fuzzy Search
  ### Usage...

ai_review: |
  ## Self Review
  ✅ All tests passing...
---

# Implement fuzzy search

## Description
Add fuzzy search capability to task list using Fuse.js library.
Users should be able to search by title, description, labels, and assignees.

## Technical Details
- Library: Fuse.js v7.0.0
- Search threshold: 0.3 (configurable)
- Indexed fields: title, description, labels, assignees

## Expected Outcome
Users can quickly find tasks even with typos or partial matches.

Configuration File

config.yml Structure

project:
  name: "My Project"
  createdAt: "2024-11-01T09:00:00Z"

board:
  columns:
    - "To Do"
    - "In Progress"
    - "Review"
    - "Done"
  priorities:
    - "low"
    - "medium"
    - "high"
    - "critical"
  completedStatuses:
    - "Done"
    - "Cancelled"

performance:
  useIndex: true              # Enable LokiJS indexing
  rebuildIndexOnStart: false  # Rebuild on every start

validations:
  close:
    check_subtasks: true
    check_dependencies: true
    check_blocked_by: true
    check_acceptance_criteria: true
    warn_missing_ai_review: true
    warn_early_close: true
    warn_late_close: true
    warn_quick_close: 300     # Seconds
    suggest_parent_close: true
    notify_unblocked: true
    allow_force: true

search:
  threshold: 0.3              # Fuse.js threshold (0.0-1.0)
  maxResults: 50              # Max search results

All settings are optional with sensible defaults.

Dual Repository System

Why Two Repositories?

BackMark uses a dual repository pattern to optimize for both simplicity and performance. The system automatically chooses the appropriate repository based on your configuration.

FileSystemRepository

Direct File Operations

How it works:

  • Reads Markdown files directly
  • Parses YAML frontmatter on demand
  • No caching or indexing
  • Simple and transparent

Best for:

  • ✅ Small projects (<100 tasks)
  • ✅ Development and testing
  • ✅ Maximum transparency
  • ✅ Zero overhead

Performance:

  • List 100 tasks: ~100ms
  • Search 100 tasks: ~150ms

LokiIndexedRepository

In-Memory Indexed Queries

How it works:

  • Maintains in-memory LokiJS database
  • Syncs with file mtime automatically
  • Incremental updates only
  • Transparent cache invalidation

Best for:

  • ✅ Large projects (100+ tasks)
  • ✅ Production use
  • ✅ Frequent queries
  • ✅ Maximum performance

Performance:

  • List 1000 tasks: ~10ms
  • Search 1000 tasks: ~20ms
  • 50-250x faster!

Enabling Indexing

backmark config set performance.useIndex true

The cache is automatically managed - no manual intervention needed!

Tech Stack

Core Dependencies

  • commander - CLI framework
  • inquirer - Interactive prompts
  • chalk - Terminal colors
  • ora - Progress spinners
  • cli-table3 - Formatted tables

File Processing

  • gray-matter - YAML frontmatter parser
  • js-yaml - YAML serialization
  • date-fns - Date manipulation

Performance & Search

  • lokijs - In-memory indexing
  • fuse.js - Fuzzy search

Quality & Validation

  • zod - Schema validation
  • typescript - Type safety
  • vitest - Testing (80%+ coverage)

Design Decisions

Why Markdown + YAML?

  • ✅ Human-readable
  • ✅ Git-friendly
  • ✅ Editor-agnostic
  • ✅ Future-proof
  • ✅ No vendor lock-in
  • ✅ Easy migration

Why Local Files?

  • ✅ 100% offline
  • ✅ Complete privacy
  • ✅ No cloud dependencies
  • ✅ Fast access
  • ✅ Version control ready
  • ✅ Backup with your files

Why TypeScript?

  • ✅ Type safety
  • ✅ Better IDE support
  • ✅ Fewer runtime errors
  • ✅ Self-documenting code
  • ✅ Easier refactoring

Why CLI (not GUI)?

  • ✅ Developer-friendly
  • ✅ Scriptable/automatable
  • ✅ SSH-friendly
  • ✅ Low resource usage
  • ✅ Fast and efficient

Data Flow

Task Creation Flow

User Command
    ↓
Commander Parser
    ↓
Create Task Command
    ↓
Generate Task ID (auto-increment)
    ↓
Build Task Object (with metadata)
    ↓
Validate with Zod Schema
    ↓
Serialize to Markdown + YAML
    ↓
Write to backlog/tasks/{id}.md
    ↓
Update Index (if enabled)
    ↓
Display Success Message

Task Query Flow

User Command (e.g., list --status "In Progress")
    ↓
Commander Parser
    ↓
List Tasks Command
    ↓
Choose Repository (FileSystem vs LokiIndexed)
    ↓
Query Tasks with Filters
    ├─ FileSystem: Scan all files, parse, filter
    └─ LokiIndexed: Query in-memory DB (fast!)
    ↓
Apply Additional Filters (status, priority, etc.)
    ↓
Sort Results
    ↓
Format as Table (cli-table3)
    ↓
Colorize Output (chalk)
    ↓
Display to User

Performance Optimizations

LokiJS Indexing

In-memory database provides sub-10ms query times for 1000+ tasks.

Incremental Sync

Only changed files are re-indexed using mtime comparison.

Lazy Loading

Task content only loaded when viewing details.

Efficient Parsing

gray-matter optimized for YAML frontmatter parsing.

Fuzzy Search Cache

Fuse.js index cached for repeated searches.

Minimal Dependencies

Only 87 packages installed, most are lightweight.

Extensibility

Custom Task Templates

Create templates in backlog/templates/:

# backlog/templates/custom.md
---
priority: "medium"
labels: ["custom"]
---

# {{title}}

## Custom Section
Your template content here...

Use with:

backmark task create-from-template custom "My Task"

Configuration Customization

Modify config.yml to customize:

  • Board columns and statuses
  • Priority levels
  • Validation rules
  • Search thresholds
  • Performance settings

Ready to Dive Deeper?