Tooling reference

This page covers the practical Celorga 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.

Install with npm install -g celorga. The commands are celorga and celorga-lsp; org2 and org2-lsp remain installed as compatibility aliases, and existing .org2 files, org2.json, and ORG2_* names keep working.

Core command families

Run celorga version or celorga --version to inspect the installed package version, and celorga --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

  • plugin for content-addressed Git extensions, commands, templates, and document renderers

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

  • workspace agent-state --dir CORPUS --json for a single-process, read-only batch of agent-state lists

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

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

  • compile corpus

  • render-chart

  • property-view

  • query-data

  • table recalculate

  • roam ...

  • crypt ...

  • export html

  • export beamer

  • publish

  • lint

  • graph audit

  • ai validate-job

  • lsp

Plugins

Celorga plugins are self-contained Git packages. A corpus records portable source intent in org2.json, while org2.plugins.lock.json pins the exact Git commit, validated manifest, and SHA-256 hash of the package contents. Installed bytes live in the machine-wide content store under ~/.org2/plugins/store/ and are shared safely by every corpus that locks the same hash. Set ORG2_PLUGIN_HOME to use a different machine-local store.

The first release accepts ordinary GitHub HTTPS URLs, github:OWNER/REPOSITORY shorthand, general HTTPS/SSH/scp-style Git URLs, file: URLs, and local Git checkouts. A package can also live in a repository subdirectory. Mutable refs are only resolved by add or update; sync always reproduces the locked commit and fails if its content hash changes.

# Preview the exact revision and content hash without changing the corpus.
celorga plugin add https://github.com/example/org2-calendar.git --dir ~/notes --json

# Record the source, lock and install the package, then trust this exact hash.
celorga plugin add github:example/org2-calendar --dir ~/notes --trust --apply

# Install a package from a monorepo.
celorga plugin add https://github.com/aviaviavi/celorga.git \
  --subdir examples/plugins/chess-pgn --dir ~/notes --trust --apply

celorga plugin list --dir ~/notes --json
celorga plugin doctor --dir ~/notes --json
celorga plugin sync --dir ~/notes
celorga plugin sync --dir ~/notes --apply
celorga plugin update org2.chess-pgn --dir ~/notes
celorga plugin update org2.chess-pgn --dir ~/notes --trust --apply
celorga plugin trust org2.chess-pgn --revoke --dir ~/notes --apply

All mutations preview unless --apply is present. remove deletes the corpus declaration and lock entry but retains immutable store bytes because another corpus may use them. The initial ecosystem intentionally has no central registry: the locked source, commit, hash, and manifest are sufficient to reproduce and audit a package. A future registry can resolve names into this same source-and-lock model without becoming the package authority.

Package manifest

A package root contains org2-plugin.json and every file it needs at runtime. Symlinks and install scripts are rejected; executable contributions use .js, .mjs, or .cjs process entries inside that self-contained package. This keeps a locked Git tree independently hashable and removes a mutable dependency-install step.

{
  "$schema": "org2:plugin-manifest:v1",
  "id": "example.cards",
  "name": "Example cards",
  "version": "0.1.0",
  "engines": { "org2": ">=0.5.2 <1.0.0" },
  "permissions": { "environment": ["EXAMPLE_API_TOKEN"] },
  "contributes": {
    "commands": [
      { "id": "import", "entry": "import.mjs" }
    ],
    "renderers": [
      {
        "id": "card",
        "languages": ["example-card"],
        "entry": "render-card.mjs"
      }
    ],
    "templates": [
      { "id": "card-note", "path": "templates/card.org" }
    ],
    "actions": [
      {
        "id": "summarize",
        "title": "Summarize with Example",
        "contexts": ["heading", "run"],
        "entry": "summarize.mjs",
        "capabilities": ["network"]
      }
    ],
    "hooks": [
      { "id": "notify", "events": ["run.blocked", "approval.requested"], "entry": "notify.mjs" }
    ]
  }
}

The engines.org field accepts exact versions, comparison sets, ^ and ~ ranges, and || alternatives. An incompatible package is rejected before it enters the store. Environment variables are absent by default apart from a small process baseline; a manifest must name each additional variable it wants to receive.

Contributions and process protocol

V1 has five additive contribution kinds:

  • Commands run explicitly with celorga plugin exec PLUGIN COMMAND -- ARG....

    The process receives one org2:plugin-invocation:v1 JSON object on stdin and returns one org2:plugin-result:v1 JSON object on stdout.

  • Templates are static package files copied into the corpus with

    celorga plugin template PLUGIN:TEMPLATE --out PATH. Output must remain inside the corpus and preview/apply rules still apply.

  • Document renderers claim one or more source-block languages. The shared CLI

    renderer gives the process the block body, arguments, source lines, and document path. It returns org2:plugin-render-result:v1 JSON containing HTML plus optional CSS, JavaScript, title, and height. The result replaces that source block in celorga export html, and the Celorga app's read mode and source preview.

  • Context-aware actions declare title, the contexts they apply to

    (note, heading, thread, run, approval), an entry, and optional capabilities (read-corpus, network). Celorga lists them in the File menu for the current note or heading and in Activity row menus for threads, runs, and approvals; scripts use celorga plugin actions --context KIND and celorga plugin action run PLUGIN:ACTION --context KIND ....

  • Lifecycle hooks subscribe to activity events such as run.blocked,

    run.completed, approval.requested, and thread.reply-received. celorga plugin hooks dispatch --apply (once, from a cursor, or with --follow) invokes them; the scheduler host in Celorga dispatches them automatically. Each event is delivered to a hook once.

