From a6b534ff6c8c998d940fc3bd201f236c182c8bb3 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 2 Jul 2026 05:58:03 -0400 Subject: fix(page): pages are info-level, not alarm-red Craig's verdict on the all-red page styling: it reads like the system is about to crash. page-me and the work-the-backlog end-of-set page now use notify info --persist, still persistent and audible, never crash-scary. status-check's success and fail notifications keep their types, since a job outcome isn't a page. The commit also carries the two loop-filed task records and the archive sweep counterpart from earlier tonight. --- .ai/workflows/page-me.org | 26 +++++------ .ai/workflows/work-the-backlog.org | 4 +- archive/task-archive.org | 54 ++++++++++++++++++++++ claude-templates/.ai/workflows/page-me.org | 26 +++++------ .../.ai/workflows/work-the-backlog.org | 4 +- todo.org | 18 ++++++++ 6 files changed, 102 insertions(+), 30 deletions(-) diff --git a/.ai/workflows/page-me.org b/.ai/workflows/page-me.org index 607ed51..8069830 100644 --- a/.ai/workflows/page-me.org +++ b/.ai/workflows/page-me.org @@ -5,9 +5,9 @@ * Overview -This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with alarm icon) and persist until manually dismissed. +This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with the blue info icon) and persist until manually dismissed. -Uses the =notify= command (alarm type) for consistent notifications across all AI workflows. +Uses the =notify= command (info type) for consistent notifications across all AI workflows. Info-level on purpose: the earlier alarm styling read as all-red urgency, and Craig's verdict was that a page "should be a persistent info notification" — noticeable, never crash-scary (2026-07-02). * Trigger Phrase @@ -63,8 +63,8 @@ Craig tells Claude when and why: Claude schedules the alarm using the =at= daemon with =notify=: #+begin_src bash -echo "notify alarm 'Page' 'Time to call the dentist' --persist" | at 3:30pm -echo "notify alarm 'Page' 'Meeting starts' --persist" | at now + 45 minutes +echo "notify info 'Page' 'Time to call the dentist' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at now + 45 minutes #+end_src The =at= daemon: @@ -89,26 +89,26 @@ Craig dismisses the notification and acts on it. ** Setting Alarms -Use the =at= daemon to schedule a =notify alarm= command: +Use the =at= daemon to schedule a =notify info= command: #+begin_src bash # Schedule for specific time -echo "notify alarm 'Page' 'Meeting starts' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at 3:30pm # Schedule for relative time -echo "notify alarm 'Page' 'Check the build' --persist" | at now + 30 minutes +echo "notify info 'Page' 'Check the build' --persist" | at now + 30 minutes # Schedule for tomorrow -echo "notify alarm 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow +echo "notify info 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow #+end_src ** Notification System -Uses the =notify= command with the =alarm= type. The =notify= command provides 8 notification types with matching icons and sounds. +Uses the =notify= command with the =info= type. The =notify= command provides 8 notification types with matching icons and sounds. #+begin_src bash -# Immediate alarm notification (for testing) -notify alarm "Page" "Your message here" --persist +# Immediate page notification (for testing) +notify info "Page" "Your message here" --persist #+end_src The =--persist= flag keeps the notification on screen until manually dismissed. All page-me notifications should use =--persist= by default. @@ -139,10 +139,10 @@ The alarm must fire. Use the =at= daemon which is designed for exactly this purp Simple invocation - Claude runs one command. No complex setup required per alarm. ** Fail Audibly -If the alarm fails to schedule, report the error clearly. Don't fail silently. +If the page fails to schedule, report the error clearly. Don't fail silently. ** Testable -The =notify alarm= command can be called directly to verify notifications work without waiting for a timer. +The =notify info= command can be called directly to verify notifications work without waiting for a timer. ** Non-Alarming Use normal urgency, not critical. The notification should be noticeable but not imply something has gone horribly wrong. diff --git a/.ai/workflows/work-the-backlog.org b/.ai/workflows/work-the-backlog.org index 642162d..b0666e7 100644 --- a/.ai/workflows/work-the-backlog.org +++ b/.ai/workflows/work-the-backlog.org @@ -149,10 +149,10 @@ Task boundaries are clean boundaries by construction: the previous task is close With paging on, fire one page when the set is done or the cap is hit — end-of-set only, never per-task: #+begin_src sh -notify alarm "Page" ": done, remaining — " --persist +notify info "Page" ": done, remaining — " --persist #+end_src -=--persist= keeps it on screen until dismissed (the page-me convention). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. +=--persist= keeps it on screen until dismissed, and =info= is the page-me urgency convention (persistent but never crash-scary). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. * Metrics diff --git a/archive/task-archive.org b/archive/task-archive.org index 9d2bfc6..e52563d 100644 --- a/archive/task-archive.org +++ b/archive/task-archive.org @@ -1664,3 +1664,57 @@ Touches four synced template workflows and needs a curation pass on the best-pra Drafted [[file:docs/design/2026-06-16-encourage-kb-contribution-spec.org][the KB-contribution spec]]: four light workflow prompts (startup nudge, triage-intake + inbox-zero end-of-flow reminders, an early wrap-up reflection feeding the existing KB receipt) plus one Craig-authored best-practices node curated from Ahrens / Matuschak / org-roam guidance. Five open sub-decisions filed as decisions-as-TODO in the spec. *** 2026-06-20 Sat @ 23:29:10 -0400 Spec ratified + built Craig ratified all five decisions (2026-06-20) and added D6 — a read-side startup consult-nudge surfacing project-relevant KB node titles, the counterpart the original write-only design lacked. Built all of it: the best-practices node (=~/org/roam/agents/20260620232112-agent-kb-best-practices.org=), startup's two Phase C nudges (consult + contribute, gated on the roam clone), the conditional capture reminders in triage-intake + inbox-zero, and the early wrap-up reflection feeding the existing receipt. Commits 76e5559 (workflows + spec) and the related lint checker f6dde4e. Trigger for the build: receipt data showed "promoted 0 / consulted no" across recent sessions. +** DONE [#C] Bash/shell language bundle :feature: +CLOSED: [2026-06-23 Tue] +:PROPERTIES: +:CREATED: [2026-06-23 Tue] +:END: +Built =languages/bash/= the same session it was filed: bash.md + bash-testing.md rules, a shellcheck PostToolUse validate hook (covers =.sh=, =.bash=, and extensionless shell scripts by shebang; 8 bats tests), a shellcheck pre-commit githook, settings.json wiring, gitignore-add.txt, and a "Bash/shell project" CLAUDE.md. shfmt left out of the blocking path on purpose (shell has no canonical style). Makefile test target now discovers =languages/*/tests/*.bats=. + +No =languages/= bundle fits a shell-heavy project. archangel (437 =.sh= files) and archsetup are bash projects with nothing that matches; installing elisp/python gives them the wrong language rules. Build a =languages/bash/= bundle on the elisp/go pattern: =claude/rules/bash.md= (style — =set -euo pipefail=, quoting, =[[ ]]=, trap/cleanup) + =bash-testing.md= (bats conventions), a PostToolUse validate hook (=shellcheck= on edited =.sh=), a =githooks/pre-commit= running shellcheck on staged shell files, =settings.json= wiring, =gitignore-add.txt=, and its own =CLAUDE.md= headed "Bash/shell project." Urgency dropped 2026-06-23: install-lang now seeds the language-neutral default CLAUDE.md when a bundle ships none, so a bash project no longer gets a mislabeled "Elisp project" header — the bundle is now the accurate-rules win, not a mislabel fix. From archangel 2026-06-23 ([[file:docs/design/2026-06-23-install-lang-claude-md-gap.org][handoff]]). +** DONE [#B] Consolidate inbox/triage workflows + scheduled inbox check :chore: +CLOSED: [2026-06-23 Tue] +:PROPERTIES: +:CREATED: [2026-06-23 Tue] +:END: +Built per the Ready spec: =process-inbox= + =monitor-inbox= + =inbox-zero= merged into one =inbox.org= engine (shared core + process/monitor/roam modes + the interactive =auto inbox zero= =/loop= mode); =triage-intake= and =no-approvals= stay separate. Callers repointed (INDEX, protocols, startup Phase C, wrap-up Step 3), old files deleted, stale-ref grep clean, workflow-integrity + sync-check + full suite green. The fully-unattended =/schedule= cron pass is vNext (see the =[#D]= task above). [[file:docs/specs/inbox-workflow-consolidation-spec.org][spec]]. +** DONE [#C] inbox-zero: delete empty roam entries on triage :feature: +CLOSED: [2026-06-23 Tue] +:PROPERTIES: +:CREATED: [2026-06-23 Tue] +:END: +Done in commit 3da2725 (empty-entry sweep folded into Phase D's reconcile, after capture-guard + pull, with the claimed-item removal) and carried into the consolidated =inbox.org= roam mode (Phase B =empty= bucket + Phase D sweep). From the roam inbox 2026-06-23. +** DONE [#C] Surface cross-project dependencies first in what's-next :feature:spec: +CLOSED: [2026-06-24 Wed] +:PROPERTIES: +:CREATED: [2026-06-24 Wed] +:END: +Tasks that depend on another project can sit for ages when the dependency is low-priority or needs its own spec process — e.g. wrap-teardown depends on =.emacs.d= for the =ai-term= companion. Craig's proposal (roam 2026-06-24): (1) an org-tag marking a task as blocked-by / depends-on another project (pick a short tag name); (2) several ways to bind dependencies into the what's-next (=open-tasks.org=) decision tree so blocked-by-dependency tasks surface first; (3) review the what's-next workflow as a whole, since many projects use it. + +Built 2026-06-24 (tag name =:blocked:=, Craig's pick): the =:blocked:= tag + =:BLOCKED_BY: : = property convention in =todo-format.md=, and =open-tasks.org= Next Mode now excludes =:blocked:= tasks from the cascade and surfaces them in a dedicated "Blocked on other projects" section with an =inbox-send= nudge offer. Applied live to the wrap-teardown task above. Commits feat(tasks) cross-project-dependency. +** DONE [#C] Task-audit: consolidate adjacent / related tasks :feature: +CLOSED: [2026-06-24 Wed] +:PROPERTIES: +:CREATED: [2026-06-24 Wed] +:END: +The task-audit workflow should also consider combining related tasks when they're adjacent, so a spread-out effort reads as one whole. Craig's example (roam 2026-06-24): the agent-agnostic / agent-source work could collapse into one item, or at least a parent task with the related ones as children. + +Built 2026-06-24: =task-audit.org= Phase C.5 reads the open-task set, spots semantic clusters by judgment, and proposes per cluster either a merge (same-work members fold into one) or a parent-with-children grouping (related-but-distinct), applied only on Craig's confirm — broader than Phase C's exact-duplicate fold. Commit feat(task-audit) consolidate. +** DONE [#B] Anki deck name from #+TITLE :bug:quick:solo: +CLOSED: [2026-06-24 Wed] +:PROPERTIES: +:CREATED: [2026-06-22 Mon] +:LAST_REVIEWED: 2026-06-24 +:END: +flashcard-to-anki.py's =default_deck_name= returns =input_path.stem= (the filename), so every deck built through =flashcard-sync= (which passes no =--deck=) is named after the file, not the curated =#+TITLE=. =flashcard-review.org= already documents the intended behavior ("the #+TITLE line drives the Anki deck name"); the script never matched it. Fix: =default_deck_name(input_path, org_text)= scans for a =#+TITLE:= line (case-insensitive, trimmed) and returns it, basename fallback when absent; =main()= passes the already-read =org_text=. Edited script + test ready (validated, 29 pass); the staging =.py= files were removed after the fix landed (see below), rationale kept: [[file:docs/design/2026-06-21-anki-titlefix-proposal.org][proposal]]. Apply to both =.ai/scripts/= and =claude-templates/.ai/scripts/=, sync-check + make test. Migration caveat: deck ID derives from the name, so decks previously built without =--deck= land as new decks on next import (old basename-named decks keep history, delete by hand). Coordinate with "Reconcile flashcard multi-tag tooling into canonical" below — both edit =flashcard-to-anki.py=, build together to avoid conflicting edits. Shared-asset, review-gated. From home 2026-06-21. + +Done 2026-06-24 (commit 060a938): applied the pre-staged script + test red-to-green (5 new =#+TITLE= tests, 29 pass total), synced both script dirs, full suite green. The two redundant staging =.py= files removed, the rationale proposal kept. +** CANCELLED [#C] Morning ops orchestrator pilot — read-only :feature: +CLOSED: [2026-06-24 Wed 05:46] +:PROPERTIES: +:CREATED: [2026-06-11 Thu] +:LAST_REVIEWED: 2026-06-15 +:END: +A scheduled headless morning run chaining the existing pieces: startup checks, the triage-intake scan, a system health check — producing the prep doc plus a report and a notify ping, with all remediation propose-only. Staged adoption from the 2026-06-11 insights report's "Self-Healing Daily Ops Orchestrator": read-only first; promote individual routine remediations to auto only after each has a track record. Known blockers to design around: headless MCP auth (interactively-authenticated servers are absent in cron runs) and the consent boundary (triage Phase D, anything destructive). + +The triage limb can reuse triage-intake's *auto mode* (added 2026-06-15, see [[file:.ai/workflows/triage-intake.org]]) — its accumulate-don't-mutate sweep is the propose-only behavior this orchestrator wants. Auto mode itself runs in-session (inherited MCP auth); the orchestrator is the durable headless schedule, so the headless-auth blocker above is the part still on this task to solve. diff --git a/claude-templates/.ai/workflows/page-me.org b/claude-templates/.ai/workflows/page-me.org index 607ed51..8069830 100644 --- a/claude-templates/.ai/workflows/page-me.org +++ b/claude-templates/.ai/workflows/page-me.org @@ -5,9 +5,9 @@ * Overview -This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with alarm icon) and persist until manually dismissed. +This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with the blue info icon) and persist until manually dismissed. -Uses the =notify= command (alarm type) for consistent notifications across all AI workflows. +Uses the =notify= command (info type) for consistent notifications across all AI workflows. Info-level on purpose: the earlier alarm styling read as all-red urgency, and Craig's verdict was that a page "should be a persistent info notification" — noticeable, never crash-scary (2026-07-02). * Trigger Phrase @@ -63,8 +63,8 @@ Craig tells Claude when and why: Claude schedules the alarm using the =at= daemon with =notify=: #+begin_src bash -echo "notify alarm 'Page' 'Time to call the dentist' --persist" | at 3:30pm -echo "notify alarm 'Page' 'Meeting starts' --persist" | at now + 45 minutes +echo "notify info 'Page' 'Time to call the dentist' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at now + 45 minutes #+end_src The =at= daemon: @@ -89,26 +89,26 @@ Craig dismisses the notification and acts on it. ** Setting Alarms -Use the =at= daemon to schedule a =notify alarm= command: +Use the =at= daemon to schedule a =notify info= command: #+begin_src bash # Schedule for specific time -echo "notify alarm 'Page' 'Meeting starts' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at 3:30pm # Schedule for relative time -echo "notify alarm 'Page' 'Check the build' --persist" | at now + 30 minutes +echo "notify info 'Page' 'Check the build' --persist" | at now + 30 minutes # Schedule for tomorrow -echo "notify alarm 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow +echo "notify info 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow #+end_src ** Notification System -Uses the =notify= command with the =alarm= type. The =notify= command provides 8 notification types with matching icons and sounds. +Uses the =notify= command with the =info= type. The =notify= command provides 8 notification types with matching icons and sounds. #+begin_src bash -# Immediate alarm notification (for testing) -notify alarm "Page" "Your message here" --persist +# Immediate page notification (for testing) +notify info "Page" "Your message here" --persist #+end_src The =--persist= flag keeps the notification on screen until manually dismissed. All page-me notifications should use =--persist= by default. @@ -139,10 +139,10 @@ The alarm must fire. Use the =at= daemon which is designed for exactly this purp Simple invocation - Claude runs one command. No complex setup required per alarm. ** Fail Audibly -If the alarm fails to schedule, report the error clearly. Don't fail silently. +If the page fails to schedule, report the error clearly. Don't fail silently. ** Testable -The =notify alarm= command can be called directly to verify notifications work without waiting for a timer. +The =notify info= command can be called directly to verify notifications work without waiting for a timer. ** Non-Alarming Use normal urgency, not critical. The notification should be noticeable but not imply something has gone horribly wrong. diff --git a/claude-templates/.ai/workflows/work-the-backlog.org b/claude-templates/.ai/workflows/work-the-backlog.org index 642162d..b0666e7 100644 --- a/claude-templates/.ai/workflows/work-the-backlog.org +++ b/claude-templates/.ai/workflows/work-the-backlog.org @@ -149,10 +149,10 @@ Task boundaries are clean boundaries by construction: the previous task is close With paging on, fire one page when the set is done or the cap is hit — end-of-set only, never per-task: #+begin_src sh -notify alarm "Page" ": done, remaining — " --persist +notify info "Page" ": done, remaining — " --persist #+end_src -=--persist= keeps it on screen until dismissed (the page-me convention). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. +=--persist= keeps it on screen until dismissed, and =info= is the page-me urgency convention (persistent but never crash-scary). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. * Metrics diff --git a/todo.org b/todo.org index fc1dfd5..38e9ec6 100644 --- a/todo.org +++ b/todo.org @@ -466,6 +466,24 @@ Craig named the ordered set (id-link conversion, host-identity guard, template-s *** 2026-07-02 Thu @ 05:26:07 -0400 Flipped the spec DOING → IMPLEMENTED All six phases built and the live trial validated. Keyword, dated history line, and Metadata mirror all flipped per the transition-ownership table. +** DONE [#B] inbox-send filename collision silently overwrote a message :bug:solo: +CLOSED: [2026-07-02 Thu] +:PROPERTIES: +:CREATED: [2026-07-02 Thu] +:END: +From archsetup (2026-07-02 0543, found in the wild): two --text sends in the same minute whose text starts with the same phrase derive identical filenames, and the second silently overwrites the first — archsetup lost a message at 05:42 and had to resend. Severity data-loss x rare-edge = P2 = [#B]. + +Resolution 2026-07-02 (auto-inbox-zero loop, standing yes): uniquify() guard in inbox-send.py — an existing target gets a -2/-3/... stem suffix, both send_text and send_file paths, extension preserved. Four red-first tests reproduce the loss (module-level with a fixed timestamp so the same-minute collision is deterministic, plus a CLI loss-proof check); 30/30 green. + +** DONE [#C] page-me notify styling — all-red too alarming :bug:solo: +CLOSED: [2026-07-02 Thu] +:PROPERTIES: +:CREATED: [2026-07-02 Thu] +:END: +From Craig via the roam inbox (2026-07-02, routed by archsetup): the page notify's all-red styling "makes me feel like somehow the system is about to crash" — should be a persistent info-level notification. + +Resolution 2026-07-02 (auto-inbox-zero loop, standing yes): pages now use notify info --persist instead of notify alarm — page-me.org (all examples + prose, with Craig's verdict recorded) and work-the-backlog.org's end-of-set page. status-check's success/fail types untouched (job outcomes, not pages). + ** DONE [#C] Template sync with gitignored-only local changes :feature: CLOSED: [2026-07-02 Thu] From Craig via the roam inbox (2026-07-02, routed by archsetup): downstream projects should still pull template updates when their local changes sit entirely in gitignored files or directories — an inbox drop or a file left to read doesn't affect the templates, yet it currently holds the sync back and projects fall behind. When worked: verify how the sync gate actually detects dirtiness today, then let gitignored-only changes pass it. -- cgit v1.2.3