chronis 0.6.1

Event-sourced task CLI powered by the AllSource embedded database (all-source.xyz)
Documentation

Chronis

The agent-native task CLI. Event-sourced, TOON-optimized, built for AI agents and humans alike.

Inspired by beads_rust — the pioneering local-first issue tracker that proved agents can self-manage tasks via CLI. Chronis takes that idea further: every action is an immutable event, state is derived from projections, and output is optimized for LLM token consumption.

Binary: cn | Crate: chronis | Storage: .chronis/ | Powered by AllSource

Why Chronis?

We built hundreds of tasks with beads_rust — 62+ issues across 280+ agent iterations. It works. But we kept hitting walls:

Problem with flat task trackers Chronis solution
No event history — "how long was this blocked?" requires grepping git log Event sourcing — every mutation is an immutable event with timestamps
Agent output burns tokens — JSON lists, ASCII tables, verbose prose TOON output--toon flag cuts token consumption ~50% on every command
No undo — agent closes wrong task, manual re-open is the only fix Temporal replay — reconstruct state at any point from the event stream
Cascade operations require scripting — closing an epic means N separate commands --cascade — claim or close an epic and all children in one command
Completed tasks clutter listings forever Archivingcn archive --all-done hides completed work, cn unarchive restores
Git sync = git commit && push, merge conflicts stall agents HTTP sync with remote Core — no git conflicts, works with dirty worktrees
No approval gates or workflow orchestration Event-sourced workflows — claim, complete, approve with first-write-wins semantics

Credit where it's due: beads_rust by @Dicklesworthstone got the core workflow right — ready -> claim -> done -> sync. Chronis preserves that simplicity while adding the observability and durability that production agent systems need.

Install

cargo install chronis

Quick Start

cn init                                          # Create .chronis/ workspace
cn task create "Design auth module" -p p0        # Create a task
cn task create "Write tests" --type=bug          # Create a bug
cn list                                          # List all tasks (human-readable)
cn list --toon                                   # List all tasks (agent-optimized)
cn ready --toon                                  # Show unblocked open tasks
cn claim <id> --toon                             # Claim a task
cn done <id> --toon                              # Complete a task
cn archive --all-done                            # Clean up finished work
cn sync                                          # Sync with remote Core

TOON: Agent-Native Output

This is chronis's signature feature. Pass --toon to any command for TOON (Token-Oriented Object Notation) output — the same format used by AllSource's MCP server. No braces, no quotes, no wasted tokens.

The problem

Traditional CLI tools output for humans. When an LLM agent runs cn list, it gets:

╭──────────┬──────┬──────────────────────┬─────┬─────────────┬─────────┬─────────╮
│ ID       │ Type │ Title                │ Pri │ Status      │ Claimed │ Blocked │
├──────────┼──────┼──────────────────────┼─────┼─────────────┼─────────┼─────────┤
│ t-abc1   │ task │ Design auth module   │ p0  │ open        │ -       │ -       │
│ t-abc2   │ bug  │ Fix login redirect   │ p1  │ in-progress │ agent-1 │ -       │
│ t-abc3   │ task │ Write integration... │ p2  │ done        │ agent-2 │ -       │
╰──────────┴──────┴──────────────────────┴─────┴─────────────┴─────────┴─────────╯

That's ~180 tokens of box-drawing characters, padding, and repeated dashes that carry zero information for the model.

The solution

cn list --toon
[id|type|title|pri|status|claimed|blocked_by|parent|archived]
t-abc1|task|Design auth module|p0|open||||false
t-abc2|bug|Fix login redirect|p1|in-progress|agent-1|||false
t-abc3|task|Write integration tests|p2|done|agent-2|||false

~60 tokens. Same information, ~50% fewer tokens. Every field is positional, pipe-delimited, with a header row. LLMs parse this natively.

TOON across all commands

Command Human output TOON output
cn list --toon ASCII table [header]\nrow\nrow
cn ready --toon ASCII table [header]\nrow\nrow
cn show <id> --toon Multi-line prose key:value lines + child/timeline tables
cn claim <id> --toon "Claimed task t-abc1 (agent: agent-1)" ok:claimed:t-abc1
cn done <id> --toon "Completed task t-abc1" ok:done:t-abc1
cn task create --toon "Created task t-abc1: Title" created:task:t-abc1:Title
cn archive --toon "Archived task t-abc1" ok:archived:t-abc1

Agent workflow (4 commands, ~20 tokens of overhead)

export CN_AGENT_ID=agent-1
cn ready --toon              # → [id|type|title|pri|status|...]\nt-abc1|task|...
cn claim t-abc1 --toon       # → ok:claimed:t-abc1
# ... agent does work ...
cn done t-abc1 --toon        # → ok:done:t-abc1
cn list --toon               # → verify state

