summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ai-prompts/quality-engineer.org114
-rw-r--r--modules/org-agenda-config.el27
-rw-r--r--modules/org-gcal-config.el16
-rwxr-xr-xscripts/delete-elisp-compiled-files.sh23
4 files changed, 144 insertions, 36 deletions
diff --git a/ai-prompts/quality-engineer.org b/ai-prompts/quality-engineer.org
index d6bb7ecb..385dbec1 100644
--- a/ai-prompts/quality-engineer.org
+++ b/ai-prompts/quality-engineer.org
@@ -638,6 +638,119 @@ Don't duplicate coverage:
- Format test cases as clear, numbered lists within each category
- Focus on practical, implementable tests that catch real-world bugs
+## Test Future-Proofing & Time-Based Testing
+
+*** Dynamic Timestamp Generation
+When tests involve dates and times:
+- **Never hardcode dates** in tests (e.g., "2025-10-24")
+ - Tests will fail when that date passes
+ - Creates maintenance burden (annual test updates)
+ - Makes tests brittle and non-deterministic over time
+
+- **Use dynamic timestamp generation** instead:
+ - Create test utilities that generate timestamps relative to "now"
+ - Examples: `test-time-today-at`, `test-time-tomorrow-at`, `test-time-days-from-now`
+ - Base time can be offset into future to avoid past-date edge cases
+
+- **Mock time via function substitution**:
+ - Replace `current-time` with test-controlled time source
+ - Use macros like `with-test-time` to scope time mocking
+ - Preserves existing function mocking patterns (don't nest cl-letf*)
+
+- **Pattern for time-based tests**:
+ #+begin_src elisp
+ (let* ((now (test-time-today-at 14 0))
+ (event-time (test-time-today-at 14 10))
+ (timestamp-str (test-timestamp-string event-time)))
+ (with-test-time now
+ (cl-letf (((symbol-function 'other-mock) (lambda () ...)))
+ (let* ((result (function-under-test event-time)))
+ (should (equal expected result))))))
+ #+end_src
+
+- **Benefits of dynamic timestamps**:
+ - Tests work indefinitely without modification
+ - Time relationships remain consistent (10 minutes = 10 minutes)
+ - Clearer test intent (relative times vs absolute dates)
+ - Easier to understand what's being tested
+ - No test failures from date expiration
+
+*** Large-Scale Test Refactoring Strategy
+When refactoring many test files (e.g., removing hardcoded timestamps):
+
+**** Strategic Planning
+- **Tackle biggest challenges first** - Largest/most complex files
+ - Eliminates intimidation factor early
+ - Makes remaining work feel manageable
+ - Builds confidence and momentum
+ - Subsequent files feel easier by comparison
+
+- **Establish consistent patterns early**:
+ - First few files set the template
+ - Document patterns in commit messages
+ - Later files follow established conventions
+ - Reduces decision fatigue
+
+- **Identify files that don't need refactoring**:
+ - Not all timestamp references require changes
+ - Copyright dates, version numbers: leave alone
+ - Tests without time dependencies: skip
+ - Focus effort where it matters
+
+**** Execution Approach
+- **Maintain 100% test pass rate throughout**:
+ - Run tests after each file refactored
+ - Never commit broken tests
+ - Validates refactoring correctness immediately
+ - Builds trust in the process
+
+- **Work in batches but commit individually**:
+ - Refactor similar test patterns together
+ - But commit each file separately with detailed message
+ - Makes git history navigable
+ - Easier to revert specific changes if needed
+
+- **Use validation infrastructure**:
+ - Pre-commit hooks catch syntax errors
+ - Makefile targets (`make validate`, `make test-file`)
+ - Automated checks prevent regressions
+
+- **Document patterns in commits**:
+ - Before/after examples in commit messages
+ - List of changes made
+ - Pattern transformations explained
+ - Future maintainers understand the approach
+
+**** Project Management Lessons
+- **Track progress visibly**:
+ - Use todo lists to show what's done vs remaining
+ - Update counts: "16/23 files (70%) complete"
+ - Provides motivation and clarity
+
+- **Celebrate milestones**:
+ - Acknowledge when biggest challenges complete
+ - Note when majority threshold reached (>50%)
+ - Recognize systematic progress
+
+- **Know when you're done**:
+ - Some files genuinely don't need changes
+ - Don't force work where it doesn't apply
+ - Project can complete before all files touched
+
+**** Real-World Example: chime.el Timestamp Refactoring
+Project scope: 23 test files, 339 tests total
+- 16 files needed timestamp refactoring (251 tests)
+- 7 files had no timestamp dependencies (88 tests)
+- Completed over multiple sessions maintaining 100% pass rate
+- Strategic approach: Always chose largest remaining file
+- Result: Future-proof test suite, tests never expire
+
+Key insight: **Not all refactoring projects require touching every file**
+- Analyze which files actually need work
+- Skip files without relevant issues
+- Focus effort on high-impact changes
+- Don't let perfectionism create unnecessary work
+
## Red Flags
Watch for and report these issues:
@@ -652,3 +765,4 @@ Watch for and report these issues:
- Flaky tests that pass/fail intermittently
- Tests that are too slow
- Tests that require manual setup or verification
+- **Hardcoded dates in tests** - Will fail when dates pass, creates maintenance burden
diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el
index 30f4606c..211ff4fe 100644
--- a/modules/org-agenda-config.el
+++ b/modules/org-agenda-config.el
@@ -54,7 +54,7 @@
(window-height . fit-window-to-buffer)))
;; reset s-left/right each time org-agenda is enabled
- (add-hook 'org-agenda-mode-hook (lambda ()
+s (add-hook 'org-agenda-mode-hook (lambda ()
(local-set-key (kbd "s-<right>") #'org-agenda-todo-nextset)
(local-set-key (kbd "s-<left>")
#'org-agenda-todo-previousset)))
@@ -68,7 +68,6 @@
(defun cj/add-files-to-org-agenda-files-list (directory)
"Search for files named \\='todo.org\\=' add them to org-project-files.
-
DIRECTORY is a string of the path to begin the search."
(interactive "D")
(setq org-agenda-files
@@ -81,10 +80,9 @@ DIRECTORY is a string of the path to begin the search."
;; agenda targets is the schedule, contacts, project todos,
;; inbox, and org roam projects.
(defun cj/build-org-agenda-list ()
- "Rebuilds the org agenda list without checking org-roam for projects.
-
+ "Rebuilds the org agenda list.
Begins with the inbox-file, schedule-file, and contacts-file.
-Then adds all todo.org files from projects-dir and code-dir.
+Then adds all todo.org files from projects-dir.
Reports elapsed time in the messages buffer."
(interactive)
(let ((start-time (current-time)))
@@ -105,7 +103,6 @@ Reports elapsed time in the messages buffer."
(defun cj/todo-list-all-agenda-files ()
"Displays an \\='org-agenda\\=' todo list.
-
The contents of the agenda will be built from org-project-files and org-roam
files that have project in their filetag."
(interactive)
@@ -118,7 +115,6 @@ files that have project in their filetag."
(defun cj/todo-list-from-this-buffer ()
"Displays an \\='org-agenda\\=' todo list built from the current buffer.
-
If the current buffer isn't an org buffer, inform the user."
(interactive)
(if (eq major-mode 'org-mode)
@@ -153,7 +149,6 @@ If the current buffer isn't an org buffer, inform the user."
(defun cj/org-agenda-skip-subtree-if-not-overdue ()
"Skip an agenda subtree if it is not an overdue deadline or scheduled task.
-
An entry is considered overdue if it has a scheduled or deadline date strictly
before today, is not marked as done, and is not a habit."
(let* ((subtree-end (save-excursion (org-end-of-subtree t)))
@@ -176,7 +171,6 @@ before today, is not marked as done, and is not a habit."
(defun cj/org-skip-subtree-if-priority (priority)
"Skip an agenda subtree if it has a priority of PRIORITY.
-
PRIORITY may be one of the characters ?A, ?B, or ?C."
(let ((subtree-end (save-excursion (org-end-of-subtree t)))
(pri-value (* 1000 (- org-lowest-priority priority)))
@@ -187,7 +181,6 @@ PRIORITY may be one of the characters ?A, ?B, or ?C."
(defun cj/org-skip-subtree-if-keyword (keywords)
"Skip an agenda subtree if it has a TODO keyword in KEYWORDS.
-
KEYWORDS must be a list of strings."
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(if (member (org-get-todo-state) keywords)
@@ -224,12 +217,10 @@ KEYWORDS must be a list of strings."
(defun cj/main-agenda-display ()
"Display the main daily org-agenda view.
-
This uses all org-agenda targets and presents three sections:
- All unfinished priority A tasks
- Today's schedule, including habits with consistency graphs
- All priority B and C unscheduled/undeadlined tasks
-
The agenda is rebuilt from all sources before display, including:
- inbox-file and schedule-file
- Org-roam nodes tagged as \"Project\"
@@ -263,9 +254,11 @@ This allows a line to show in an agenda without being scheduled or a deadline."
;; Install CHIME from GitHub using use-package :vc (Emacs 29+)
(use-package chime
- :vc (:url "https://github.com/cjennings/chime.el" :rev :newest)
- :after (alert org-agenda)
:demand t
+ ;; :vc (:url "https://github.com/cjennings/chime.el" :rev :newest) ;; using latest on github
+ :after (alert org-agenda)
+ :ensure nil ;; using local version
+ :load-path "~/code/chime.el"
:bind
("C-c A" . chime-check)
:config
@@ -275,9 +268,13 @@ This allows a line to show in an agenda without being scheduled or a deadline."
;; Modeline display: show upcoming events within 2 hours
(setq chime-enable-modeline t)
- (setq chime-modeline-lookahead 120)
+ (setq chime-modeline-lookahead 180)
(setq chime-modeline-format " ⏰ %s")
+ ;; Tooltip settings: show up to 20 upcoming events (regardless of how far in future)
+ ;; chime-modeline-tooltip-lookahead defaults to 525600 (1 year) - effectively unlimited
+ (setq chime-modeline-tooltip-max-events 20)
+
;; Modeline content: show title and countdown only (omit event time)
(setq chime-notification-text-format "%t (%u)")
diff --git a/modules/org-gcal-config.el b/modules/org-gcal-config.el
index b3c63663..0c309a0e 100644
--- a/modules/org-gcal-config.el
+++ b/modules/org-gcal-config.el
@@ -109,7 +109,21 @@ enabling bidirectional sync so changes push back to Google Calendar."
(setq org-gcal-local-timezone (cj/detect-system-timezone))
;; Reload client credentials (should already be loaded by org-gcal, but ensure it's set)
- (org-gcal-reload-client-id-secret))
+ (org-gcal-reload-client-id-secret)
+
+ ;; Auto-save gcal files after sync completes
+ (defun cj/org-gcal-save-files-after-sync (&rest _)
+ "Save all org-gcal files after sync completes."
+ (dolist (entry org-gcal-fetch-file-alist)
+ (let* ((file (cdr entry))
+ (buffer (get-file-buffer file)))
+ (when (and buffer (buffer-modified-p buffer))
+ (with-current-buffer buffer
+ (save-buffer)
+ (message "Saved %s after org-gcal sync" (file-name-nondirectory file)))))))
+
+ ;; Advise org-gcal--sync-unlock which is called when sync completes
+ (advice-add 'org-gcal--sync-unlock :after #'cj/org-gcal-save-files-after-sync))
;; Set up automatic initial sync on boot with error handling
;;(run-with-idle-timer
diff --git a/scripts/delete-elisp-compiled-files.sh b/scripts/delete-elisp-compiled-files.sh
index b64a6540..5976c92b 100755
--- a/scripts/delete-elisp-compiled-files.sh
+++ b/scripts/delete-elisp-compiled-files.sh
@@ -2,23 +2,6 @@
location=$HOME/.emacs.d/
-echo ""; echo "You are about to delete emacs lisp compiled files (.eln and .elc) recursively from $location";
-
-# Show the files it will delete
-echo "The following files will be deleted:"
-find $location -type f \( -name "*.eln" -o -name "*.elc" \) -print
-
-
-echo ""; echo ""
-read -p "Are you sure you want to continue? (y/n) " -n 1 -r
-echo # move to a new line
-if [[ $REPLY =~ ^[Yy]$ ]]
-then
- echo "Deleting files..."
- find $location -type f \( -name "*.eln" -o -name "*.elc" \) -exec rm -f {} +
- echo "Files deleted."
-else
- echo "Operation cancelled."
-fi
-
-echo ""; echo ""
+echo "Deleting emacs lisp compiled files (.eln and .elc) from $location..."
+find $location -type f \( -name "*.eln" -o -name "*.elc" \) -exec rm -f {} +
+echo "Done."