From 445a0be315f3d6fff585f1558262fb5dacd10d12 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Fri, 31 Jul 2026 11:04:42 -0500 Subject: feat(agenda): refresh the render cache on a timer, without the daemon The surface reading this has to stay correct while Emacs is down, and emacsclient is exactly the thing that cannot. So the writer is a batch Emacs and a user timer runs it every five minutes. - scripts/agenda-render-cache writes the cache from a batch Emacs. It resolves the agenda file list through the config's own resolver rather than restating it, so adding a calendar source stays a one-place change. - The timer uses OnCalendar so Persistent actually applies. On a monotonic schedule it is silently ignored, and a machine that slept would come back to a stale file with nothing to trigger a catch-up. - The window is now three days, yesterday through tomorrow. A consumer drawing a rolling window centred on now has nothing to draw for the part of its span outside today, which late in the evening is half the surface. The keyword list moved to user-constants, where a batch Emacs can reach it without this config's package dependencies. Without it org did not recognise DOING or VERIFY, so it stopped parsing those headlines as tasks at all: the keyword and priority cookie stayed glued to the front of every title and each row reported no keyword and not-done. The file parsed cleanly and was wrong, which is the failure worth guarding. The bats tests run against a fixture and this checkout, not the machine's real agenda and installed config. Asserting over live data let the same suite pass on a day that happened to have no keyworded entries. --- scripts/agenda-render-cache | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100755 scripts/agenda-render-cache (limited to 'scripts') diff --git a/scripts/agenda-render-cache b/scripts/agenda-render-cache new file mode 100755 index 00000000..b1e4d490 --- /dev/null +++ b/scripts/agenda-render-cache @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Write the agenda render cache for an external renderer. +# +# Runs a batch Emacs rather than talking to the daemon, because the surface +# that reads the cache has to keep working while Emacs is down -- and +# emacsclient is exactly the thing that cannot. Nothing here touches a running +# Emacs, so it is safe to fire from a timer alongside an active session. +# +# The agenda file list is resolved the same way the editor resolves it, by +# loading the config's own resolver rather than restating the list here. A +# second copy of that list would drift the first time a calendar source is +# added. +# +# Usage: agenda-render-cache +# Env: EMACS_D -- config directory (default ~/.emacs.d) +# EMACS -- emacs binary (default emacs) +# AGENDA_RENDER_FILES -- colon-separated org files to read instead of +# the configured agenda list. Lets a caller ask +# about a known set, and lets the tests run +# against a fixture rather than whatever happens +# to be on the machine's real agenda today. + +set -euo pipefail + +EMACS_D="${EMACS_D:-$HOME/.emacs.d}" +EMACS="${EMACS:-emacs}" + +if [ ! -d "$EMACS_D/modules" ]; then + echo "agenda-render-cache: no modules directory at $EMACS_D/modules" >&2 + exit 1 +fi + +# -Q keeps the daemon's init out of it: this needs three modules, not a full +# editor. load-prefer-newer stops a stale .elc from answering for changed +# source, which would silently write yesterday's logic. +exec "$EMACS" --batch -Q \ + --eval '(setq load-prefer-newer t)' \ + -L "$EMACS_D/modules" \ + --eval '(progn + (require (quote user-constants)) + (require (quote org-agenda-config)) + (setq org-todo-keywords cj/org-todo-keywords) + (require (quote agenda-query)) + (setq org-agenda-files + (let ((override (getenv "AGENDA_RENDER_FILES"))) + (if (and override (not (string-empty-p override))) + (split-string override ":" t) + (cj/--org-agenda-scan-files)))) + (princ (cj/agenda-render-cache-update)) + (terpri))' -- cgit v1.2.3