Compare to JSON-based tools where the same loop burns ~200+ tokens on structural overhead alone.

Commands

Task Lifecycle

Command Alias Description
cn init Initialize a .chronis/ workspace in the current directory
cn task create <title> Create a task (see flags below)
cn list [--status=open] cn ls List tasks, optionally filtered by status
cn ready cn r Show tasks that are open and unblocked
cn show <id> cn s Task details, children, and event timeline
cn claim <id> cn c Claim a task (uses CN_AGENT_ID env var, defaults to "human")
cn done <id> [--reason=...] cn d Mark a task as done
cn approve <id> Approve a task
cn archive <ids...> Archive tasks (hide from default listings)
cn archive --all-done Archive all completed tasks
cn archive --done-before=30 Archive tasks done 30+ days ago
cn unarchive <ids...> Restore archived tasks

All commands accept --toon for agent-optimized output.

Task Creation Flags

cn task create "Title" \
  -p p1 \                    # Priority: p0 (critical), p1, p2 (default), p3
  --type=epic \              # Type: task (default), epic, bug, feature
  --parent=<id> \            # Parent task ID (for hierarchy under epics)
  --blocked-by=<id1>,<id2> \ # Tasks that block this one
  -d "Description text"      # Description

Bulk Actions (Cascade)

Cascade operations apply to a task and all its children — useful for closing out an entire epic:

cn claim <epic-id> --cascade             # Claim epic + all children
cn done <epic-id> --cascade              # Mark epic + all children as done
cn done <epic-id> --cascade --reason="Sprint complete"

Cascade walks the parent-child tree depth-first, processing children before the parent. Tasks already in the target state are skipped.

Archiving

Archive tasks to declutter your default listings. Archived tasks are hidden from cn list and cn ready but preserved in the event stream — nothing is deleted.

cn archive t-abc1 t-abc2                 # Archive specific tasks
cn archive --all-done                    # Archive all completed tasks
cn archive --done-before 30              # Archive tasks done 30+ days ago
cn unarchive t-abc1                      # Restore an archived task

cn list                                  # Excludes archived (default)
cn list --archived                       # Show only archived tasks
cn list --all                            # Show everything including archived

Dependencies

cn dep add <task-id> <blocker-id>      # Add a blocker
cn dep remove <task-id> <blocker-id>   # Remove a blocker

Sync

Chronis supports two modes of operation, configured in .chronis/config.toml:

Mode How it works cn sync
Embedded (default) Local event store per machine Exchanges events with a remote Core over HTTP
Remote All commands talk directly to a remote Core No-op (already shared)

Embedded mode with sync

Each machine has its own embedded Core for zero-latency reads. cn sync pushes local events to a remote Core and pulls new remote events — no git involved, works with dirty worktrees.

cn sync     # Pull from remote Core, push local events

How it works:

  1. Pull — fetch new events from remote Core (skip events that originated here)
  2. Push — send local events to remote Core (skip events that came from remote)
  3. Deduplication is idempotent — UUID tracking prevents duplicates even with timestamp overlap, plus source_id metadata prevents echo loops

Multi-machine workflow:

# Machine A                    # Machine B
cn task create "Auth" -p p0    cn task create "Docs" -p p2
cn sync                        cn sync          # pulls A's tasks, pushes B's
cn sync                        # pulls B's task

Remote mode

Point chronis at a shared Core instance. Writes go to the remote Core, reads use a local in-memory projection cache that's bootstrapped on startup by replaying all remote events. No sync step needed.

# .chronis/config.toml
mode = "remote"

[sync]
remote_url = "http://core.example.com:3900"

Configuration

# .chronis/config.toml
mode = "embedded"                          # "embedded" or "remote"
instance_id = "a1b2c3d4-..."              # Auto-generated, identifies this machine

[sync]
remote_url = "http://core.example.com:3900"  # Remote Core for sync target or primary backend

Visualization

cn tui                        # Interactive ratatui TUI (j/k nav, Tab view, c/d/a actions)
cn serve [--port=3905]        # Embedded web viewer (Axum + HTMX)
cn serve --open               # Auto-open browser

Migration from beads_rust

One command migrates your existing beads issues to chronis — preserving IDs, status, priorities, dependencies, and parent-child relationships:

cn migrate-beads              # Import issues from .beads/ directory
cn migrate-beads --beads-dir=/path/to/.beads

Both .beads/ and .chronis/ can coexist during transition. Nothing is deleted.

Chronis vs beads_rust

