krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

docs/review-2026-10-05.md

2fa201330f61c801f777baa3f2943d49d8758cda
orgstar/docs/review-2026-10-05.md rendered · source · history · blame · raw

136 lines · 13382 bytes

  1# Orgstar review, 2026-10-05
  2
  3Branch `buffers` at a831086. Emacs 31.1 / Org 9.8.7 installed, so the oracle tests ran.
  4
  5## 1. Current state
  6
  7**Scope shipped.** The design doc's phases 1 through 6 all have code: lossless parser with incremental reparse, TextKit 2 editor with folding and org-indent, SQLite/FTS index with a dirty-buffer overlay, three-way-merge save path with recovery copies, heading/list/table/timestamp/fill commands, agenda (day/week, TODO list, tag match, saved views, habits, reminders), native table formulas with Emacs fallback, Babel for shells/python/elisp plus output-only runners, capture with a global hotkey, HTML/Markdown export natively and PDF/ODT/LaTeX/text through `emacs --batch`, clocking with a menu-bar item, board/kanban, Emacs/Mac/Doom keymaps with a Vim modal engine, themes, config.toml with two-way sync, and import from an Emacs config. About 37k lines of Swift. 107 commits, all dated Oct 4 and 5.
  8
  9**Quality bar.** Where a command exists it is checked byte-for-byte against Emacs: 31 oracle tests run by default and fail if Emacs is missing. 341 tests pass at a6139aa (the commit before Buffers). Performance gates pass on an M-series Mac (open 75 ms for 1.5 MB, typing p95 6.5 ms, memory 9.6x), not yet on the M1 Air reference machine.
 10
 11**Architecture holds.** OrgCore/OrgIndex/OrgWorkspace/OrgPresentation import no AppKit/UIKit/SwiftUI; the only platform code is FSEvents behind `#if os(macOS)`. Commands are pure functions over `EditContext`, so the iOS path the design promises is still open.
 12
 13**Stale docs.** README says "Status: phase 1 (read-only viewer)". The design doc still names OrgSwift as the renderer and has every phase-1 checkbox unchecked. The Emacs importer tells users "Orgstar has no fonts or themes to set yet" (`Sources/OrgApp/EmacsImport.swift:451`).
 14
 15### Defects found
 16
 17| Where | Problem |
 18| --- | --- |
 19| `Tests/OrgAppTests/AppTests.swift:316-374` | `AgendaModel.refresh(open:)` now takes `[String: String]`; the tests still pass `nil` and a tuple. The OrgAppTests target does not compile at a831086. |
 20| `Sources/OrgCore/Keymap/Presets.swift:558,579` | `C-x b` is bound twice; the later `app.quick-open` wins, so the new buffer switch is unreachable in the Emacs preset. `SPC b b` is also bound twice (:90, :99); there the later `buffer.switch` wins and :90 is dead. |
 21| `Sources/OrgEditorAppKit/OrgEditor.swift:731` | After any edit, hidden ranges are rebuilt without `bodyFolds` (line 464 passes them). CONTENTS-folded bodies reappear while the view still thinks they are folded. |
 22| `Sources/OrgApp/WorkspaceModel.swift:57,158` | `startWatching()` runs once at launch; `addRoot` never restarts it. A folder added during a session gets no FSEvents until relaunch. |
 23| `Sources/OrgApp/DocumentSession.swift:356-410` | Refile/archive into another file read the target from disk and write through `Saver`, ignoring that file's open buffer; the buffer catches up only by a later merge. |
 24| `Sources/Orgstar/EditorView.swift:86` | Every per-buffer editor's `onChange` calls `session.bufferChanged()`, which acts on the current entry. A disk change merged into a background buffer schedules no autosave for it. |
 25| `Sources/OrgApp/BabelRunner.swift:48` | stderr is discarded, and the Task is not kept, so a run cannot be cancelled. |
 26| `Sources/OrgEditorAppKit/LineNumbers.swift:12,35` | Line numbering counts newlines from offset 0 on every draw and splits the whole string on every newline edit. Added after the performance gates and not re-measured. |
 27| `Sources/Orgstar/ClockViews.swift:129` | Clock report reads files from disk and ignores unsaved buffers. |
 28| `scripts/build-app.sh` | No `CFBundleDocumentTypes`, so `.org` files can't be opened from Finder or `open`; no icon; no URL scheme. |
 29
 30## 2. Gaps against Emacs org-mode
 31
 32Grouped by how often a daily org user hits them. "Known" means the project's own plan or code already records it as not done.
 33
 34### Hit every day
 35
 36- **Links.** No follow (`C-c C-o`, click, RET), no insert (`C-c C-l`), no store, no `id:` creation. Links are only styled. `#+LINK` abbreviations, `attachment:`, `CUSTOM_ID` lookup are absent too.
 37- **TODO logging.** No `CLOSED:` on DONE (`org-log-done`), no `!`/`@` state notes, no `LOGBOOK` notes (`org-log-into-drawer` is hard-coded nil in `Repeat.swift:3`), no note prompt. Fast keys in `#+TODO` are parsed but unused; no fast selection. No `S-left/right` keyword cycling, no `ORDERED`/blocking.
 38- **Tags.** Free-text prompt only. No fast selection, no `#+TAGS` groups, no completion from the workspace, no file tags (known).
 39- **Drawer and block folding.** Property and LOGBOOK drawers are always open. Emacs folds them.
 40- **Capture.** No `file+datetree`, `id` or `clock` targets; `%^g`, `%^t`, `%^C`, `%:keyword` fail; only `:prepend` is honoured (known). The user's own notes include journal and habit files, which typically depend on datetree.
 41- **Agenda actions.** No filters (`/`), no clock-in, refile or archive from the agenda, no bulk actions, no log or clockreport mode, no follow mode, fixed prefix format.
 42- **Subtree editing.** No cut/copy/paste/clone, no `org-sort`, no `org-mark-subtree`, no `C-c *` / `C-c -` toggles, no `org-toggle-comment`.
 43- **Structure templates** (`C-c C-,`, `<s TAB`) and **`org-edit-special`** (`C-c '`).
 44- **Clocktable** dynamic block and `org-dblock-update`; effort estimates (known).
 45- **`C-c C-c`** covers only item, table, TBLFM and src. Missing: heading (tags), timestamp, property, footnote, clock, cookie, `#+RESULTS`/`#+CALL`, keyword refresh, dynamic block.
 46
 47### Hit weekly
 48
 49- Timestamps: no calendar picker, no date ranges in the prompt, no `org-log-reschedule`/`redeadline`, no `C-c C-y` (known).
 50- Footnotes: parsed and exported, no new/goto/sort/normalize commands.
 51- Tables: no in-cell field formulas or formula editor, no sort/transpose/CSV import-export, no `<10>` shrink, no field editor (known).
 52- Navigation: no narrowing, `org-goto`, sparse trees, `org-occur`, speed keys.
 53- Properties: set only; no delete, no `_ALL` completion, no column view.
 54- Archive: no archive-tag toggle, no archive-to-sibling, no `datetree/` location.
 55- Export: `#+OPTIONS`, TOC, `:noexport:`, `#+INCLUDE`, macros, LaTeX fragments, subscripts, `#+HTML_HEAD` all ignored; Markdown is GFM, not ox-md. Pandoc path from the design is absent.
 56- Babel: `:session`, `:noweb`, `:file` (dot/plantuml images), `#+CALL`, inline `src_` execution, tangle (known).
 57- Visibility: `#+STARTUP` only handles visibility words; `indent`, `logdone`, `hidestars`, `align` ignored. `VISIBILITY` property ignored.
 58- Inline images: none.
 59
 60### Parser and settings
 61
 62- Not parsed: radio targets, `$…$` LaTeX, LaTeX environments, inline tasks, entities and subscripts as objects, export snippets, citations, `call_`, diary sexps, table.el tables, alphabetical bullets (even with the option on), description-list terms and `[@N]` counters as nodes.
 63- File-level `:PROPERTIES:` drawer is treated as a generic drawer.
 64- Ignored keywords: `#+TAGS`, `#+CONSTANTS`, `#+COLUMNS`, `#+LINK`, `#+OPTIONS`, `#+SETUPFILE`, `#+INCLUDE`, `#+MACRO`, `#+EXCLUDE_TAGS`.
 65
 66### Editor and platform
 67
 68- No inline completion of any kind; no spell check; no auto-pairing; no heading-click fold; no checkbox or timestamp click; soft wrap can't be turned off; `gj`/`gk` are plain `j`/`k`.
 69- Vim: no macros, marks, jumps, `gv`, block visual, surround, commentary, evil-org text objects (known).
 70- Conflict UI has no diff view; Syncthing conflict copies open as ordinary files with no diff (known).
 71- iCloud placeholders: design calls for them, only `startDownloadingUbiquitousItem` in the reconciler exists.
 72- Distribution: ad-hoc signed, no icon, no notarization, no Sparkle, no Homebrew cask, no document types, no URL scheme (`org-protocol`), no Shortcuts/AppleScript, no Spotlight or Quick Look extension.
 73- iOS: `Package.swift` declares the platform; no target exists.
 74- VoiceOver checklist never run; hidden markup and stars are still in the accessibility value.
 75
 76### Intentionally out (design non-goals)
 77
 78Elisp runtime, block storage, Mac App Store, Babel on iOS, native Calc symbolic math, sync providers beyond Syncthing and iCloud.
 79
 80## 3. Prioritised feature list
 81
 82Ordered by what stops the app from replacing Emacs for an org-only user, then by cost. Each tier is roughly one plan per bullet.
 83
 84### Tier 0: fix before building more
 85
 861. Make OrgAppTests compile and pass; fix the two duplicate bindings.
 872. `bodyFolds` in `storageEdited`; restart watching on `addRoot`; per-buffer `onChange`; refile/archive through the target's open buffer.
 883. Babel: capture stderr into the message, keep the Task for cancel.
 894. Re-run the gates (`ORGSTAR_GATES=1`) with line numbers, pretty entities and the block band on; fix line-number counting.
 905. `CFBundleDocumentTypes` for `.org`/`.org_archive`, an icon. Update README status.
 91
 92### Tier 1: daily-use parity
 93
 941. **Links**: open (`C-c C-o`, ⌘-click, RET on a link), insert with completion over headings/files/ids, store link, `org-id-get-create`, `#+LINK` abbreviations, `CUSTOM_ID`. The index already has link rows and `headings(withID:)`.
 952. **TODO logging**: `org-log-done`, per-keyword `!`/`@`, `org-log-into-drawer`, note prompt through the existing `Prompt` loop. Habits already parse "State" notes, so this also fixes habit tracking for users with `org-log-into-drawer t`.
 963. **Fast TODO and fast tag selection**, `#+TAGS` groups, tag completion from the index. Honour `org-todo-keywords` with several sequences from config.
 974. **Drawer and block folding** in `Presentation.hiddenRanges`, default folded, TAB on a drawer line.
 985. **Capture targets and escapes**: `file+datetree` (day/week/month, `:tree-type`), `id`, `clock`; `%^g`, `%^t`/`%^T`/`%^u`/`%^U`, `%^C`, typed prompts; `:immediate-finish`, `:empty-lines`, `:clock-in`/`:clock-keep`, `:jump-to-captured`.
 996. **Agenda actions**: filter by tag/category/regexp/effort, clock in/out, refile, archive, bulk mark and act, log mode, `org-agenda-prefix-format`.
