161 lines
6.5 KiB
Org Mode
161 lines
6.5 KiB
Org Mode
:PROPERTIES:
|
|
:ID: 97d0945e-9405-4da8-82e1-0e742063a99a
|
|
:CREATED: [2026-03-31 Tue 16:14]
|
|
:EDITED: [2026-04-07 Tue 13:42]
|
|
:END:
|
|
#+TITLE: SKILL: Org-GTD Archive Roam Daily (Universal Literate Note)
|
|
#+STARTUP: content
|
|
#+FILETAGS: :emacs:gtd:roam:archiving:psf:
|
|
|
|
* Overview
|
|
The *Org-GTD Archive Roam Daily* skill enables chronological archiving of completed GTD tasks. Instead of a flat archive file, tasks are moved to their respective `org-roam-dailies` files based on their `:CREATED:` property, preserving contextual and temporal integrity.
|
|
|
|
* Phase A: Demand (PRD)
|
|
:PROPERTIES:
|
|
:STATUS: FROZEN
|
|
:END:
|
|
|
|
** 1. Purpose
|
|
Define the requirements for chronologically-aware task archiving.
|
|
|
|
** 2. User Needs
|
|
- *Temporal Alignment:* Archive tasks to the daily file matching their creation date.
|
|
- *Context Preservation:* Maintain all properties and sub-elements during the move.
|
|
- *Robust Extraction:* Correctly parse `:CREATED:` property timestamps.
|
|
- *Fail-safe Logic:* Default to current date if `:CREATED:` is missing (with a warning).
|
|
|
|
** 3. Success Criteria
|
|
*** TODO Successful extraction of [YYYY-MM-DD] from :CREATED:
|
|
*** TODO Automated creation of non-existent daily files during archive
|
|
*** TODO Subtree relocation verification
|
|
|
|
|
|
* Phase B: Blueprint (PROTOCOL)
|
|
:PROPERTIES:
|
|
:STATUS: SIGNED
|
|
:END:
|
|
|
|
|
|
* Phase B: Blueprint (PROTOCOL)
|
|
:PROPERTIES:
|
|
:STATUS: DRAFT
|
|
:END:
|
|
|
|
** 1. Architectural Intent
|
|
The system will utilize Emacs Lisp to extract the `:CREATED:` property from a GTD task, derive a date, locate (or create) the corresponding `org-roam-daily` file, and move the task subtree to that file. Error handling will include a fallback mechanism to the current date, accompanied by a user notification. The design emphasizes modularity and testability.
|
|
|
|
** 2. Semantic Interfaces (Lisp Signatures)
|
|
|
|
- `(org-gtd-archive-roam-daily &optional subtree)`
|
|
- *Purpose:* Main entry point. Archives a GTD task to its corresponding `org-roam-daily` file based on the `:CREATED:` property.
|
|
- *Arguments:*
|
|
- `subtree` (optional): The subtree to archive. Defaults to the current subtree.
|
|
- *Return Value:* `t` on success, `nil` on failure.
|
|
- *Side Effects:* Modifies Org files.
|
|
|
|
- `(org-gtd-archive-roam-daily-extract-created-date subtree)`
|
|
- *Purpose:* Extracts the date from the `:CREATED:` property of a subtree.
|
|
- *Arguments:*
|
|
- `subtree`: The subtree to extract the date from.
|
|
- *Return Value:* A string in `YYYY-MM-DD` format, or `nil` if the property is missing or invalid.
|
|
|
|
- `(org-gtd-archive-roam-daily-get-daily-file-path date)`
|
|
- *Purpose:* Determines the file path for a given date, according to `org-roam-dailies` conventions.
|
|
- *Arguments:*
|
|
- `date`: A string in `YYYY-MM-DD` format.
|
|
- *Return Value:* A string representing the file path.
|
|
|
|
- `(org-gtd-archive-roam-daily-ensure-daily-file-exists file-path)`
|
|
- *Purpose:* Creates an `org-roam-daily` file if it does not already exist.
|
|
- *Arguments:*
|
|
- `file-path`: The path to the `org-roam-daily` file.
|
|
- *Return Value:* `t` if the file exists (either originally or after creation), `nil` if creation failed.
|
|
- *Side Effects:* Creates a new file.
|
|
|
|
- `(org-gtd-archive-roam-daily-move-subtree subtree file-path)`
|
|
- *Purpose:* Moves a subtree to a specified file.
|
|
- *Arguments:*
|
|
- `subtree`: The subtree to move.
|
|
- `file-path`: The destination file path.
|
|
- *Return Value:* `t` on success, `nil` on failure.
|
|
- *Side Effects:* Modifies Org files.
|
|
|
|
- `(org-gtd-archive-roam-daily-warn message)`
|
|
- *Purpose:* Displays a warning message to the user.
|
|
- *Arguments:*
|
|
- `message`: The warning message to display.
|
|
- *Return Value:* `nil`
|
|
- *Side Effects:* Displays a message.
|
|
|
|
|
|
* Implementation
|
|
|
|
** Emacs Lisp Logic (org-gtd-archive-roam-daily.el)
|
|
#+begin_src elisp :tangle projects/org-gtd-archive-roam-daily/org-gtd-archive-roam-daily.el
|
|
(require 'org-roam-dailies)
|
|
(require 'org-element)
|
|
(require 'org-time)
|
|
|
|
(defun amero-get-org-heading-created-property ()
|
|
"Extract the :CREATED: property from the current Org heading.
|
|
Returns a time string or nil if not found."
|
|
(interactive)
|
|
(save-excursion
|
|
(org-back-to-heading t)
|
|
(org-entry-get (point) "CREATED")))
|
|
|
|
(defun amero-parse-created-timestamp (timestamp-string)
|
|
"Parse an Org-mode timestamp string like '[2026-03-16 Mon 14:05]'
|
|
into an Emacs internal time object.
|
|
Returns nil if parsing fails."
|
|
(ignore-errors
|
|
(org-time-string-to-time timestamp-string)))
|
|
|
|
(defun amero-get-daily-note-file (time-object)
|
|
"Get the Org-roam daily note file for a given Emacs TIME-OBJECT.
|
|
Creates the file if it doesn't exist. Returns the file path."
|
|
(let* ((date-string (format-time-string org-roam-dailies-capture-templates-date-format time-object))
|
|
(file-path (expand-file-name (concat date-string ".org")
|
|
(expand-file-name org-roam-dailies-directory org-roam-directory))))
|
|
;; Ensure the directory exists
|
|
(unless (file-exists-p (file-name-directory file-path))
|
|
(make-directory (file-name-directory file-path) t))
|
|
;; Create file if it doesn't exist
|
|
(unless (file-exists-p file-path)
|
|
(with-temp-buffer
|
|
(insert (format "#+title: %s\n" date-string))
|
|
(write-file file-path)))
|
|
file-path))
|
|
|
|
(defun org-gtd-archive-roam-daily ()
|
|
"Archive the current Org heading to an Org-roam daily file
|
|
based on its :CREATED: property."
|
|
(interactive)
|
|
(unless (org-before-first-heading-p (point))
|
|
(user-error "Point is not on an Org heading or within an Org file."))
|
|
|
|
(let* ((created-timestamp-string (amero-get-org-heading-created-property))
|
|
(created-time-object (and created-timestamp-string
|
|
(amero-parse-created-timestamp created-timestamp-string)))
|
|
(heading-start (save-excursion (org-back-to-heading t) (point)))
|
|
(heading-end (save-excursion (org-end-of-subtree t) (point)))
|
|
(heading-content (buffer-substring-no-properties heading-start heading-end))
|
|
daily-file-path)
|
|
|
|
(unless created-time-object
|
|
(user-error "No date error: Heading is missing a valid :CREATED: property."))
|
|
|
|
(setq daily-file-path (amero-get-daily-note-file created-time-object))
|
|
|
|
(with-current-buffer (find-file-noselect daily-file-path)
|
|
(goto-char (point-max))
|
|
(insert "\n\n" heading-content)
|
|
(save-buffer))
|
|
|
|
;; Remove the original heading
|
|
(delete-region heading-start heading-end)
|
|
(message "Archived heading to %s" daily-file-path)))
|
|
|
|
(provide 'org-gtd-archive-roam-daily)
|
|
#+end_src
|