Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions docs/comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# org-workspace vs orgparse vs organice

> A factual comparison for developers who work with org-mode files in Python.

All three libraries solve different problems. This page helps you pick the right one.

---

## TL;DR

| Capability | orgparse | organice | org-workspace |
|-----------|---------|---------|---------------|
| Parse org files | ✅ | ✅ (via JS) | ✅ (via orgparse fork) |
| Write / mutate org files | ❌ | ✅ (in-browser) | ✅ |
| Python library | ✅ | ❌ (JS/TS) | ✅ |
| Multi-file workspace | ❌ | ❌ | ✅ |
| Atomic task claiming | ❌ | ❌ | ✅ |
| Concurrency primitives | ❌ | ❌ | ✅ |
| GTD query layer | ❌ | ❌ | ✅ |
| Dependency DAG (Plan) | ❌ | ❌ | ✅ |
| AI agent integration | ❌ | ❌ | ✅ |
| Round-trip safe serialization | ⚠️ partial | ✅ (JS) | ✅ |

---

## orgparse

[orgparse](https://github.com/karlicoss/orgparse) is a mature, well-tested Python parser for org-mode. It's the right choice if you want to **read** org files and don't need to write back.

**Strengths:**
- Battle-tested and widely used
- Clean, Pythonic API for reading headings, properties, and text
- Good performance on large files

**Limitations:**
- Read-only: no write support in the upstream library (PR #77 adds it but hasn't merged)
- No multi-file workspace concept
- No query layer for GTD patterns
- No concurrency support

**When to use:** Reporting, analytics, read-only tooling over org files.

org-workspace includes a vendored fork of orgparse with write support from PR #77. If your project already depends on orgparse for reading, org-workspace is a compatible upgrade path.

---

## organice

[organice](https://github.com/200ok-ch/organice) is a web application for editing org-mode files in a browser, with sync support for Dropbox, WebDAV, and Nextcloud. It's a frontend tool, not a Python library.

**Strengths:**
- Clean mobile and desktop web interface
- Real org-mode support including agenda, TODO states, and tags
- Sync integrations with common file storage services

**Limitations:**
- JavaScript/TypeScript only — no Python API
- Designed for human editing, not programmatic access
- No agent integration, task claiming, or batch operations

**When to use:** You want a Dropbox-synced org agenda in a browser.

---

## org-workspace

org-workspace is a **Python library for AI agents and automation** that need to read, write, and reason over org-mode files. It was built to let autonomous agents safely coordinate over a shared task system without stepping on each other.

**Strengths:**
- Full read/write Python API
- Multi-file workspace with dirty-file tracking
- Concurrency primitives for multi-agent coordination (TaskClaim, OptimisticLock, FileLock)
- GTD query layer (agenda, deadlines, overdue, next_action, ai_tasks)
- Dependency DAG with topological sort (Plan)
- Round-trip safe serialization: unmodified files are never rewritten
- GTD and nightshift state configurations out of the box
- Content-addressed ID generation with dedup detection

**Limitations:**
- Newer project (v0.5.3, Beta) — fewer real-world deployments than orgparse
- No standalone CLI
- No browser or sync interface

**When to use:** Building AI agents, automation pipelines, or developer tools that need to read and write org-mode task lists programmatically.

---

## Feature deep-dive: write safety

One reason to choose org-workspace over rolling your own orgparse wrapper: write safety is surprisingly hard.

The naive approach — read → modify in-memory → serialize → write — routinely causes subtle bugs: property drawers getting duplicated, timestamps reformatted, tags reordered, or entire lines truncated on long values. These bugs don't fail loudly; they corrupt your files silently.

org-workspace's serializer is designed around the round-trip invariant: a file that's loaded and immediately saved must produce byte-identical output for unchanged nodes. This is validated in the test suite on real org files. The `CatastrophicShrinkError` guard will refuse to write a file that's significantly smaller than the original, catching serializer regressions before they reach disk.

---

## FAQ

**Can I use org-workspace alongside orgparse?**
Yes. org-workspace vendors its own copy of orgparse internally, so there's no conflict. You can use the upstream orgparse for reading and org-workspace for writing if needed.

**Does org-workspace work with Emacs's org-mode?**
Yes — org-workspace produces valid org-mode syntax. Files modified by org-workspace open correctly in Emacs without reformatting.

**What Python versions are supported?**
Python 3.10–3.13.

**Is it production-ready?**
It's at Beta stability (v0.5.3). The core API is stable; the advanced concurrency and Plan features are in use in Datacore's own autonomous agent pipeline.
140 changes: 140 additions & 0 deletions docs/quick-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# org-workspace Quick Start

> From install to your first AI-readable task list in under five minutes.

## Install

```bash
pip install org-workspace
```

## Your first five minutes

Assume you have at least one org-mode file. If not, create a minimal one:

```
~/org/inbox.org:

* TODO Write my first task
:PROPERTIES:
:ID: abc123
:END:
This is what org-workspace can read and write.
```

Now open Python:

```python
from pathlib import Path
from org_workspace import OrgWorkspace, Query

# Point at your org directory (or a single file)
ws = OrgWorkspace(roots=[Path.home() / "org"])

# Find all tasks tagged for AI execution
q = Query(ws)
for task in q.ai_tasks(states=["TODO"]):
print(task.heading, task.tags)

# Create a task programmatically
new_task = ws.create_node(
file=Path.home() / "org" / "inbox.org",
heading="Summarise my meeting notes",
state="TODO",
tags=["AI", "research"],
body="Use the attached notes in ~/org/notes/2026-07-meeting.org",
)

# Save — only modified files are written, unchanged files are left alone
ws.save()
print("Task created:", new_task.id)
```

That's it. You've loaded a workspace, queried it, created a task, and saved safely.

## What just happened

**`OrgWorkspace`** loads and indexes all your org files. It tracks which files are dirty and guarantees round-trip safety — if you read a file and write it back unchanged, the bytes on disk don't change.

**`Query`** is a stateless query layer on top of the workspace. `ai_tasks()` finds any heading tagged `:AI:` — the convention Datacore uses to hand work to autonomous agents overnight.

**`create_node()`** inserts a new heading with a unique content-addressed ID. It will never silently overwrite an existing node.

## The key primitives

| Primitive | What it does |
|-----------|-------------|
| `OrgWorkspace` | Load, index, and safely write org files |
| `Query` | Read-only queries: agenda, deadlines, AI tasks, next action |
| `NodeView` | Stateless view of a single node; detects staleness automatically |
| `TaskClaim` | Atomic task claiming — prevents two agents running the same task |
| `OptimisticLock` | Hash-based conflict detection for concurrent writes |
| `Plan` | Parse `DEPENDS_ON` into a dependency DAG, topological sort |

## Common patterns for AI agents

### Claim a task before executing

```python
from pathlib import Path
from org_workspace import OrgWorkspace, Query
from org_workspace.concurrency import TaskClaim

ws = OrgWorkspace(roots=[Path.home() / "org"])
q = Query(ws)
tc = TaskClaim(ws)

for task in q.ai_tasks(states=["TODO"]):
if tc.claim(task, agent_id="my-agent"):
# Only one agent gets here; others skip this task
ws.transition(task, "EXECUTING", agent="my-agent")
ws.save()
# ... do work ...
ws.transition(task, "DONE", agent="my-agent")
tc.release(task, "my-agent")
ws.save()
```

### Find overdue tasks

```python
overdue = q.overdue()
for task in overdue:
print(f"{task.heading} — deadline: {task.deadline}")
```

### Build a dependency plan

```python
from org_workspace import Plan

# Plan wraps a project subtree — find the root node first
roots = q.by_property("ID", "your-project-id")
if roots:
plan = Plan(roots[0], ws)
for task in plan.ready_tasks():
print("Ready to run:", task.heading)
for task, blocker in plan.blocked_tasks():
print("Blocked:", task.heading, "(waiting on:", blocker, ")")
```

## GTD states

org-workspace ships with a default GTD state sequence and an extended nightshift sequence for autonomous execution:

```python
from org_workspace import StateConfig

# Default GTD: TODO → NEXT → WAITING → DONE
config = StateConfig.default()

# Nightshift: adds QUEUED, EXECUTING, REVIEW, FAILED for agent pipelines
config = StateConfig.nightshift()
ws = OrgWorkspace(roots=[...], state_config=config)
```

## Where to go next

- [GitHub](https://github.com/datacore-one/org-workspace) — source, issues, discussions
- `pip install org-workspace` then `python -c "from org_workspace import OrgWorkspace; help(OrgWorkspace)"`
- Issues and feature requests welcome on GitHub
Loading