aboutsummaryrefslogtreecommitdiff
path: root/README.org
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-25 21:36:26 -0500
committerCraig Jennings <c@cjennings.net>2026-05-25 21:36:26 -0500
commitdf36db0d2d75207c5982d68f179e71785d8706ec (patch)
treeea6cf5a4bdc1a456b03423607e6b34673fba2998 /README.org
parent1de0623168d13c51fb9a7c8a87f89030642ae4d0 (diff)
downloadpearl-df36db0d2d75207c5982d68f179e71785d8706ec.tar.gz
pearl-df36db0d2d75207c5982d68f179e71785d8706ec.zip
docs: rewrite the README editing section for the unified save model
The README still described the old per-field push commands and the org-sync hook mode, both removed in save-model-v2. I rewrote the editing section around the one write path: you edit in the buffer, save the ticket, and Pearl reconciles each changed field against the remote. It now leads with pearl-save-issue / pearl-save-all, spells out how each field is edited (the cookie for priority, a picker for state/assignee/labels, hand-editing for text), explains that picking a constrained field marks it changed rather than pushing, and describes the conflict gate for both free-text and atomic fields. The old "Org TODO sync" section drops the removed enable/disable hooks and keeps the state-to-keyword mapping as render config, with pearl-edit-state as the way to change state. The intro, the feature list, and the use-package and prefix-key examples lose their stale references. I kept the keyword-cycle-to-state and keyword-derivation work named as planned follow-ups rather than claiming they work, since they're not in yet.
Diffstat (limited to 'README.org')
-rw-r--r--README.org64
1 files changed, 35 insertions, 29 deletions
diff --git a/README.org b/README.org
index 37c2cb6..1ae0131 100644
--- a/README.org
+++ b/README.org
@@ -7,7 +7,7 @@
" /We like lists because we don't want to die./ "
— /Umberto Eco/
-Pearl (backronym: *Pearl Edits and Reflects Linear*) brings Linear issues into Emacs as a working Org file. Fetch your open issues, a project, a Linear Custom View, an ad-hoc filter, or a saved local query; Pearl renders each issue as an Org heading with the description and comments in the body and Linear metadata in a namespaced property drawer. Edit what you need, then push it back explicitly with conflict-aware commands.
+Pearl (backronym: *Pearl Edits and Reflects Linear*) brings Linear issues into Emacs as a working Org file. Fetch your open issues, a project, a Linear Custom View, an ad-hoc filter, or a saved local query; Pearl renders each issue as an Org heading with the description and comments in the body and Linear metadata in a namespaced property drawer. Edit what you need in the buffer, then save the ticket; Pearl reconciles each changed field against the remote with a conflict check.
** Features
:PROPERTIES:
@@ -17,11 +17,11 @@ Pearl (backronym: *Pearl Edits and Reflects Linear*) brings Linear issues into E
- Fetch open issues, project issues, server-side Linear Custom Views, ad-hoc filters, or named local saved queries
- Read issues as an Org outline: title as heading, description in the body, comments as a chronological subtree
- Keep structured fields in =LINEAR-*= properties: id, URL, team, state, priority, assignee, labels, timestamps, and sync hashes
-- Edit descriptions, titles, and your own comments in place, then push them with a three-way conflict check
+- Edit any field -- title, description, priority, state, assignee, labels, your own comments -- in the buffer, then save; Pearl reconciles each change through a conflict gate
- Set priority, state, assignee, and labels by command, using Linear ids behind display-name completion
- Add and delete your own comments, create issues, delete issues, and open the current issue or view in Linear
- Refresh the active view from the source recorded in the file, or refresh one issue at point
-- Optionally sync Org TODO keyword changes back to Linear workflow states
+- Render Linear workflow states as Org TODO keywords
- Use one transient dispatcher, =M-x pearl-menu=, for the whole command surface
- [[file:TESTING.org][Well-tested]] with isolated ERT files, request fixtures, and coverage support
@@ -77,7 +77,7 @@ In =config.el=:
#+begin_src emacs-lisp
(use-package! pearl
:commands (pearl-menu pearl-list-issues pearl-new-issue
- pearl-run-view pearl-enable-org-sync)
+ pearl-run-view pearl-save-issue)
:init
(setq pearl-org-file-path (expand-file-name "gtd/linear.org" org-directory))
:config
@@ -160,7 +160,7 @@ For muscle memory, Pearl also defines an opt-in prefix keymap, =pearl-prefix-map
;; :bind-keymap ("C-; L" . pearl-prefix-map)
#+end_src
-With that prefix, =C-; L s s= saves the ticket at point, =C-; L s a= saves every ticket in the file, =C-; L e p= edits its priority, =C-; L n t= creates a ticket, and =C-; L m= opens the full transient. If you use =which-key=, each step shows a labeled menu.
+With that prefix, =C-; L s s= saves the ticket at point, =C-; L s a= saves every ticket in the file, =C-; L e s= picks its state, =C-; L n t= creates a ticket, and =C-; L m= opens the full transient. If you use =which-key=, each step shows a labeled menu.
Every command is also available directly through =M-x=.
@@ -188,29 +188,40 @@ Sorting is local and deterministic. Query filters are AND-only; use a Linear Cus
*** Editing issues
-All issue commands work from anywhere inside an issue subtree.
+Pearl has one write path. Edit a ticket however you like in the buffer, then save it -- Pearl diffs each field against what it last fetched and pushes only what changed. Nothing pushes the moment you pick a value, and there is no per-field "push" command to remember. All issue commands work from anywhere inside an issue subtree.
-| Command | What it changes |
-|-----------------------------------+--------------------------------------------------|
-| =pearl-sync-current-issue= | Push the edited description body |
-| =pearl-sync-current-issue-title= | Push the edited heading title |
-| =pearl-edit-state= | Pick a workflow state (pushed at next save) |
-| =pearl-edit-assignee= | Pick a team member as assignee (at next save) |
-| =pearl-edit-labels= | Pick labels; empty selection clears (at save) |
-| =pearl-add-comment= | Add a new Linear comment |
-| =pearl-edit-current-comment= | Push edits to one of your own comments |
-| =pearl-delete-current-comment= | Delete one of your own comments after confirming |
-| =pearl-delete-current-issue= | Soft-delete the current issue after confirmation |
-| =pearl-open-current-issue= | Open the issue URL in a browser |
-| =pearl-open-current-view-in-linear= | Open the active view in Linear |
+| Command | What it does |
+|-----------------------------------+-------------------------------------------------------|
+| =pearl-save-issue= | Save every changed field of the ticket at point |
+| =pearl-save-all= | Save every changed ticket in the file (confirms once) |
+| =pearl-edit-description= | Edit the description in a focused compose buffer |
+| =pearl-edit-state= | Pick a workflow state (reaches any of the team's) |
+| =pearl-edit-assignee= | Pick a team member as assignee |
+| =pearl-edit-labels= | Pick labels; an empty selection clears them |
+| =pearl-add-comment= | Add a new Linear comment |
+| =pearl-edit-current-comment= | Edit one of your own comments |
+| =pearl-delete-current-comment= | Delete one of your own comments after confirming |
+| =pearl-delete-current-issue= | Soft-delete the current issue after confirmation |
+| =pearl-open-current-issue= | Open the issue URL in a browser |
+| =pearl-open-current-view-in-linear= | Open the active view in Linear |
-Description, title, and comment pushes use the same conflict gate: unchanged local text sends nothing; a local edit against an unchanged remote pushes; if both local and remote changed since fetch, Pearl refuses to clobber either side and reports the conflict. Destructive conflict choices stash local text in =*pearl-conflict-backup*= first.
+How each field is edited:
-Only comments you authored are editable or deletable. Pearl refuses edits and deletes to comments from another person, a bot, or an integration before making an API call. A comment delete is permanent (Linear has no restore for it), so deleting a comment with unsaved local edits prompts to confirm discarding them.
+- *Title and description* -- type in the heading and the body. =pearl-edit-description= pops a focused compose buffer if you'd rather not edit inline.
+- *Priority* -- the Org priority cookie (=C-c ,=, or =S-up= / =S-down=). =[#A]/[#B]/[#C]/[#D]= are Urgent/High/Medium/Low; no cookie is None.
+- *State* -- =pearl-edit-state=, completing over the team's workflow states.
+- *Assignee and labels* -- =pearl-edit-assignee= / =pearl-edit-labels=, completing over the team's members and labels.
+- *Comments* -- edit your own in place, or =pearl-add-comment= for a new one.
-*** Org TODO sync
+Picking a constrained field writes the value into the buffer and marks it changed; it doesn't push until you save. The display name or label text is there to read -- Pearl reconciles by the underlying id, and a refresh rewrites the display from the remote, so hand-editing the visible name has no effect.
-=pearl-state-to-todo-mapping= maps Linear state names to Org TODO keywords for rendering and optional state sync:
+At save, each field runs through a conflict gate. An unchanged local value sends nothing; a local change against an unmoved remote pushes; if both moved since fetch, Pearl refuses to clobber either side and asks what to do. Free-text fields (description, title, comments) offer an smerge merge; atomic fields (state, priority, assignee, labels) offer use-mine / use-theirs. Destructive text choices stash the local text in =*pearl-conflict-backup*= first.
+
+Only comments you authored are editable or deletable. Pearl refuses edits and deletes to comments from another person, a bot, or an integration before making an API call. A comment delete is permanent (Linear has no restore for it), so deleting one with unsaved local edits prompts to confirm discarding them.
+
+*** State and the TODO keyword
+
+=pearl-state-to-todo-mapping= maps Linear state names to Org TODO keywords, so a fetched issue renders with a keyword you recognize:
#+begin_src emacs-lisp
(setq pearl-state-to-todo-mapping
@@ -222,12 +233,7 @@ Only comments you authored are editable or deletable. Pearl refuses edits and de
("Done" . "DONE")))
#+end_src
-Make sure your =org-todo-keywords= include every keyword you map to. Enable and disable the save/TODO-change hooks with:
-
-#+begin_src emacs-lisp
- (pearl-enable-org-sync)
- (pearl-disable-org-sync)
-#+end_src
+Make sure your =org-todo-keywords= include every keyword you map to. Change a ticket's state with =pearl-edit-state=, which reaches every state on the team -- including ones the keyword set doesn't cover. (Cycling the keyword to drive the state change directly, and deriving the keyword set from the team's real states, are planned follow-ups.)
** The Org File
:PROPERTIES: