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
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,40 @@ jobs:
- name: API and MCP process smoke tests
run: python3 tests/server_smoke.py

- name: Browser task workflow
run: |
python3 -m venv "$RUNNER_TEMP/org-browser-tests"
"$RUNNER_TEMP/org-browser-tests/bin/pip" install -r tests/requirements-browser.txt
"$RUNNER_TEMP/org-browser-tests/bin/playwright" install --with-deps chromium
"$RUNNER_TEMP/org-browser-tests/bin/python" tests/browser_smoke.py

- name: Vulnerability check
run: |
dotnet list OrgCli.slnx package --vulnerable --include-transitive --format json > audit.json
cat audit.json
jq -e '[.. | objects | .vulnerabilities? // empty | .[]] | length == 0' audit.json

platforms:
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "9.0.x"
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Publish standalone binary
run: dotnet publish src/OrgCli/OrgCli.fsproj -c Release --use-current-runtime --self-contained -p:PublishSingleFile=true -o artifacts/platform
- name: Native watcher, locking and API/MCP tests
shell: bash
run: |
if [ "$RUNNER_OS" = "Windows" ]; then
python tests/server_smoke.py artifacts/platform/org.exe
else
python tests/server_smoke.py artifacts/platform/org
fi
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ jobs:
- name: API and MCP process smoke tests
run: python3 tests/server_smoke.py

- name: Browser task workflow
run: |
python3 -m venv "$RUNNER_TEMP/org-browser-tests"
"$RUNNER_TEMP/org-browser-tests/bin/pip" install -r tests/requirements-browser.txt
"$RUNNER_TEMP/org-browser-tests/bin/playwright" install --with-deps chromium
"$RUNNER_TEMP/org-browser-tests/bin/python" tests/browser_smoke.py

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## Unreleased

- Recover stale task workflows with an audited reset, respect manually reopened cancellations, and automatically refresh the board without overwriting drafts.
- Run packaged watcher, locking and API/MCP smoke tests on native macOS and Windows CI runners.

- Detect direct requirement edits during claims and review; block stale completion evidence, preserve claims across refiling, and show actionable recovery in the board.

- Add a shared human/agent task workflow: dependency-aware ready queues, project/owner assignment, expiring claims, handoffs, evidence-backed submission, and separate-actor review. Existing Org TODOs can be adopted without migration.
- Add `org task` commands, four task API/MCP operations, and a browser task board embedded in the same executable.
- Fix repeated directory traversal in CLI full-text search; avoid redundant timestamp writes and configuration formatting during cache refresh.
- Unify overdue scheduled work and file-local completion handling across CLI and API agendas.
- Fix empty Org properties/keywords consuming the following line, and rebuild older cached projections automatically.

- HTTP and stdio MCP servers now watch the workspace for external edits, update only affected projections, and reuse cached snapshots between changes. Startup, directory changes, watcher errors, and periodic reconciliation repair missed notifications.
- Add virtual-filesystem tests and live watcher tests for atomic saves, renames, and deletion.

## 2.0.0-rc.1

First release candidate for the standalone Org engine and optional API/MCP interfaces. Try it against a copy of your Org workspace before adopting it for daily use.
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ YA. ,A9 MM 8M YM. , MM MM

---

## Tasks for humans and agents

The shared task workflow provides dependency-aware ready queues, assignments, expiring claims, evidence, and review. It works with existing Org TODO headings and keeps coordination state in readable Org properties and logbooks.

```sh
org task ready -d ~/org
org task create "Prepare the proposal" -d ~/org --actor daniel --acceptance "Scope and costs verified"
org serve -d ~/org --mcp
# Human board: http://127.0.0.1:8765/
```

Humans use the CLI or built-in browser board; agents use the same operations through MCP or HTTP. See the [task workflow guide](docs/task-workflow.md) for claims, handoffs, review, and the agent operating loop.

## What it is

A parser and CLI for org-mode files: headlines, TODO states, priorities, tags, timestamps, property drawers, clock entries, links. Output is structured (text or JSON) and file edits use checked, staged replacements. Multi-file edits have a recovery journal.
Expand Down Expand Up @@ -187,7 +200,7 @@ It does **not** read or write Emacs's org-roam database. Emacs can index the sha

