Tooling reference

This page covers the practical Org2 tooling surface today. For the product framing around compiler-style corpus checks, see Maintenance and health workflows.

The CLI is the shared backend for editor integrations and automation. You can use it directly or through VS Code commands.

Core command families

Run org2 version or org2 --version to inspect the installed package version, and org2 --help for current top-level usage. AI architecture, job design, draft artifacts, and provider boundaries are documented separately in AI processing architecture, AI job manifests, AI draft artifacts, and AI adapter interface so the deterministic CLI surface stays clear.

Main workflows:

  • agenda

  • todo

  • plan

  • capture

  • archive, refile

  • fmt

  • search, id, query, backlinks

  • index, entity, approvals, clock

  • agent capabilities, agent context/search/fetch/bundle, context, brief

  • goal, agent-profile, run, review, workflow, artifact, runtime, mcp, eval

  • corpus identity inspection, validation, and preview-first initialization

  • workspace agenda, workspace search for read-only projections across explicitly mounted corpora

  • source list, source doctor, source status, source bind, source import, source sync for external source mirrors and staged review packets

  • compile corpus

  • render-chart

  • query-data

  • roam ...

  • crypt ...

  • export html

  • export beamer

  • publish

  • lint

  • graph audit

  • ai validate-job

  • lsp

Corpus identity

Portable identity lives under the corpus key in the root org2.json. It lets clones and local mounts refer to the same personal, shared, or project corpus without treating a machine-local path as identity.

org2 corpus show --dir ~/notes --json
org2 corpus validate --dir ~/notes
org2 corpus init --dir ~/team-notes --id team-notes --name "Team Notes" --kind shared
org2 corpus init --dir ~/team-notes --id team-notes --name "Team Notes" --kind shared --apply

corpus init previews by default, preserves unrelated org2.json settings, creates standard corpus zones when applied, and refuses to silently replace a different existing identity. See Shared corpora and collaboration.

Federated workspace reads

Repeat --mount to combine agenda or search results from identified corpora:

org2 workspace agenda --mount ~/notes --mount ~/team-notes --recursive --json
org2 workspace search "quarterly plan" --mount ~/notes --mount ~/team-notes --recursive --json

These commands are read-only composition over the normal agenda and search engines. Results include a corpus object with stable ID, name, kind, and the local root needed for navigation. Invalid, unavailable, or duplicate-identity mounts appear in issues. The command never discovers a person's app mounts and never establishes a destination for capture or edits.

External source mirrors

The root org2.json can declare portable, non-secret Slack and Notion source intent. Org2 delegates crawling to the MIT-licensed slacrawl and notcrawl binaries instead of implementing provider APIs or desktop-cache parsing itself:

{
  "externalSources": {
    "team-slack": {
      "type": "slack",
      "scopes": ["engineering", "product"],
      "workspaceId": "T012345",
      "media": "metadata-only",
      "rawZone": "raw/connectors/slack/team",
      "ingestion": {
        "since": "14d",
        "maxItems": 5000,
        "reviewZone": "views/connectors/slack/team"
      },
      "schedule": {
        "enabled": true,
        "kind": "interval",
        "everyMinutes": 120,
        "timezone": "local"
      }
    },
    "company-notion": {
      "type": "notion",
      "media": "metadata-only",
      "schedule": {
        "enabled": true,
        "kind": "daily",
        "time": "02:00",
        "timezone": "America/Los_Angeles"
      }
    }
  }
}

The corpus declaration contains no credentials or machine paths. Each authorized machine creates its own binding outside the corpus under ORG2_INDEX_HOME (default ~/.org2/index):

org2 source list --dir ~/notes --json
org2 source bind team-slack --config ~/.slacrawl/team.toml --dir ~/notes
org2 source bind team-slack --config ~/.slacrawl/team.toml --dir ~/notes --apply
org2 source doctor --dir ~/notes --json
org2 source status --dir ~/notes --json
org2 source import team-slack --dir ~/notes --json
org2 source sync team-slack --ingest --apply --dir ~/notes --json

bind previews by default and writes its local file with owner-only permissions when applied. Bindings may override the crawler binary, crawler config, and working directory. Archive database, cache, and Markdown storage locations remain explicit in the crawler's machine-local config. status reports crawler archive counts and freshness. import reads the local archive, applies profile scopes, since, and item limits, then previews raw JSON captures plus review-required Org2 packets. A Notion integration token is itself the API access boundary, so authenticated API records are accepted even when they lack a desktop workspace label; configured scopes still filter desktop-cache fallbacks. Externally supplied bodies are fixed-width quoted in the review packet so headings, TODOs, drawers, and other source syntax cannot become active Org semantics. Pass --apply to write the artifacts. sync --ingest --apply updates the crawler and stages them in one locked operation. Crawler subprocesses time out after 30 minutes by default so scheduled app work cannot remain wedged indefinitely; pass --timeout SECONDS to choose a different bounded deadline for doctor, status, import, or sync. Existing files are rewritten only when their deterministic contents changed, and apply removes obsolete packet pairs only when their raw envelope proves they were generated for that exact source profile. Promotion into canonical notes/ remains a separate human review step.

The optional schedule declaration is portable source intent. kind: "interval" requires a positive integer everyMinutes; kind: "daily" requires a 24-hour time in HH:MM form. timezone accepts local or an IANA timezone name. source list --json validates and returns the normalized schedule.

The Mac app's Sources surface calls this same CLI contract. It shows source health and archive counts, previews imports, runs Sync & Stage, reveals the review zone, and stores an optional Notion API token in macOS Keychain. While the app process is running it checks configured schedules once per minute. A newly enabled or edited schedule begins with its next future occurrence instead of immediately replaying old time slots; after that, a missed occurrence catches up after wake or on the next app launch. Failed automatic runs retain a visible error and retry after 15 minutes. Last-attempt and next-run bookkeeping is machine-local app state, not canonical corpus data. Credentials never enter org2.json, raw captures, or review packets. The app and other agents can therefore manage the same corpus profile without making app state canonical.

Command conventions and stability

