summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--docs/plans/2026-04-10-org-refile-design.md70
-rw-r--r--docs/plans/2026-04-10-org-refile-implementation.md108
-rw-r--r--lisp/ss-org.el14
-rw-r--r--tests/ss-org-tests.el88
5 files changed, 285 insertions, 0 deletions
diff --git a/README.md b/README.md
index 408ec5f..da53922 100644
--- a/README.md
+++ b/README.md
@@ -171,6 +171,11 @@ and `WAIT` once the question has been asked and the answer is pending.
Completing an item prompts for a note so the answer can be recorded in the
task log, and those state logs are stored in a `LOGBOOK` drawer.
+Org refile uses the current `org-agenda-files` set as its target space and can
+move entries to any heading within those files. Refile selection uses full
+outline paths, including the file name, so the existing Vertico, Orderless, and
+Marginalia stack can present a clearer path-based destination prompt.
+
The configured capture templates cover:
- journal tasks
diff --git a/docs/plans/2026-04-10-org-refile-design.md b/docs/plans/2026-04-10-org-refile-design.md
new file mode 100644
index 0000000..ae7e881
--- /dev/null
+++ b/docs/plans/2026-04-10-org-refile-design.md
@@ -0,0 +1,70 @@
+# Org Refile Design
+
+## Context
+
+The configuration already has the core minibuffer completion stack in place:
+
+- `lisp/ss-ui.el` enables Vertico, Orderless, and Marginalia globally.
+- `lisp/ss-org.el` owns base Org configuration.
+- `lisp/ss-agenda.el` already discovers `org-agenda-files` from the journal and
+ PARA directories.
+
+The missing piece is Org refile configuration. The requested behavior is to
+refile Org entries to any heading in the current `org-agenda-files`, using a
+single, path-oriented prompt that works well with the existing Vertico-based
+completion UI.
+
+## Options Considered
+
+### 1. Recommended: configure built-in Org refile against `org-agenda-files`
+
+- Use Org's standard `org-refile` command and target machinery.
+- Refresh `org-agenda-files` before refile so the target set stays aligned with
+ the existing agenda discovery rules.
+- Configure path-based completion so Vertico and Orderless present the target
+ list cleanly.
+
+This keeps the workflow conventional, reuses existing repository structure, and
+avoids maintaining a parallel refile implementation.
+
+### 2. Add a custom wrapper command with richer target formatting
+
+- Build a custom candidate list for headings across agenda files.
+- Pass the chosen destination back into Org's refile internals.
+
+This could show more custom metadata, but it duplicates behavior Org already
+provides and increases maintenance cost for little practical gain.
+
+### 3. Add more completion packages just for refile
+
+- Introduce a Vertico extension or a separate package to alter refile prompts.
+
+This adds package surface area without first exhausting the built-in Org and
+completion capabilities already present in the config.
+
+## Chosen Design
+
+Use option 1.
+
+- Configure `org-refile-targets` to use `org-agenda-files` with unrestricted
+ heading depth.
+- Enable outline-path completion so identically named headings are
+ distinguishable by their parent path.
+- Use the direct, path-based completion flow rather than an additional outline
+ navigation step.
+- Refresh `org-agenda-files` before refile by reusing the existing agenda file
+ discovery helper instead of copying the directory rules.
+- Keep the change inside the existing module boundaries: Org behavior in
+ `lisp/ss-org.el`, with a UI tweak in `lisp/ss-ui.el` only if the current
+ completion categories need one.
+
+## Verification
+
+- Add focused ERT coverage for the refile setup helper and variable values.
+- Run `emacs --batch -Q --load ./init.el` from the repository root.
+
+## Documentation Impact
+
+`README.md` should be updated to describe that Org refile targets any heading in
+`org-agenda-files` and uses the configured minibuffer completion stack for
+path-based target selection. \ No newline at end of file
diff --git a/docs/plans/2026-04-10-org-refile-implementation.md b/docs/plans/2026-04-10-org-refile-implementation.md
new file mode 100644
index 0000000..687e86d
--- /dev/null
+++ b/docs/plans/2026-04-10-org-refile-implementation.md
@@ -0,0 +1,108 @@
+# Org Refile Implementation Plan
+
+> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Configure Org refile so entries can be moved to any heading in `org-agenda-files` with a clearer Vertico-friendly path-based prompt.
+
+**Architecture:** Reuse the existing agenda discovery helper to keep `org-agenda-files` current before refile, then configure built-in Org refile variables in `lisp/ss-org.el`. Add narrow ERT coverage for the helper and settings, and update `README.md` so the documented workflow matches the configuration.
+
+**Tech Stack:** Emacs Lisp, Org mode, ERT, Vertico, Orderless, Marginalia
+
+---
+
+## Chunk 1: Refile Configuration
+
+### Task 1: Add refile refresh helper and Org settings
+
+**Files:**
+- Modify: `lisp/ss-org.el`
+- Reference: `lisp/ss-agenda.el`
+
+- [ ] **Step 1: Write the failing test**
+
+Add ERT coverage that loads `ss-org`, stubs agenda refresh behavior, runs the
+new setup helper, and asserts:
+
+- `org-refile-targets` points at `org-agenda-files`
+- unlimited heading depth is enabled
+- outline-path completion is enabled
+- the direct path completion flow is selected
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Run: `emacs --batch -Q -L lisp -l tests/ss-org-tests.el -f ert-run-tests-batch-and-exit`
+Expected: FAIL because the refile helper and settings do not exist yet.
+
+- [ ] **Step 3: Write minimal implementation**
+
+- Add a small helper in `lisp/ss-org.el` that refreshes `org-agenda-files`
+ before refile, reusing `ss-refresh-org-agenda-files` when available.
+- Configure Org refile variables during `ss-org-setup`.
+
+- [ ] **Step 4: Run test to verify it passes**
+
+Run: `emacs --batch -Q -L lisp -l tests/ss-org-tests.el -f ert-run-tests-batch-and-exit`
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+Wait for verification and user approval before creating a commit.
+
+## Chunk 2: Documentation and Startup Verification
+
+### Task 2: Document the refile workflow
+
+**Files:**
+- Modify: `README.md`
+
+- [ ] **Step 1: Write the failing test**
+
+The failing condition is documentation drift: the README currently does not
+describe refile behavior.
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Review `README.md` and confirm it lacks refile documentation.
+
+- [ ] **Step 3: Write minimal implementation**
+
+- Add a short note describing that Org refile targets any heading in
+ `org-agenda-files` and uses path-based minibuffer completion.
+
+- [ ] **Step 4: Run test to verify it passes**
+
+Review the updated text against the code for accuracy.
+
+- [ ] **Step 5: Commit**
+
+Wait for verification and user approval before creating a commit.
+
+### Task 3: Verify the full startup path
+
+**Files:**
+- Verify only
+
+- [ ] **Step 1: Write the failing test**
+
+Use the repository's normal startup verification path in addition to the new
+targeted ERT coverage.
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Not applicable before implementation.
+
+- [ ] **Step 3: Write minimal implementation**
+
+No code changes in this task.
+
+- [ ] **Step 4: Run test to verify it passes**
+
+Run: `emacs --batch -Q --load ./init.el`
+Expected: exits successfully.
+
+Run: `emacs --batch -Q -L lisp -l tests/ss-org-tests.el -f ert-run-tests-batch-and-exit`
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+Wait for verification and user approval before creating a commit. \ No newline at end of file
diff --git a/lisp/ss-org.el b/lisp/ss-org.el
index 6f1ae1b..88590fa 100644
--- a/lisp/ss-org.el
+++ b/lisp/ss-org.el
@@ -243,6 +243,19 @@ This is for significant navigation points only and clears forward history."
(interactive)
(find-file (ss-require-existing-file ss-moc-file)))
+(defun ss-org-refresh-agenda-files-for-refile (&rest _args)
+ "Refresh `org-agenda-files' before refile when agenda setup is available."
+ (when (fboundp 'ss-refresh-org-agenda-files)
+ (ss-refresh-org-agenda-files)))
+
+(defun ss-org-configure-refile ()
+ "Configure Org refile to target any heading in `org-agenda-files'."
+ (setq org-refile-targets '((org-agenda-files :regexp . "^\\*+ "))
+ org-refile-use-outline-path 'file
+ org-outline-path-complete-in-steps nil)
+ (unless (advice-member-p #'ss-org-refresh-agenda-files-for-refile #'org-refile)
+ (advice-add 'org-refile :before #'ss-org-refresh-agenda-files-for-refile)))
+
(defun ss-org-setup ()
"Initialize base Org configuration."
(use-package org
@@ -259,6 +272,7 @@ This is for significant navigation points only and clears forward history."
"DONE(d)" "CANCELLED(x@)"))
org-log-done 'note
org-log-into-drawer t)
+ (ss-org-configure-refile)
(add-hook 'org-mode-hook
(lambda ()
(setq-local org-hide-emphasis-markers t)
diff --git a/tests/ss-org-tests.el b/tests/ss-org-tests.el
new file mode 100644
index 0000000..b8e1eb5
--- /dev/null
+++ b/tests/ss-org-tests.el
@@ -0,0 +1,88 @@
+;;; ss-org-tests.el --- Tests for ss-org -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Focused ERT coverage for Org refile configuration.
+
+;;; Code:
+
+(add-to-list 'load-path (expand-file-name "../lisp" (file-name-directory load-file-name)))
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'ss-org)
+
+(ert-deftest ss-org-configure-refile-sets-org-variables ()
+ (let ((old-refile-targets (and (boundp 'org-refile-targets)
+ org-refile-targets))
+ (old-refile-use-outline-path (and (boundp 'org-refile-use-outline-path)
+ org-refile-use-outline-path))
+ (old-outline-path-complete-in-steps
+ (and (boundp 'org-outline-path-complete-in-steps)
+ org-outline-path-complete-in-steps)))
+ (unwind-protect
+ (progn
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile)
+ (setq org-refile-targets nil
+ org-refile-use-outline-path nil
+ org-outline-path-complete-in-steps t)
+ (ss-org-configure-refile)
+ (should (equal org-refile-targets
+ '((org-agenda-files :regexp . "^\\*+ "))))
+ (should (eq org-refile-use-outline-path 'file))
+ (should-not org-outline-path-complete-in-steps))
+ (setq org-refile-targets old-refile-targets
+ org-refile-use-outline-path old-refile-use-outline-path
+ org-outline-path-complete-in-steps
+ old-outline-path-complete-in-steps)
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile))))
+
+(ert-deftest ss-org-configure-refile-discovers-headings-in-agenda-files ()
+ (let* ((file (make-temp-file "ss-org-refile-" nil ".org"
+ "* Alpha\n** Beta\n"))
+ (file-name (file-name-nondirectory file))
+ (org-agenda-files (list file))
+ (old-refile-targets (and (boundp 'org-refile-targets)
+ org-refile-targets))
+ (old-refile-use-outline-path (and (boundp 'org-refile-use-outline-path)
+ org-refile-use-outline-path))
+ (old-outline-path-complete-in-steps
+ (and (boundp 'org-outline-path-complete-in-steps)
+ org-outline-path-complete-in-steps))
+ targets)
+ (unwind-protect
+ (progn
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile)
+ (ss-org-configure-refile)
+ (with-current-buffer (find-file-noselect file)
+ (setq targets (org-refile-get-targets)))
+ (should (assoc (format "%s/Alpha" file-name) targets))
+ (should (assoc (format "%s/Alpha/Beta" file-name) targets)))
+ (setq org-refile-targets old-refile-targets
+ org-refile-use-outline-path old-refile-use-outline-path
+ org-outline-path-complete-in-steps
+ old-outline-path-complete-in-steps)
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile)
+ (when-let ((buffer (get-file-buffer file)))
+ (kill-buffer buffer))
+ (delete-file file))))
+
+(ert-deftest ss-org-configure-refile-adds-refresh-advice ()
+ (unwind-protect
+ (progn
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile)
+ (ss-org-configure-refile)
+ (should (advice-member-p #'ss-org-refresh-agenda-files-for-refile
+ 'org-refile)))
+ (advice-remove 'org-refile #'ss-org-refresh-agenda-files-for-refile)))
+
+(ert-deftest ss-org-refresh-agenda-files-for-refile-reuses-agenda-helper ()
+ (let (called)
+ (cl-letf (((symbol-function 'ss-refresh-org-agenda-files)
+ (lambda (&rest _args)
+ (setq called t))))
+ (ss-org-refresh-agenda-files-for-refile)
+ (should called))))
+
+;;; ss-org-tests.el ends here \ No newline at end of file