Actions and hooks cannot mutate the corpus silently. The process receives an org2:plugin-action-invocation:v1 object containing only the selected object (the note or heading text with its revision, a thread's recent messages and activity explanation, or a run and approval with its explanation, or the event) and no corpus path unless it requests read-corpus. On macOS it runs under sandbox-exec with corpus and plugin-trust writes denied, corpus reads denied without read-corpus, and network denied without network. It returns org2:plugin-action-result:v1 with optional text and up to 50 proposals: exact-text edit (the text must occur once; expectedSha256 pins the file), create under views/, thread-post, or run-comment. Proposals are stored as pending org2:plugin-proposal:v1 records in .org2/plugin-proposals/.

celorga plugin actions --context heading --json
celorga plugin action run example.helper:summarize --context heading --file notes/a.org --line 12
celorga plugin proposals list --status pending
celorga plugin proposals apply PROPOSAL_ID            # preview with unified diffs
celorga plugin proposals apply PROPOSAL_ID --apply    # every change must still apply cleanly
celorga plugin proposals dismiss PROPOSAL_ID --apply
celorga plugin hooks dispatch --since 1h              # dry run
celorga plugin hooks dispatch --apply

The npm package ships JSON Schemas for the manifest, lock, invocation, command result, action result, and renderer result under spec/v0/plugin-*.schema.json. Plugin stdout is bounded to 1 MiB; renderer fields have smaller per-field limits, and a package is limited to 10,000 files and 64 MiB of content.

The shared parser still owns the canonical Celorga AST. V1 renderer languages are therefore additive source-block syntax, not arbitrary parser replacement. The versioned contribution model leaves room for future importers, syntax semantic modules, data sources, and other integration points without making their code part of core. Built-in chart and plot languages remain reserved.

Trust boundary

Downloading or locking a plugin never executes it. Execution requires the package's exact content hash in the current machine's trust file. A later Git commit has a different hash and is inert until separately reviewed and trusted; plugin trust --revoke --apply stops execution of the locked hash immediately.

A trusted command or renderer process is native code with the user's file and network access. The reduced environment limits accidental credential inheritance but is not an OS sandbox; actions and hooks additionally run under the macOS sandbox described above (other platforms rely on the proposal boundary and the absence of a corpus path). Trust packages as carefully as any local executable. Renderer output has a second boundary: Celorga puts it in an opaque-origin iframe with a deny-by-default content-security policy, no network connections, no forms, frames, objects, workers, or top-level navigation, and only inline code plus data: or blob: media. Renderer failure stays local to the block and appears as an actionable error instead of preventing the rest of the document from rendering.

The repository includes a dependency-free chess PGN renderer and note template as a complete reference package.

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.

celorga corpus show --dir ~/notes --json
celorga corpus validate --dir ~/notes
celorga corpus init --dir ~/team-notes --id team-notes --name "Team Notes" --kind shared
celorga 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.

Batched workspace agent state

celorga workspace agent-state --dir CORPUS --json reads runs, workflows, goals, and agent profiles in one process. Its org2:workspace-agent-state:v1 response has runs, workflows, goals, and profiles sections. Each contains the existing list envelope under value, or a section-local error string, plus elapsedMilliseconds. Clients must check each section for errors; a successful process exit does not mean every section succeeded.

This is a read-only batch for one corpus, not an atomic transaction across files. It does not accept --mount. Existing individual list commands and their filters remain available.

Federated workspace reads

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

celorga workspace agenda --mount ~/notes --mount ~/team-notes --recursive --json
celorga 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, Notion, and email source intent. For Slack and Notion, Celorga delegates crawling to the MIT-licensed slacrawl and notcrawl binaries instead of implementing provider APIs or desktop-cache parsing itself; email profiles read IMAP directly (see Email sources):

{
  "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):

celorga source list --dir ~/notes --json
celorga source add team-slack --source-json '{"type":"slack","workspaceId":"T012345","syncArgs":["--source","bot","--latest-only"],"ingestion":{"since":"14d"}}' --dir ~/notes          # preview
celorga source add team-slack --source-json '{"type":"slack","workspaceId":"T012345","syncArgs":["--source","bot","--latest-only"],"ingestion":{"since":"14d"}}' --dir ~/notes --apply
celorga source add team-slack --update --source-json '{"workspaceId":null,"ingestion":{"since":"7d"}}' --dir ~/notes --apply
celorga source bind team-slack --config ~/.slacrawl/team.toml --dir ~/notes
celorga source bind team-slack --config ~/.slacrawl/team.toml --dir ~/notes --apply
celorga source doctor --dir ~/notes --json
celorga source status --dir ~/notes --json
celorga source import team-slack --dir ~/notes --json
celorga source sync team-slack --ingest --apply --dir ~/notes --json
celorga source schedule team-slack --pause --apply --dir ~/notes --json
celorga source schedule team-slack --kind interval --every-minutes 120 --timezone local --apply --dir ~/notes --json
celorga source schedule team-slack --kind daily --time 08:30 --timezone America/Los_Angeles --apply --dir ~/notes --json

source add PROFILE --source-json JSON creates one externalSources entry of any supported type (slack, notion, or email) and previews until --apply. It accepts only the documented keys, validates schedules, corpus-relative zones, and email settings, and refuses credential-like keys (token, password, secret, apiKey, …) or token-shaped values, so a secret cannot be written into org2.json this way. --update merges into an existing profile instead: top-level and ingestion keys are replaced, null removes a key, and the type cannot change. 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 Org 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. source schedule previews by default and atomically updates only that profile's schedule when applied. --pause and --resume preserve its cadence; setting --kind interval or --kind daily changes the cadence and wall-clock configuration.

The Mac app's Sources surface calls this same CLI contract. It shows source health and archive counts, previews imports, runs one connector immediately with Run Now, pauses or resumes scheduled sync, edits interval or daily cadence and time zone, reveals the review zone, adds or reconfigures any supported source type from one form (Add Source → Slack, Notion, or Email; Configure… on an existing source) through celorga source add, and stores each type's credential (Slack bot token, Notion integration token, or email password) in macOS Keychain, passing it to the sync process as SLACK_BOT_TOKEN, NOTION_TOKEN, or ORG2_EMAIL_PASSWORD. Set Up with AI opens an AI chat that walks through the same setup as an optional alternative. A manual run refreshes only the connector that ran. 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.

Email sources

type: "email" profiles sync an IMAP mailbox without a crawler binary. Mail providers list IMAP (for reading) next to SMTP (for sending); Celorga uses the IMAP settings to read new mail and records the SMTP submission server only for reference. The declaration stays non-secret:

{
  "externalSources": {
    "mail": {
      "type": "email",
      "email": {
        "host": "imap.example.com",
        "port": 993,
        "security": "tls",
        "username": "you@example.com",
        "mailboxes": ["INBOX", "Clients"],
        "smtp": { "host": "smtp.example.com", "port": 587 }
      },
      "ingestion": { "since": "14d", "maxItems": 2000 },
      "schedule": { "enabled": true, "kind": "interval", "everyMinutes": 30 }
    }
  }
}
celorga source add-email mail --host imap.example.com --username you@example.com --mailbox INBOX --smtp-host smtp.example.com --dir ~/notes          # preview
celorga source add-email mail --host imap.example.com --username you@example.com --mailbox INBOX --smtp-host smtp.example.com --dir ~/notes --apply
celorga source bind mail --password-command "security find-generic-password -s imap.example.com -a you@example.com -w" --dir ~/notes --apply
ORG2_EMAIL_PASSWORD=... celorga source doctor mail --dir ~/notes --json
celorga source sync mail --ingest --apply --dir ~/notes --json

The password comes from ORG2_EMAIL_PASSWORD (or the variable named by bind --password-env), a machine-local bind --password-command that prints it, or Celorga's Keychain, which passes it to one sync process. It is never written to org2.json, bindings, raw captures, or packets. security is tls (default, port 993) or starttls (port 143); certificates are verified, and unencrypted none is accepted only for a loopback test server. Sync opens each mailbox read-only (EXAMINE) and fetches with BODY.PEEK, so it never marks mail as read or changes flags. The first sync covers ingestion.since (default 14 days); later syncs fetch only messages above a machine-local UIDVALIDITY/UID cursor, and a changed UIDVALIDITY restarts from the window. Messages are decoded (MIME encoded words, quoted-printable, base64, charsets; text/plain preferred over HTML converted to text), bounded to maxMessageBytes (default 512 KB) and 20,000 characters of text, and staged as raw JSON plus review-required packets grouped by mailbox and month. Email packets accumulate: new mail merges into the staged month instead of replacing earlier messages. import previews without advancing the cursor; sync --apply (with or without --ingest) fetches and stages in one locked operation. doctor logs in and reports each mailbox's message count; status reports the cursor per mailbox.

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:

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

Agenda

Purpose: query scheduled/deadline items across Org files.

celorga 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

celorga todo toggle --file notes.org --line 42 --apply
celorga todo set --file notes.org --line 42 --status in_progress --apply
celorga todo set --file notes.org --line 42 --keyword SKIPPED --apply
celorga plan set --file notes.org --line 42 --kind scheduled --date 2026-03-25 --apply

Set corpus defaults through Settings → General → TODO States in Celorga, or with the guarded CLI:

celorga todo-config show --dir ~/notes
celorga todo-config set --dir ~/notes --sequences-json '["TODO WAITING | DONE SKIPPED"]'
celorga todo-config set --dir ~/notes --sequences-json '["TODO WAITING | DONE SKIPPED"]' --apply

todo-config returns JSON. set previews by default, validates active and terminal states, and preserves unrelated configuration. Pass --if-revision with the revision from show or a preview to reject stale settings; use --sequences-json '[]' to restore built-in defaults. Settings are stored in org2.json as todo.sequences, an array of Org-style sequence strings. Multiple sequences are supported. Corpus configuration changes invalidate affected agenda and compiled-context caches without rewriting note files.

File-local declarations take precedence over corpus defaults. Declare custom states in the target file, for example #+TODO: TODO MISSED | DONE SKIPPED. --keyword selects the exact spelling; --status also accepts declared custom states alongside standard aliases. Unknown keywords fail with an actionable error. toggle follows the current state’s sequence. Terminal transitions maintain CLOSED, and --logbook records exact state changes. JSON mutation results include oldKeyword and newKeyword alongside the compatible status buckets. Omit --apply to preview.

Point daily navigation at your own dated files with daily-config:

celorga daily-config infer --dir ~/notes --file journal/2026/09/2026-09-29-wind-down.md
celorga daily-config set --dir ~/notes --template 'journal/{YYYY}/{MM}/{YYYY}-{MM}-{DD}-wind-down.md'
celorga daily-config set --dir ~/notes --template 'journal/{YYYY}/{MM}/{YYYY}-{MM}-{DD}-wind-down.md' --apply
celorga daily-config show --dir ~/notes --date 2026-10-01

infer ranks candidate formats from one example path (ambiguous day/month orders return both readings). Formats are corpus-relative paths using {YYYY}, {YY}, {MM}, {M}, {DD}, {D}, {MMM}, {MMMM}, {ddd}, and {dddd}, and must include a month and a day. set previews by default, accepts --if-revision, and stores the format as roam.dailyFileTemplate; --clear restores the roam.dailiesDir/YYYY-MM-DD.org convention. Invalid saved formats are ignored rather than writing outside the corpus.

Checkbox edits

celorga checkbox --file notes.org --line 12
celorga checkbox --file notes.org --line 12 --apply
celorga checkbox set --file notes.org --line 12 --status checked --apply
celorga checkbox cycle --file notes.org --line 12 --fix-cookies --apply
celorga checkbox --file notes.org --line 12 --format json
celorga checkbox fix-cookies --file notes.org
celorga checkbox fix-cookies --file notes.org --apply

The default action, cycle, advances [ ] → [-] → [X] → [ ]. toggle is an alias for that three-state cycle. set --status accepts unchecked, indeterminate, or checked. The one-based --line must point to the list item itself; prose, headings, source/example blocks, and drawers are not checkbox targets. An invalid target or option exits with an error and leaves the file unchanged.

Use fix-cookies to recalculate stale [n/m] and [p%] progress cookies across the file; empty [/] and [%] placeholders are filled at the same time. Each cookie uses the checkbox total from its innermost containing heading; checked items contribute to the completed and total counts, while unchecked and indeterminate items contribute only to the total. This prevents a recursive ancestor total from overwriting a correct descendant cookie. The ordinary cycle and set actions still change only the selected checkbox marker by default. Add --fix-cookies to either action to recalculate cookies from the new checkbox state within the same guarded preview or write.

Writes require --apply. The default output is a diff, and --format text returns the complete edited source. For cycle and set, --format json returns an org2:checkbox-edit:v1 envelope with the original revision, old/new states, and changed/applied flags. For fix-cookies, it returns org2:checkbox-cookie-fix:v1 with the same revision guard plus the exact line, old value, expected value, and owned checkbox total for every edit. Pass the preview's revision as --if-revision when applying later to reject stale edits. column in a marker edit is the zero-based UTF-16 position of the marker character, suitable for LSP clients.

Checkbox edits preserve indentation, list numbering, counter cookies, unrelated text, line endings, final-newline presence, symlinks, and file permissions. Setting an already-selected state or fixing a file whose cookies are current reports no change.

The LSP offers Org2: Cycle checkbox to … as a refactor.rewrite code action on checkbox lines. It edits the current unsaved buffer through the client, using the same checkbox semantics as the CLI.

Capture / archive / refile

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

Archiving moves the selected subtree to a companion archive file (by default FILE_archive for .org 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

Deterministic formatting for .org and .org2 files. File-based formatting automatically rewrites accepted input conveniences such as fenced source blocks, #+begin_org2 aliases, and backtick inline code to ordinary Org syntax when the target ends in .org. Mixed directory operations choose the profile independently for each file. Use --canonical-org for stdin, which has no filename, or to force the canonical profile for another target. Celorga, the LSP, and VS Code follow the same extension-aware rule. Syntax canonicalization remains independent of optional general format-on-save settings.

celorga fmt --dir ~/notes --recursive --check
celorga fmt --dir ~/notes --recursive --apply
celorga fmt --file note.org --apply
celorga fmt --stdin --canonical-org

Table formula evaluation

celorga table recalculate --file FILE [--line N] [--formula-index N] [--apply] [--format text|diff|json] evaluates the selected table's safe #+TBLFM: formulas. It previews by default, selects the first formula line unless --formula-index is given, and performs an atomic revision-guarded write only with --apply. Invalid, remote, Emacs Lisp, and otherwise unsupported expressions return diagnostics without changing the file.

Chart rendering

celorga render-chart renders an SVG artifact from #+chart: / #+plot: metadata or an adjacent #+begin_src chart block attached to an Org table. Fenced chart input remains accepted as typing sugar. Editor clients share this backend, so each extension uses the same table parsing and chart semantics.

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

The first renderer supports deterministic bar, line, and bucketed histogram charts with x and y column mappings. Canonical .org documents use source blocks:

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

#+begin_src chart histogram
x: bucket
y: fetches
sort: y-desc
source: previous-table
#+end_src

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
#+begin_src chart line
x: day
y: fetches
source: package_fetches_result
#+end_src

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.

Saved property views

celorga property-view provides the shared runtime for Celorga's saved property tables and cards. All responses are JSON. Definitions are portable org2:property-view:v1 JSON files in views/ID.org2-view.json, with the filename matching the definition ID. Querying scans ordinary .org and .org2 sources in the active corpus, respects configured ignore patterns, and does not follow symlinks or read hidden directories. It reuses compiled corpus nodes and their effective property inheritance; there is no SQL or separate canonical store.

{
  "schema": "org2:property-view:v1",
  "id": "open-work",
  "title": "Open work by assignee",
  "layout": "cards",
  "scope": { "kind": "heading", "filePrefix": "notes/" },
  "columns": ["title", "STATUS", "ASSIGNEE", "EFFORT"],
  "match": "all",
  "filters": [{ "field": "STATUS", "operator": "is", "value": "open" }],
  "sort": [{ "field": "EFFORT", "direction": "asc" }],
  "groupBy": "ASSIGNEE",
  "limit": 500
}

scope.kind is file, heading or all; optional filePrefix is a case-sensitive corpus-relative path prefix and limits file discovery before compilation. columns supports 1–24 fields. Lowercase built-in fields title/document/file/kind/todo/tags/id are read-only; document is the containing Org document title, while other field names normalize to uppercase property keys. match combines zero to 30 filters with all or any; zero filters select every scoped row. Operators is/isNot/contains compare case-insensitive strings, exists/missing test nonempty/empty values, matches tests a case-insensitive JavaScript regular expression, gt/lt compare finite numeric values, on/before/after compare the first YYYY-MM-DD found in the field (plain dates, Org timestamps, ISO date-times, or dated file names) with a YYYY-MM-DD value, and active/terminal use the compiled TODO workflow for the todo field. Missing values never satisfy numeric or date comparisons; invalid regular expressions and non-date values for date operators are rejected when the definition is parsed. Filter values may contain date variables, resolved in local time each time the view runs and stored verbatim in the definition: {today}, {yesterday} and {tomorrow} yield YYYY-MM-DD; ±N followed by d, w, m or y offsets them, as in {today-7d} or {today+1m}; {month} yields YYYY-MM and {year} yields YYYY. Other brace text stays literal. For example, [{"field":"file","operator":"on","value":"{today}"},{"field":"CREATED","operator":"on","value":"{today}"}] with match: any lists files from today without hard-coding a date. sort supports up to eight fields with asc/desc; two numeric values sort numerically, otherwise case-insensitive lexical order applies, with source keys as stable tie-breakers. Optional groupBy emits a group value per row; clients preserve sorted order within each group. limit is 1–5,000; total and truncated report omitted rows.

celorga property-view list --dir CORPUS
celorga property-view suggest --prompt 'Show unfinished project tasks grouped by project' --dir ~/notes
celorga property-view query --dir CORPUS --view open-work
celorga property-view query --dir CORPUS --definition '{"schema":"org2:property-view:v1","id":"all-notes","title":"All notes","layout":"table","scope":{"kind":"file"},"columns":["title","ASSIGNEE"],"match":"all","filters":[],"sort":[],"limit":500}'
celorga property-view save --dir CORPUS --definition 'JSON_DEFINITION'
celorga property-view save --dir CORPUS --definition 'JSON_DEFINITION' --apply
celorga property-view edit --dir CORPUS --file notes/work.org --kind heading --line 12 --property STATUS --value review --if-revision 'sha256:SOURCE_REVISION'

For updates to an existing definition, both preview and apply require its --if-revision from list or the previous save. New saves require the destination to be absent. Each query row includes file, kind, line, source revision, local properties, inheritedProperties, requested values, and editable. Pass the exact row identity and revision to edit, inspect its old/proposed values and content, then repeat with --apply. Atomic writes check the same revision before replacement. A stale row must be queried again; never guess a line after the source changes.

Editing uses canonical parsed source ranges, retains unrelated lines and CRLF line endings, and writes only the selected local drawer. Inherited values become local overrides; an empty value is explicit and does not remove a property. IDs, built-in fields and ORG2_ metadata cannot be changed through this command. Paths outside the active corpus, hidden state and raw/ imports are rejected. Property views do not mutate TODO keywords or stable links. Malformed or duplicate property drawers fail closed. A view definition can be copied to another corpus without machine bindings; its selected corpus-relative sources must exist there for results to appear.

Data queries

celorga query-data is the DuckDB-backed bridge for local and remote datasets described in Org notes. It scans #+begin_src dataset blocks, creates DuckDB views for explicit csv, parquet, or json paths/URLs, named Org tables, ClickHouse queries, or saved Metabase questions, optionally creates reusable SQL views from #+begin_src sql view=NAME blocks, then runs a #+begin_src sql results=NAME block and materializes the returned rows. Equivalent fences remain accepted as typing sugar.

#+begin_src dataset fetches
type: csv
path: ./data/package-fetches.csv
engine: duckdb
#+end_src

#+begin_src 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
#+end_src

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

#+begin_src sql view=california_fetches
SELECT state, fetches
FROM fetches
WHERE state = 'CA'
#+end_src

#+begin_src sql results=fetches_by_state
SELECT state, sum(fetches) AS fetches
FROM california_fetches
GROUP BY state
#+end_src

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

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

#+begin_src sql results=remote_fetches_by_state
SELECT state, count(*) AS fetches
FROM remote_fetches
GROUP BY state
#+end_src

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

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

#+begin_src dataset fetches
type: table
source: raw_fetches
engine: duckdb
#+end_src

#+begin_src sql results=fetches_total
SELECT sum(fetches) AS fetches
FROM fetches
#+end_src
celorga query-data --file report.org --results fetches_by_state
celorga query-data --file report.org --line 42 --format json
celorga query-data --file report.org --inspect --include-script
celorga query-data --file report.org --results fetches_by_state --out views/fetches_by_state.org
celorga query-data --file report.org --results fetches_by_state --apply
celorga query-data --file report.org --all-results --apply --format json
cat report.org | celorga 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, advances existing #+updated: and ORG2_OBSERVED_AT file metadata from the latest successful result timestamp, 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. Celorga 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:

celorga agent capabilities
celorga 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. celorga context is the human/prompt-friendly shorthand, celorga brief renders a cited synthesis, and celorga compile corpus emits a complete schema-versioned corpus artifact.

See Agent quickstart for the operating contract and common recipes.

Explainable activity, events, and waits

celorga activity explains agent work from structured records only: host presence files under .org2/openclaw-chat.store/live/, committed chat transcript metadata and message provenance, the AI chat inbox, durable run event logs, workflow trigger attempts, and workflow dispatch locks. It never scrapes terminal output.

celorga activity explain --dir ~/notes                 # everything working or waiting on you
celorga activity explain --run RUN_ID --dir ~/notes --json
celorga activity explain --thread THREAD_ID --dir ~/notes --json
celorga activity explain --workflow WORKFLOW_ID --dir ~/notes
celorga activity hosts --dir ~/notes --json
celorga activity events --since 2h --type run.*,approval.* --dir ~/notes --json
celorga activity events --follow --json --dir ~/notes  # NDJSON stream until interrupted
celorga thread wait THREAD_ID --until reply --timeout 600 --dir ~/notes --json
celorga run wait RUN_ID --until approval|blocked|completed --timeout 3600 --dir ~/notes

Each org2:activity-explanation:v1 item names its state (working, queued, needs-you, your-turn, failed, scheduled, due, paused, idle, settled, or done), a stable reason.code with a one-sentence summary, reportedBy (host, host kind and connection state, runtime, destination, model, last actor), lastSignal (heartbeat, transition, message, or trigger attempt with its age), confidence (live while the reporting host's presence is fresh, cached for committed records, uncertain when presence is stale or a delivery is unresolved) with a reason, and blocking entries for the exact approval (run, approval ID, action, risk class, fingerprint, and the decision command), clarification question, delivery failure, artifact review, or unread reply. Explanations never decide approvals.

activity hosts classifies each Celorga host as online (heartbeat within 150 seconds), authentication-needed (online, but a destination reports that it needs sign-in), reconnecting (missed heartbeats for up to 15 minutes), stale (silent longer), or offline (signed off cleanly). Silent hosts keep their last-known turns as cached state. failoverHostRefs lists online hosts that have one of the same destinations enabled.

activity events emits org2:activity-event:v1 records: run.created, run.queued, run.running, run.waiting-approval, run.blocked, run.completed, run.failed, run.canceled, approval.requested, approval.decided, thread.prompted, thread.working, thread.reply-received, thread.needs-you, thread.idle, workflow.dispatched, and host.<state>. Run and approval events are replayed from append-only run event logs with their original timestamps and stable IDs, so a consumer can resume with --since. --follow replays history and then watches the run, chat store, presence, inbox, and workflow directories, with a polling floor for synchronization tools that bypass file events.

Waits evaluate their condition against durable state before watching, so a transition that lands between a prompt and the wait is never missed. thread wait --until reply treats the latest prompt (or --after MESSAGE_ID / --since) as its baseline and is satisfied by a committed assistant message or a queued thread post reply. needs-you, idle, and working use the same explanation states as activity explain. run wait accepts approval, blocked, needs-you, running, completed, failed, terminal, or status:STATUS. Exit status is 0 when matched, 2 when the run reached a terminal state that can no longer satisfy the condition, and 124 on --timeout.

Chat thread automation

celorga thread reads the corpus-owned AI chat transcript from its authoritative sharded store, with compatibility for a legacy .org2/openclaw-chat.json transcript. Reads also project pending operation-journal entries, so active and settled threads retain the same stable ID, messages, session key, context, and timestamps while queued changes remain visible. Settlement is a reversible work-state transition, not deletion.

celorga thread list --dir ~/notes --state all --json
celorga thread show THREAD_ID --dir ~/notes --json
celorga 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
celorga 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
celorga thread post ROOM_ID --message "The export is ready for review" \
  --author "Research Agent" --request-turn @codex \
  --idempotency-key RUN_ID:review --dir ~/notes --apply
celorga thread configure ROOM_ID --agent-turn-limit 6 --dir ~/notes --apply
celorga thread configure ROOM_ID --agent-turn-limit default --dir ~/notes --apply
celorga thread settle THREAD_ID --dir ~/notes          # preview
celorga thread settle THREAD_ID --dir ~/notes --apply
celorga thread reopen THREAD_ID --dir ~/notes --apply
celorga thread configure --auto-settle 604800 --dir ~/notes --apply
celorga thread configure --auto-settle never --dir ~/notes --apply
celorga thread auto-settle --dir ~/notes                # preview eligible IDs
celorga 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. Without --request-turn, the post does not invoke, steer, or enqueue any AI runtime.

In a shared room, --request-turn AGENT (repeatable, or comma-separated; MCP requestTurn) asks room agents to respond to the post. Each value is a destination ID or @mention; Celorga resolves it against the room's enabled agents when it delivers the post, queues one turn per agent with the post as its request, and adds a room notice for any name it cannot resolve. The CLI rejects --request-turn for a single-agent thread. The request is part of the idempotent envelope, so a retry never runs a turn twice.

Agents in a shared room can also start each other's turns: when an agent's reply @mentions another agent in the room, that agent's turn is queued with the reply as its request, and its answer is captioned "requested by" the first agent. Mentions inside source blocks, Markdown code, or Org verbatim and code markup are ignored, an agent cannot request its own turn, and only agents already in the room are reached. thread configure ROOM_ID --agent-turn-limit N caps how many agent-requested turns may run back to back before a person replies (default 4, maximum 50, 0 turns hand-offs off, default restores the default); a message from a person resets the count. When the limit stops a hand-off, the room shows a notice instead of starting the turn. The same setting is in the room's Default menu in Celorga.

thread settle/reopen, thread configure --auto-settle never|SECONDS, thread configure ROOM_ID --agent-turn-limit N|default, and thread auto-settle are also preview-first. With --apply they atomically queue org2:ai-chat-operation:v1 envelopes under .org2/ai-chat-inbox/operations/ instead of directly rewriting the transcript. Celorga drains these operations in deterministic order while running or when the corpus opens, commits each resulting transcript and settlement-settings generation durably, and only then removes that operation file. An invalid entry or failed durability barrier remains in the journal for inspection and retry.

The selected Mac chat is identified in Codex, Claude Code, Pi, OpenCode, 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 celorga 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.

Deterministic chat repair

On macOS, thread repair uses Celorga's native transcript reconciler without starting an AI turn. It merges complete writer heads by original message ID, honors deletion markers, verifies shards and attachment hashes, and retains original files as recovery evidence. Preview does not write even a merged shard. Apply publishes a new repair head; malformed or incomplete tips and unknown future fields remain untouched and produce an error.

npm run build:server
celorga thread repair --dir ~/notes --json
celorga thread repair --dir ~/notes --if-revision PREVIEW_REVISION --apply --json
celorga thread repair --dir ~/notes --watch --interval 900 --apply
celorga server chat-repair --interval 900 --apply
celorga server chat-repair --interval off --apply

The native worker defaults to the checkout's built OpenOrgServer; --executable PATH selects another built worker. A watch interval accepts 10–86400 seconds, defaults to 120, and runs one check at a time. The desktop offers Settings → AI Chat → Conversations → Check synced chat history with Off, 2 minutes, 5 minutes, 15 minutes, and 1 hour. The headless server stores chatRepairIntervalSeconds in its machine-local configuration; restart it after a configuration change. Existing server configurations default to 120 seconds. Checks inspect only chat storage, use a small fingerprint to skip unchanged stores, and retry incomplete syncs on a later interval. Ordinary note conflicts and intentional message deletions are outside this recovery operation.

Durable agent runs and review

Goals and named workers are portable corpus records, distinct from the runtime that executes them. celorga goal create/update/list/show manages org2:goal:v1 files under goals/. celorga 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, default runtimes, 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 and default-runtime 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, an optional defaultRuntime of openclaw, codex, or claude, and repeatable non-secret runtime bindings such as openclaw:customer-support or codex:default. The default runtime is a user-visible starting preference, not a binding or a fixed destination; chat users can override it before the conversation starts. Credentials, session IDs, destination configuration, provider/model choice, and machine paths do not belong in the profile. Resolve the active runtime identity before delegation:

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

The resolved agentRef is the portable worker ID; OpenClaw, Codex, Claude Code, Pi, and OpenCode 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.

celorga 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.

celorga run create --title "Prepare the weekly operating review" \
  --goal "Prepare the review with cited claims and a validated PDF export" \
  --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.org --capability agent-context --dir ~/notes
celorga run list --dir ~/notes --json
celorga run show RUN_ID --dir ~/notes --json
celorga run start RUN_ID --dir ~/notes
celorga run step RUN_ID step-1 --status completed --dir ~/notes
celorga run validation RUN_ID --name citations --status passed --dir ~/notes
celorga run runtime RUN_ID --provider openai --model gpt-5 \
  --tokens-used 18420 --elapsed-seconds 93.4 --dir ~/notes
celorga run approval-request RUN_ID --title "Release report" \
  --action "publish operating review" --risk external-action --role owner --dir ~/notes
celorga run approval-decide RUN_ID APPROVAL_ID --decision approved \
  --actor avi --role owner --fingerprint sha256:... \
  --receipt approval:local:123 --dir ~/notes
celorga run approval-resolve \
  --decision-key artifact:gmail:gog:DRAFT_ID --dir ~/notes --json
celorga run approval-reconcile --dir ~/notes --json       # preview
celorga run approval-reconcile --dir ~/notes --apply      # repair historical duplicates
celorga run artifact-review RUN_ID ARTIFACT_ID --status reviewed \
  --actor avi --dir ~/notes
celorga 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

--title is concise display metadata, limited to 120 characters. Put the complete task brief and execution instructions in --goal. When an older caller omits --title, Celorga derives and stores a bounded title from the goal; workflow runs always preserve the workflow title separately from its instructions.

Other lifecycle operations include resume, retry, block, fail, cancel, complete-external, reopen-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 reconcile-source RUN_ID reports direct readable-projection drift and its canonical values without writing; add --if-revision REVISION --apply to restore the generated title, status, goal, approval summary, and other readable fields from the unchanged machine-state block. Celorga exposes this guarded operation as Repair & Retry after a run approval encounters that exact conflict. 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 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 reopen-external RUN_ID --summary "Corrected open-run outcome" --actor NAME is the narrow repair for a run mistakenly completed externally from waiting-approval; it restores that same run and its retained approval IDs to the pending queue. run outcome may record or revise an outcome before completion. Blocking requires --reason with the specific clarification or next action; Celorga 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. Agent Work's approval-level Mark Done Elsewhere action records the exact item as canceled with an external receipt; it does not complete the containing run. 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.

celorga 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. Run discovery keeps a disposable machine-local fingerprint index, so repeat queue loads reopen only changed run records. JSON clients can repeat --run-detail ID to receive selected full run records in the same response instead of starting separate run show processes. celorga 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. celorga doctor reports a pending writable duplicate as an error.

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

celorga doctor --dir ~/notes
celorga 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:

celorga run show RUN_ID --with-revision --dir ~/notes --json
celorga 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 celorga 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

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

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

celorga 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

celorga 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

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

celorga 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 celorga 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. celorga 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 create writes a prompt, destination reference, optional exact model and reasoning effort, and optional schedule as a plain org2:workflow:v1 file under the visible top-level workflows/. workflow save RUN_ID generalizes a completed run into a fuller draft workflow. Packages can also 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 Automations and workflows for the plain-text ownership and destination-neutral execution contract.

celorga workflow create weekly-product-update \
  --title "Weekly product update" \
  --prompt "Prepare a cited update from this week's product notes." \
  --destination-ref builtin.claude \
  --model opus \
  --schedule "0 9 * * 1" --timezone America/Los_Angeles --dir ~/notes
celorga workflow save RUN_ID --id weekly-operating-review --version 1.0.0 --dir ~/notes
celorga workflow validate weekly-operating-review --dir ~/notes
celorga workflow run weekly-operating-review --input week=2026-W29 --dir ~/notes
celorga workflow activate weekly-operating-review --dir ~/notes
celorga workflow schedule weekly-operating-review --cron "0 9 * * 1" \
  --timezone America/Los_Angeles --model gpt-5.6-sol \
  --reasoning-effort high --gate-event capture --gate-path notes/ --dir ~/notes
celorga workflow signal weekly-operating-review --event capture \
  --signal-id capture-2026-W29 --changed notes/inbox.org --dir ~/notes
celorga workflow due --now 2026-07-20T16:00:00Z --dir ~/notes --json
celorga workflow gate weekly-operating-review --trigger schedule --dir ~/notes --json
celorga workflow pause weekly-operating-review --dir ~/notes
celorga workflow delete weekly-operating-review --dir ~/notes
celorga workflow delete weekly-operating-review --dir ~/notes --apply
celorga workflow migrate --dir ~/notes
celorga workflow triggers weekly-operating-review --changed data/metrics.csv --dir ~/notes --json
celorga workflow triggers weekly-operating-review --event capture --dir ~/notes --json
celorga workflow package weekly-operating-review --dir ~/notes --json
celorga workflow corpus-template weekly-operating-review --out dist/weekly-review-template.json --dir ~/notes

Schedule syntax accepts positive whole-number intervals such as every 15m, every 4h, or every 1d, plus deterministic five-field cron expressions with an optional IANA timezone. --model and --reasoning-effort write exact invocation choices into the canonical workflow; --clear-model and --clear-reasoning-effort restore destination/runtime defaults. workflow due reports only the latest missed occurrence for each active automation and identifies occurrences suppressed by an active attempt. Event hosts can also ask workflow triggers which declarations are due; --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, destination reference, requested model/reasoning configuration, and consumed signal IDs. run list --json exposes attempt roll-ups so clients can summarize history without conflating retries or recurring executions. workflow delete previews by default and requires --apply; it removes the definition while preserving prior runs and does not cancel an already-dispatched attempt.

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/. Celorga 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:

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

Artifact dependency and freshness graph

celorga 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.

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

Runtime policies, MCP, and connector snapshots

celorga 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.

celorga runtime select private-local --capability text --dir ~/notes --json
celorga runtime select deep-analysis --capability tool-use --dir ~/notes --json
celorga 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.

celorga mcp serve starts a stdio Model Context Protocol server. It exposes paginated org2://corpus/ resources, workflow prompts, bounded cited org2_search, org2_fetch, and org2_context retrieval, read-only coordination queries, and trusted local run/thread mutations. Resource reads include a SHA-256 revision and line count. The headless server can expose the five read-only tools through bearer-authenticated Streamable HTTP; create one hash-only credential per client with server token create. 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.

See MCP and agent skills for local stdio and remote Streamable HTTP configuration, the exact current resource, prompt, eight-tool stdio, and five-tool read-only HTTP surfaces, write boundaries, protocol smoke tests, and the packaged general Celorga skill. The MCP server remains intentionally narrower than the CLI.

celorga mcp serve --dir ~/notes
celorga mcp client-add crm --command crm-mcp --arg=--stdio --capability accounts --env CRM_TOKEN --dir ~/notes
celorga mcp discover crm --snapshot crm-capabilities --dir ~/notes --json
celorga 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

celorga 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.

celorga eval run RUN_ID --expect test/fixtures/weekly-review.expected.json --dir ~/notes
celorga eval replay weekly-operating-review --fixture test/fixtures/weekly-review.replay.json --dir ~/notes
celorga 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.

celorga 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

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

celorga ai validate-job --job examples/jobs/weekly-summary.org2-ai.json
celorga 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.

celorga 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.

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

celorga 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.

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

celorga 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.

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

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

Compiled corpus artifacts

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

celorga compile corpus --dir ~/notes --recursive --out compiled/corpus.json
celorga compile corpus --dir ~/notes --recursive --format jsonl --out compiled/corpus.jsonl
celorga 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 celorga lint for generated-output health checks.

For repeated whole-corpus reads, --incremental stores compiled semantics plus per-file cache metadata in the machine-local Celorga 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 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 Celorga core.

celorga search "Chris Martin" --dir ~/notes --recursive --context 3 --sort date-desc
celorga search "Mercor" --dir ~/notes --recursive --sort relevance
celorga index --dir ~/notes --recursive --file ~/notes/inbox.org --incremental --format json
celorga query "Chris Martin" --dir ~/notes --recursive --subtree --sort date-desc --format json
celorga query "Sentra" --dir ~/notes --recursive --format json
celorga 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.

celorga 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.

celorga index builds disposable machine-local search data. Its JSON artifact remains inspectable, and Celorga 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, celorga 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.

JSON Canvas

The canvas family operates on JSON Canvas 1.0 documents. The .canvas file is canonical; native board layout and local resource previews consume shared TypeScript output.

celorga canvas create --dir ~/notes --file boards/project.canvas
celorga canvas create --dir ~/notes --file boards/project.canvas --apply
celorga canvas show --dir ~/notes --file boards/project.canvas --json
celorga canvas targets --dir ~/notes --query architecture --json
celorga canvas edit --dir ~/notes --file boards/project.canvas --if-revision sha256:HASH --stdin --apply --json
celorga canvas import --dir ~/notes --file boards/imported.canvas --from /path/to/source.canvas --apply
celorga canvas export --dir ~/notes --file boards/project.canvas --out /path/to/new-copy.canvas --apply

edit reads a JSON array from standard input. Supported operations are add-node with node, update-node with id and patch, remove-node with id, and corresponding add-edge, update-edge, and remove-edge operations. A node removal also removes edges incident to it. Patch operations cannot change IDs. The runtime validates the entire result before one atomic revision-guarded write. It accepts at most 100 operations per edit. New files and exports require an absent destination; existing documents are never overwritten through import/export.

[{"action":"add-node","node":{"id":"card-1","type":"text","x":0,"y":0,"width":300,"height":200,"text":"Planning notes"}}]

show returns org2:canvas:v1 with the source file, SHA-256 revision, complete document, and derived resources keyed by node ID. Resource status is ready, missing, ambiguous, or unsupported. Notes resolve through shared corpus compilation and provide source file/line, stable ID, and a bounded snippet. File paths are relative to the active corpus root. A file node’s optional org2Ref: "id:ID" preserves stable navigation after a source rename; subpath: "#id:ID" also resolves a heading by its stable ID. Ordinary #heading title and #CUSTOM_ID subpaths resolve only when unique within the file. The extension is inert in clients that do not interpret it.

Optional top-level arrays and unknown fields are retained. Unknown node types preserve their geometry and payload with a placeholder preview. Known node types and edge fields are validated; dimensions must be positive integers, with coordinates and dimensions within ±1,000,000. Documents are bounded to 8 MiB, 1,000 nodes, and 5,000 edges. Supported local PNG/JPEG/GIF/WebP previews are bounded to 8 MiB per image and 16 MiB per board. Notes preview at most 1,200 characters. No remote preview is fetched. Only HTTP(S) and mail links are offered for explicit opening.

Mutations stay inside the active corpus and exclude raw/ and hidden state. Resource loading rejects outside paths, hidden files, and symlink traversal. Stable-ID searches and the note picker observe corpus ignores and exclude raw archives, hidden/build directories, and archive folders. Missing references remain in imported documents. Export copies the source bytes exactly to an explicitly selected new destination; attachment files are not embedded in the portable JSON document.

Roam workflows

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

Celorga 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. celorga 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. celorga 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.

Local neighborhoods and individual mentions

celorga roam connections --dir ~/notes --id alpha-stable --depth 2 --format json
celorga roam connections --dir ~/notes --file ~/notes/project.org --line 17 --format json
celorga roam mention-link --dir ~/notes --file ~/notes/meeting.org --mention KEY --target alpha-stable --if-revision sha256:HASH --format json
# Inspect the preview, then repeat the same command with --apply.

connections returns org2:connections:v1: a nullable neighborhood with its focus, nodes, directed edges, depth, and truncation flag, plus mentions and mentionsTruncated. File and heading nodes retain one-based source lines and IDs. Graph edges reuse the roam graph's explicit-ID and unambiguous title/alias resolution. Missing focus IDs yield an empty neighborhood. A file/line focus chooses the containing stable-ID heading or file node. The command scans the active root recursively; it does not follow symlinks or implicit corpus mounts. Corpus ignorePatterns and default archive/build/hidden-directory exclusions apply.

Each exact mention includes its source file, one-based line, zero-based UTF-16 start/exclusive end columns, context, stable occurrence key, source SHA-256 revision, candidate IDs/files/titles, and ambiguous flag. Results include matches for the focus's title and aliases in other files; they do not interpret semantic suggestions as exact text edits. Existing links and linkify-protected text remain unchanged. The response is bounded to 60 graph nodes and 200 mentions; explore another focus or link and refresh to continue.

mention-link requires one occurrence key, an explicit destination ID, and the source's --if-revision. It rechecks the occurrence and current target index, refuses duplicate target IDs and stale source, and previews the resulting source. --apply makes one atomic revision-guarded write preserving line endings and file mode. It never batch-links ambiguous text or publishes a corpus. Celorga's Context → Graph panel consumes these same commands.

Org-crypt

Celorga 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
celorga crypt decrypt --file secrets.org --line 42 --passphrase 'your-passphrase' --apply
celorga crypt encrypt --file secrets.org --line 42 --passphrase 'your-passphrase' --apply

# Multi-recipient public-key mode
celorga crypt encrypt --file secrets.org --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
celorga crypt encrypt --file secrets.org --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
celorga crypt reencrypt --file secrets.org --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 Org file that contains the subtree, so they work across machines when the key file lives in the synced project.

Export + publish

celorga export html --file README.org --out README.html --apply
celorga export html --dir docs/site --recursive --out-dir site --apply
celorga export beamer --file talks/demo.org --out publish/demo.tex --apply
celorga export beamer --file talks/demo.org --out publish/demo.pdf --pdf --apply
celorga publish document --file reports/brief.org --to web --out-dir publish/brief
celorga publish document --file reports/brief.org --to web --out-dir publish/brief --apply
celorga publish document --file talks/briefing.org --to beamer-pdf --out-file publish/briefing.pdf
celorga publish document --file reports/brief.org --to google-docs --folder-id DRIVE_FOLDER_ID
celorga publish document --file talks/briefing.org --to google-slides --folder-id DRIVE_FOLDER_ID
celorga publish document --file reports/evidence.org --to google-sheets --folder-id DRIVE_FOLDER_ID
celorga publish docs-site --config org2.json

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

publish document is the share boundary for an arbitrary document or the subtree selected by --line. It creates a separate disclosure-safe projection before rendering: property drawers, TODO state, tags, planning and clock data, comments, commented subtrees, dynamic blocks, table formulas, raw HTML, internal IDs, and unresolved local links are not sent to the destination. Referenced raster images are embedded into the output, subject to size limits. The preview reports included assets, removal counts, hashes, and warnings without writing anything.

The web destination writes a self-contained index.html plus manifest.json. Its HTML uses the same responsive document stylesheet as the Celorga reader: the content column is fluid up to its readable maximum width, text and charts stay inside the viewport, and wide tables receive their own horizontal scroll container. Deterministic chart and plot blocks are embedded as SVG when their source table is inside the published document or selected subtree; charts cannot resolve data from outside that disclosure boundary. The bundle has a restrictive content-security policy, no-referrer behavior, and search indexing disabled unless --allow-indexing is explicit. It contains no route back into the corpus. A static bundle does not itself authenticate viewers: Celorga hosting, a self-hosted publisher, or another web host must enforce public, secret-link, or per-person access. Updating a non-empty output directory requires --replace-existing.

The beamer-pdf destination applies a second presentation-safety pass to the same projection, compiles slide headings with a fixed safe Beamer profile, and writes a PDF. Raw LaTeX headers and export blocks are never inherited by this publishing path. Use export beamer instead when you deliberately need reviewable author-controlled LaTeX.

In Celorga for macOS, choose Publish Document… → Local Link to run this projection and host a Web page, document PDF, or compiled Beamer slides (PDF) directly from the Mac behind a high-entropy capability URL. Publishing the same source scope and format again replaces the sealed artifact behind the existing URL; links and their listener port also survive app relaunch. The local host is available only while Celorga is running and uses HTTP on the local/private network; do not use it on an untrusted network. The result screen can copy or open the link and revoke it immediately. Settings → Sharing lists the source path, output format, URL, and start time for every active link and can open, copy, stop, or stop all publications.

Google Drive publishing uses one authorization and four formats:

  • google-docs renders deterministic DOCX and imports an editable Google Doc.

  • google-slides renders deterministic ODP from the selected slide headings and imports an editable Google Slides deck.

  • google-sheets renders deterministic ODS with one sheet per Org table and imports an editable Google Sheets workbook. A selection with no table fails before upload.

  • google-drive-pdf uploads a disclosure-safe fixed-layout PDF as an ordinary Drive file. The Mac publish sheet renders that PDF from the sealed HTML projection; direct CLI callers supply it with --pdf-file FILE.pdf.

Images never need a temporary public URL. All four formats use the narrow https://www.googleapis.com/auth/drive.file scope and read a short-lived access token from ORG2_GOOGLE_DRIVE_ACCESS_TOKEN by default; tokens are never accepted as command-line arguments or stored in the publication. A newly created file inherits the destination folder's Google Drive permissions, and Celorga does not alter those permissions. The initial implementation uses Drive's multipart import and therefore rejects upload artifacts over 5 MB; convert unsupported image formats, reduce embedded images, or use the web bundle destination for larger documents. Google documents support embedded PNG, JPEG, and GIF images; the safe Beamer PDF path supports PNG and JPEG.

The macOS publish sheet performs Google's installed-desktop-app authorization flow in the system browser. It uses a random loopback callback port, a per-request PKCE verifier and state value, and the drive.file scope. The access and refresh credentials are stored in macOS Keychain; an expired access token is refreshed before the sheet passes it to the CLI process through the environment, never in command arguments. Disconnecting removes the credential from that Mac.

Distributed Celorga builds bundle the registered desktop client pair through the OpenOrgGoogleOAuthClientID and OpenOrgGoogleOAuthClientSecret application properties. Normal users click Connect Google Drive and never create a Google Cloud project or import credentials. Use a custom OAuth client remains an advanced option for self-built and separately branded deployments; it accepts the downloaded JSON for a Google Cloud client of application type Desktop app, not Web application. Celorga masks an imported client secret and stores it only with the resulting OAuth credential in macOS Keychain. It sends the pair only to Google's token endpoint for authorization-code exchange and refresh; it never passes the client secret to the Celorga runtime, the corpus, a published artifact, or a command-line argument. Google's installed-app guidance notes that installed applications cannot keep a client secret confidential in the same sense as a server application. The flow still uses a loopback redirect, PKCE, and a per-request state value.

Developers can set ORG2_GOOGLE_OAUTH_CLIENT_JSON to a protected client JSON path at build time, or provide the paired ORG2_GOOGLE_OAUTH_CLIENT_ID and ORG2_GOOGLE_OAUTH_CLIENT_SECRET values. --google-oauth-client-json PATH is the equivalent local build option. Distribution packaging requires a complete client configuration and fails rather than producing a release that sends users through custom-client setup. The protected JSON is read during the build and is never copied into source, although the resulting desktop client pair is necessarily present in the application bundle. See OAuth 2.0 for desktop apps, Google OAuth 2.0 overview, and Drive authorization scopes.

Publishing to Google is deliberately one-way. CLI callers must retain the returned file ID in durable destination state. Celorga stores that non-secret binding in the published scope's property drawer, using format-specific ORG2_PUBLISH_GOOGLE_* properties for the file ID, URL, latest observed Drive version, and publish time; whole-document bindings live in the file drawer and subtree bindings live on the selected headline. OAuth credentials remain in Keychain and are never stored in those properties. Google Docs exports preserve headings, lists, tables, links, images, and inline bold, italic, underline, strike, code, and verbatim styling through the generated DOCX import. Replacing an existing Doc, Slides deck, Sheet, or PDF requires the explicit --document-id ID and --replace-existing pair. --if-version VERSION remains accepted as advisory destination metadata for compatible clients, but Google may advance its version counter during server-side conversion, so Celorga does not treat that counter as a durable edit lock. Instead, Celorga fetches the current Drive record and strong ETag through the Drive v2 file resource immediately before replacement, verifies the target type and edit permission, refuses a target with comments because replacement could detach their anchors, and sends that ETag through If-Match on the guarded v2 upload so a change racing the replacement fails atomically. The v3 file resource does not expose this ETag, although new-file creation continues to use v3. The explicit replacement can overwrite prior remote content; inspect the linked artifact first when collaborators may have edited it. Permissions, comments, suggestions, and Google revision history belong to Google; Celorga does not attempt bidirectional synchronization.

# Create after inspecting the preview.
ORG2_GOOGLE_DRIVE_ACCESS_TOKEN="$TOKEN" \
  celorga publish document --file reports/brief.org --to google-docs \
  --folder-id DRIVE_FOLDER_ID --apply --format json

# Other native Google formats use the same credential and folder policy.
ORG2_GOOGLE_DRIVE_ACCESS_TOKEN="$TOKEN" \
  celorga publish document --file talks/briefing.org --to google-slides \
  --folder-id DRIVE_FOLDER_ID --apply --format json

ORG2_GOOGLE_DRIVE_ACCESS_TOKEN="$TOKEN" \
  celorga publish document --file reports/evidence.org --to google-sheets \
  --folder-id DRIVE_FOLDER_ID --apply --format json

# Explicit full-document replacement. The last-seen version is optional metadata.
ORG2_GOOGLE_DRIVE_ACCESS_TOKEN="$TOKEN" \
  celorga publish document --file reports/brief.org --to google-docs \
  --document-id GOOGLE_DOCUMENT_ID \
  --replace-existing --apply --format json

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 Celorga AST and maps both legacy Org Beamer properties and backend-neutral Celorga 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:

celorga 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:

Headless server CLI

celorga server init|start|status|pair|revoke|stop|permissions|token|mcp|assign|service|push-config manages the macOS headless relay, scheduler, and optional read-only Streamable HTTP MCP listener. Configuration is machine-local. server permissions --filesystem-access read-only|workspace-write|full-access previews or atomically updates that host's local agent filesystem policy. server token create|list|revoke manages per-client corpus:read bearer credentials while persisting only hashes; creating the first token enables MCP and revoking the final token disables it. server mcp enable|disable previews endpoint, port, and exact browser-origin changes. All applied permission, token, or MCP changes require a restart. server status reports the active policy and non-secret MCP metadata. server assign previews a symbolic automationHostRef change in org2.json. The workflow due --host-ref HOST and scheduled workflow run --host-ref HOST interfaces enforce that owner. See Headless server setup for the complete lifecycle and limitations.

Project note commands

celorga project list|show|create|adopt|update operates on ordinary Org files. List returns org2:project-list:v1 with projects and per-file diagnostics; create, adopt, and update return org2:project-edit:v1 with proposed content, project metadata, baseRevision (null for new files), and applied. Mutations preview unless --apply is supplied.

Create accepts --title, --color, --file, and --id. Preserve the preview's ID and path when applying. The default path is beneath roam.nodesDir (or notes), in projects/. Optional --description TEXT supplies the initial brief; without it, creation adds no placeholder sections or TODOs. Adopt takes an existing file path and preserves its body and file-level ID. Pass an adoption preview's --id and --if-revision using its project ID and baseRevision when applying. Update accepts --thread UUID, --remove, and --color. Adopt/update accept --if-revision sha256:... for guarded writes.

Color is optional; omitted PROJECT_COLOR means no color. Use --color none to clear a color. Colors may be blue, teal, green, orange, red, purple, gray, or a six-digit hexadecimal RGB value such as #4A90D9. Project notes may live anywhere in nonhidden corpus directories, respecting configured ignore patterns. Put the marker in the first 8 KiB; notes are limited to 1 MiB. Duplicate IDs and malformed notes produce diagnostics instead of ambiguous membership. Briefs contain at most 12,000 characters. workspace agent-state also exposes an independently fallible projects section.

The metadata is #+ORG2_KIND: project, #+TITLE:, #+PROJECT_COLOR:, #+PROJECT_THREADS: (space-separated chat UUIDs), and a file-level properties drawer containing :ID:. All remaining content uses normal Org headings, TODOs, tags, planning, and links. There is no separate project database.

Property views validate source projections against canonical AST ranges: headings and property drawers in examples do not become editable rows. Malformed source notes are omitted with per-file diagnostics in query results, also shown in the Mac view.

Raw captures remain immutable: graph reads may include them, but unlinked-mention actions exclude raw/ and the mutation command rejects raw source edits.

Browser clip import

The integrations/browser-clip/ Chromium extension produces org2:browser-clip:v1 JSON in .org2clip files: url, title, optional author, ISO capturedAt, mode (article or selection), template (note or task), and plain-text content. The Mac Capture window exposes this same runtime import contract.

celorga browser-clip import --file article.org2clip --dir ~/notes --json
# Copy revision and clipRevision from that preview:
celorga browser-clip import --file article.org2clip --dir ~/notes \
  --if-revision sha256:DESTINATION_HASH --if-clip-revision CLIP_HASH --apply --json

An absent destination reports revision absent. Optional --template note|task overrides the saved template and must match between preview and apply. The importer shares capture's source/provenance rendering, preserves an immutable content-addressed raw envelope under raw/browser/, and atomically appends a literal-source note to views/browser-clips.org. Preview writes nothing. Both source-clip and destination revisions are checked before apply; replaying the same clip/template is idempotent. Duplicate detection reads canonical heading provenance drawers, supports CRLF and indentation, and ignores quoted examples and body-only drawers. Imports are limited to 2 MB of text and 4 MB JSON, require HTTP(S) URLs without embedded credentials, and reject paths that escape the active corpus via symlinks. This command does not fetch URLs or promote content to canonical notes.

The task template chooses the first active state in the destination file or corpus TODO workflow, falling back to TODO. Raw-source provenance uses a file-relative link from the imported note to its immutable capture.