Org files are authoritative. The CLI owns `.org-index.db` under the selected directory (`--db` overrides it). Queries create and refresh it automatically; deleting it loses no note data. Parsed document snapshots, identity lookup, and SQLite FTS5 share one projection.

Every refresh fingerprints file contents and effective parser configuration. Unchanged files reuse their parsed snapshot; changes with unchanged timestamps are still detected. This currently reads the selected corpus on each refresh, trading some I/O for reliable freshness without a watcher. `fts --no-sync` explicitly accepts stale results.
Every refresh fingerprints file contents and effective parser configuration. Unchanged files reuse their parsed snapshot; changes with unchanged timestamps are still detected. Ordinary CLI queries read the selected corpus on each refresh. The optional HTTP/MCP server watches external changes and reuses snapshots between changes, with periodic full reconciliation as a fallback. `fts --no-sync` explicitly accepts stale results.

- `org add` always assigns a UUID `ID`, independent of index state.
- Use `id:<uuid>` for standard IDs, `custom:<value>` for existing CUSTOM_IDs, or `pos:<offset>` with a file for explicit character offsets.
Expand Down Expand Up @@ -264,7 +277,7 @@ python3 tests/server_smoke.py
- Interactive or TUI features. This is a tool for scripts, not humans at a terminal.
- Tables, spreadsheets, babel/code block evaluation.
- Capture templates. Appending to a file is trivial; no tool needed.
- File watching. Queries refresh the owned index automatically.
- A separately installed background daemon. Watching runs only while the optional HTTP/MCP server is running.

## License

Expand Down
18 changes: 15 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ org mcp --stdio -d ~/org # Local client starts this on demand

Both server modes accept `--db`, `--config`, and `--read-only`. The selected directory is the workspace boundary. Capture writes to its `inbox.org`; requests cannot choose arbitrary file paths. `--read-only` disables mutations and removes them from discovery. Source files and the CLI-owned index are shared with ordinary CLI use; org-roam is unnecessary.

## External edits and cache freshness

Both `org serve` and `org mcp --stdio` watch the physical workspace recursively. File notifications are coalesced and processed in the background approximately every 200 ms; a request also processes pending notifications before using the index. Unchanged requests reuse parsed snapshots and do not scan or hash the corpus. Writes made through the service update the affected projection immediately, without waiting for a watcher event.

External edits are eventually visible, not transactionally synchronized with an editor: a request can race an event that has not arrived yet. Atomic saves, file creation/deletion, and renames are supported. Directory changes, watcher errors/overflow, and startup trigger full reconciliation. The server also verifies content every 60 seconds to recover from silently missed events (including on filesystems with unreliable notifications). These full passes can delay requests while refreshing a large workspace. Failed refreshes remain pending and are retried; requests requiring that refresh fail rather than silently returning the old cache.

The watcher is owned by the server process and stops with it. Ordinary CLI commands retain their existing scan-based freshness. Virtual-host tests inject notifications directly; they never attach a watcher to the user's filesystem.

## HTTP

The listener is loopback-only. Set `ORG_API_TOKEN` before starting the process to require `Authorization: Bearer <token>` on every endpoint. Without it, local clients can call the API without credentials. Requests with unrelated Host or Origin headers are rejected; no cross-origin browser access is enabled. Request bodies are limited to 1 MiB. This is a local bridge, not a public multi-user service: OAuth and remote binding are not implemented.
The listener is loopback-only. Set `ORG_API_TOKEN` before starting the process to require `Authorization: Bearer <token>` on data and tool endpoints. Without it, local clients can call the API without credentials. Requests with unrelated Host or Origin headers are rejected; no cross-origin browser access is enabled. Request bodies are limited to 1 MiB. This is a local bridge, not a public multi-user service: OAuth and remote binding are not implemented.

- `GET /health`: readiness of the process.
- `GET /api/v1/tools`: operation names, input schemas, descriptions, and annotations.
Expand All @@ -33,7 +41,11 @@ curl -s http://127.0.0.1:8765/api/v1/capture \