Feature beads_rust (bd) Chronis (cn)
Storage model SQLite + JSONL snapshot Event-sourced (WAL + Parquet)
History Current state only Full temporal event stream
Agent output JSON / human text TOON (~50% fewer tokens)
Cascade operations Manual scripting --cascade flag
Archiving Not available cn archive --all-done
Approval workflows Not available cn approve with event trail
Sync git commit && push (fails with dirty worktree) HTTP sync via remote Core (works anytime)
Undo Manual re-open Replay from event stream
TUI Separate binary (beads_viewer) Built-in (cn tui)
Web UI Separate project Built-in (cn serve)
Queryable timeline No cn show <id> with full event history
Data durability SQLite CRC32 WAL + Snappy Parquet

Both tools share the same core workflow: ready -> claim -> done. If you're coming from beads_rust, the transition is straightforward — and cn migrate-beads handles the data.

Workflow

cn ready  -->  cn claim <id>  -->  (do work)  -->  cn done <id>  -->  cn archive --all-done  -->  cn sync

For agent orchestration, set CN_AGENT_ID to identify which agent claims tasks:

export CN_AGENT_ID=agent-1
cn claim <id>     # Records "agent-1" as the claimer

Best Practices

Git Ignore for .chronis/

Sync is now HTTP-based (no git involvement), so the gitignore is simple — ignore everything except config:

# Chronis (all data is local or synced via HTTP — only config needs tracking)
.chronis/wal/
.chronis/storage/
.chronis/sync/
Path Contents Git? Why
.chronis/wal/ WAL segments (binary, CRC32) Ignore Machine-local binary data
.chronis/storage/ Parquet files (binary, Snappy) Ignore Machine-local, rebuilt from WAL
.chronis/sync/ Sync state (timestamps, dedup) Ignore Machine-local sync cursor
.chronis/config.toml Workspace config Track Shared settings (mode, remote URL)

When to Use TOON

Use --toon when the consumer is an LLM or a script. Don't use it when a human is reading.

Use --toon Don't use --toon
AI agents consuming output (MCP, orchestration) Interactive terminal use by humans
CI/CD pipelines parsing task state Debugging with cn show
Piping to cut, awk, or scripts Demos and screenshots
Large task lists (50+) where savings compound Onboarding new team members
Repeated polling (cn ready in a loop) TUI (cn tui) or web viewer (cn serve)

For agent environments, set the flag once:

alias cn='cn --toon'

See docs/BEST_PRACTICES.md for the full guide.

Architecture

Chronis wraps AllSource's embedded library. Every mutation (create, claim, done, approve, dependency change) emits an event into the WAL. A TaskProjection folds these events into queryable task state stored in a DashMap (~12us reads).

cn CLI (clap)
    |
    v
TaskRepository trait
    |
    v
CoreBackend (Embedded | Remote)
    |                        |
    v                        v
EmbeddedCore           HttpCoreClient
    |                   (remote Core API)
    +--> WAL (CRC32, fsync) --> Parquet (Snappy)
    +--> DashMap (in-memory reads)
    +--> TaskProjection (event folding)

In embedded mode, data lives in .chronis/ at the project root. In remote mode, all data lives on the remote Core instance.

.chronis/
  wal/            # Write-ahead log segments (embedded mode only)
  storage/        # Columnar event storage (embedded mode only)
  sync/           # Sync state — timestamps and dedup cursors
  config.toml     # Workspace config (mode, instance_id, remote_url)
  .gitignore      # Excludes local data directories

Event Types

All state is derived from these events:

Event Emitted by
task.created cn task create
task.updated (future: cn task update)
task.dependency.added cn dep add or --blocked-by flag
task.dependency.removed cn dep remove
workflow.claimed cn claim (first-write-wins)
workflow.step.completed cn done
workflow.approval.granted cn approve
task.archived cn archive
task.unarchived cn unarchive

Quality Gates

cargo fmt --check             # Formatting
cargo clippy -- -D warnings   # Lints (zero warnings)
cargo test                    # 35 tests across 3 suites

Test suites

Suite Tests What it covers
task_lifecycle 11 Create, claim, done, approve, dependencies, epics, archiving, timeline
config 9 Config parsing, defaults, TOML roundtrip, mode handling, file loading
sync_state 15 CoreBackend (embedded + remote), projections, entity filtering, dedup (UUID + metadata), sync state roundtrip, workspace init

Acknowledgments

Chronis exists because beads_rust proved that agents can self-manage tasks through a simple CLI. The ready -> claim -> done workflow that beads_rust pioneered is the foundation chronis builds on. We're grateful to @Dicklesworthstone and the beads community for blazing that trail.