Audit summary for CLI consistency: most commands already use --dir for corpus roots, --recursive for traversal, --format for alternate output, and --apply for mutating writes. The lowest-risk cleanup is to make JSON output easier and consistent across commands.

Conventions:

  • Prefer --format json for stable machine-readable output; --json is a shorthand alias where JSON output is supported.

  • Preview is the default for mutating commands. Pass --apply to write files.

  • Use --dir DIR with --recursive for corpus-wide scans, or --file FILE / --files FILE ... for bounded scans.

  • Text/report output is human-facing and may evolve; JSON output is the integration surface.

Examples:

org2 agenda --dir ~/notes --recursive --json
org2 lint --dir ~/notes --recursive --format json
org2 roam node new --dir ~/notes --title "Acme Corp" --apply --json

Agenda

Purpose: query scheduled/deadline items across Org files.

org2 agenda --dir ~/notes --recursive --from 2026-03-01 --to 2026-03-31

Important behavior:

  • Includes both SCHEDULED: and DEADLINE: rows in-range.

  • Supports broad filter/sort/group dimensions in CLI + VS Code integration.

  • Normalizes common TODO aliases into stable status buckets (including wait/hold/pause-style states).

TODO + planning edits

org2 todo toggle --file notes.org2 --line 42 --apply
org2 todo set --file notes.org2 --line 42 --status in_progress --apply
org2 plan set --file notes.org2 --line 42 --kind scheduled --date 2026-03-25 --apply

Capture / archive / refile

org2 capture --file inbox.org2 --title "Quick note" --template note --apply
org2 archive --file notes.org2 --pos 120:0 --apply
org2 refile --file notes.org2 --pos 120:0 --to-file projects.org2 --to-pos 40:0 --apply

Archiving moves the selected subtree to a companion archive file (by default FILE_archive for .org and .org2 files, otherwise FILE.archive). Archived subtrees receive a property drawer with ARCHIVED_AT, ARCHIVE_SOURCE, ARCHIVE_SOURCE_LINE, ARCHIVE_HEADING_PATH, and ARCHIVE_ORIGINAL_ID when the original subtree had an ID. Use --format diff to preview the exact removal/append operation, or --format json for editor/agent integrations.

Formatter

Canonical formatting for Org2 files.

org2 fmt --dir ~/notes --recursive --check
org2 fmt --dir ~/notes --recursive --apply

Chart rendering

org2 render-chart renders an SVG artifact from #+chart: / #+plot: metadata or an adjacent fenced ```chart block attached to an org2 table. Editor clients share this backend, so each extension uses the same table parsing and chart semantics.

org2 render-chart --file report.org2 --block-id quarterly_revenue --out /tmp/quarterly-revenue.svg
org2 render-chart --file report.org2 --line 42 --format json

The first renderer supports deterministic bar, line, and bucketed histogram charts with x and y column mappings. New hand-written examples should prefer fenced chart blocks when they are easier to scan:

| bucket | fetches |
|--------+---------|
| 0-10   | 14      |
| 11-50  | 32      |

```chart histogram
x: bucket
y: fetches
sort: y-desc
source: previous-table
```

For data-backed reports, a chart block can also point at a named materialized result table in the same note:

#+name: package_fetches_result
#+results: query-fetches-by-company
| day        | fetches |
|------------+---------|
| 2026-06-10 | 84      |

#+name: package_fetches_chart
```chart line
x: day
y: fetches
source: package_fetches_result
```

Add sort: x-asc, sort: x-desc, sort: y-asc, or sort: y-desc when a chart preview should be ordered independently of the table rows. JSON output includes ok, format, artifact, source, diagnostics, and svg so VS Code, Vim, or agent tooling can show useful errors for missing columns, unsupported chart types, missing chart sorts, missing named source tables, or missing chart blocks.

Data queries