1007. **Subtree commands**: cut/copy/paste/clone, `org-sort` (entries and lists), mark subtree, `C-c *`/`C-c -`, toggle comment.
1018. **Structure templates** and **`org-edit-special`** (a sheet with the tree-sitter grammar and the block's language).
1029. **Clocktable** dynamic block, `org-dblock-update`, `Effort` property and `org-clock-modeline-total`.
10310. **`C-c C-c` contexts**: heading tags, timestamp normalise, property line, footnote, cookie, `#+RESULTS`, keyword refresh, dynamic block.
104
105### Tier 2: weekly parity
106
1071. Date picker popover for `org-read-date`; date ranges; `org-log-reschedule`/`redeadline`; `C-c C-y`.
1082. Footnote commands (new, goto, sort, normalise).
1093. Tables: in-cell `=`/`:=` formulas, formula editor, sort, transpose, CSV import/export, `<N>` shrink.
1104. Narrowing, `org-goto`, sparse trees / `org-occur` (reuse the FTS and tag matcher), speed keys.
1115. Property delete, `_ALL` completion, column view (read-only first).
1126. Archive tag toggle, archive to sibling, `datetree/` archive location.
1137. Export: `#+OPTIONS` (toc, num, tags, todo, `^:{}`), `:noexport:`/`:export:`, `#+INCLUDE`, macro expansion, MathJax for LaTeX fragments, subscripts, `#+HTML_HEAD`; export dialog with destination and open-after.
1148. Babel: `:session` for shells and python, `:file` with inline image results (dot, plantuml, mermaid), `:noweb`, `#+CALL`, inline `src_`.
1159. Inline images (`file:` links to images, `#+ATTR_ORG: :width`), toggled like `org-toggle-inline-images`.
11610. Parser completions: radio targets, `$…$`, LaTeX environments, entities and subscripts as objects, inline tasks, file-level property drawer, alphabetical bullets, `#+TAGS`/`#+CONSTANTS`/`#+COLUMNS`/`#+LINK`/`#+MACRO`.
11711. `#+STARTUP` beyond visibility (`indent`, `logdone`, `hidestars`, `align`), `VISIBILITY` property.
11812. Conflict and Syncthing-copy diff view; recovery folder browser.
11913. Vim: macros, marks, jumps, `gv`, block visual, surround, commentary, evil-org text objects, `:e`/`:bd`/`:bn`.
12014. Editor: completion popover (tags, todo, properties, links, templates), spell check, heading click to fold, checkbox click, truncate-lines toggle, `gj`/`gk`.
121
122### Tier 3: distribution and beyond Emacs
123
1241. Notarised build, Sparkle, Homebrew cask, Developer ID signing.
1252. `org-protocol://` URL scheme and a Shortcuts/App Intents capture action; share extension later.
1263. Spotlight importer and Quick Look extension for `.org`.
1274. Backlinks pane from the index's link rows; an ID graph is cheap once link resolution exists.
1285. Column view and clock reports as live sidebars rather than dynamic blocks.
1296. iCloud placeholders and `NSFilePresenter` path, then the iOS target: capture and agenda first, per the design.
1307. VoiceOver pass and an accessibility value that omits hidden markup.
131
132## 4. Notes on process
133
134- The whole repo was written in two days. The oracle discipline is what makes that credible; keep it mandatory for every Tier 1 command (each maps to an org function: `org-open-at-point`, `org-todo` with logging, `org-set-tags-command`, `org-capture` datetree, `org-sort`).
135- Prefer one plan per Tier 1 bullet, as the existing `docs/plans` do, and record "not yet" lists the same way; those lists were the fastest way to produce this review.
136- The design's phase gates were never ticked and the M1 Air run is still owed. Measure once before Tier 1 adds more rendering work.