Language reference

This is the practical language reference for Org2 v0.

For the normative draft spec, see spec/v0/SPEC.org.

Spec map

Use this mapping when deciding where behavior is defined:

Headings

  • Syntax: one or more stars, a space, then title text.

  • Example:

* Project
** Subproject
*** Task
  • Heading level is the number of stars.

  • Level skips are accepted in v0.

TODO keywords

Headline TODO states parsed into the canonical AST:

  • TODO

  • IN_PROGRESS

  • DONE

  • CANCELLED / CANCELED

Workflow tools may accept aliases such as PROG or WAITING and normalize them to one of the canonical TODO keywords above.

Example:

* TODO Ship docs
* IN_PROGRESS Implement parser tests
* DONE Publish changelog

Priority cookies

  • Syntax: [#A] style cookie after TODO keyword (or at headline start when no TODO keyword).

* TODO [#A] Critical task

Tags

  • Tags are suffixes at headline end: :tag: or :tag1:tag2:

* TODO Draft docs :docs:website:

Drawers

Property drawers

  • Drawer delimiters: :PROPERTIES: ... :END:

  • Common keys used by Org2 workflows:

    • :ID:

    • :CUSTOM_ID:

    • :EFFORT:

    • :ROAM_ALIASES:

:PROPERTIES:
:ID: 123e4567-e89b-12d3-a456-426614174000
:EFFORT: 45
:END:

Generic drawers

  • Generic :NAME: ... :END: drawers are preserved.

Planning lines

Recognized planning keywords:

  • SCHEDULED:

  • DEADLINE:

  • CLOSED:

SCHEDULED: <2026-02-21 Sat>
DEADLINE: <2026-02-25 Wed>
CLOSED: [2026-02-20 Fri]

Timestamps

Supported timestamp forms:

  • Active: <2026-02-21 Sat>

  • Inactive: [2026-02-21 Sat]

  • With time: <2026-02-21 Sat 09:30>

  • Ranges: <2026-02-21 Sat>--<2026-02-23 Mon>

  • Repeaters (agenda projection): +1w, ++1m, .+2d

Lists

  • Unordered markers: -, +, *

  • Ordered markers: 1., 1)

  • Checkboxes: [ ], [X], [x], [-]

  • Checkbox progress cookies: [n/m] and [p%] may appear in headings or list items; compile output exposes computed checkbox totals and lint reports stale cookies.

  • Nested lists via indentation.

Links

Supported link forms:

  • Bracket links: [[https://example.com][label]]

  • Plain URLs: https://example.com

  • File links: [[file:path/to/file.org]]

  • ID links: [[id:UUID][label]] (roam-style link)

  • Wiki links: [[Node Title]] (roam-style link)

Both ID links and Wiki links are first-class in Org2 roam workflows.

Link abbreviations are supported with built-ins, config, and #+LINK directives.

Built-in abbreviations:

  • gh:https://github.com/%s

  • gl:https://gitlab.com/%s

  • yt:https://www.youtube.com/watch?v%s=

  • wiki:https://en.wikipedia.org/wiki/%s

You can add/override abbreviations in org2.json:

{
  "links": {
    "linearTeam": "scarf",
    "abbreviations": {
      "linear": "https://linear.app/scarf/issue/%s",
      "gh": "https://github.example.com/%s"
    }
  }
}

And define file-local abbreviations with Org-compatible syntax:

  • Define: #+LINK: linear https://linear.app/scarf/issue/%s

  • Use: [[linear:APP-3718][APP-3718]] or [[linear:APP-3718]]

Precedence (highest to lowest):

  1. file-local #+LINK

  2. org2.json links.abbreviations

  3. built-ins

Org2 expands abbreviation targets when rendering/exporting and for LSP document-link targets.

Inline emphasis

  • Bold: *bold*

  • Italic: /italic/

  • Underline: _underline_

  • Strike: +strike+

  • Verbatim: =verbatim=

  • Code: ~code~

Blocks

Recognized block forms include:

  • Source blocks: #+begin_src ... #+end_src

  • Example blocks: #+begin_example ... #+end_example

  • Quote blocks: #+begin_quote ... #+end_quote

  • Verse blocks: #+begin_verse ... #+end_verse

  • Center blocks: #+begin_center ... #+end_center

  • Comment blocks: #+begin_comment ... #+end_comment

Syntactic sugar / aliases

Org2 supports a friendlier fenced-code alias for source blocks:

```ts
const x = 1
```

This is treated as syntactic sugar for:

#+begin_src ts
const x = 1
#+end_src

Prefer the fenced form for new hand-written examples and docs. The verbose #+begin_src form remains fully supported for compatibility and explicit Org-style interchange.

Fenced source blocks use exactly three backticks for the opener and closer. Four-or-more backticks remain ordinary text in v0, which keeps Markdown examples that quote org2 fences from accidentally becoming executable org2 source, chart, dataset, or SQL blocks.

Tables

  • Pipe table rows: | a | b |

  • Horizontal separator rows: |---+---|

  • Table content is parsed and preserved.

Tables, source blocks, and common blocks can carry adjacent affiliated keyword metadata:

#+name: quarterly_revenue
#+chart: line x=quarter y=revenue
#+dataset: finance.revenue
| quarter | revenue |
|---------+---------|
| 2026-Q1 | 1200    |

Recognized affiliated keys include NAME, CAPTION, PLOT, CHART, DATASET, VIEW, RESULTS, HEADER/HEADERS, and ATTR_* keys. The parser preserves them on the following table or block as affiliatedKeywords for chart renderers, dataset indexes, SQL views, and exporters.

For new chart examples, prefer an adjacent fenced chart block when it is easier to read:

#+name: fetch_buckets
| bucket | fetches |
|--------+---------|
| 0-10   | 14      |
| 11-50  | 32      |

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

This is ergonomic syntax sugar for chart metadata over the preceding table. Fenced chart blocks may also point at a named table or materialized result elsewhere in the same note:

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

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

The current renderer supports bar, line, and bucketed histogram charts over the previous table or a named table source. Add sort: x-asc, sort: x-desc, sort: y-asc, or sort: y-desc when the visual should order marks independently of the table's source row order.

The CLI can render chart-affiliated tables directly:

npm run org2 -- render-chart --file report.org2 --block-id quarterly_revenue --format svg

Local datasets and SQL views

Org2 can describe local/ad hoc data workflows with fenced dataset and sql blocks. This is intended for notebook-like reports over explicit local files, read-only URLs, and named org2 tables, with DuckDB as the first execution bridge:

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

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

```chart bar
source: fetches_by_state
x: state
y: fetches
```

org2 query-data reads these blocks, creates DuckDB views for csv, parquet, json, URL-backed sources, or named org2 table datasets, creates any declared SQL views, runs a selected SQL result block through the local DuckDB CLI, and materializes the returned rows as a named org table or JSON envelope. Materialized org tables include a #+query-data: provenance line with the result id, row count, optional freshness token, selected SQL hash, full DuckDB script hash, and ran_at timestamp:

npm run org2 -- query-data --file report.org2 --results fetches_by_state --out report.fetches.org
npm run org2 -- query-data --file report.org2 --line 42 --format json
npm run org2 -- query-data --file report.org2 --inspect --include-script
cat report.org2 | npm run org2 -- query-data --stdin --results fetches_by_state --format json

Use --stdin when an editor, agent, or notebook-style client wants to run the current buffer without saving it first. Use --line N to select the SQL result block containing or after the cursor line. JSON output includes resultBlocks with each available SQL result id and source range so clients can populate result pickers or recover from duplicate-id diagnostics without reparsing the file. Use --inspect to return parsed datasets, SQL views, result block metadata, structural diagnostics, and selected-query provenance without running DuckDB; --include-script also returns the generated DuckDB SQL for the selected result. Relative file dataset paths are resolved from the current working directory for stdin input. SQL result ids must be unique within the note so materialized tables and provenance stay deterministic. Dataset ids and SQL view ids must also be distinct because they become DuckDB relation names during execution. Dataset blocks may include metadata-only credential: or auth: references such as env:SCARF_API_TOKEN, secret:analytics-token, or profile:product-analytics and config: or profile: references such as config:dashboard-local; these are exposed as JSON metadata for clients and agents, but are not inserted into the generated DuckDB SQL. Inline bearer tokens, passwords, and other literal secrets are rejected. SQL result blocks can include artifact=PATH to record the intended materialized output in JSON provenance and the #+query-data: line; --out FILE stamps the actual written artifact path. Executed JSON provenance also includes ranAt, and materialized org tables stamp ran_at, so editor, agent, and dashboard clients can compare declared freshness with the actual query run time. Result blocks can also include freshness=24h, ttl=24h, or max-age=24h so clients can decide when materialized rows should be refreshed.

Use sql view=NAME when a report needs a reusable local projection before the final result table:

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

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

Use url: instead of path: when DuckDB should read a declared HTTP(S) or file: URL directly:

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

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

Use type: table with source: NAME when the source data already lives in a named org2 table:

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

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

```sql results=fetches_total
SELECT sum(fetches) AS fetches
FROM fetches
```

This is deliberately scoped to explicit local/ad hoc data sources. Org2 stores the source path, read-only URL, table/view name, optional column and primary-key metadata, credential/config reference, query, and materialized result/provenance; secrets, bearer tokens, and remote warehouse credentials belong in external tooling, not in notes.

Keyword/directive lines

Org2 parses keyword lines in #+KEY: VALUE form.

Commonly used in export/publish:

  • #+TITLE:

  • #+SUBTITLE:

  • #+AUTHOR:

  • #+DATE:

  • #+DESCRIPTION:

  • #+KEYWORDS:

  • #+LANGUAGE:

  • #+HTML_HEAD:

  • #+ID:

Unknown directives are preserved for round-tripping.

Comments

  • Line comments: lines beginning with #.

  • Comment blocks are also supported (see Blocks).

Losslessness goal

Org2 aims for practical round-tripping of supported constructs. When behavior is ambiguous, defer to the current implementation and spec fixtures.