Generate a new UUID for each new capture. Reuse it only when retrying the identical request. Replays survive process restarts because capture identity and a request fingerprint are stored in the Org heading. Reusing a UUID with a different request is a conflict.

## Operations
## Task workflow

The built-in browser board is at `/`. The empty page shell loads without a token; all data requests still require the configured bearer token. `tasks`, `task_create`, `task_update`, and `task_action` expose the same workflow to HTTP and MCP clients. See the [task workflow guide](task-workflow.md) for schemas, leases, dependency rules, review, and examples.

## Note operations

| Name | Arguments | Result |
| --- | --- | --- |
Expand Down Expand Up @@ -68,7 +80,7 @@ A local client can launch the executable directly:

The exact configuration file depends on the client. Stdio emits only protocol messages on stdout; logs go to stderr. Closing stdin shuts down the process. HTTP clients use `http://127.0.0.1:8765/mcp` after starting `org serve --mcp`; a remote assistant needs an appropriate local bridge/tunnel. Direct ChatGPT account connection has not been tested by this change.

MCP exposes the same seven operations and structured result envelopes as HTTP. Writes are annotated; tool-level failures set `isError`. The SDK handles initialization, protocol negotiation, message framing, cancellation messages, and transport lifecycle. No assistant-specific database is created.
MCP exposes the same operations and structured result envelopes as HTTP. Writes are annotated; tool-level failures set `isError`. The SDK handles initialization, protocol negotiation, message framing, cancellation messages, and transport lifecycle. No assistant-specific database is created.

## Implementation and tests

Expand Down
6 changes: 5 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Org files contain note data and identities. SQLite contains replaceable parsed d

Refresh hashes the selected file contents and effective configuration, including a projection version. It reuses unchanged snapshots and replaces changed file projections transactionally. Missing files are removed. Queries filter by their selected file set; querying a subdirectory does not discard unrelated cached documents. Duplicate IDs remain visible and resolving them fails instead of silently selecting a row.

Freshness currently requires reading the selected corpus. Warm queries avoid parsing unchanged Org text, but are not constant-time filesystem operations. There is no watcher, daemon, mtime-only shortcut, or separate roam cache. `fts --no-sync` is an explicit stale-read option. There is no claim of a simultaneous snapshot across external editors and all files: files are refreshed individually.
Ordinary CLI freshness requires reading the selected corpus. The optional HTTP/MCP server maintains an in-memory map of parsed snapshots and watches the physical workspace recursively. Notifications are coalesced into bounded batches; only affected file projections are refreshed. Requests drain pending changes under the same lock used by the background worker, and service writes update projections immediately. Startup, directory changes, watcher failures, and a 60-second verification interval trigger full reconciliation. Notifications are eventually delivered, so requests can briefly race external edits; full verification repairs missed notifications. There is no separately installed daemon, mtime-only shortcut, or separate roam cache. `fts --no-sync` is an explicit stale-read option. There is no claim of a simultaneous snapshot across external editors and all files: files are refreshed individually.

## Org-roam extension

Expand Down Expand Up @@ -46,3 +46,7 @@ The parser is still an Org subset, not Emacs's complete grammar. Unterminated so
## Optional interfaces in one binary

`org serve` runs a loopback HTTP API; `--mcp` adds the MCP endpoint. `org mcp --stdio` is an on-demand subprocess mode. Both use `Application.WorkspaceService`, an explicit host-scoped application boundary using the same index and mutation primitives as ordinary CLI commands. There is no separate server executable and no listener starts during ordinary CLI use. See [API documentation](api.md).

## Task coordination

`TaskWorkflow` implements the shared task state machine independently of transports. It stores durable coordination in Org properties and logbooks, uses standard TODO completion and UUID IDs, and consumes workspace document projections. Mutations take a workspace lock before reconciliation and a checked file commit; read-only inspection shares the watched cache. The CLI, browser board, HTTP, and MCP expose the same operations. See [task workflow](task-workflow.md) for the lifecycle and the local coordination boundary.
Loading
Loading