org2 query-data is the DuckDB-backed bridge for local and remote datasets described in org2 notes. It scans fenced ```dataset blocks, creates DuckDB views for explicit csv, parquet, or json paths/URLs, named org2 tables, ClickHouse queries, or saved Metabase questions, optionally creates reusable SQL views from ```sql view=NAME blocks, then runs a fenced ```sql results=NAME block and materializes the returned rows.

```dataset fetches
type: csv
path: ./data/package-fetches.csv
engine: duckdb
```

```sql results=fetches_by_state artifact=views/fetches_by_state.org freshness=24h
SELECT state, count(*) AS fetches
FROM fetches
GROUP BY state
ORDER BY fetches DESC
```

Reusable SQL views can sit between datasets and the final materialized result:

```sql view=california_fetches
SELECT state, fetches
FROM fetches
WHERE state = 'CA'
```

```sql results=fetches_by_state
SELECT state, sum(fetches) AS fetches
FROM california_fetches
GROUP BY state
```

Read-only HTTP(S) or file: URLs can be recorded with url: when DuckDB can read the source directly:

```dataset remote_fetches
type: csv
url: https://data.example.test/package-fetches.csv
engine: duckdb
credential: env:SCARF_API_TOKEN
config: profile:product-analytics
```

```sql results=remote_fetches_by_state
SELECT state, count(*) AS fetches
FROM remote_fetches
GROUP BY state
```

Named org2 tables can also become local DuckDB views without an intermediate CSV export:

#+name: raw_fetches
| state | fetches |
|-------+---------|
| CA    | 42      |
| NY    | 24      |

```dataset fetches
type: table
source: raw_fetches
engine: duckdb
```

```sql results=fetches_total
SELECT sum(fetches) AS fetches
FROM fetches
```
org2 query-data --file report.org2 --results fetches_by_state
org2 query-data --file report.org2 --line 42 --format json
org2 query-data --file report.org2 --inspect --include-script
org2 query-data --file report.org2 --results fetches_by_state --out views/fetches_by_state.org
org2 query-data --file report.org2 --results fetches_by_state --apply
org2 query-data --file report.org2 --all-results --apply --format json
cat report.org2 | org2 query-data --stdin --results fetches_by_state

The default output is a named org table with #+name: fetches_by_state so it can be pasted or written into the note and consumed by render-chart via source: fetches_by_state. --apply inserts that generated block after the selected SQL block or replaces the existing #+query-data: result...= block in place. --all-results --apply runs every named SQL result first, applies the complete set in memory, and writes the source file once only after all results succeed; any query failure leaves the file unchanged. Materialized tables include a compact provenance line with the result id, row count, explicit source dependencies, optional artifact/freshness, query and script hashes, and ran_at timestamp. Add artifact=PATH to record an intended materialized output and freshness=24h, ttl=24h, or max-age=24h when clients should know how long a result can be treated as fresh. SQL result ids must be unique within the note, and dataset ids and SQL view ids share DuckDB's relation namespace. Inline bearer tokens and passwords are rejected. JSON output includes diagnostics, dataset metadata, SQL views, available result blocks, provenance, rows, and the generated org table; --include-script includes DuckDB setup SQL. Use --inspect to parse without running DuckDB. Org2 uses its bundled DuckDB engine by default; --duckdb PATH is an explicit CLI-engine override. ClickHouse and Metabase datasets resolve named dataSources profiles from the nearest org2.json; secrets are read only from profile-named environment variables or the Mac app's Keychain workflow. Metabase datasets may target saved questions or include reproducible native SQL with a configured database ID. Remote refresh is explicit: HTML export and merely opening a note in the Mac rendered view never execute warehouse queries. See the language reference for profile and dataset examples.

Agent discovery and retrieval

Agents should begin by querying the installed version rather than relying on remembered documentation:

org2 agent capabilities
org2 agent --help

The capability command emits org2:capabilities:v1 JSON covering workflows, safety rules, client roles, and canonical documentation entry points. The remaining agent subcommands return bounded org2:agent-context:v1 JSON with citations and source ranges. org2 context is the human/prompt-friendly shorthand, org2 brief renders a cited synthesis, and org2 compile corpus emits a complete schema-versioned corpus artifact.

See Agent quickstart for the operating contract and common recipes.

Chat thread automation

org2 thread reads the corpus-owned AI chat transcript at .org2/openclaw-chat.json. Active and settled threads retain the same stable ID, messages, session key, context, and timestamps; settlement is a reversible work-state transition, not deletion.

org2 thread list --dir ~/notes --state all --json
org2 thread show THREAD_ID --dir ~/notes --json
org2 thread post THREAD_ID --message "The export is ready" \
  --author "Research Agent" --agent-ref AGENT_REF \
  --source run:RUN_ID --idempotency-key RUN_ID:complete --dir ~/notes
org2 thread post THREAD_ID --message "The export is ready" \
  --author "Research Agent" --agent-ref AGENT_REF \
  --source run:RUN_ID --idempotency-key RUN_ID:complete --dir ~/notes --apply
org2 thread settle THREAD_ID --dir ~/notes          # preview
org2 thread settle THREAD_ID --dir ~/notes --apply
org2 thread reopen THREAD_ID --dir ~/notes --apply
org2 thread configure --auto-settle 604800 --dir ~/notes --apply
org2 thread configure --auto-settle never --dir ~/notes --apply
org2 thread auto-settle --dir ~/notes                # preview eligible IDs
org2 thread auto-settle --dir ~/notes --apply

thread post is a no-turn background delivery surface for agents and automations. Preview is the default; --apply atomically queues an org2:ai-chat-inbox-message:v1 envelope under .org2/ai-chat-inbox/. MCP clients can perform the same delivery with org2_thread_post. The running Mac app watches that append-only inbox, merges the attributed assistant message on its main actor, reopens a settled destination, marks the post unread when the thread is not visible, runs the normal sound/push notification path, persists the transcript, and only then removes the envelope. If the app is offline, it drains queued envelopes when that corpus is opened. --idempotency-key is scoped to the destination thread and prevents retries from duplicating a delivered post. --agent-ref preserves the portable agent identity; --author supplies its readable chat label; --source can retain a run or provider reference. The post does not invoke, steer, or enqueue either AI runtime.

The selected Mac chat is identified in both Codex and OpenClaw workspace prompts by ORG2_AI_CHAT_THREAD_ID. Pass that marker into a subagent, cron task, or other background worker only when it is explicitly expected to report after its parent turn ends; also pass the active corpus root, readable author, source/run reference, and a stable idempotency key. Embedded Codex advertises org2_thread_post as a native dynamic tool, while org2 mcp serve exposes the same tool through MCP tools/list. The OpenClaw lifecycle plugin adds the conditional CLI guidance to workflow and continuation prompts. It deliberately does not mirror every foreground completion because the ordinary assistant reply already reaches the thread.

The transcript schema keeps the legacy isArchived field for older clients and adds settledAt plus settlementSettings.autoSettleAfterSeconds. Existing archived threads therefore load as settled, while older unclassified threads remain active. Auto-settlement excludes the selected thread, pinned or unread threads, pending turns, and a latest message that is still sending, failed, or interrupted. Empty threads and threads with a historical failure followed by a successful delivery remain eligible once inactive.

Durable agent runs and review

Goals and named workers are portable corpus records, distinct from the runtime that executes them. org2 goal create/update/list/show manages org2:goal:v1 files under goals/. org2 agent-profile create/update/list/show manages org2:agent-profile:v1 files under agent-profiles/. Both create and update preview by default and require --apply to write.

The Mac workspace projects these records into Agent Work → Goals and Agent Work → Agents. The catalogs show status, ownership or primary-goal relationships, runtime bindings, measures, and linked-run counts while the detail pane renders the canonical file. Relationship controls navigate between goals, profiles, and filtered runs. Lifecycle status changes use the same goal update and agent-profile update commands instead of app-private state.

An agent profile may declare responsibilities, capabilities, skills, reporting and goal relationships, and repeatable non-secret runtime bindings such as openclaw:customer-support or codex:default. Credentials, session IDs, provider/model choice, and machine paths do not belong in the profile. Resolve the active runtime identity before delegation:

org2 agent-profile resolve --runtime openclaw \
  --runtime-agent-id customer-support --dir ~/notes --json
org2 run create --goal "Resolve the customer issue" \
  --agent-ref customer-support --goal-ref customer-trust --dir ~/notes
org2 todo assign --file notes/support.org2 --line 12 --assignee "Customer Support" \
  --agent-ref customer-support --goal-ref customer-trust --apply

The resolved agentRef is the portable worker ID; OpenClaw and Codex are runtimes, not profile IDs. A profile's primaryGoalRef is returned as the default goalRef. Existing selected-work refs take precedence over a runtime default. AGENT_REF and GOAL_REF flow through agent context, durable runs, and authored workflows; ASSIGNEE remains the readable display label. Resolution fails on ambiguous active bindings or a missing primary goal and returns found: false for an unbound runtime identity, allowing clients to omit refs rather than guess.

org2 run manages org2:agent-run:v1 records under .org2/runs/. Each record contains readable Org sections plus a canonical JSON block; writes are atomic. The state machine supports queued, running, waiting-approval, blocked, completed, failed, and canceled states. Invalid transitions fail instead of silently rewriting history.

org2 run create --goal "Prepare the weekly operating review" \
  --accept "Claims retain citations" --accept "PDF passes export validation" \
  --risk local-draft --owner avi --assignee analyst \
  --policy deep-analysis --token-limit 50000 --cost-limit-usd 10 --time-limit-seconds 1800 \
  --step agent:"Draft cited review" --step validation:"Validate citations and export" \
  --context notes/operations.org2 --capability agent-context --dir ~/notes
org2 run list --dir ~/notes --json
org2 run show RUN_ID --dir ~/notes --json
org2 run start RUN_ID --dir ~/notes
org2 run step RUN_ID step-1 --status completed --dir ~/notes
org2 run validation RUN_ID --name citations --status passed --dir ~/notes
org2 run runtime RUN_ID --provider openai --model gpt-5 \
  --tokens-used 18420 --elapsed-seconds 93.4 --dir ~/notes
org2 run approval-request RUN_ID --title "Release report" \
  --action "publish operating review" --risk external-action --role owner --dir ~/notes
org2 run approval-decide RUN_ID APPROVAL_ID --decision approved \
  --actor avi --role owner --fingerprint sha256:... \
  --receipt approval:local:123 --dir ~/notes
org2 run approval-resolve \
  --decision-key artifact:gmail:gog:DRAFT_ID --dir ~/notes --json
org2 run approval-reconcile --dir ~/notes --json       # preview
org2 run approval-reconcile --dir ~/notes --apply      # repair historical duplicates
org2 run artifact-review RUN_ID ARTIFACT_ID --status reviewed \
  --actor avi --dir ~/notes
org2 run complete RUN_ID \
  --summary "Prepared the cited operating review and validated its PDF export." \
  --highlight "All claims retain citations" --next-action "Share after owner review" --dir ~/notes

Other lifecycle operations include resume, retry, block, fail, cancel, complete-external, fork, assign, comment, outcome, runtime, artifact, and artifact-review. Retrying a canceled or failed run that still carries pending approvals reopens it directly in waiting-approval so the review queue cannot be bypassed or left hidden. run approval-reconcile previews historical provider-key duplicates and requires --apply before canceling their stale pending approvals and dedicated projection runs. run runtime records observable provider/model identity and optional token, cost, or elapsed usage without storing credentials. run artifact-review records a human review decision on an existing artifact and, for a linked Org/Org2 file inside the corpus, updates its ORG2_REVIEW_STATUS metadata at the same time; it also repairs stale review state on historical completed records. Normal completion requires --summary with a human-readable result and refuses pending approvals or review-required artifacts; repeat --highlight and --next-action to populate the reviewer-facing outcome. When a person confirms that an unfinished run's outcome was completed outside this workflow, run complete-external RUN_ID --summary "Where or how it was completed" --actor NAME records an explicit completed-externally audit event, marks unfinished workflow steps skipped, and closes the run while preserving unresolved approvals, artifact review states, and validations as history. Agents must not infer external completion on their own. run outcome may record or revise an outcome before completion. Blocking requires --reason with the specific clarification or next action; Org2 rejects an unactionable reasonless blocked state. run normalize converts legacy AGENT_RUN_ID headings into shared records while retaining a citation to their source location.

Pending approvals own their waiting boundary. run block refuses to replace waiting-approval while a decision remains pending. A genuinely independent clarification or operational condition must add --separate-from-approval; the override still rejects reasons that simply ask for approval, review, or edits.

An approval belongs to one canonical durable run and has an ID unique within that run. Its native sha256: fingerprint covers the immutable request material (title, action, risk, reviewer constraints, and request note); every client decides that exact identity, and approval-decide --fingerprint rejects stale or altered material. Provider-backed requests must preserve an exact Provider draft: PROVIDER:TOOL:DRAFT_ID line. The CLI treats that value as a decision key: a repeated request for matching material reuses the existing pending approval, replacement material supersedes the prior pending version, and a stale version cannot be decided after a newer request exists. run approval-resolve --decision-key KEY returns the canonical approval even after it has left the pending queue, so an execution guard can verify the durable decision without trusting private adapter metadata. Historical duplicate projections are retained as canceled audit records rather than being falsely marked approved. A decision explanation is stored separately as decisionNote instead of rewriting the request. A revised decision requires --note "Requested changes", because a status without direction cannot produce a meaningful replacement. A run may have several pending approvals; it remains waiting-approval while approvals in the current boundary are still pending. Approving, rejecting, or canceling one item removes only that exact RUN_ID/APPROVAL_ID pair from the queue and leaves unrelated siblings actionable. Once the current boundary is fully decided, the run returns to running and the worker may perform only approved actions; rejected or canceled actions remain durable exclusions. A revision request remains distinct: once the boundary is resolved it moves the run to blocked, and the Mac workspace turns an eligible revision-only blocked workflow back over to its correlated OpenClaw session with the durable decision note. The agent must create new review material and a replacement approval on the same run without performing the protected action. Requesting that replacement approval opens a new boundary, so its later approval resumes the run without rewriting the older revision request in history. An approval decision does not clear a separate clarification or operational block. An approval may name both a required role and a specific reviewer. approval-decide refuses a mismatched --role or --actor, and the decision event preserves the asserted role plus any release receipt. This is a durable workflow boundary; operating-system or organizational identity enforcement can sit above it.

org2 approvals returns the unified org2:approvals:v2 decision queue. Its kind field distinguishes durable run approvals from standalone headline approvals; run items include runId, approvalId, fingerprint, decisionKeys, the run goal/status, and pending/total approval counts. OpenClaw and other producers use those keys to discover an existing provider boundary instead of inventing another authority. Deciding a run item must use run approval-decide so the queue and Agent Work's Runs tab are two projections of the same canonical .org2/runs/*.org2 record. Standalone heading approvals remain ordinary corpus TODOs and keep their file/line/ID identity. A legacy heading nested under a Gmail provider-draft task and repeated durable approvals for that same draft collapse to the newest durable decision. Recording that decision automatically supersedes older pending copies and closes dedicated duplicate review runs; stale material remains inspectable in history but cannot return to either actionable surface. org2 review list returns the broader org2:review-queue:v1 inspection feed, which also includes review-required artifacts, blocked clarification requests, and warning/failed validation.

A heading carrying ORG2_RUN_ID plus an exact approval ID, a unique matching approval title, or an otherwise unambiguous single pending decision is a derived run projection. The unified queue omits it as a second writable decision and keeps the run approval canonical. org2 doctor reports a pending writable duplicate as an error.

Before an agent acts on a large or long-lived workspace, org2 doctor performs a read-only consistency pass over run records, workflow definitions, approval identities, recurring attempt metadata, and linked corpus headings:

org2 doctor --dir ~/notes
org2 doctor --dir ~/notes --json

The versioned org2:agentic-doctor:v1 JSON report distinguishes errors, warnings, and informational migration findings. It detects terminal runs with pending decisions, waiting runs without a decision, duplicate provider keys, conflicting workflow attempt identities, ungrouped OpenClaw cron series, broken run/approval links, direct readable-header drift, and likely duplicate open projections. Hard contradictions produce a nonzero exit status. The command never repairs or normalizes files: review the cited evidence, then use the canonical lifecycle command or an explicit source edit to settle the underlying state.

Every CLI and MCP run mutation uses a short local write lock, an atomic durable rename, and the SHA-256 revision read with the run. Workflow updates use the same guarded file primitive while retaining their documented visible-field authoring behavior. If another client or text editor changes a file first, the mutation fails rather than overwriting it. New run and workflow IDs also require the target to be absent. Long-lived clients can make the run boundary explicit:

org2 run show RUN_ID --with-revision --dir ~/notes --json
org2 run comment RUN_ID --author agent --body "Checked current state" \
  --if-revision sha256:... --dir ~/notes

The snapshot envelope is org2:run-snapshot:v1 and includes revision, sourceIssues, and the parsed run. A lifecycle write also refuses readable run headers that disagree with the machine state, directing the caller to org2 doctor instead of normalizing an ambiguous direct edit. An abandoned .org2.lock is reported by the doctor; confirm that no writer remains before removing it.

Work ledgers for recurring account workflows

org2 ledger keeps high-volume recurring bookkeeping out of a monolithic agent prompt or TODO file. One stable account becomes one canonical Org2 file under notes/LEDGER/accounts/ACCOUNT_ID.org2.

org2 ledger create account-outreach acme \
  --title "Acme Corp" --identity domain:acme.example \
  --alias Acme --field segment=enterprise \
  --context "Reviewed commercial context." --apply --dir ~/notes

org2 ledger event account-outreach acme \
  --type approval-linked --key approval:acme:gmail-draft \
  --run RUN_ID --approval APPROVAL_ID \
  --decision-key gmail:gog:DRAFT_ID --apply --dir ~/notes

org2 ledger event account-outreach acme \
  --type outreach-sent --key send:acme:gmail-message \
  --run RUN_ID --approval APPROVAL_ID \
  --external-id gmail:message:MESSAGE_ID --apply --dir ~/notes

org2 ledger list account-outreach --eligible \
  --as-of 2026-07-31T12:00:00Z --cooldown-days 90 --dir ~/notes --json

org2 ledger resolve account-outreach \
  --identity "Acme Corp" --identity growth@acme.example \
  --identity acme.example --dir ~/notes --json

Creates, updates, and events preview by default and require --apply. Account source uses the versioned org2:work-ledger-account:v1 schema, readable aliases/identity/context fields, guarded SHA-256 revisions, arbitrary string --field KEY=VALUE metadata, and append-only events. Applied mutations also hold a ledger-wide lock while checking global uniqueness, so concurrent writers cannot claim the same identity or event key in two account files. Every event requires a ledger-wide idempotency key. Identity keys and event keys may not be claimed by two accounts. Repeating identical event material is a no-op; reusing its key for different material fails. Human edits to title, state, aliases, identity keys, and context are accepted as canonical authoring; disagreement between readable event history and structured event state is reported by org2 doctor, makes the account ineligible, and blocks a later structured write until explicitly reconciled.

An approval-linked event points to the native RUN_ID/APPROVAL_ID; it does not copy approval status into the account. Recording outreach-sent with a linked approval requires that canonical decision to be approved and requires an external message/receipt ID. Eligibility excludes paused, suppressed, or closed accounts, accounts with linked pending approvals, approved/proposed outreach that has not yet reached a matching sent/canceled/skipped outcome, and accounts contacted inside the requested cooldown. Carry the same run/approval linkage on the settlement event; for work without a native approval, pass --data with the same named cycle value. This prevents the next scheduler tick from recommending the same account during the gap between approval and execution, including when an account has more than one outreach cycle. org2 doctor also checks ledger identity/event collisions, broken run or approval references, path/identity mismatches, and sent outreach linked to a non-approved decision.

The account file is curated canonical knowledge, so aliases, stable identity, commercial history, and human context belong under notes/. Raw CRM exports, email/provider payloads, and collector results remain under raw/ and should be referenced through event --source values. Generated rankings or candidate lists belong in views/.

ledger resolve accepts a name, email, domain, provider organization ID, or fully qualified identity key and reports ambiguous matches instead of selecting one silently.

Reusable workflows and triggers

workflow save RUN_ID converts a completed run into a draft org2:workflow:v1 file under the visible top-level workflows/. Packages declare semantic version, compatibility, parameters and defaults, context rules, deterministic/agent/tool/approval steps, capabilities, risk, outputs, validation, approval boundaries, and manual/schedule/file-change/capture/meeting-import triggers. See Workflows for the plain-text ownership and OpenClaw execution contract.

org2 workflow save RUN_ID --id weekly-operating-review --version 1.0.0 --dir ~/notes
org2 workflow validate weekly-operating-review --dir ~/notes
org2 workflow run weekly-operating-review --input week=2026-W29 --dir ~/notes
org2 workflow activate weekly-operating-review --dir ~/notes
org2 workflow schedule weekly-operating-review --cron "0 9 * * 1" \
  --timezone America/Los_Angeles --gate-event capture --gate-path notes/ --dir ~/notes
org2 workflow signal weekly-operating-review --event capture \
  --signal-id capture-2026-W29 --changed notes/inbox.org2 --dir ~/notes
org2 workflow gate weekly-operating-review --trigger openclaw-schedule --dir ~/notes --json
org2 workflow pause weekly-operating-review --dir ~/notes
org2 workflow migrate --dir ~/notes
org2 workflow triggers weekly-operating-review --changed data/metrics.csv --dir ~/notes --json
org2 workflow triggers weekly-operating-review --event capture --dir ~/notes --json
org2 workflow package weekly-operating-review --dir ~/notes --json
org2 workflow corpus-template weekly-operating-review --out dist/weekly-review-template.json --dir ~/notes

Schedule syntax is intentionally deterministic and small: every 15m, every 4h, or every 1d. The interval must be a positive whole number, so every 0m is invalid. Event hosts ask workflow triggers which declarations are due, then instantiate the workflow; --event accepts only capture or meeting-import and rejects unknown event names. workflow signal persists capture, meeting-import, or file-change evidence. A schedule with --gate-event and/or --gate-path starts an attempt only when a matching signal is newer than its prior attempt boundary; otherwise workflow gate and workflow run --trigger return a deterministic skip result without creating a run. The logical workflow identity stays stable while each eligible execution receives distinct attempt identity, schedule/trigger provenance, and consumed signal IDs. run list --json exposes attempt roll-ups so clients can summarize history without conflating retries or recurring executions.

workflow package emits distribution metadata for one provider-independent workflow. workflow corpus-template wraps that metadata with ordinary starter files and the standard raw/, notes/, views/, compiled/, run, and workflow zones as org2:corpus-template:v1. The package is data, not an installer: recipients review it, initialize/open a corpus with their client, and place the workflow record in workflows/. Org2 still reads the legacy early-alpha .org2/workflows/ location; workflow migrate moves non-conflicting files into the visible directory.

The built-in meeting-to-controlled-execution package preserves meeting provenance, extracts cited decisions/tasks/questions, delegates bounded work, refreshes configured data/charts, compiles finished artifacts, requests release review, and gates publication:

org2 workflow install-builtin meeting-to-controlled-execution --dir ~/notes

Artifact dependency and freshness graph

org2 artifact graph evaluates a JSON declaration containing artifact id, path, sources, optional previous sourceHashes, and optional rebuild command. Outputs are fresh, stale, missing, or conflicted. artifact rebuild propagates upstream staleness to downstream outputs and emits an ordered reviewable plan; --apply on graph stores the derived graph at .org2/artifact-graph.json.

org2 artifact graph --manifest compiled/artifacts.json --dir ~/notes --json
org2 artifact rebuild --manifest compiled/artifacts.json --dir ~/notes

Runtime policies, MCP, and connector snapshots

org2 runtime init creates .org2/runtime-policy.json. Symbolic policies select an eligible descriptor by required capabilities, privacy, transport, context window, and cost class. Built-in policy names are private-local, fast-draft, deep-analysis, vision, regulated-cloud, and offline. Runtime records contain aliases and model metadata, never credentials.

org2 runtime select private-local --capability text --dir ~/notes --json
org2 runtime select deep-analysis --capability tool-use --dir ~/notes --json
org2 runtime verify-paths --capability text --dir ~/notes --json

runtime verify-paths is the portability check used by workflow test suites: it fails unless the configured baseline capability is available through both an enabled local/private descriptor and an enabled hosted descriptor. Execution remains adapter-owned; this check validates that switching paths does not require changing a workflow package or corpus state.

org2 mcp serve starts a stdio Model Context Protocol server. It exposes bounded org2://corpus/ resources, workflow prompts, and typed tools for listing, creating, and transitioning runs. mcp client-add records explicitly configured external servers in .org2/mcp-clients.json. mcp discover performs an initialize/list handshake, reports the server's resources, tools, and prompts, and can persist a provenance-bearing discovery snapshot. mcp snapshot writes any external result under raw/connectors/mcp/ with source URI, retrieval time, identity, freshness, and payload so durable work does not depend on an invisible live response.

org2 mcp serve --dir ~/notes
org2 mcp client-add crm --command crm-mcp --arg=--stdio --capability accounts --env CRM_TOKEN --dir ~/notes
org2 mcp discover crm --snapshot crm-capabilities --dir ~/notes --json
org2 mcp snapshot account-123 --source mcp://crm/accounts/123 \
  --identity account:123 --fresh-until 2026-07-15T00:00:00Z \
  --input /tmp/account.json --dir ~/notes

Client configuration records environment-variable names for setup and discovery, never their values. The child process inherits credentials from the invoking environment or operating-system credential helper.

Outcome evaluation and replay fixtures

org2 eval run compares an observable run against JSON expectations for terminal status, artifacts, passing validations, citations, approval boundaries, and protected paths. It writes org2:workflow-eval:v1 results under .org2/evals/. Metrics come from inspectable run records: elapsed time, tokens, cost, citation coverage, and artifact review/freshness.

org2 eval run RUN_ID --expect test/fixtures/weekly-review.expected.json --dir ~/notes
org2 eval replay weekly-operating-review --fixture test/fixtures/weekly-review.replay.json --dir ~/notes
org2 eval fixture RUN_ID --output test/fixtures/weekly-review.run.json --dir ~/notes

eval replay deterministically instantiates the declared workflow against fixture inputs and checks version, resolved goal, plan steps, capabilities, and risk boundary without invoking a provider. eval fixture replaces people, providers, source locations, and receipts with synthetic values. Review the result before distributing it; free-form artifact contents are not copied by the command.

Corpus lint

Lint is the first compiler-style health pass for artifact metadata and corpus structure. It belongs to the broader maintenance and health workflow alongside formatter checks, graph queries, publish previews, and ID/index repair passes.

org2 lint --dir ~/notes --recursive

Current checks include:

  • invalid or missing ORG2_ARTIFACT_ROLE on generated outputs

  • missing/invalid provenance, generator, and generated-at metadata

  • duplicate IDs and unresolved provenance references

  • stale generated artifacts when provenance file mtimes or ORG2_SOURCE_HASHES no longer match

  • unresolved id: links, unresolved wiki links, and ambiguous wiki links/aliases using the same graph index as roam workflows

  • conventional corpus-flow checks when you organize notes as raw/ -> notes/ -> compiled/ -> views/ -> publish/

See Corpus flow for the canonical zone model, artifact roles, and generated-output trust boundaries.

AI job manifest validation

org2 ai validate-job validates repeatable AI workflow manifests without calling a provider or loading secrets.

org2 ai validate-job --job examples/jobs/weekly-summary.org2-ai.json
org2 ai validate-job --job examples/jobs/weekly-summary.org2-ai.json --format json

Use this in CI or editor workflows before a future AI runner consumes a job. The validator checks corpus input selection, task type, symbolic adapter names, generated output targets, provenance/review requirements, and common accidental secret leaks. See AI job manifests for the schema and examples.

org2 ai run writes a reviewable generated draft artifact from a validated job. The current implementation is provider-free and uses manifest input.files as deterministic source material, including simple glob patterns. It stamps ORG2_PROVENANCE, ORG2_SOURCE_HASHES, prompt/template, adapter/model, generated-at, and ORG2_REVIEW_STATUS metadata. Without --apply it previews the draft.

org2 ai run --job examples/jobs/weekly-summary.org2-ai.json --out views/weekly-summary.org2
org2 ai run --job examples/jobs/weekly-summary.org2-ai.json --out views/weekly-summary.org2 --apply

org2 ai suggest-links ranks compiler-provided roam/linkify candidates with the deterministic mock adapter. It scans the graph, aliases, exact linkify matches, represented-node semantic suggestions, and title-case entity mentions, then emits review-only suggestions with confidence, reasons, source context, and citations. It never edits canonical notes directly; --apply only writes the suggestion report when --out is provided.

org2 ai suggest-links --dir ~/notes --recursive --format json
org2 ai suggest-links --dir ~/notes --recursive --out views/link-suggestions.org2 --apply

org2 ai promote is the safe acceptance path. It refuses drafts until ORG2_REVIEW_STATUS is reviewed, then appends the reviewed body to a canonical note only when --apply is present and marks the draft promoted.

org2 ai promote --file views/weekly-summary.org2 --to-file notes/weekly-summary.org2 --apply

See AI draft artifacts for the full format, review checklist, and VS Code command-palette entry points.

Compiled corpus artifacts

org2 compile corpus emits a stable, schema-versioned knowledge artifact that LLM tools, search indexes, and other automation can consume. Org2 only compiles local plain-text notes into structured data; it does not call an LLM or require any hosted AI service.

org2 compile corpus --dir ~/notes --recursive --out compiled/corpus.json
org2 compile corpus --dir ~/notes --recursive --format jsonl --out compiled/corpus.jsonl
org2 compile corpus --dir ~/notes --recursive --incremental --out compiled/corpus.json

The JSON form includes:

  • standardized artifact metadata: role, generator, generated-at timestamp, source provenance, source hashes, and review status

  • files with relative paths, absolute paths, SHA-256 hashes, line counts, titles, and IDs

  • file and heading nodes with source ranges for citations

  • heading TODO state, priority cookies, tags, planning timestamps, explicit properties, inherited inheritedProperties, overlaid effectiveProperties, IDs, and aliases

  • links and resolved backlinks for explicit id: links and unambiguous wiki links

  • text snippets suitable for previews or retrieval pipelines

Use JSONL for larger corpora when downstream tools prefer one record per line; the first JSONL record carries the same artifact metadata header as the JSON form. If you wrap compiled artifacts in Org files, stamp the wrapper with the same ORG2_ARTIFACT_*, ORG2_SOURCE_HASHES, and ORG2_REVIEW_STATUS fields and keep using org2 lint for generated-output health checks.

For repeated whole-corpus reads, --incremental stores compiled semantics plus per-file cache metadata in the machine-local Org2 index home and reparses only added or changed files. Deleted files are dropped from the next artifact, while corpus-wide backlinks, entities, relations, and statistics are rebuilt from reusable file semantics so the output remains equivalent to a full compile. --cache FILE overrides the derived cache location. Human-facing context, agent context, and brief commands use this incremental cache automatically.

Search + cited query

Use search for literal, case-insensitive full-text lookup over .org and .org2 files, and query when you want cited context suitable for answering factual questions from notes. Directory scans are non-recursive unless --recursive is set. The External LLM consumer pattern page shows how provider-agnostic agents can turn this JSON into cited model context without adding LLM calls to Org2 core.

org2 search "Chris Martin" --dir ~/notes --recursive --context 3 --sort date-desc
org2 search "Mercor" --dir ~/notes --recursive --sort relevance
org2 index --dir ~/notes --recursive --file ~/notes/inbox.org2 --incremental --format json
org2 query "Chris Martin" --dir ~/notes --recursive --subtree --sort date-desc --format json
org2 query "Sentra" --dir ~/notes --recursive --format json
org2 query actions --object 'id:PERSON-ID' --dir ~/notes --recursive --recent-days 30 --format json

Useful flags:

  • --context N includes surrounding source lines.

  • --limit N caps returned matches.

  • --sort relevance puts active TODO matches first, then heading matches before raw text; exact heading matches lead heading substrings and deeper body matches.

  • --sort date-desc is useful for “when did I last...” style questions over date-named daily files and heading timestamps.

  • --subtree deduplicates matches to cited heading/subtree sections with sourceRange, headingAncestry, matchedLines, and full subtree context.

  • --answer-context adds a plain subtree text block for downstream local LLM prompts.

  • --date-from / --date-to and --file-zone narrow results by local corpus zones and source dates.

  • --format json returns machine-readable file, line, heading, snippet, source range, and context fields.

org2 query actions is the bounded, deterministic action-item projection for a person or other node. It resolves the target by stable ID or unique title, returns active TODOs directly linked or assigned to that node, and also inherits TODOs from a containing meeting that links to the node. Recently completed items require a completion or meeting date inside --recent-days. --open-limit and --completed-limit bound the displayed arrays while counts retains the complete totals. The command uses the incremental corpus cache and performs no model call or brief generation.

org2 index builds disposable machine-local search data. Its JSON artifact remains inspectable, and Org2 also maintains a versioned binary sidecar for faster repeat loads; an absent, stale, or unreadable sidecar safely falls back to JSON. After an initial build, org2 index --incremental --file FILE replaces or removes only that file's index entry; if no compatible base index exists, it safely falls back to a complete build. Watcher-backed clients may search that maintained index with --index current, avoiding a directory walk and per-file freshness check on every query. The macOS app uses both paths for filesystem events so frequent agent edits and live searches do not repeatedly scan the corpus. auto remains the conservative CLI default for callers without a watcher.

Roam workflows

org2 roam db-sync --dir ~/notes --recursive --apply
org2 roam node new --dir ~/notes --title "Acme Corp" --apply
org2 roam backlinks --id 123e4567-e89b-12d3-a456-426614174000 --dir ~/notes --recursive --format json
org2 roam linkify --dir ~/notes --recursive
org2 roam graph --dir ~/notes --recursive --format report
org2 roam graph --dir ~/notes --recursive --format json

Org2 supports both title-based wiki links and explicit id: links, with backlinks across both forms. Linkify and graph reports are intended as compiler-style maintenance passes: preview references, strengthen links deliberately, and inspect corpus connectivity without leaving plain files. org2 roam linkify --format json reports exact title/alias replacements plus review-only represented-node suggestions with confidence, evidence tokens, and source ranges for headings or multi-line paragraphs; --apply only writes exact safe replacements, never semantic suggestions. org2 roam graph --format report emits a human-readable maintenance view with graph summary, orphan/high-degree nodes, alias/title collisions, unresolved or ambiguous links with file/line citations, and linkify suggestions; --format json includes the same maintenance payload for scripts and CI.

Org-crypt

Org2 keeps the existing Org-style :crypt: convention and encrypts at the subtree boundary. A subtree can be encrypted symmetrically with a passphrase, or to multiple public-key recipients so the same private note can be opened by your user key, another device key, and an agent key without sharing one password around.

# Passphrase/symmetric mode
org2 crypt decrypt --file secrets.org2 --line 42 --passphrase 'your-passphrase' --apply
org2 crypt encrypt --file secrets.org2 --line 42 --passphrase 'your-passphrase' --apply

# Multi-recipient public-key mode
org2 crypt encrypt --file secrets.org2 --line 42 \
  --recipient user@example.com \
  --recipient agent@example.com \
  --apply

# Public-key recipient files are useful for keys that are not in your keyring
org2 crypt encrypt --file secrets.org2 --line 42 \
  --recipient-file keys/agent-public.asc \
  --apply

# Or make sharing per-entry with an Org property drawer
# Relative recipient-file paths resolve from the current note's directory.
* Private note shared with an agent :crypt:
:PROPERTIES:
:CRYPT_RECIPIENT_FILE: keys/agent-public.asc
:END:
Only the entries with this property are encrypted to that agent.

# Migration: decrypt an existing block, then re-encrypt it for the new recipients
org2 crypt reencrypt --file secrets.org2 --line 42 \
  --passphrase 'old-passphrase-if-needed' \
  --recipient user@example.com \
  --recipient agent@example.com \
  --apply

Per-entry recipient properties let one corpus mix private-only and shared-with-agent notes without a global editor setting. :CRYPT_RECIPIENT: / :CRYPT_RECIPIENTS: add GPG recipient identifiers, while :CRYPT_RECIPIENT_FILE: / :CRYPT_RECIPIENT_FILES: add armored public key files. Multiple values can be separated with commas. Relative recipient-file paths are resolved relative to the Org2 file that contains the subtree, so they work across machines when the key file lives in the synced project.

Export + publish

org2 export html --file README.org --out README.html --apply
org2 export html --dir docs/site --recursive --out-dir site --apply
org2 export beamer --file talks/demo.org2 --out publish/demo.tex --apply
org2 export beamer --file talks/demo.org2 --out publish/demo.pdf --pdf --apply
org2 publish docs-site --config org2.json

Useful export options include TOC, heading numbering, stylesheet injection, and Org-file-link rewrite to HTML targets.

export beamer previews by default. Without --pdf it produces reviewable .tex; with --pdf it runs pdflatex twice so references, outlines, and overlays settle before the PDF is written. Select another installed command or absolute executable path with --latex-engine. The compiler invokes the engine directly with non-interactive, halt-on-error flags and does not enable shell escape.

Presentation export reads the shared Org2 AST and maps both legacy Org Beamer properties and backend-neutral Org2 slide properties into one presentation model. See Language reference for slide structure, columns, blocks, notes, overlays, and raw backend escape hatches.

LSP

Start language server:

org2 lsp

Current implemented capabilities include:

  • definitions/references/hover/completion/signature help

  • rename + linked editing for IDs/file links

  • file-rename link updates

  • formatting (document/range/on-type)

  • semantic tokens, inlay hints, code lens, call hierarchy

For full details, see: