diff options
Diffstat (limited to '.ai')
58 files changed, 809 insertions, 115 deletions
diff --git a/.ai/notes.org b/.ai/notes.org index c49c9ee..5ce7047 100644 --- a/.ai/notes.org +++ b/.ai/notes.org @@ -1,5 +1,5 @@ #+TITLE: Claude Code Notes - Rulesets -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-06 * About This File @@ -61,7 +61,7 @@ This section tracks decisions that need Craig's input before work can proceed. ** Current Reminders -(None currently — will be added as needed) +- =[2026-07-14]= Review the sentry spec (docs/specs/2026-07-14-sentry-workflow-spec.org) — Craig's explicit ask at wrap: strongly suggest he reviews it before ending the next session. All 12 review findings and 10 decisions are resolved and folded in; the spec is open in his Emacs; the READY flip and the [#B] build task both wait on his deep read. ** Instructions for This Section @@ -82,6 +82,6 @@ Format: Markers maintained by workflows to record when they last ran. Read by other workflows that gate their behavior on freshness. :LAST_AUDIT: 2026-07-04 -:LAST_INBOX_PROCESS: 2026-07-04 (1 handoff: leftover kb-hygiene script report — already filed as todo.org [#C] KB orphan-node review pass; deleted the duplicate) +:LAST_INBOX_PROCESS: 2026-07-14 (3 handoffs: sentry proposal from work accepted + worked in-session; archsetup FYI acknowledged; lint-followups.org deleted per Craig) Format: one =:MARKER: YYYY-MM-DD= line per workflow. Workflows overwrite their own marker on completion. diff --git a/.ai/protocols.org b/.ai/protocols.org index 5e18ab9..5cd69d4 100644 --- a/.ai/protocols.org +++ b/.ai/protocols.org @@ -1,5 +1,5 @@ #+TITLE: Claude Code Protocols -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2025-11-05 * About This File @@ -367,6 +367,12 @@ Craig's shell aliases =ls= to =exa=, which prints nothing to non-TTY pipes (e.g. - Applies to =ls -la=, =ls -t=, glob expansions piped through =ls=, and any =ls= invocation whose output gets read programmatically. - Symptom if forgotten: the Bash tool returns empty output and you mistakenly conclude the directory is empty. +*** zsh does not word-split unquoted variables +The Bash tool runs zsh, which (unlike bash) does not split an unquoted =$var= on whitespace. =chrome $urls= passes all the space-joined URLs as one malformed argument. + +- Loop over the values, use an array, or force the split with =${=var}=. +- Symptom if forgotten: a command that "works in bash" gets one garbled argument and fails, often silently (from the takuzu session, 2026-07-11). + ** Miscellaneous Information - Craig currently lives in New Orleans, LA - Craig's phone number: 510-316-9357 @@ -406,14 +412,27 @@ Full usage: =notify --help= or see =~/.local/bin/notify= - =atq= - list all scheduled alarms - =atrm [number]= - remove an alarm by its queue number -** Paging Craig — desktop vs. away from the machine +** Paging Craig — the agent pager -"Page me" has two channels; pick by where Craig is. +"Page me" has two channels; pick by where Craig is. Both work from any agent runtime — nothing here is Claude-specific. - *At his laptop/desktop* — desktop =notify ... --persist= (above). It reaches him on the machine and stays up until dismissed. -- *Away from his laptop/desktop* — page his phone over Signal via the *signal-mcp* tool =send_message_to_user=, addressed to Craig's account UUID =b1b5601e-6126-47f8-afaa-0a59f5188fde= (his primary number reads as unregistered in Signal's directory — never page a phone number). The message goes out from the dedicated pager account (+15045173983) and fires a normal mobile push. This is the live cross-device path, verified working 2026-06-30. -Do *not* use the old =page-signal= shell script — it was removed from the rulesets canonical 2026-06-12 and its =~/.local/bin/page-signal= symlink no longer exists. The signal-mcp tool is the only supported Signal path; =notify --persist= is the only supported desktop path. + #+begin_src bash + notify info "Title" "Message" --persist + #+end_src + +- *Away from his laptop/desktop* — page his phone over Signal with the *agent pager*: + + #+begin_src bash + agent-page "Message for Craig's phone" + #+end_src + + =agent-page= (in =~/.local/bin= via the rulesets install) sends from the dedicated pager identity (+15045173983, registered in velox's signal-cli) to Craig's Signal account UUID, firing a normal mobile push. On velox it sends directly; on any other tailnet machine it ssh-relays the send to velox. Verified end to end 2026-07-13. Never page Craig's phone *number* — it reads as unregistered in Signal's directory; the script already targets the UUID. + + Caveats: velox must be up and on the tailnet (the script says so and names the desktop fallback when the relay fails), and the signal-cli account wants a periodic =receive= — both tracked on the rulesets Signal-pager task, which owns the full runbook. + +On velox, Claude sessions may also have the *signal-mcp* tool (=send_message_to_user=, same pager identity) — fine to use there, but it exists only in velox's local MCP config, so =agent-page= is the portable habit. Do *not* use the old =page-signal= shell script (removed 2026-06-12). * Session Protocols diff --git a/.ai/references/calendar-reference.org b/.ai/references/calendar-reference.org index b44c0f1..5791b08 100644 --- a/.ai/references/calendar-reference.org +++ b/.ai/references/calendar-reference.org @@ -1,5 +1,5 @@ #+TITLE: Calendar Reference -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings Tool recipes, authentication, and credentials for Craig's calendar setup. Three access methods, in order of preference. diff --git a/.ai/scripts/lint-org.el b/.ai/scripts/lint-org.el index 90b1b1d..55727ef 100644 --- a/.ai/scripts/lint-org.el +++ b/.ai/scripts/lint-org.el @@ -2,16 +2,19 @@ ;; ;; Usage: ;; emacs --batch -q -l lint-org.el FILE.org [FILE.org ...] +;; report only (the default) — categorize without modifying the file. +;; A linter reports, it doesn't write; mutation requires --fix. +;; --check is accepted as an explicit alias of this default. +;; +;; emacs --batch -q -l lint-org.el --fix FILE.org [FILE.org ...] ;; apply mechanical fixes in place, emit judgment items on stdout for the ;; command layer to walk ;; -;; emacs --batch -q -l lint-org.el --check FILE.org [FILE.org ...] -;; report only — categorize without modifying the file -;; -;; emacs --batch -q -l lint-org.el --followups-file=PATH FILE.org +;; emacs --batch -q -l lint-org.el --fix --followups-file=PATH FILE.org ;; apply mechanical fixes; if any judgment items remain, append them to ;; PATH as an org section dated today. Used by wrap-it-up to defer the ;; judgment walk to the next morning's review without blocking the wrap. +;; (--followups-file only writes in --fix mode.) ;; ;; Mechanical categories (auto-fixed): ;; item-number add [@N] directive to drifted bullets @@ -66,7 +69,9 @@ Each plist has :kind (mechanical-fixed | judgment), :line, :checker, :msg. Mechanical entries from --check mode also carry :preview t.") (defvar lo-check-only nil - "Non-nil means run in report-only mode — no buffer writes.") + "Non-nil means run in report-only mode — no buffer writes. +The CLI defaults this to t (a linter reports, it doesn't write); +`--fix' is what enables writes on a command-line run.") (defvar lo-current-file nil "Path of the file currently being processed.") (defvar lo-followups-file nil @@ -355,24 +360,40 @@ logical row, matching wrap-org-table.el's grouping." (defun lo--check-tables () "Scan the current buffer for org tables violating the table standard. -Emits one judgment item per violating table." +Emits one judgment item per violating table. Pipe-led lines inside +#+begin_/#+end_ blocks are content (ASCII art, shell pipes), not tables, +and are skipped — the same block rule `wot-process-file' applies." (save-excursion (goto-char (point-min)) - (while (re-search-forward "^[ \t]*|" nil t) - (let ((start-line (line-number-at-pos)) - (lines nil)) - (beginning-of-line) - (while (and (not (eobp)) (looking-at "[ \t]*|")) - (push (buffer-substring-no-properties (line-beginning-position) - (line-end-position)) - lines) + (let ((in-block nil)) ; the open block's type, e.g. "example" — nil outside + (while (not (eobp)) + (cond + ;; Type-matched close only: literal #+end_src quoted inside an + ;; example block must not clear the flag (see wot-process-file). + ((and (not in-block) + (looking-at "^[ \t]*#\\+begin_\\([^ \t\n]+\\)")) + (setq in-block (downcase (match-string 1))) + (forward-line 1)) + ((and in-block + (looking-at-p (format "^[ \t]*#\\+end_%s\\([ \t]\\|$\\)" + (regexp-quote in-block)))) + (setq in-block nil) (forward-line 1)) - (let ((violations (lo--table-violations (nreverse lines)))) - (when violations - (lo--emit-judgment - 'org-table-standard start-line - (format "table violates the org-table standard: %s — wrap-org-table.el reflows it" - (string-join violations "; "))))))))) + ((and (not in-block) (looking-at-p "^[ \t]*|")) + (let ((start-line (line-number-at-pos)) + (lines nil)) + (while (and (not (eobp)) (looking-at "[ \t]*|")) + (push (buffer-substring-no-properties (line-beginning-position) + (line-end-position)) + lines) + (forward-line 1)) + (let ((violations (lo--table-violations (nreverse lines)))) + (when violations + (lo--emit-judgment + 'org-table-standard start-line + (format "table violates the org-table standard: %s — wrap-org-table.el reflows it" + (string-join violations "; "))))))) + (t (forward-line 1))))))) ;;; --------------------------------------------------------------------------- ;;; level-2 dated-header check (claude-rules/todo-format.md) @@ -677,6 +698,13 @@ After printing, also append judgments to `lo-followups-file' when set." ;;; CLI (defun lo-main () + ;; Report-only is the CLI default; --fix is the only way a command-line run + ;; writes to disk. The old mutate-by-default reformatted five files in one + ;; pass before anyone confirmed anything (work project, 2026-07-09). + (setq lo-check-only t) + (when (member "--fix" command-line-args-left) + (setq lo-check-only nil) + (setq command-line-args-left (delete "--fix" command-line-args-left))) (when (member "--check" command-line-args-left) (setq lo-check-only t) (setq command-line-args-left (delete "--check" command-line-args-left))) @@ -688,7 +716,7 @@ After printing, also append judgments to `lo-followups-file' when set." (setq command-line-args-left (delete followups command-line-args-left)))) (if (null command-line-args-left) (progn - (princ "Usage: emacs --batch -q -l lint-org.el [--check] [--followups-file=PATH] FILE.org ...\n") + (princ "Usage: emacs --batch -q -l lint-org.el [--fix] [--check] [--followups-file=PATH] FILE.org ...\n") (kill-emacs 1)) (let ((files command-line-args-left)) (setq command-line-args-left nil) @@ -707,7 +735,7 @@ this file without firing the CLI dispatch — under `ert-run-tests-batch-and-exi the trailing args are things like `-f ert-run-tests-batch-and-exit'." (and command-line-args-left (cl-every (lambda (a) - (cond ((member a '("--check")) t) + (cond ((member a '("--check" "--fix")) t) ((string-prefix-p "--followups-file=" a) t) ((string-prefix-p "-" a) nil) (t (file-readable-p a)))) diff --git a/.ai/scripts/task-review-staleness.sh b/.ai/scripts/task-review-staleness.sh index ed43712..50e0257 100755 --- a/.ai/scripts/task-review-staleness.sh +++ b/.ai/scripts/task-review-staleness.sh @@ -19,9 +19,14 @@ # deeper headings, and cookie-less headings are not review units. # # A task is stale (count mode), and sorts oldest (list mode), when its -# :LAST_REVIEWED: property is missing or unparseable (NIL sorts first), or -# when its age strictly exceeds the threshold (age > N days; age == N is -# still fresh). +# :LAST_REVIEWED: property is missing (NIL sorts first) or when its age +# strictly exceeds the threshold (age > N days; age == N is still fresh). +# +# :LAST_REVIEWED: accepts a bare date (2026-07-09) or an org-native +# timestamp ([2026-07-09 Thu] or <2026-07-09 Thu ...>); both normalize to +# the ISO date. A value that is present but parses to neither is a data +# error: the script warns loudly to stderr (file:line:value) and leaves it +# out of the stale count rather than silently treating it as never-reviewed. set -euo pipefail @@ -46,7 +51,7 @@ num="$2" # only the property drawer between a qualifying heading and the next heading # is scanned. extract_tasks() { - awk ' + awk -v fname="$todo_file" ' function flush() { if (in_task) printf "%s\t%s\t%s\n", hline, (have_lr ? lr : "NONE"), heading } @@ -65,7 +70,21 @@ extract_tasks() { v = $0 sub(/^[ \t]*:LAST_REVIEWED:[ \t]*/, "", v) sub(/[ \t]*$/, "", v) - lr = v; have_lr = 1 + raw = v + # Accept org-native stamps: strip a leading [ or < (inactive/active + # timestamp bracket) and take the leading ISO date, so [2026-07-09 Thu] + # and 2026-07-09 both normalize to 2026-07-09. + sub(/^[[<]/, "", v) + if (match(v, /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/)) { + lr = substr(v, RSTART, RLENGTH); have_lr = 1 + } else { + # Present but unparseable — a data error, not "never reviewed". Warn + # loudly (file:line:value) instead of silently folding it into the + # stale count, where a re-review in the same bad format would never + # drop the number and nothing would explain why. + printf "task-review-staleness: %s:%d: unparseable :LAST_REVIEWED: %s (expected YYYY-MM-DD or [YYYY-MM-DD Day])\n", fname, NR, raw > "/dev/stderr" + lr = "INVALID"; have_lr = 1 + } next } END { flush() } @@ -98,9 +117,16 @@ while IFS=$'\t' read -r hline value heading; do continue fi - # Unparseable date → treat as NIL (stale). + # Malformed stamp: extract_tasks already warned. A data error is not + # "never reviewed", so it stays out of the stale count. + if [ "$value" = "INVALID" ]; then + continue + fi + + # A shape-valid but impossible date (e.g. 2026-13-45) slips past the awk + # regex; warn and skip rather than silently counting it. if ! rev_epoch=$(date -d "$value" +%s 2>/dev/null); then - count=$((count + 1)) + echo "task-review-staleness: $todo_file: unparseable :LAST_REVIEWED: $value" >&2 continue fi diff --git a/.ai/scripts/tests/lint-org-cli.bats b/.ai/scripts/tests/lint-org-cli.bats index d457696..b9faef6 100644 --- a/.ai/scripts/tests/lint-org-cli.bats +++ b/.ai/scripts/tests/lint-org-cli.bats @@ -20,6 +20,24 @@ teardown() { [[ "$output" == *"lint-org: file="* ]] } +@test "lint-org.el default invocation is report-only — file untouched" { + # bare #+begin_src is a mechanical fix (→ #+begin_example) that the old + # default applied on disk; a linter reports, it doesn't write + printf '* H\n\n#+begin_src\nx\n#+end_src\n' > "$TMPFILE" + before="$(cat "$TMPFILE")" + run emacs --batch -q -l "$SCRIPTS_DIR/lint-org.el" "$TMPFILE" + [ "$status" -eq 0 ] + [[ "$output" == *"would-fix"* ]] + [ "$(cat "$TMPFILE")" = "$before" ] +} + +@test "lint-org.el --fix applies mechanical fixes on disk" { + printf '* H\n\n#+begin_src\nx\n#+end_src\n' > "$TMPFILE" + run emacs --batch -q -l "$SCRIPTS_DIR/lint-org.el" --fix "$TMPFILE" + [ "$status" -eq 0 ] + grep -q '#+begin_example' "$TMPFILE" +} + @test "wrap-org-table.el loads and runs without -L on the load path" { run emacs --batch -q -l "$SCRIPTS_DIR/wrap-org-table.el" --width=120 "$TMPFILE" [ "$status" -eq 0 ] diff --git a/.ai/scripts/tests/task-review-staleness.bats b/.ai/scripts/tests/task-review-staleness.bats index 488b023..79aad79 100644 --- a/.ai/scripts/tests/task-review-staleness.bats +++ b/.ai/scripts/tests/task-review-staleness.bats @@ -49,6 +49,16 @@ task_unreviewed() { printf '** %s [#%s] %s\nBody.\n\n' "$keyword" "$prio" "$title" >> "$TODO" } +# Emit a qualifying task whose LAST_REVIEWED is an org-native inactive +# timestamp — [YYYY-MM-DD Day] — matching the CREATED:/CLOSED: cookies that +# sit in the same drawer. The date is derived from an ISO date via `date`. +task_reviewed_org() { + local keyword="$1" prio="$2" title="$3" isodate="$4" + local org="[$(date -d "$isodate" '+%F %a')]" + printf '** %s [#%s] %s\n:PROPERTIES:\n:LAST_REVIEWED: %s\n:END:\nBody.\n\n' \ + "$keyword" "$prio" "$title" "$org" >> "$TODO" +} + # ---- Normal cases ---------------------------------------------------- @test "staleness: empty file reports zero" { @@ -85,6 +95,20 @@ task_unreviewed() { [ "$output" = "2" ] } +@test "staleness: org-native bracketed LAST_REVIEWED parses — recent is fresh" { + task_reviewed_org TODO A "Reviewed five days ago, org stamp" "$D5" + run bash "$SCRIPT" "$TODO" 30 + [ "$status" -eq 0 ] + [ "$output" = "0" ] +} + +@test "staleness: org-native bracketed LAST_REVIEWED parses — old is stale" { + task_reviewed_org TODO A "Reviewed forty days ago, org stamp" "$D40" + run bash "$SCRIPT" "$TODO" 30 + [ "$status" -eq 0 ] + [ "$output" = "1" ] +} + # ---- Boundary cases -------------------------------------------------- @test "staleness: age exactly equal to threshold is fresh" { @@ -136,9 +160,23 @@ task_unreviewed() { [ "$output" = "0" ] } -@test "staleness: malformed LAST_REVIEWED is treated as stale" { +@test "staleness: malformed LAST_REVIEWED warns to stderr and is not counted" { task_reviewed TODO A "Bad date" "not-a-date" - run bash "$SCRIPT" "$TODO" 30 + # stdout carries only the count — the malformed stamp is not folded in. + run bash -c "bash '$SCRIPT' '$TODO' 30 2>/dev/null" + [ "$status" -eq 0 ] + [ "$output" = "0" ] + # stderr carries the loud warning naming the offending value. + run bash -c "bash '$SCRIPT' '$TODO' 30 2>&1 1>/dev/null" + [ "$status" -eq 0 ] + [[ "$output" == *"not-a-date"* ]] + [[ "$output" == *"LAST_REVIEWED"* ]] +} + +@test "staleness: malformed stamp is excluded while real stale tasks still count" { + task_reviewed TODO A "Real stale" "$D40" + task_reviewed TODO B "Broken stamp" "garbage" + run bash -c "bash '$SCRIPT' '$TODO' 30 2>/dev/null" [ "$status" -eq 0 ] [ "$output" = "1" ] } @@ -161,6 +199,17 @@ task_unreviewed() { [[ "${lines[2]}" == *"Reviewed recently"* ]] } +@test "staleness --list: org-native bracketed stamp sorts by its real date" { + task_reviewed TODO A "Bare recent" "$D5" + task_reviewed_org TODO B "Org-stamped old" "$D40" + run bash "$SCRIPT" --list "$TODO" 10 + [ "$status" -eq 0 ] + # The org-bracketed old stamp must sort ahead of the bare recent one — + # proof it parsed to a real date rather than falling to 0000-00-00. + [[ "${lines[0]}" == *"Org-stamped old"* ]] + [[ "${lines[1]}" == *"Bare recent"* ]] +} + @test "staleness --list: takes only the requested count" { task_unreviewed TODO A "First" task_reviewed TODO B "Second" "$D40" diff --git a/.ai/scripts/tests/test-lint-org.el b/.ai/scripts/tests/test-lint-org.el index d14879f..8e3e190 100644 --- a/.ai/scripts/tests/test-lint-org.el +++ b/.ai/scripts/tests/test-lint-org.el @@ -620,6 +620,29 @@ followups file on the next run." ;;; --------------------------------------------------------------------------- ;;; org-table-standard check (width budget + rules between rows) +(ert-deftest lo-table-inside-example-block-not-flagged () + "Pipe-led ASCII art inside an example block is not a table; no judgment." + (let* ((run (lo-test--run + "* H\n\n#+begin_example\n| client |----->| server |\n| box | | box |\n#+end_example\n" + 1 t)) + (judgments (lo-test--judgments (plist-get run :issues)))) + (should-not (memq 'org-table-standard (lo-test--checkers judgments))))) + +(ert-deftest lo-table-inside-src-block-not-flagged () + "Shell pipes inside a src block are not a table; no judgment." + (let* ((run (lo-test--run + "* H\n\n#+begin_src sh\n| sort\n| uniq -c\n#+end_src\n" 1 t)) + (judgments (lo-test--judgments (plist-get run :issues)))) + (should-not (memq 'org-table-standard (lo-test--checkers judgments))))) + +(ert-deftest lo-real-table-after-block-still-flagged () + "Block safety must not mask a genuine violation later in the file." + (let* ((run (lo-test--run + "* H\n\n#+begin_example\n| art |\n#+end_example\n\n| a | b |\n| 1 | 2 |\n" + 1 t)) + (judgments (lo-test--judgments (plist-get run :issues)))) + (should (memq 'org-table-standard (lo-test--checkers judgments))))) + (ert-deftest lo-table-over-budget-emits-judgment () "A table line rendering wider than 120 surfaces as an org-table-standard judgment." (let* ((wide (make-string 130 ?x)) diff --git a/.ai/scripts/tests/test-wrap-org-table.el b/.ai/scripts/tests/test-wrap-org-table.el index 8d1ecb6..0b3b375 100644 --- a/.ai/scripts/tests/test-wrap-org-table.el +++ b/.ai/scripts/tests/test-wrap-org-table.el @@ -186,3 +186,45 @@ (should (string-match-p "Prose before\\." content)) (should (string-match-p "Prose after\\." content)))) (delete-file file)))) + +;;; --------------------------------------------------------------------------- +;;; block safety — pipe lines inside #+begin_/#+end_ blocks are never tables + +(defconst wot-test--block-content + "#+begin_example +| client |----->| server | +| box | | box | +#+end_example +" + "An example block whose ASCII-art lines start with pipes.") + +(defun wot-test--process-content (content budget) + "Write CONTENT to a temp file, run `wot-process-file' at BUDGET, return result." + (let ((file (make-temp-file "wot-test" nil ".org"))) + (unwind-protect + (progn + (with-temp-file file (insert content)) + (wot-process-file file budget) + (with-temp-buffer (insert-file-contents file) (buffer-string))) + (delete-file file)))) + +(ert-deftest wot-process-file-leaves-example-block-byte-identical () + (let ((content (concat "* Diagram\n\n" wot-test--block-content))) + (should (equal (wot-test--process-content content 120) content)))) + +(ert-deftest wot-process-file-reformats-table-but-not-block () + (let* ((content (concat "* Doc\n\n" wot-test--block-content "\n" + wot-test--wide-input)) + (result (wot-test--process-content content 40))) + (should (string-match-p (regexp-quote wot-test--block-content) result)) + (should (string-match-p (regexp-quote wot-test--wide-expected) result)))) + +(ert-deftest wot-process-file-skips-pipes-in-src-block () + (let ((content "* Pipeline\n\n#+begin_src sh\n| sort\n| uniq -c\n#+end_src\n")) + (should (equal (wot-test--process-content content 120) content)))) + +(ert-deftest wot-process-file-literal-inner-end-marker-stays-in-block () + "A literal #+end_src quoted inside an example block must not close it." + (let ((content (concat "* Doc\n\n#+begin_example\n#+begin_src sh\nx\n" + "#+end_src\n| art |----| art |\n#+end_example\n"))) + (should (equal (wot-test--process-content content 120) content)))) diff --git a/.ai/scripts/wrap-org-table.el b/.ai/scripts/wrap-org-table.el index ddbea65..173e44d 100644 --- a/.ai/scripts/wrap-org-table.el +++ b/.ai/scripts/wrap-org-table.el @@ -228,22 +228,40 @@ continuation lines merge back into their logical row before re-wrapping." ;;; file layer (defun wot-process-file (file &optional budget) - "Reformat every org table in FILE in place to BUDGET width." + "Reformat every org table in FILE in place to BUDGET width. +Pipe-led lines inside #+begin_/#+end_ blocks (example, src, quote, …) are +content, not tables — ASCII art in an example block once got mangled into a +bordered table — so block regions are skipped verbatim." (with-temp-buffer (insert-file-contents file) (goto-char (point-min)) - (while (re-search-forward "^[ \t]*|" nil t) - (let ((start (line-beginning-position))) - (while (and (not (eobp)) - (save-excursion (beginning-of-line) - (looking-at "[ \t]*|"))) + (let ((in-block nil)) ; the open block's type, e.g. "example" — nil outside + (while (not (eobp)) + (cond + ;; Only the matching #+end_<type> closes a block: an example block + ;; often quotes literal #+begin_src/#+end_src lines, and a boolean + ;; flag would let that inner literal end-marker re-expose the rest + ;; of the block to reformatting. + ((and (not in-block) + (looking-at "^[ \t]*#\\+begin_\\([^ \t\n]+\\)")) + (setq in-block (downcase (match-string 1))) (forward-line 1)) - (let* ((end (point)) - (table (buffer-substring-no-properties start end)) - (reformatted (wot-reformat-table-string table budget))) - (delete-region start end) - (goto-char start) - (insert reformatted)))) + ((and in-block + (looking-at-p (format "^[ \t]*#\\+end_%s\\([ \t]\\|$\\)" + (regexp-quote in-block)))) + (setq in-block nil) + (forward-line 1)) + ((and (not in-block) (looking-at-p "^[ \t]*|")) + (let ((start (point))) + (while (and (not (eobp)) (looking-at-p "^[ \t]*|")) + (forward-line 1)) + (let* ((end (point)) + (table (buffer-substring-no-properties start end)) + (reformatted (wot-reformat-table-string table budget))) + (delete-region start end) + (goto-char start) + (insert reformatted)))) + (t (forward-line 1))))) (write-region (point-min) (point-max) file))) ;;; --------------------------------------------------------------------------- @@ -289,7 +307,21 @@ so the ERT suite can `require' this file without firing the CLI dispatch." (t (file-readable-p a)))) command-line-args-left))) -(when (and noninteractive (wot--cli-invocation-p)) +(defun wot--entry-script-p () + "Non-nil when wrap-org-table.el itself was named on the command line. +lint-org.el `require's this file, and a load-triggered dispatch would run +the table reformatter over lint-org's file arguments — that's how a lint +invocation once reformatted the files it was only supposed to report on. +Only dispatch when a -l/--load argument names this very file." + (and load-file-name + (cl-loop for (flag arg) on command-line-args + thereis (and (member flag '("-l" "--load")) + (stringp arg) + (file-exists-p arg) + (string= (file-truename (expand-file-name arg)) + (file-truename load-file-name)))))) + +(when (and noninteractive (wot--entry-script-p) (wot--cli-invocation-p)) (wot-main)) (provide 'wrap-org-table) diff --git a/.ai/sessions/2026-07-04-12-57-audit-closeouts-and-startup-sync-guard.org b/.ai/sessions/2026-07-04-12-57-audit-closeouts-and-startup-sync-guard.org new file mode 100644 index 0000000..c0d569b --- /dev/null +++ b/.ai/sessions/2026-07-04-12-57-audit-closeouts-and-startup-sync-guard.org @@ -0,0 +1,68 @@ +#+TITLE: Session Context +#+DATE: 2026-07-04 + +* Summary + +** Active Goal + +Work a short list: commit the fable→opus model switch, run a task review, double-check the speedrun and finish it, then the routing feature. The review turned into a full task audit ("how many are actually completed"), which drove the rest. + +** Decisions + +- Craig redirected task-review → full task audit, scoped to completion. +- Closed out all three code-complete tasks (docs-lifecycle, wrap-up routing, memory-sync): flip spec to IMPLEMENTED, close the parent, promote pending manual validation to standalone tasks. Same shape each time. +- Accepted + applied home's startup skip-sync-when-behind proposal (shared-asset change, skeptical-reviewed, Craig-approved). +- Pushed all three session commits to origin. + +** Data Collected / Findings + +- Of 18 open tasks, none were sitting done-and-verified; three were code-complete pending validation. The speedrun (autonomous-batch) was already fully done — IMPLEMENTED, live-trialed, archived in 5dc7da3. +- Cross-machine memory sync verified as a live round-trip: on ratio (this machine) the velox-created probe was present with no conflicts; then over tailscale to velox, the probe deletion had propagated, both clones at HEAD 8c5ee01, both roam-sync timers active. Probe cleaned up. +- Spec board now carries no DOING specs. +- The commit no-attribution rule caught an accidental Claude-Session trailer on the first model-switch commit (amended out before it went anywhere). + +** Files Modified + +- .claude/settings.json — model fable→opus (73835a2). +- todo.org + docs/specs/{docs-lifecycle,wrapup-routing}-spec.org + notes.org — three tasks closed, two specs flipped IMPLEMENTED, :LAST_AUDIT: stamped (44af1b2). +- claude-templates/.ai/workflows/startup.org + mirror — Phase A proj_behind guard skips the template rsync when the project branch is behind upstream (bc4befa). + +** Next Steps + +- The three promoted validation tasks (docs-lifecycle manual checks; wrap-routing manual e2e; work/unknown-project KB write-refusal) need live cross-project or in-Emacs sessions to close. +- home is applying the startup fix as a local stopgap; it can drop the stopgap after pulling rulesets. + +KB: promoted 0 / consulted no + +* Session Log + +** 2026-07-04 Sat @ 11:06 CDT — Startup + inbox + +Ran startup. Session-context was absent — previous session wrapped cleanly. Project repo clean/current (no ff needed); rulesets skipped its pull on a dirty tree (uncommitted =.claude/settings.json= model switch fable→opus, plus this session's own drops). =.ai/= synced from templates clean. + +One pending inbox handoff: =inbox/2026-07-01-0041-kb-hygiene-report.org=, a report from the automated =kb-hygiene.sh= script listing 41 orphan =:agent:= KB nodes. Checked todo.org — already filed as =** TODO [#C] KB orphan-node review pass :chore:= (line 42), whose body cites this exact report and instructs regenerating the list rather than trusting the snapshot. So the inbox file is a leftover; deleted it (script source, already captured, no reply owed). Stamped =:LAST_INBOX_PROCESS:=. + +Noted but not touched: =.claude/settings.json= carries an uncommitted model switch (fable→opus) — Craig's local config, left as-is. =inbox/lint-followups.org= is a task-review-health nudge (2 top-level tasks unreviewed >30 days), correctly excluded by inbox-status. + +** 2026-07-04 Sat @ 11:xx CDT — Model commit, task audit, three closeouts + +Committed the fable→opus switch (73835a2; first amend removed an accidental Claude-Session trailer that violates the no-attribution rule). Then ran a full task audit (Craig redirected from task-review to "how many are actually completed"). + +Audit finding: of 18 open tasks, zero were sitting done-and-verified, but three were code-complete with only validation pending. Also surfaced that the no-approvals speedrun (Craig's item 3) was already fully done — autonomous-batch spec IMPLEMENTED, live-trialed (metrics commits), task archived in 5dc7da3. + +Closed out all three code-complete tasks (Craig approved A, then walked B and C): +- A: docs-lifecycle convention — flipped spec to IMPLEMENTED, closed parent, promoted manual-validation + vNext to standalone tasks. +- B: wrap-up routing (Craig's item 4) — same shape; build was fully shipped + green, flipped spec IMPLEMENTED, closed parent, promoted manual e2e + transcript vNext. +- C: memories-sync VERIFY — implementation was IMPLEMENTED already; verified check 4 live this session. We're on ratio, so the ratio half was a local check (probe from velox present, zero conflicts). Then velox came back reachable — did the full round-trip over tailscale: velox pulled ratio's probe deletion, both clones at the same HEAD (8c5ee01), zero conflicts, both timers active. Deleted the spent probe. Closed the VERIFY; promoted the residual work/unknown-project write-refusal checks to a standalone task. + +Spec board now has no DOING specs. Stamped :LAST_AUDIT: 2026-07-04. + +Committed the audit closeouts as 44af1b2 (todo.org + both specs + notes.org), voice-passed. Two commits now sit unpushed ahead of origin (73835a2 model switch, 44af1b2 audit). + +** 2026-07-04 Sat @ ~12:53 CDT — Inbox: home's startup behind-guard fix applied + +Two inbox handoffs from home arrived mid-session (intro + proposal): startup Phase A step 3 rsyncs templates onto a project's stale committed .ai/ baseline when the project branch is behind/diverged, producing phantom drift that conflicts on reconcile. Ran the skeptical review (correct, complete, safe, universal); recommended accept; Craig approved applying now. + +Applied in canonical claude-templates/.ai/workflows/startup.org: a second guard computes proj_behind from git rev-list @{u}...HEAD and skips the rsync when behind>0, composing with the rulesets-clean guard. Added companion prose (step-3 heading + a rsync-notes bullet, incl. the no-auto-discard reasoning and the 6/22 flashcard-revert precedent). sync-check --fix propagated to the mirror; make test green (all ERT 0-unexpected, bats ok). Committed bc4befa. Replied to home (delivered to their inbox), deleted both inbox files, updated :LAST_INBOX_PROCESS:. Inbox clean. + +Three unpushed commits now: 73835a2, 44af1b2, bc4befa. diff --git a/.ai/sessions/2026-07-11-02-30-inbox-clearout-six-proposals-shipped.org b/.ai/sessions/2026-07-11-02-30-inbox-clearout-six-proposals-shipped.org new file mode 100644 index 0000000..3555f8f --- /dev/null +++ b/.ai/sessions/2026-07-11-02-30-inbox-clearout-six-proposals-shipped.org @@ -0,0 +1,74 @@ +#+TITLE: Session Context +#+AUTHOR: Craig Jennings +#+DATE: 2026-07-11 + +* Summary + +** Active Goal + +Process the full project inbox (11 handoffs) and then the roam inbox. Craig chose to work the shared-asset proposals in order rather than deferring them, so the session became a full inbox clear-out with six proposals shipped. + +** Decisions + +- Craig approved all six shared-asset proposals after skeptical review, one batch at a time: triage-intake gmail cap, PR-review drop-praise, ui-prototyping rule, desktop-capture rule, bug-matrix double-count, staleness org-date parsing. +- Placement calls: ui-prototyping and desktop-capture each got a standalone claude-rules/ file (not folded into existing workflows), matching the docs-lifecycle.md pattern spec-create already points at. +- chime's staleness fix used the "both" approach: accept the org-native bracketed stamp AND warn loudly on genuinely-unparseable values, rather than silently counting them stale. +- settings.json model flip (opus→fable, harness-written at 02:12) reverted to opus per Craig — matches the deliberate 2026-07-04 switch (73835a2). +- Bare "wrap it up" → teardown mode. + +** Data Collected / Findings + +- The AI-attribution handoff's work had already landed as commit 6def7c4 (tree was clean); the 15 remaining "& Claude" files are all in .ai/sessions/ (historical, correctly held back). Confirmed agreement with everything the work session held back. +- personal-gmail is the only gmail-family plugin rulesets owns, so the 100-cap fix touched one plugin. +- The sent triage-intake copy carried the old "& Claude" author line; applied only the two Scan blocks, did not reintroduce it. +- ~/.claude/settings.json is a symlink to the repo file, so a git checkout reverts both at once (not a hardlink). +- Roam inbox churns fast (12 items at 01:01 startup, 6 at 02:16 process). Git history confirmed exactly 3 rulesets-owned items ever passed through and all 3 were filed — no rulesets item was missed; the count drop was foreign-item/other-machine churn. + +** Files Modified + +- Six proposal commits (see Next Steps for shas). Rules/skills: review-code/SKILL.md, voice/SKILL.md, voice/references/voice-profile.org, claude-rules/{commits,todo-format,verification}.md; new claude-rules/{ui-prototyping,desktop-capture}.md. Workflows: triage-intake.personal-gmail.org, spec-create.org, spec-review.org, task-review.org (+ mirrors). Script: task-review-staleness.sh + its bats (+4 tests) + mirrors. +- todo.org: 5 tasks filed (org-table bug [#B], Signal pager [#C], 3 roam ideas [#C]). notes.org: :LAST_INBOX_PROCESS: stamped 2026-07-11. 11 inbox handoffs renamed PROCESSED-*. + +** Next Steps + +- All inbox work done; nothing carried forward. Filed backlog tasks await future sessions: org-table helpers data-loss bug ([#B] :bug:solo:, has a repro test), Signal pager doc ([#C], reconcile signal-mcp vs signal-cli first), and the 3 roam ideas. +- Shipped shas: 81ca16f, 99bd213, 53f6ce6, 8ddd3d3, 06ca6c7, cfbabf1 (+ housekeeping b2aeaf6, 95dd661). + +KB: promoted 0 / consulted no + +* Session Log + +** Startup (2026-07-11 01:01 CDT) + +Ran startup.org Phases A.0/A/B. rulesets pull clean, make install nothing new, project repo current. .ai/ synced from templates. No session-context anchor (prior session wrapped cleanly). Read the 5 most recent session summaries and all 11 inbox handoffs. Verified the AI-attribution handoff's work already landed as commit 6def7c4 (tree clean). Created this anchor. + +** Inbox processing (Craig chose: file the 2 tasks, work proposals in order) + +Filed two todo.org tasks: [#B] :bug:solo: org-table helpers corrupt example blocks (graded Critical×rare = P2 = [#B]); [#C] :feature:spec: document/own the Signal pager (flagged the signal-mcp vs signal-cli/account-404211 reconcile). Replied to work (bug + attribution) and home (signal), marked those 3 inbox files PROCESSED. + +Skeptical-reviewed the two work proposals: +- triage-intake.personal-gmail.org: clean 2-block Scan addition (100-cap date-slice walk + mandatory backlog-residue probe). Only other diff is the "& Claude" author-line regression, which I will NOT reintroduce. Low-risk correctness fix. Verdict: approve. personal-gmail is the only gmail-family plugin in rulesets. +- PR-review tightening: (1) no praise at all on approvals — drop the "bare positive" carve-out at review-code/SKILL.md:453, :278, examples :458-465, and /voice pattern #40; lead with substantive pointer then verdict. (2) always print full inline prose at the gate — tighten review-code Phase 5 (:276) + commits.md Shape 1. Craig's own ruling from a real session. Verdict: approve. + +Both awaiting Craig's approve/park. + +** Work proposals shipped (Craig approved both) + +- 81ca16f fix(triage-intake): personal-gmail 100-cap date-slice walk + backlog-residue probe (canonical + mirror). Did NOT reintroduce the "& Claude" author line from the sent copy. +- 99bd213 docs(review): drop praise from approve summaries (removed bare-positive carve-out from review-code Posted Summary Voice + voice #40 + voice-profile §40 examples/history + quick-ref table), require full inline prose at the gate (review-code Phase 5 + commits.md Shape 1). +- make test green (269 ok, pytest 12), both authored as Craig, not pushed yet. +- Replied to work on both; marked the 3 work inbox files PROCESSED. + +Remaining inbox proposals to work (Craig's chosen order): archsetup ui-prototyping (2 files), archsetup off-workspace-captures rule, chime staleness-parse, chime bug-matrix double-count. + +** Full inbox cleared — 6 proposals shipped, all 11 handoffs processed + +Shipped commits: 81ca16f (triage 100-cap), 99bd213 (drop-praise), 53f6ce6 (ui-prototyping rule), 8ddd3d3 (desktop-capture rule), 06ca6c7 (bug-matrix), cfbabf1 (staleness org-date TDD, +4 tests, suite 273 ok), b2aeaf6 (housekeeping: 2 tasks filed + processed markers + inbox marker). Replied to every sender (work, home, archsetup, chime). notes.org :LAST_INBOX_PROCESS: stamped 2026-07-11. Nothing pushed yet. + +FLAG: .claude/settings.json model flipped opus→fable at 02:12 (hardlinked to ~/.claude/settings.json, harness-written, not my edit). Reverts the deliberate 2026-07-04 opus switch (73835a2). Left unstaged — needs Craig's call (revert to opus, or accept fable). + +** Roam inbox-zero (Craig: "then inbox-zero again") + +Roam inbox had 6 items at process time (roam-sync churns fast). Bucketed: 3 claimed (rulesets:), 3 foreign (emacs.d ×1, archsetup ×2), 0 unowned, 0 empty. Filed the 3 rulesets items into todo.org (all [#C]: roam-only-startup investigation :spec:, keep-WIP-from-blocking-sync-gate :feature:, install-ai-on-PATH :chore:) — committed 95dd661. Removed the 3 rulesets items from the roam inbox (foreign left untouched), handed git to roam-sync → adeb011, pushed clean. Phase E done. + +Session state: 8 rulesets commits this session (6 inbox proposals + 2 housekeeping), roam inbox reconciled. Nothing pushed to rulesets origin yet. settings.json model-flip still unstaged/unresolved. diff --git a/.ai/sessions/2026-07-13-23-48-runtime-portability-pager-local-llm.org b/.ai/sessions/2026-07-13-23-48-runtime-portability-pager-local-llm.org new file mode 100644 index 0000000..8e55623 --- /dev/null +++ b/.ai/sessions/2026-07-13-23-48-runtime-portability-pager-local-llm.org @@ -0,0 +1,191 @@ +#+TITLE: Session Context — 2026-07-13 +#+AUTHOR: Craig Jennings + +* Summary + +** Active Goal + +A marathon (05:14–23:59) that became four arcs: (1) the runtime-portability children — all eight resolved, ending with a working local-LLM agent lane; (2) the EAT sixel investigation — persistent terminal images plus two dead .emacs.d agents root-caused (OOM from an EXDATE infinite loop, fixed cross-project); (3) the agent pager — Signal paging reconciled, verified, wrapped in a universal agent-page command; (4) full backlog hygiene — inbox zero on both surfaces repeatedly, task-review staleness driven to zero. + +** Decisions + +- Craig kept the fable model pin (d5bc9b3 accidentally committed only a case change — harness rewrote the file pre-staging; corrected in e91073d). +- Thin-pointer AGENTS.md over a generated monolith for non-Claude bootstrap ("1 is the way to go. definitely"). +- All four inventory decisions approved (PreCompact prose downgrade, Stop-teardown via codex notify/manual, Signal-pager portability note, knowledge-base.md capture-layer sentence). +- Signal pager re-graded [#C]→[#B]; ntfy two-way-comms task KILLED (infrastructure torn down 07-04; agent-page + Signal runbook are successors). +- Drop all four legacy ollama models after verification; MoE over dense for Strix Halo; Vulkan backend forced over ROCm (systemd override). +- "Agent pager" naming (the Slough House of pagers); paging documented as two channels everywhere. +- EXDATE fix applied from here (cross-project, Craig-directed); jotto SVG rule and takuzu EAT-visuals rule both accepted with changes; .emacs.d's validate-el.sh load-prefer-newer fix accepted verbatim. + +** Data Collected / Findings + +- tmux transmits sixel only when TERM_SIXEL is set AND the client's cell pixel size is known; stock EAT 0.9.4 silently drops the CSI 14 t query (no t case in its dispatch). EAT persists sixel in scrollback natively; through tmux, passthrough images are write-once but native-path images survive redraws. +- Both .emacs.d agent deaths were kernel OOM kills (111GB emacs) from calendar-sync--get-exdates reading (match-end 0) after split-string clobbered the match data — infinite loop only on comma-separated EXDATE lines far enough into the string. My first two diagnoses (stale sentinel; double-reply keystroke leak) were wrong; the double-reply was real but non-lethal. +- signal-mcp is a velox-local MCP server, not claude.ai-side; the pager identity +15045173983 lives in velox's signal-cli; ratio's signal-cli is Craig's own number (no push). Receive-staleness warnings on both accounts. +- Strix Halo: ROCm allocates correctly but first-touch loads at <25MB/s (unusable for 61GB); Vulkan loads the same model in 44s. gpt-oss:120b: 37.9 tok/s; qwen3-coder:30b: 76 tok/s; both 100% GPU. num_ctx matters (native 128K balloons loads). codex --oss needs --local-provider=ollama explicitly (config.toml append lands in the last TOML table, silently inert). +- The remembered old launcher was aix + hey, removed from dotfiles 2026-04-20 (dab0d5a); bin/ai is their unified successor. lint-org.el's mutate-on-lint defect self-demonstrated during this wrap (no damage — no pipes in example blocks). + +** Files Modified + +Rulesets (16 commits, all pushed through 3984392): SVG rule (cac1aa1), visuals rule + zsh note (8703150), model pin ×2 (d5bc9b3, e91073d), runtime child tasks (1b638e6), inventories doc + decisions + skill parity (44bc344, 0ee94ab), AGENTS.md entry + install paths (6cd3aa3), launcher --runtime / local lane / agent picker (04c3b29, 1b98a9e, 3eed2e1), agent-page + two-channel paging docs (49be354), validate-el.sh fix (7e61dc0), model-floor status (21d14e0), review close-outs (0de6e09, 3984392). Plus dotfiles 5055ef1 (tmux sixel conf, pushed) and .emacs.d 56a3ed86 (EXDATE fix, committed there). System: ollama 0.31.2 + Vulkan override; ~/.codex/AGENTS.md link; two KB node writes (roam-synced). + +** Next Steps + +- Craig's maiden voyage: ai → pick local:gpt-oss:120b → takuzu; watch for skipped startup/gates/session-log/commit-rules — that's the model-floor eval running live. The formal scripted eval remains on the child. +- Signal-pager [#B]: runbook, receive timer on velox, ssh-vs-linked-device decision. +- Watch for .emacs.d's ai-term multi-LLM work (they're shelling out to ai --print-runtimes); upstream EAT PR is their [#D]. +- Backlog is fully current; org-table [#B] bug and launcher-hardening [#C] are the natural next builds. + +* Session Log + +** 2026-07-13 Mon 15:25 — SECOND .emacs.d death root-caused: OOM from an EXDATE infinite loop (my first diagnosis was wrong) + +Craig reported death #2 (launch 14:32, [exited] 14:55:36) — AFTER my double-reply fix, so that diagnosis fell. Corrections to the record: the transcript "queue-operations" are harness task-notification bookkeeping, NOT leaked keystrokes (my death-1 keystroke story loses its evidence — the double-reply leak was real and measured but likely never killed anything); the final "background task killed" entries are claude's SIGHUP handling. Hard evidence chain: EAT scrollback shows no bash prompt before [exited] (session destroyed under a live claude); journalctl nails BOTH deaths to the second — kernel OOM killer at 13:41:57 (emacs, 111GB anon-rss!) and 14:55:36, each inside the session's tmux-spawn scope, which systemd then failed, killing the pane → single-window session gone. The bomb: tests/test-calendar-sync--get-exdates.el's two comma-separated tests (NEW in a9e61207, the off-anchor EXDATE fix) hang forever: calendar-sync--get-exdates sets pos from (match-end 0) AFTER split-string, whose internal string-match clobbers match data → pos becomes the last comma's offset within the VALUE; when that's smaller than the EXDATE line's position, the loop re-matches the same line forever, growing the list to OOM. Verified: exact test string hangs the loaded .elc; bytecode disassembly confirms match-end after split-string; comma-less values don't clobber (failed string-match preserves match data), which is why only the two new tests bomb. Fix shape: capture (match-end 0) into a local BEFORE split-string (or save-match-data). Daemon check: calendar-sync loaded but NO timer → no live-daemon exposure right now. Side finding for the handoff: test-ai-term--show-or-create.el leaks one real tmux list-sessions call past its mocks (read-only, benign, but a mock gap; caught with a tmux PATH shim). Suite-under-shim full run unnecessary — OOM evidence is conclusive. Instruction-bootstrap build parked mid-scoping (no edits yet). Next: Craig decides fix-from-here vs handoff to the .emacs.d agent. + +** 2026-07-13 Mon 23:50 — inbox cleared again + task review COMPLETE (staleness zero) + +Handoff round: .emacs.d's validate-el.sh load-prefer-newer fix accepted verbatim (diff = exactly the two claimed evals; suite green; commit 7e61dc0 PUSHED; reply sent); takuzu ack deleted (all five routed captures filed there as three [#B] tasks); two more acks arrived mid-work and were processed (.emacs.d multi-LLM work underway, will shell out to ai --print-runtimes — interop achieved; home recorded the pager recipe) — sent home a one-line addendum pointing at agent-page so its memory doesn't fossilize the raw ssh recipe. Then the remaining 11 reviews, Craig-delegated: ntfy two-way-comms KILLED (CANCELLED — home tore down ntfy 07-04; Signal pager + agent-page are the successors); triage-intake phone-push re-pointed at agent-page (send half buildable now, recv waits on the Signal runbook); coverage-summary docs task tagged :quick:solo:; three tasks gained missing drawers; the rest confirmed. STALENESS NOW ZERO — the whole backlog is current. Commit 3984392 PUSHED. Emacsd handoff sent earlier re ai-term alignment. Everything on origin through 3984392. Remaining: Craig's takuzu local trial (his keyboard, likely tomorrow) and the wrap. + +** 2026-07-13 Mon 23:22 — agent picker shipped: bare ai asks which brain first + +Craig asked where fzf went and wanted the flow to offer LLMs before projects. Built TDD (9/9 launcher bats, suite 441/0): build_runtime_choices (claude first, codex-as-ChatGPT, one local:<model> line per live ollama model, 3s-timeout query, dead server drops lines), pick_runtime feeding the runtime map, dispatch restructured (picker only on bare ai without --runtime/AI_RUNTIME; check_deps moved after final runtime known), --print-runtimes test seam, usage updated (sed range widened to 2,23 for the grown header). Live print verified: all four lines. Commit 3eed2e1, PUSHED. Also answered: the remembered old launcher was aix + hey in dotfiles, removed 2026-04-20 (dab0d5a) when bin/ai unified them — no shadowed script exists; and the rofi/zenity UI-picker exploration stands offered but unbuilt (rofi -dmenu -multi-select = nearest drop-in; zenity = real GTK checklist; fuzzel single-select only). Roam-ask "agentically democratic" now substantially delivered. Still open: Craig's takuzu local trial, 11 task reviews, wrap. 23:24: sent emacsd the ai-term alignment handoff (runtime map strings incl. the --local-provider gotcha, picker shape + --print-runtimes as a shell-out option, verified perf numbers + Vulkan/num_ctx caveats, AGENTS.md bootstrap note) — Craig is actively working ai-term multi-LLM with that project. + +** 2026-07-13 Mon 23:06 — local runtime LIVE: ai --runtime local = codex --oss over ollama + +Craig pushed (e91073d..21d14e0 landed) and chose to wire the local lane. Shipped TDD (7/7 launcher bats, suite 439/0): local → codex --oss --local-provider=ollama -m $AI_LOCAL_MODEL (default gpt-oss:120b), AGENT_BIN/AGENT_CMD split for the deps check. End-to-end verified: codex exec --oss through local gpt-oss returned a correct completion (~7K tokens). Gotchas learned: codex needs the provider named (flag beats config — appending oss_provider to config.toml lands in the last TOML table and silently does nothing; a stray append was made and removed, ~/.codex/config.toml back to original). Also: upstream-patch question answered — .emacs.d shipped its durable home as c53671b0 (DONE, pushed) and filed the upstream PR as its own [#D] with working/eat-sixel-patch/; nothing needed from rulesets. Recommendation given for the live local trial: takuzu (well-traveled, 78+ tests, 5 fresh small tasks, known Claude baseline); watch for skipped startup/gates/session-log/commit-rules + practical tok/s. Commit 1b98a9e PUSHED. During the edit I briefly clobbered the 16:39 dated heading in todo.org (Edit replaced a heading instead of inserting above it) — caught and repaired in the same minute. Remaining for the session: Craig's live takuzu trial (his keyboard), the leftover 11 task reviews, wrap. + +** 2026-07-13 Mon 20:20 — local inference stack verified end to end; model thread CLOSED + +The gpt-oss saga resolved: ROCm on gfx1151 allocates/offloads correctly but first-touch loads at <25MB/s — two 40-min load attempts died to client timeouts (one mine, one the ceiling; a third was killed externally ~19:00, unexplained). qwen3-coder verified first on ROCm (5s load, 76 tok/s, 100% GPU) proving the generate path, isolating the problem to big-model load speed. Fix: systemd override /etc/systemd/system/ollama.service.d/strix-halo-vulkan.conf (OLLAMA_IGPU_ENABLE=1 + ROCR_VISIBLE_DEVICES="") → Vulkan/RADV as sole inference device → gpt-oss:120b loads in 44 SECONDS, "verified", 37.9 tok/s, 100% GPU. Craig's approved drops executed: tinyllama + llama3 rm'd from the service store, the orphaned 45GB ~/.ollama user store purged. KB node rewritten with the end-state (backend, override path + revert note, per-model numbers, num_ctx gotcha, MoE-not-dense lesson) and roam-synced. Model-floor child updated: remaining work is purely the instruction-following eval (gpt-oss as target, qwen as comparator). Commit 21d14e0. Unpushed now 9: 44bc344..21d14e0. All threads from today are closed or cleanly parked; ready to wrap whenever Craig calls it. + +** 2026-07-13 Mon 18:32 — agent pager shipped; pager thread effectively closed for today + +Full sequence since 17:15: routed all 10 foreign roam items to owners via inbox-send (roam inbox at true zero, takuku typo flagged); Signal reconcile — pager identity +15045173983 lives in velox's signal-cli (signal-mcp = velox-local MCP config, NOT claude.ai-side; corrected the 14:40 inference), ratio's signal-cli is Craig's own number; live page from velox buzzed Craig's phone (confirmed). Then Craig directed universal paging + the "agent pager" rename: NEW claude-templates/bin/agent-page (velox-direct/ssh-relay, UUID baked in, fallback hint; 4 bats, PATH-stubbed; live-verified through the real script — second buzz), protocols.org Paging-Craig rewritten (two channels, signal-mcp demoted), page-me.org + work-the-backlog + INDEX updated, mirror synced, make install linked it (velox inherits next session). Suite 438/0. Commits: 0de6e09 (review+filings), 49be354 (agent pager, incl. the Signal task's dated entries + interim-recipe-to-home note). home got the recipe handoff at 18:14. Signal task remains [#B] with sharpened deliverables: runbook, receive timer, ssh-vs-linked-device. Unpushed count now 8 (44bc344..49be354). Model verification still pending: gpt-oss smoke test backgrounded (bkpjmp2iu), then qwen3-coder smoke, then the four legacy drops + KB update. + +** 2026-07-13 Mon 17:15 — task review finished (Craig delegated the close) + +Craig said "finish the task review" — applied my judgments across the remaining 6 (+1 from the morning = 8 of 18 total; 11 remain for future daily rotations). Changes: Signal pager re-graded [#C]→[#B] (paging is a real capability gap post-inventory — flagged for veto); install-ai-on-PATH tagged :quick:solo:; wrap-up-routing validation gained its missing PROPERTIES drawer; the rest confirmed as-is, all stamped 2026-07-13. Committed 0de6e09 together with the inbox-zero filings. Next per Craig: "handle some of those roam inbox items" — all 10 remaining are foreign, so the sanctioned shapes are inbox-send routing or launching owner agents; presenting options. + +** 2026-07-13 Mon 17:08 — inbox zero (roam mode) run + +Craig called inbox zero mid task-review (review paused at item 2 of 7, roam-only startup, recommendation pending). Local inbox clear. Roam scan: 12 total, 2 rulesets-claimed, 9 foreign (4 archsetup, 4 takuzu, 1 emacsd), 1 mis-prefixed "takuku:" (plainly takuzu's — left in place, typo flagged to Craig), 0 unowned, 0 empty. Claimed items deduped against today's work: the agent-switching ask is partly shipped (--runtime claude|codex 04c3b29; codex = the ChatGPT CLI; ollama/qwen = the reserved local runtime on the model-floor child) — folded as a dated entry on the generic-agent-runtime parent. The new work filed as [#C] "ai launcher hardening — bug hunt + refactor pass" :refactor:solo: (interactive runtime picker rides it). Roam edit: both claimed items removed under a clean capture-guard, roam-sync triggered (10 items remain, all foreign). todo.org edits uncommitted. Monitor for pulls retired at Craig's cue earlier; pull ETA ~18:30 at 12MB/s. + +** 2026-07-13 Mon 16:45 — session plumbing closed with a no-build verdict (8 of 8 children resolved; model-floor eval pending pulls) + +Assessed the four plumbing pieces: session-context-path runtime-aware by design; anchor cycle plain-files-portable; self-inject harness-agnostic (payload is the only Claude-shaped part — codex auto-flush = payload variant, second injected line carries the resume instruction, no hook needed); session-clear-resume.sh stays Claude-only. Section added to the inventories doc, child closed, commit 3ef5580 (suite 434/0). ALL EIGHT runtime children are now resolved or reduced: 6 shipped/closed, launcher shipped, model-floor's remaining work is the evaluation itself, blocked only on the pulls (~19:00 ETA). Unpushed: 44bc344, 0ee94ab, 6cd3aa3, 04c3b29, 3ef5580. + +** 2026-07-13 Mon 16:42 — launcher runtime flag shipped (7 of 8 children done) + +While the pulls run (rate settled ~0.5GB/min, ETA ~19:00): built ai --runtime claude|codex TDD — 6-test bats file driving a new --print-launch seam (prints the pane command, no tmux), runtime→CLI map with local reserved (clear error naming the model-eval dependency), AI_RUNTIME env support, runtime-aware deps check, CLAUDE_CMD→AGENT_CMD at all three launch sites. Verified: codex --help confirms positional-prompt parity with claude; live smoke prints correct commands both runtimes. Suite 434/0. Commit 04c3b29; child closed as dated entry. NOTE: Craig's real sessions launch via .emacs.d's ai-term (aiv-<proj> per-project sessions), NOT bin/ai (shared "ai" session, window per project) — the Emacs-side multi-LLM support is .emacs.d's June handoff, unchanged here. Craig also approved dropping tinyllama + llama3:8b ("annoying little pest") — all four legacy models go after verification. Remaining child: session plumbing, local model floor (eval blocked on pulls). Unpushed: 44bc344, 0ee94ab, 6cd3aa3, 04c3b29. + +** 2026-07-13 Mon 16:40 — ollama upgraded 0.17.7→0.31.2, model pulls running + +Craig picked option 1 (drop both llamas after the new models verify) and directed the full sequence. Research (WebSearch): Strix Halo wants MoE models (bandwidth-bound decode; dense 70B ≈ single-digit t/s) — gpt-oss:120b (~55 t/s reported on this chip) + qwen3-coder:30b (~100 t/s, 256K ctx) are the picks; ollama 0.17.7 was far behind. Upgraded via the official installer (sudo, NOPASSWD): 0.31.2, systemd unit recreated, service active. GPU discovery healthy out of the box: ROCm sees gfx1151 iGPU, 107.4GiB available; Vulkan RADV device present but gated behind OLLAMA_IGPU_ENABLE=1 (community says Vulkan outperforms ROCm on gfx1151 — A/B during the model-floor eval, not now). DISCOVERY: the service uses /usr/share/ollama/.ollama (system store) — holds two MORE models my earlier "complete" inventory missed (tinyllama 637MB, llama3:8b 4.7GB); the 45GB ~/.ollama store is separate and invisible to the service. KB node needs both corrections (stores + new versions) after pulls complete. Pulls running in background (btkrc1y0n, sequential: gpt-oss:120b 65GB then qwen3-coder:30b 19GB) into the system store; du-based Monitor armed (be6nrzl5d). Disk: 3.0T free, no constraint (df is aliased to dfc — use command df). Pending Craig: extend the drop list to tinyllama + llama3? Pending me after pulls: smoke-test both models w/ GPU offload check, ollama rm the drops, purge the orphaned ~/.ollama user store, update KB node + model-floor child, session log. + +** 2026-07-13 Mon 16:15 — local-runtime inventory gathered and promoted to the KB + +Craig asked about ollama; gathered the full local-inference picture. ratio: ollama 0.17.7 (/usr/local/bin, no systemd unit, on-demand), 45GB store with llama3.1:8b + llama3.3 70B, Strix Halo Radeon 8060S iGPU + 125GiB unified RAM — a real inference box, and the on-disk 70B matches the model-floor estimate. velox: no ollama, Iris Xe, 60GiB — out of scope for local inference. codex CLI installed + authed on ratio. Promoted to the KB as agents/20260713161222-local-llm-inference-inventory-daily-drivers.org (committed, roam-sync triggered — first KB write in a long while, the contribute nudge finally earned). Folded the inventory into the model-floor child's body: the evaluation can run TODAY on ratio against the on-disk 70B (scripted startup + publish-flow transcript, grade instruction-following) — no procurement needed. todo.org edit uncommitted. + +** 2026-07-13 Mon 16:10 — instruction bootstrap shipped (6 of 8 children done) + +Memory sentry ran clean during Craig's relaunch (zero alerts, daemon flat 1.97GB, third .emacs.d session alive since 15:44 and healthy). Queue resumed: built the thin-pointer AGENTS.md TDD (bats red→green): canonical claude-templates/AGENTS.md; make install CODEX_DIR stanza (→ ~/.codex/AGENTS.md, live on ratio now; velox auto-picks-up via startup's make install); install-ai.sh seed-only copy at bootstrap; rulesets root tracked symlink as dogfood. NEW scripts/tests/install-agents-entry.bats (3 tests) + 2 install-ai.bats tests. Riders: todo.org line-1357 prose reworded (lint misread "file:→id:" as a dead link), lint-followups.org cleared. Suite 428/0. Commit 6cd3aa3 (also closes the child + adds a dated entry on the agent-source task's thread 2). Inbox: two .emacs.d acks processed + deleted (the dead 14:34 one, and the live session's 15:47 — HEAD 56a3ed86 confirmed, staged eat work intact, mock-leak filed [#C] there, no action needed). Unpushed rulesets commits: 44bc344, 0ee94ab, 6cd3aa3. Remaining children: launcher runtime flag (scoping next — codex mapping clear, local mapping blocked on the model-floor/default-runtime decision), session plumbing, local model floor. Then the org-table bug + 6 task reviews. + +** 2026-07-13 Mon 15:32 — EXDATE bomb fixed, .emacs.d suite green end-to-end, agent cleared to relaunch + +Craig chose fix-from-here. Applied the capture-line-end-before-split fix to calendar-sync-recurrence.el (with a why-comment naming the clobber mechanism), deleted + recompiled the .elc, reloaded into the daemon (live Emacs calendar-safe again). Verification: the two previously-infinite tests pass in microseconds; all 12 exdates tests green; all 56 calendar-sync files green; then the FULL suite ran to completion for the first time today — all 599 elisp files + 18 bats green, including the dead agent's uncommitted eat-config guard tests. Committed 56a3ed86 in .emacs.d with a pathspec so the agent's staged eat work stayed untouched; not pushed. Superseding handoff sent to .emacs.d inbox (2026-07-13-1530): corrected diagnosis (journalctl OOM evidence), fix details, the show-or-create mock-leak finding, the double-reply note downgraded to latent-bug status, explicit safe-to-relaunch incl. the suite. Craig can relaunch the .emacs.d agent now. Resume thread: instruction-bootstrap build (thin-pointer AGENTS.md, Craig approved shape) was mid-scoping when death #2 interrupted — Makefile install stanza + install-ai.sh seeding + canonical claude-templates/AGENTS.md + bats, TDD. + +** 2026-07-13 Mon 14:55 — pin pulled: four decisions applied, skill parity done + +Craig pulled the pin and approved all four inventory decisions. Applied: knowledge-base.md capture-layer sentence (non-Claude runtimes capture into the session log), Signal-pager task gained the runtime-portability motivation entry, decisions recorded in the doc, VERIFY closed as a dated entry. Commit 44bc344. Then the skill-parity child: inventoried 11 skills + 18 commands, key finding is that one resolution sentence in the bootstrap entry file makes all 29 portable to any file-reading harness (auto-invocation degrades to by-name, which the publish flow already uses; flush excluded, session-plumbing owns it; native registration optional nicety). Section appended to the inventories doc, child closed as dated entry, commit 0ee94ab. Both commits local/unpushed (with 44bc344). 5 of 8 children done. Remaining: instruction bootstrap (needs Craig's design input, overlaps the Multiple agent-source improvements task), launcher runtime flag, session plumbing, local model floor. + +** 2026-07-13 Mon 14:30 — PIVOT RESOLVED: .emacs.d agent death root-caused and fixed + +Craig's urgent pivot: the .emacs.d agent session (launched 13:20) died — tmux session aiv-_emacs_d gone, [exited] in the launcher shell. Investigation chain: stale-sentinel theory disproved (multiple turns survived; no sentinels; hook tests clean). Real cause: DOUBLE XTWINOPS REPLIES. The .emacs.d agent had picked up our eat-xtwinops handoff, designed an advice-based fix (cj/--eat-answer-xtwinops :before eat--t-handle-output, written to modules/eat-config.el 13:33, test file 13:32), and evaluated it into the live daemon — where MY morning spike (patched parser with its own CSI t clause, loaded 10:52) was still active. Two answerers → tmux consumes reply #1, forwards reply #2 to the active pane as raw keystrokes (ESC + junk) — and Emacs ptys report 0 pixel size so EVERY frame resize re-queries. Its transcript (83070ecd, ends 13:41:57) shows the signature: four empty queue-operations, then background make test killed as claude exited. Exact final exit trigger unreconstructable from the transcript; the leak itself confirmed empirically: probe showed 2 replies per query. Fix: reloaded stock eat.elc into the daemon 14:27 (advice survives redefinition and is now sole answerer); re-probe = 1 reply. Probe buffer cleaned up. Handoff sent to .emacs.d (2026-07-13-1427): root cause, safe-to-resume, the latent no-op-claim bug in its advice comment (must guard against upstream shipping the clause or the double-reply returns), rerun its killed ERT, and the coordination lesson (our 10:58 handoff should have disclosed the live spike — our omission). Craig can relaunch the .emacs.d agent now; it resumes from its own anchor. + +** 2026-07-13 Mon @ 14:16:49 -0500 — SUSPENDED (urgent pivot; supersedes the 13:22 entry) + +Open threads, most active first: +- ACTIVE: runtime-portability children under the generic-agent-runtime parent (todo.org ~line 231). Done: hook parity, MCP portability, memory story — all three now dated entries pointing at docs/design/2026-07-13-runtime-portability-inventories.org. AWAITING CRAIG: the *** VERIFY with four decisions (PreCompact prose downgrade; Stop-teardown via Codex notify/manual; Signal-pager portability note; knowledge-base.md capture-layer sentence) — all recommended yes, he was about to answer when the pivot hit. Next child queued: skill parity (decision matrix over voice/review-code/flush etc.: re-register per harness vs fold into .ai/workflows/), then instruction bootstrap, launcher flag, session plumbing, local model floor. +- PINNED (from 13:22, still queued): org-table helpers bug ([#B] :bug:solo:, todo.org ~line 66) + the todo.org "→id" dead-link rider from lint-followups.org. +- PINNED: task review, 6 of 7 unwalked; they stay at the head of the next batch. +- SET ASIDE: sixel/EAT — .emacs.d has the patch filed [#B] with the durable-home design call coming to Craig; after it lands + velox pulls dotfiles and .emacs.d, run the velox test (client_cell_width nonzero, then magick sixel in a tmux window, Craig's eyes). + +Pending decisions: the four VERIFY items above. Nothing else blocks. + +Shipped since the 13:22 suspend (all pushed): 828f70a..1b638e6 (SVG rule cac1aa1, visuals rule 8703150, model d5bc9b3, todo folds dc13bce, child-task filing 1b638e6) and e91073d (model fix — d5bc9b3 had committed opus→Opus from a harness rewrite, not the fable flip; e91073d lands fable as Craig chose). + +Uncommitted work: todo.org (three children converted to dated entries + the new *** VERIFY), docs/design/2026-07-13-runtime-portability-inventories.org (new file, untracked), this anchor. Commit shape when resumed: docs+todo together as the inventories chore/feat commit. + +Key findings not recorded elsewhere: none — everything is in the inventories doc, the todo entries, or this log. Inbox is clear (the 13:23 .emacs.d ack was read and deleted). + +Background work: none running. + +Resume hint: read the *** VERIFY under the generic-agent-runtime parent, get Craig's four answers, apply the two tiny approved edits (Signal-pager note + knowledge-base.md sentence), commit todo.org + the inventories doc, then start the skill-parity matrix. + +** 2026-07-13 Mon @ 13:22:43 -0500 — SUSPENDED + +Craig stuck a pin in everything mid task-review walk. Resume-weighted state: + +Open threads, most active first: +- PINNED: org-table helpers bug ([#B] :bug:solo:, todo.org line 66). Craig queued it as the next work item ("2 then 1" — review done-ish, this is the 1). Fully specified in the task body: two defects (line-based table detection mangles example/src blocks; lint-org.el writes without --fix), repro from work 2026-07-09, regression test named. TDD it: canonical scripts live in claude-templates/.ai/scripts/, mirror-synced; tests are ERT via make test. Start here. +- PINNED: task review, stopped after 1 of 7. Batch was the staleness --list top 7; task 1 (build-to-prototype [#B]) kept + stamped 2026-07-13. Items 2-7 (roam-only startup, WIP sync gate, install-ai PATH, org-table bug, Signal pager, KB orphan pass) unreviewed — they stay at the head of the next run's batch automatically. +- SET ASIDE: sixel/EAT thread — rulesets + dotfiles halves are DONE and shipped; waiting on Craig to fast-track the eat-xtwinops.patch handoff sitting in .emacs.d/inbox/ (2026-07-13-1058-from-rulesets-*). After .emacs.d lands it and velox pulls dotfiles + .emacs.d, run the velox test: restart EAT/tmux server on velox, tmux display -p '#{client_cell_width}' nonzero, then magick logo: sixel:- into a tmux window, Craig eyeballs. Note ratio's working state is runtime-only until the .emacs.d patch lands (Emacs restart loses it; tmux server restart loses the feature flags — the dotfiles conf now covers that half). +- DEFERRED: lint-followups.org rider — dead file link at todo.org (was line 1314 pre-edit; re-grep for "→id"), promised as a rider on the next work item. +- POST-SUSPEND ADDITION (13:27): Craig asked for the ChatGPT/local-LLM gap assessment to be filed. Found the existing parent (** TODO [#D] Generic agent runtime support — Codex spec v0) and added 8 child tasks under it (instruction bootstrap, skill parity, hook parity, launcher flag, session plumbing, memory story, MCP portability, local model floor) plus a dated decomposition entry. todo.org edit uncommitted, riding with the review stamp. + +Pending decisions / open questions for Craig: none blocking. (Model flip resolved: keep fable, committed.) + +Shipped this session (all local, UNPUSHED except dotfiles): +- rulesets cac1aa1 — SVG-first rendering rule (emacs.md + ui-prototyping.md cross-link), jotto proposal accepted, reply sent. +- rulesets 8703150 — Showing Craig Visuals rule (interaction.md) + zsh word-split note (protocols.org canonical+mirror), takuzu proposal accepted with corrections, reply sent. +- rulesets d5bc9b3 — model opus→fable (Craig: keep). rulesets dc13bce — todo.org Signal-pager fold. +- dotfiles 5055ef1 — tmux terminal-features sixel lines, COMMITTED AND PUSHED to origin; inbox handoff there rewritten as applied-FYI. +- Handoffs delivered: jotto (acceptance), takuzu (acceptance + capability correction), emacsd (eat-xtwinops.patch + intro), dotfiles (superseded by direct apply). + +Uncommitted work: todo.org (the :LAST_REVIEWED: 2026-07-13 stamp on the build-to-prototype task — one drawer line, safe to commit with the next housekeeping/wrap). .ai/session-context.org is this live anchor (untracked, stays). + +Key findings not recorded elsewhere: none — the sixel investigation's durable facts live in the two rule commits, the emacsd/dotfiles handoffs, and this log. The four unpushed rulesets commits are the main crash exposure; push happens at wrap. + +Background work: none running. tmux sixel test windows killed; *sixel-probe* buffer killed; tmux server carries runtime-only settings (terminal-features sixel entries, allow-passthrough all) that are now redundant with the pushed dotfiles conf (passthrough) or pending the .emacs.d patch (features). + +Resume hint: start the org-table helpers bug via TDD (regression test first: a #+begin_example block of pipe-prefixed lines must survive wrap-org-table.el byte-identical), and fold in the todo.org:1314 link fix as the rider. + +** 2026-07-13 Mon 05:14 — Startup + inbox processing begins + +Ran startup: rulesets pull skipped (dirty tree — .claude/settings.json shows model opus→fable, same harness flip Craig reverted on 2026-07-11), nothing new to link, no branches behind, .ai/ synced from templates. No crashed session. Notes.org: no reminders, no pending decisions. Staleness: 18 top-level tasks unreviewed >7 days. Roam inbox: 8 items, none rulesets-owned (4 takuzu, 3 archsetup, 1 emacs.d). + +Inbox had 3 handoffs. Processed the home FYI (2026-07-11-1208, Signal pager ack): folded as a dated sub-entry into the [#C] "Document (and own) the Signal pager" task body — home confirms rulesets owns it, and reports signal-mcp wasn't connected in its 2026-07-09 session and its signal-cli is registered as Craig's own number (note-to-self pushes nothing), so home currently has no live page channel. Deleted the inbox file. No reply sent — the handoff was itself home's ack of our earlier reply; nothing to close. + +Remaining: two shared-asset proposals (jotto SVG-rendering rule, takuzu EAT-image-display rule) — skeptical reviews done, surfacing to Craig for approval next. + +** 2026-07-13 Mon 05:25 — jotto SVG rule shipped + +Craig approved option 1 (accept with changes). Added the SVG Rendering section to claude-rules/emacs.md (consider-by-default phrasing, full constraint sheet, hybrid guidance verbatim) and the svg.el port-target paragraph to claude-rules/ui-prototyping.md section 3. /review-code --staged: approve, one Minor wording nit self-fixed (1:1 repetition). make test green (273 ok, ERT clean — the 9 grep hits were test names containing "error"). Commit cac1aa1 as Craig. Replied to jotto via inbox-send.py (landed in jotto's inbox), deleted the inbox file. Note: bare inbox-send isn't on PATH in this shell — used .ai/scripts/inbox-send.py directly. Settings.json model flip still undecided (Craig hasn't answered keep-or-revert). Next: takuzu EAT-image proposal. + +** 2026-07-13 Mon 09:40 — EAT sixel capability verified live (takuzu claim corrected) + +Craig challenged takuzu's "EAT can't display images" claim; verified it's wrong on capability. Findings: EAT 0.9.4 fully implements Sixel — advertises it in DA (\e[?12;4c, feature 4 = sixel), answers XTSMGRAPHICS, renders via eat-sixel-render-formats (xpm first; daemon has xpm+svg). Live test: magick logo → sixel, wrapped in tmux passthrough DCS (ESC Ptmux; with doubled ESCs), cat from a VISIBLE pane → full-color 640px image rendered in the EAT buffer (Craig's screenshot 09:36:42). Caveats found: (1) image is a transient screen-anchored overlay — any scroll/redraw drops it; (2) tmux allow-passthrough "on" drops passthrough from invisible panes — set allow-passthrough "all" globally at RUNTIME ONLY (needs tmux.conf for persistence, both daily drivers); (3) img2sixel/libsixel 1.10.5 on ratio is broken — exit 0, zero bytes, all formats; use magick ... sixel: instead; (4) writes into the Claude Code TUI pane get instantly repainted over — draw in a separate visible pane. Test window killed, cleanup done. Next: re-present takuzu disposition with corrected facts. + +** 2026-07-13 Mon 09:55 — Root cause of sixel vanishing: tmux grid, not EAT + +Craig asked why the image is irretrievable once offscreen. Isolated it: created a background plain-EAT buffer (*sixel-probe*, eat-make, no window shown) in the daemon, fed it the RAW sixel (no tmux), scrolled 60 lines, re-scanned: all 60 image cells persist at pos 163, before display-begin 654 — EAT keeps sixel in scrollback natively (each cell is a buffer char with an XPM-tile display property, per eat--t-sixel-flush-line). So tmux is the killer: passthrough bypasses tmux's grid, tmux's model of those lines is text-only, and every repaint (scroll/window-switch/output) rewrites them, destroying EAT's image cells. Nothing upstream stores the pixels → irretrievable. Follow-up lead: tmux 3.7b binary has sixel compiled in, but client_termname is xterm-256color (TERM override somewhere in the launch chain — EAT would normally set eat-truecolor), whose terminfo doesn't advertise sixel, so tmux's native sixel path never engages. Native path would need terminal-features 'xterm*:sixel' + client reattach to test. *sixel-probe* buffer left alive so Craig can see the scrollback image; kill at wrap. + +** 2026-07-13 Mon 11:00 — EAT XTWINOPS spike: full native-sixel fix, verified, handed off + +Craig chose the spike (option 1). Found the final gate in tmux source (cloned 3.7b shallow to scratchpad): tty_cmd_sixelimage falls back to the text placeholder when TERM_SIXEL is unset OR tty xpixel/ypixel are 0. First was cleared by terminal-features 'xterm*:sixel'; second was the blocker — Emacs never fills pty pixel fields, and tmux's fallback query (CSI 18t + CSI 14t at attach) is silently swallowed by EAT (no 't' case in its CSI dispatch; confirmed client_cell_width 0x0). Spike: copied eat.el to scratchpad, added eat--t-send-window-size-report (answers CSI 14/16/18 t from char-width/height + disp dims, mirroring the XTSMGRAPHICS reply) + one dispatch case. Byte-compiled clean, loaded into the live daemon (survived — this session runs in EAT), probe confirmed replies (R14 4;576;880, R16 6;24;11, R18 8;24;80). After Craig's reattach: client_cell_width 11x24, and the invisible-window grid-redraw test PASSED — wizard image drawn from tmux's grid, survives window switches, scrolling, AND resizing (Craig verified visually). Note: first probe buffer vanished mid-session (unexplained; daemon uptime 1.5d rules out restart), recreated fine. img2sixel (libsixel 1.10.5) broken on ratio — magick sixel: is the working encoder. Velox verified over tailscale: same sixel-enabled tmux 3.7b + imagemagick — ready once configs land. Handoffs sent: emacsd got the eat-xtwinops.patch + intro (durable home, TDD productionization, upstream to codeberg akib/emacs-eat); dotfiles got the two tmux.conf lines. Everything on ratio is RUNTIME-ONLY until those land (Emacs restart loses the patch, tmux server restart loses the flags). Remaining: takuzu disposition (rule now reflects working persistent sixel), model-flip question, cleanup (sixel-final window, *sixel-probe* buffer). + +** 2026-07-13 Mon 12:00 — takuzu rule shipped, dotfiles committed + pushed + +Craig approved option 1 and directed the dotfiles commit+push. Applied: (1) rulesets 8703150 — Showing Craig Visuals section in interaction.md (never rely on SendUserFile display; browser/imv lane per desktop-capture.md; sixel quick-glance lane behind the client_cell_width capability gate) + zsh no-word-split note in protocols.org canonical + mirror (sync-check --fix). Suite green 423/0. Corrective acceptance reply sent to takuzu (its capability claim corrected with the CSI 14 t findings); inbox file deleted — local inbox now clear. (2) dotfiles 5055ef1 committed as Craig and pushed to origin (cjennings.net): two terminal-features sixel lines in common/.tmux.conf (allow-passthrough already on); dotfiles suite 10/10 OK; the pending rulesets handoff in dotfiles/inbox rewritten as an already-applied FYI. Cleanup done earlier this turn-series: sixel-final tmux window killed, *sixel-probe* buffer killed. Plan: Craig fast-tracks the emacsd patch, then a velox end-to-end test after velox pulls both repos. Still open: model flip (settings.json, unanswered), todo.org Signal-pager fold uncommitted. + +** 2026-07-13 Mon 13:35 — resumed: runtime-portability work begun, three inventories done + +Craig pulled the pin ("commit and push that, then let's work on all those remaining tasks"). Pushed 828f70a..1b638e6 (5 commits incl. the child-task filing), then found and fixed a defect: d5bc9b3 had committed opus→Opus (harness rewrote the file to "Opus" before staging), NOT the fable flip Craig chose — corrected + pushed as e91073d. Mid-work inbox arrival: .emacs.d ack of the EAT patch (filed [#B] there, design call going to Craig) — pure FYI, deleted. Then the three inventory children: wrote docs/design/2026-07-13-runtime-portability-inventories.org (hooks: only PreCompact + Stop carry porting work, AskUserQuestion moot, validators ride githooks; MCP: nine local servers portable, signal-mcp is claude.ai-side only so paging has NO off-Claude path — Signal-pager task is the fix; memory: KB already cross-agent, one sentence gap in knowledge-base.md). Converted the three children to dated entries, added a *** VERIFY carrying the four decisions (all recommended yes). todo.org + new doc uncommitted. Next children: skill parity (decision matrix), instruction bootstrap, launcher flag, session plumbing, local model floor. + +** 2026-07-13 Mon 12:15 — task review stopped early at Craig's call + +Craig picked "2 then 1" (task review, then org-table bug). Review batch was the 7 oldest-unreviewed; task 1 (build-to-prototype rule extension [#B]) kept as-is and stamped :LAST_REVIEWED: 2026-07-13 (assessed not-quick, not-solo — placement decision pending). Craig then said "let's stop here" — review closed early, 6 tasks untouched for the next run. The org-table bug (queued item 1) not started. todo.org edit uncommitted; ambiguity noted: "stop here" might mean stop-review or end-session — asked Craig which. + +** 2026-07-13 Mon 12:05 — model flip kept, housekeeping committed + +Craig chose to keep the fable model flip (deliberate this time, unlike the 2026-07-11 revert). Committed d5bc9b3 (settings model opus→fable) and dc13bce (todo.org Signal-pager fold). Both mechanical, subject-only messages; the 11:45 green suite run covers this tree state. Working tree now clean except the live session-context file. Rulesets has 4 unpushed commits (cac1aa1, 8703150, d5bc9b3, dc13bce); push at wrap per usual flow. Session remaining: nothing pending from the inbox; awaiting Craig's next move (emacsd fast-track, then velox test). diff --git a/.ai/sessions/2026-07-14-02-50-sentry-spec-review-lint-org-fix.org b/.ai/sessions/2026-07-14-02-50-sentry-spec-review-lint-org-fix.org new file mode 100644 index 0000000..280ab5b --- /dev/null +++ b/.ai/sessions/2026-07-14-02-50-sentry-spec-review-lint-org-fix.org @@ -0,0 +1,84 @@ +#+TITLE: Session Context — 2026-07-14 +#+DATE: 2026-07-14 + +* Summary + +** Active Goal + +Take the work project's sentry proposal (one supervisor loop running a project's hygiene passes overnight, with locking) from inbox arrival to a fully-dispositioned spec: decision walk, spec-create, adversarial review, findings walk. Mid-walk, fix the lint-org data-corruption bug the review surfaced as a sentry prerequisite. Plus two inbox waves (8 handoffs total) processed along the way. + +** Decisions + +- Sentry design, all resolved live with Craig (recorded as 10 DONE decisions + 12 DONE findings in the spec): host-suffixed sentry branch (Craig's call — unpushed commits on main diverge across two daily drivers); host-local locks under /run/user/<uid>/agent-locks/ (a lock inside the roam repo would ride roam-sync's git add -A to the other machine); mkdir-atomic lock helper with heartbeat refresh + bounded-wait-then-defer (flock can't span tool calls); interactive entry gates, NO report-only mode anywhere (Craig's explicit direction — passes run fully or skip); roam-sync stays the roam repo's only committer; :COMMIT_AUTONOMY: yes gates sentry entirely; detection-over-configuration pass portability; launch contract with in-place checkout (worktrees rejected: untracked inbox drops invisible there); inbox pass under the no-approvals contract; suite at entry + conditional fire-end run only; KB promotion pass cut to vNext (heuristic task filed); wrap-up refuses during a live loop, "stop sentry" owns shutdown; entry ff-only reconcile; persistent stall notify after 2 skips. +- Craig corrected a misread mid-turn: the .emacs.d signel request meant "remove signel mentions from live workflows," not "run a broadcast + question smoke's scope." No broadcast ran; smoke's scope stands; nothing had been sent. +- Spec stays DRAFT deliberately — Craig reviews deeply before the READY flip. + +** Data Collected / Findings + +- The 2026-07-09 org-file corruption's true mechanism: wrap-org-table.el's load-time CLI dispatch fired when lint-org.el merely required it, running the table reformatter over lint-org's file arguments (--check runs were immune because the flag made wot's dispatch decline). The filed defects (block-blind scanning, mutate-by-default) were real but secondary. +- flock binds to a living process; agent Bash calls are short-lived shells, so locks for agent workflows need mkdir-atomicity + staleness semantics, never flock. +- rg skips hidden directories by default — a grep over .ai/ paths silently misses everything without --hidden (bit this session during the signel audit). +- No locking existed anywhere in .ai/scripts/ (verified); the roam sync-conflict forks are the observed cost. + +** Files Modified + +- docs/specs/2026-07-14-sentry-workflow-spec.org — NEW: the sentry spec, DRAFT, 10/10 decisions, 12/12 findings resolved, voice-passed. docs/design/2026-07-14-sentry-workflow-proposal.org — NEW: preserved origin proposal + cover note. +- Commit 951b6fc — lint-org/wrap-org-table corruption fix, test-first (6 new tests): block-type-aware scanning, lint-org CLI report-only by default (--fix required; wrap-it-up + /lint-org command updated), wot entry-script dispatch guard. Commit 32afa41 — work's .flac threading adopted into process-meeting-transcript.org. Plus the signel history-line chore. All pushed (ad5bb07). +- todo.org — sentry build task [#B] + cross-host [#D] + /schedule [#D] + KB-heuristic [#D] filed; org-table bug closed DONE and archived to Resolved. +- notes.org — :LAST_INBOX_PROCESS: 2026-07-14; Active Reminder: Craig reviews the sentry spec before ending the next session. + +** Next Steps + +- FIRST: Craig's deep read of the sentry spec (his explicit ask — surface the reminder at startup). READY flip, then spec-response decomposes the build. +- The paused task review: batch of 7 selected, task 1 (sentry build) walked but no action picked; 6 untouched. The sentry build task is also the one 30-day-staleness hit (never-reviewed, filed tonight) — the next review cycle covers it. +- Sentry build follows the spec's 4 phases once READY (agent-lock helper first). + +KB: promoted 2 / consulted no + +* Session Log + +** Startup + inbox processing + +Session started 2026-07-14 00:11 CDT on ratio. Fresh session, templates synced, no reminders, staleness 0, roam inbox 0. + +Inbox: three items. (1+2) Sentry workflow proposal from work (intro + full proposal) — passed value gate (Q2), skeptical review found: real problem (verified no flock anywhere in .ai/scripts/), sound invariants, but four issues: roam lock held across commit-push conflicts with inbox.org's edit-then-roam-sync discipline; flock can't span short-lived tool-call shells (needs PID/staleness lockfile or mkdir semantics); unattended per-pass commits need gating on :COMMIT_AUTONOMY:; passes 3 and 10 are sender-specific (need auto-skip/config). Craig picked option 2: work it now, walk decisions, spec/build tonight. Then a task review. (3) archsetup FYI (maint update-queue CVE item done, dotfiles 6757b51) — acknowledged, deleted. Craig also directed deleting the standing inbox/lint-followups.org. + +Proposal preserved to docs/design/2026-07-14-sentry-workflow-proposal.org (cover note merged in). Reply sent to work: accepted, being worked now. + +** Sentry decision walk (all decided with Craig, one by one) + +- D1 commit target: dedicated sentry branch, host-suffixed (sentry/YYYY-MM-DD-<host>). Craig's call, overriding my current-branch rec: unpushed commits on main across two daily drivers = diverged main, breaks startup ff + template sync. Morning flow: review, squash-merge, delete branch. Prior unmerged sentry branch at entry → skip fire. +- D2 roam-write lock scope: host-local only; cross-host conflict-surfacing filed as follow-up. +- D3 interval: hourly default, config knob. +- D4 gates: interactive at entry (Craig is at terminal when he launches) — dirty tree stops and surfaces what's dirty + options (finish the job / stash / rollback which changes); red suite same shape. NO report-only mode anywhere (Craig's explicit direction): unattended fire hitting unsafe state skips with one digest line; next fire retries. Morning-approval queue for destructive/judgment actions stays (permanent division of labor, not a degraded mode). +- D5 roam git: roam-sync stays the only committer; lock wraps edit + trigger. Reconciliation surfaced: knowledge-base.md + roam-sync.sh header still say agents pull/commit/push inline — join companion-file list. +- D6 locks: shared .ai/scripts/ lock helper, mkdir atomicity, PID/host/timestamp metadata, age-based staleness reclaim, acquire/release/status, bats-tested. (flock can't span tool calls.) +- D7 autonomy: :COMMIT_AUTONOMY: yes gates sentry entirely — absent/no → declines to start, names the marker. +- D8 portability: auto-skip by detection (existence probes per pass), no config. +- D9 path: spec-first (spec-create → docs/specs/2026-07-14-sentry-workflow-spec.org). + +** Sentry spec authored + +Wrote docs/specs/2026-07-14-sentry-workflow-spec.org (DRAFT, ID f6c51f27-d7a2-4b63-9ff9-5ba005a66dfb): full spine — summary, problem, goals/non-goals/scope, two-altitude design (user + implementer), 5 alternatives with why-nots, 9 DONE decisions, 4 implementation phases, acceptance criteria, readiness dimensions, risks. Voice pass ran (/voice general per spec-create Phase 4): ~20 em-dashes reduced to structural-only, 2 prose fragments completed, passives activated, one correctness catch (design said "never pulls" roam but pass 1 is an ff-only pull — reconciled). Self-rubric: Ready. Cross-linked: todo.org gets the [#B] build task + two [#D] vNext deferrals (cross-host conflict surfacing, unattended /schedule variant). Next: spec-review gate, then Craig's queued task review. + +** Adversarial spec review (Craig's weakness-loop method) + +Task review started (batch of 7, task 1 = sentry build) but Craig redirected: run the spec through review now via a weakness loop — 20 weaknesses, check each against the spec, loop until <5 new, then notify him for deep review. Ran 3 rounds (20 → 7 → <5). Recorded 10 blocking + 2 non-blocking findings in the spec's Review findings section. Heavy hitters: overnight working-tree ownership (sentry checkout vs Craig's live session — biggest open design question); roam-write lock is sentry-only unless inbox.org/knowledge-base.md writers also acquire it; lock dir inside roam repo would be committed by roam-sync's git add -A (relocate to /run/user); sentry's session-context writes trip its own dirty-tree skip; pass 8 depends on the filed lint-org mutate-on-lint [#B] bug; unattended inbox semantics; per-pass commits vs verification.md suite rule (needs a recorded decision); KB lesson source undefined; lock wait/heartbeat mechanics; wrap-up-during-loop undefined. Rubric: Not ready; status stays DRAFT. Iteration history entry added. Craig notified for his deeper review. Task review batch (7 tasks) still pending — resumes after. + +** Findings walk + lint-org fix shipped mid-walk + +Walking the 12 findings with Craig one by one. Decisions so far: F1 = launch contract (in-place checkout, sentry owns the repo overnight, dirty-skip backstop, Emacs revert caveat documented). F2 = all roam writers acquire the lock (inbox.org core §5 + knowledge-base.md write recipe gain acquire/release; graceful degradation when helper absent; bounded-wait-then-surface for interactive callers). F3 = both locks under /run/user/<uid>/agent-locks/, helper owns path scheme, ~/.cache fallback. F4 = spine-set exclusion in dirty check + fire-end digest commit + path via session-context-path. + +At F5 Craig redirected: fix the lint-org bug now. Done, TDD (red shown first): block-type-aware scanning in wrap-org-table.el wot-process-file + lint-org.el lo--check-tables (type-matched end marker so literal inner markers can't re-expose a block); lint-org CLI report-only by default, writes behind --fix (--check kept as alias; wrap-it-up + /lint-org command updated to pass --fix). Root cause discovered during red-phase debugging: wrap-org-table's load-time CLI dispatch fired on lint-org's require and ran the reformatter over lint-org's file args — THE 2026-07-09 corruption mechanism (--check runs were immune because the flag made wot's dispatch decline). Entry-script guard added (dispatch only when -l names wrap-org-table.el itself). /review-code --staged ran: one Important (boolean in-block flag cleared by literal inner end-marker) fixed in-review + regression test; verdict Approve. Suite 0 failures. Committed 951b6fc (unpushed). todo.org bug task closed DONE; spec finding F5 completed DONE. Minor follow-up noted (not filed): lo--check-indented-headings still has the naive boolean flag, benign (suppression-only). + +Second archsetup FYI (tailscale-amber fixed, dotfiles 4840463 + archsetup aed151b, velox TOML hand-refresh is archsetup's item) acknowledged + deleted. Inbox clean. + +Next: resume walk at F6 (unattended inbox semantics), then F7-F12, then Craig's deep spec review, then the paused task review (task 1 of 7 pending — sentry build task, now also needs the F5 resolution reflected... its body references the spec; fine as-is). + +** Findings walk completed — all 12 dispositioned, spec updated + +F6 = inbox pass under the no-approvals contract, everything to the morning queue. F7 = suite policy recorded as Decision 10 (entry run + conditional fire-end run when non-org files changed, no per-pass runs). F8 = KB promotion pass CUT to vNext (Craig), lesson-detection-heuristic task filed [#D] :spec: (blocks re-adding). F9 = bounded-wait-then-defer + heartbeat refresh, reclaims surfaced. F10 = wrap-up REFUSES during live sentry (Craig picked simple over shutdown-pre-step); "stop sentry" operation owns loop-cancel + branch/queue disposition. F11 = entry ff-only reconcile, diverged joins interactive gate. F12 = persistent notify after 2nd consecutive unmerged-branch skip, then at most daily. All 12 findings flipped DONE in place; design/scope/decisions/acceptance/risks updated to match (pass list now 10, Decisions [10/10], locks at /run/user/<uid>/agent-locks/, launch-contract paragraph in user altitude). Status stays DRAFT for Craig's deep read; responder entry added to iteration history. + +** Second inbox wave (5 handoffs) + Craig's broadcast correction + +Two more archsetup FYI closures (memory-killer = live-refresh hairline artifact, filed [#C] their side; net-panel speedtest history filed [#C] their side) deleted. work's process-meeting-transcript .flac update: diff-verified verbatim (12 hunks, pure .flac threading), adopted into canonical, committed 32afa41, confirmation sent to work. .emacs.d's signel-deprecation broadcast request: I misread it as run-a-broadcast + flag smoke's scope; Craig corrected mid-turn — NO broadcast, smoke's scope is well defined and needed, and the actual ask was removing signel mentions from live workflows. Broadcast draft deleted from /tmp before anything sent; nothing reached smoke. Grep gotcha: rg skips hidden dirs by default, so .ai/ paths need --hidden. One live-workflow mention found (triage-intake.org:417 incident history line) — rewritten to keep the lesson, drop the client name (commit next to 32afa41: chore(workflows): drop retired Signal-client name). Session archives + frozen design doc keep theirs as history. Reply sent to .emacs.d. Inbox clean (0 pending). diff --git a/.ai/workflows/INDEX.org b/.ai/workflows/INDEX.org index 88721ed..b031dbe 100644 --- a/.ai/workflows/INDEX.org +++ b/.ai/workflows/INDEX.org @@ -1,5 +1,5 @@ #+TITLE: Workflow Index -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-04-25 * Purpose @@ -110,8 +110,8 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e - Situational triggers: "broadcast the <event> to all projects", "broadcast that <situation>", "let every project know I'll be away ..." - =flashcard-review.org= — review an org-drill flashcard file, restructure cards to question-form headings (no answer hints), audit content accuracy against project source-of-truth via subagent, rewrite source preserving SRS state, regenerate the Anki =.apkg= to =~/sync/phone/anki/=. Person cards use "Who is X? Tell me about their Y."; talking-points cards stay as-is. Script behavior: =flashcard-to-anki.py= strips =:PROPERTIES:= drawers + =SCHEDULED:= / =DEADLINE:= planning lines from Anki output. - Triggers: "review the flashcards", "update the flashcards", "review the drill deck", "update the drill deck", "refresh the Anki cards", "let's run the flashcard-review workflow" -- =page-me.org= — set a timed notification. - - Triggers: anything containing the word "page" used as a verb ("page me", "page me in 10 minutes", "page me at 3pm") +- =page-me.org= — set a timed notification (desktop =notify=; phone via =agent-page= when Craig is away). + - Triggers: anything containing the word "page" used as a verb ("page me", "page me in 10 minutes", "page me at 3pm", "page my phone") - =status-check.org= — proactive long-running-job updates. - Triggers: "keep me posted on this", "provide status checks on this job", "let me know when it's done", "monitor this for me". Auto: any job estimated 10+ min. - =create-workflow.org= — define a new workflow. diff --git a/.ai/workflows/add-calendar-event.org b/.ai/workflows/add-calendar-event.org index 2650fb7..5dd6c42 100644 --- a/.ai/workflows/add-calendar-event.org +++ b/.ai/workflows/add-calendar-event.org @@ -1,5 +1,5 @@ #+TITLE: Add Calendar Event Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/broadcast.org b/.ai/workflows/broadcast.org index 60e9ed1..cc14f00 100644 --- a/.ai/workflows/broadcast.org +++ b/.ai/workflows/broadcast.org @@ -1,5 +1,5 @@ #+TITLE: Broadcast Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-29 * Overview diff --git a/.ai/workflows/clean-todo.org b/.ai/workflows/clean-todo.org index a1b2af5..48d3084 100644 --- a/.ai/workflows/clean-todo.org +++ b/.ai/workflows/clean-todo.org @@ -1,5 +1,5 @@ #+TITLE: Clean-Todo Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-11 * Overview diff --git a/.ai/workflows/code-quality.org b/.ai/workflows/code-quality.org index 2406f4c..3ac3e9d 100644 --- a/.ai/workflows/code-quality.org +++ b/.ai/workflows/code-quality.org @@ -1,5 +1,5 @@ #+TITLE: Code-Quality Sweep Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-28 * Overview diff --git a/.ai/workflows/daily-prep.org b/.ai/workflows/daily-prep.org index b6989e7..3103bc7 100644 --- a/.ai/workflows/daily-prep.org +++ b/.ai/workflows/daily-prep.org @@ -1,5 +1,5 @@ #+TITLE: Daily Prep Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-11 * Overview diff --git a/.ai/workflows/delete-calendar-event.org b/.ai/workflows/delete-calendar-event.org index 5bb92a1..7de0086 100644 --- a/.ai/workflows/delete-calendar-event.org +++ b/.ai/workflows/delete-calendar-event.org @@ -1,5 +1,5 @@ #+TITLE: Delete Calendar Event Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/edit-calendar-event.org b/.ai/workflows/edit-calendar-event.org index 662f0b4..27a9dd3 100644 --- a/.ai/workflows/edit-calendar-event.org +++ b/.ai/workflows/edit-calendar-event.org @@ -1,5 +1,5 @@ #+TITLE: Edit Calendar Event Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/email-assembly.org b/.ai/workflows/email-assembly.org index 003459c..699dbc0 100644 --- a/.ai/workflows/email-assembly.org +++ b/.ai/workflows/email-assembly.org @@ -1,5 +1,5 @@ #+TITLE: Email Assembly Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-01-29 * Overview diff --git a/.ai/workflows/extract-email.org b/.ai/workflows/extract-email.org index 3a70bea..c68bafe 100644 --- a/.ai/workflows/extract-email.org +++ b/.ai/workflows/extract-email.org @@ -1,5 +1,5 @@ #+TITLE: Extract Email Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-06 * Overview diff --git a/.ai/workflows/find-email.org b/.ai/workflows/find-email.org index 0ef9615..d71ed3e 100644 --- a/.ai/workflows/find-email.org +++ b/.ai/workflows/find-email.org @@ -1,5 +1,5 @@ #+TITLE: Find Email Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/first-session.org b/.ai/workflows/first-session.org index 60118a2..147026f 100644 --- a/.ai/workflows/first-session.org +++ b/.ai/workflows/first-session.org @@ -1,5 +1,5 @@ #+TITLE: First Session Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings Run this workflow on the first Claude Code session for a new project. It establishes the git/.ai policy, orients Claude to the diff --git a/.ai/workflows/flashcard-review.org b/.ai/workflows/flashcard-review.org index 31027b3..09af348 100644 --- a/.ai/workflows/flashcard-review.org +++ b/.ai/workflows/flashcard-review.org @@ -1,5 +1,5 @@ #+TITLE: Drill Deck Review Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-30 * Overview diff --git a/.ai/workflows/helper-mode.org b/.ai/workflows/helper-mode.org index cdec200..a6acfa7 100644 --- a/.ai/workflows/helper-mode.org +++ b/.ai/workflows/helper-mode.org @@ -1,5 +1,5 @@ #+TITLE: Helper Mode Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-15 * Overview diff --git a/.ai/workflows/inbox.org b/.ai/workflows/inbox.org index acfd11d..3bd9335 100644 --- a/.ai/workflows/inbox.org +++ b/.ai/workflows/inbox.org @@ -1,5 +1,5 @@ #+TITLE: Inbox Workflow (Engine) -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-23 * Overview diff --git a/.ai/workflows/journal-entry.org b/.ai/workflows/journal-entry.org index 3f476a7..c70dfe8 100644 --- a/.ai/workflows/journal-entry.org +++ b/.ai/workflows/journal-entry.org @@ -1,5 +1,5 @@ #+TITLE: Journal Entry Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2025-11-07 * Overview diff --git a/.ai/workflows/meeting-prep.org b/.ai/workflows/meeting-prep.org index 162ae30..563328b 100644 --- a/.ai/workflows/meeting-prep.org +++ b/.ai/workflows/meeting-prep.org @@ -1,5 +1,5 @@ #+TITLE: Meeting-Prep Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-10 * Overview diff --git a/.ai/workflows/meeting-prep.pre-wire.org b/.ai/workflows/meeting-prep.pre-wire.org index 6a156c0..3e27c2a 100644 --- a/.ai/workflows/meeting-prep.pre-wire.org +++ b/.ai/workflows/meeting-prep.pre-wire.org @@ -1,5 +1,5 @@ #+TITLE: Meeting-Prep — Pre-Wire Method (supporting doc) -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-10 Supporting document for the [[file:meeting-prep.org][meeting-prep workflow]]'s Phase 3.5. The workflow carries the condensed, in-flow version of pre-wiring; this file is the full Manager Tools method, kept beside the workflow (same name + =.pre-wire= suffix) so it travels with the workflow. Source casts: "How to Prewire a Meeting" (2007) and "Peer Prewire" (2015). diff --git a/.ai/workflows/no-approvals.org b/.ai/workflows/no-approvals.org index 9e1c894..5f54b96 100644 --- a/.ai/workflows/no-approvals.org +++ b/.ai/workflows/no-approvals.org @@ -1,5 +1,5 @@ #+TITLE: No-Approvals Mode -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-28 * Overview diff --git a/.ai/workflows/open-tasks.org b/.ai/workflows/open-tasks.org index 02a0847..205d95c 100644 --- a/.ai/workflows/open-tasks.org +++ b/.ai/workflows/open-tasks.org @@ -1,5 +1,5 @@ #+TITLE: Open Tasks Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-04-25 * Overview diff --git a/.ai/workflows/page-me.org b/.ai/workflows/page-me.org index 8069830..bfa92c6 100644 --- a/.ai/workflows/page-me.org +++ b/.ai/workflows/page-me.org @@ -1,5 +1,5 @@ #+TITLE: Page Me Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-01-31 #+UPDATED: 2026-02-27 @@ -113,6 +113,19 @@ notify info "Page" "Your message here" --persist The =--persist= flag keeps the notification on screen until manually dismissed. All page-me notifications should use =--persist= by default. +** Paging Craig's phone (away from the machine) + +The timed =notify= alarm above is the desktop channel. When Craig is away from the machine (or asks to be paged "on my phone"), use the agent pager instead — a Signal push to his phone from any machine or agent runtime: + +#+begin_src bash +agent-page "Build finished — ready for your eyes" + +# Timed phone page: same at-daemon pattern, different channel +echo "agent-page 'Meeting starts in 5'" | at 3:25pm +#+end_src + +Channel selection and the pager's mechanics live in protocols.org "Paging Craig — the agent pager". When in doubt, fire both: the desktop notification persists for whenever he returns, the phone push reaches him now. + ** Managing Alarms #+begin_src bash diff --git a/.ai/workflows/process-meeting-transcript.org b/.ai/workflows/process-meeting-transcript.org index 4dd340f..d0806ad 100644 --- a/.ai/workflows/process-meeting-transcript.org +++ b/.ai/workflows/process-meeting-transcript.org @@ -1,5 +1,5 @@ #+TITLE: Process Meeting Transcript Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-03 * Overview @@ -10,16 +10,16 @@ This workflow defines the process for processing meeting recordings from start t Trigger this workflow when: - Craig says "process the transcript" or "process the recording" or similar -- New recording files (.mkv) appear in ~/sync/recordings/ after meetings +- New recording files (.mkv, .m4a, or .flac) appear in ~/sync/recordings/ after meetings - Craig wants to process meeting recordings into labeled transcripts * Prerequisites -- Recording file(s) exist in ~/sync/recordings/ (*.mkv) +- Recording file(s) exist in ~/sync/recordings/ (*.mkv, *.m4a, or *.flac) - Calendar files available at ~/.emacs.d/data/*cal.org for meeting titles - AssemblyAI transcription script at ~/.emacs.d/scripts/assemblyai-transcribe - AssemblyAI API key stored in ~/.authinfo.gpg (machine api.assemblyai.com) -- ffmpeg available for audio extraction +- ffmpeg available for audio extraction (video .mkv only; .m4a and .flac skip extraction) * The Workflow @@ -43,13 +43,13 @@ Classification is per recording, not per session — a single run can carry this Find and match recording files with calendar events. *Run sub-steps 1 and 3 (recording list + calendar dump) as a single parallel batch* — they're independent. Sub-step 2 (parse timestamps) and sub-step 4 (matching) work from those two outputs in-memory, so they're sequential after the batch. -1. **List recordings:** Find all recording files in ~/sync/recordings/ (video .mkv or audio-only .m4a) +1. **List recordings:** Find all recording files in ~/sync/recordings/ (video .mkv or audio-only .m4a / .flac) #+begin_src bash - ls -la ~/sync/recordings/*.mkv ~/sync/recordings/*.m4a 2>/dev/null + ls -la ~/sync/recordings/*.mkv ~/sync/recordings/*.m4a ~/sync/recordings/*.flac 2>/dev/null #+end_src - Audio-only recordings (.m4a) are used when no screen content is expected. These skip Step 3 (audio extraction) since they're already in a transcribable format. + Audio-only recordings (.m4a or lossless .flac) are used when no screen content is expected. These skip Step 3 (audio extraction) since they're already in a transcribable format. FLAC is the recorder's current audio format — its frames are self-contained, so an interrupted recording still decodes. -2. **Extract timestamps:** Parse date/time from each filename (format: YYYY-MM-DD-HH-MM-SS.mkv or .m4a) +2. **Extract timestamps:** Parse date/time from each filename (format: YYYY-MM-DD-HH-MM-SS.mkv, .m4a, or .flac) 3. **Match with calendar:** Check ~/.emacs.d/data/*cal.org for meetings at those times #+begin_src bash @@ -74,7 +74,7 @@ Per =cross-project.md=: transcribe and label here, then deliver the *labeled tra ** Step 3: Extract Audio (video recordings only) -*Skip this step for .m4a files* — they are already audio and can go directly to transcription. +*Skip this step for .m4a and .flac files* — they are already audio and can go directly to transcription. For .mkv video recordings, extract audio for transcription: @@ -96,11 +96,11 @@ Output: /tmp/FILENAME.m4a (temporary, deleted after transcription) #+begin_src bash # For .mkv files (audio was extracted to /tmp/): ~/.emacs.d/scripts/assemblyai-transcribe /tmp/FILENAME.m4a > ~/sync/recordings/FILENAME.txt - # For .m4a files (transcribe directly): + # For .m4a or .flac files (transcribe directly — AssemblyAI accepts both natively): ~/.emacs.d/scripts/assemblyai-transcribe ~/sync/recordings/FILENAME.m4a > ~/sync/recordings/FILENAME.txt #+end_src -2. **Clean up:** Delete intermediate .m4a file after successful transcription (only for .mkv extractions — do NOT delete original .m4a recordings) +2. **Clean up:** Delete intermediate .m4a file after successful transcription (only for .mkv extractions — do NOT delete original .m4a or .flac recordings) #+begin_src bash rm /tmp/FILENAME.m4a #+end_src @@ -181,13 +181,13 @@ Present the speaker identification table to Craig for confirmation: ** Step 9: Copy Recording to Meetings Folder -1. Ensure engagement meetings folder exists and patterns are in .gitignore (~*/meetings/*.mkv~ and ~*/meetings/*.m4a~) +1. Ensure engagement meetings folder exists and patterns are in .gitignore (~*/meetings/*.mkv~, ~*/meetings/*.m4a~, and ~*/meetings/*.flac~) 2. Copy the recording file with descriptive name: #+begin_src bash # Video recordings: cp ~/sync/recordings/YYYY-MM-DD-HH-MM-SS.mkv {engagement}/meetings/YYYY-MM-DD_HH-MM-meeting-name.mkv - # Audio-only recordings: + # Audio-only recordings (.m4a or .flac — copy with the original extension): cp ~/sync/recordings/YYYY-MM-DD-HH-MM-SS.m4a {engagement}/meetings/YYYY-MM-DD_HH-MM-meeting-name.m4a #+end_src Example: ~deepsat/meetings/2026-02-03_11-02-standup-ipm-grooming.mkv~ diff --git a/.ai/workflows/read-calendar-events.org b/.ai/workflows/read-calendar-events.org index be66bf4..5eac529 100644 --- a/.ai/workflows/read-calendar-events.org +++ b/.ai/workflows/read-calendar-events.org @@ -1,5 +1,5 @@ #+TITLE: Read Calendar Events Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/readability-audit.org b/.ai/workflows/readability-audit.org index 8223a03..90ad366 100644 --- a/.ai/workflows/readability-audit.org +++ b/.ai/workflows/readability-audit.org @@ -1,5 +1,5 @@ #+TITLE: Readability Audit Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-28 * Overview diff --git a/.ai/workflows/rename-artifact.org b/.ai/workflows/rename-artifact.org index 7b9f15b..a8d1246 100644 --- a/.ai/workflows/rename-artifact.org +++ b/.ai/workflows/rename-artifact.org @@ -1,5 +1,5 @@ #+TITLE: Rename an .ai Artifact -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-31 * Summary diff --git a/.ai/workflows/send-email.org b/.ai/workflows/send-email.org index 065f925..82d2286 100644 --- a/.ai/workflows/send-email.org +++ b/.ai/workflows/send-email.org @@ -1,5 +1,5 @@ #+TITLE: Email Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-01-26 * Overview diff --git a/.ai/workflows/session-harvest.org b/.ai/workflows/session-harvest.org index c48d689..54a7c09 100644 --- a/.ai/workflows/session-harvest.org +++ b/.ai/workflows/session-harvest.org @@ -1,5 +1,5 @@ #+TITLE: Session-Harvest Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-11 * Overview diff --git a/.ai/workflows/spec-create.org b/.ai/workflows/spec-create.org index 1249181..39758a0 100644 --- a/.ai/workflows/spec-create.org +++ b/.ai/workflows/spec-create.org @@ -1,5 +1,5 @@ #+TITLE: Spec-Create Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-09 * Overview @@ -47,6 +47,8 @@ Capture, in this order: ** Phase 2 — Design, alternatives, decisions 1. *Design* — overview first, then detail. Write the reasoning as *prose, not bullet dumps* — prose exposes weak logic that bullets let you hide. Use bullets only for genuinely enumerable lists. When the thing has an interface, use the *two-altitude* split (Rust RFC): explain it once for a user/caller, once for an implementer. + + *Non-trivial UI.* When the deliverable is a real UI (a panel, a multi-control surface, an interacting visual layout — not a single dialog, a CLI flag, or a one-off prompt), the design isn't settled on the page. Run the research → ~5 distinct working-prototype directions → iterate-one-to-final process in =claude-rules/ui-prototyping.md= before treating the UI design as done, and add a =Prototype iterations= subsection under the spec's status heading linking every iteration (final linked in the design section). A UI design decision moves to =DONE= only once it's been seen working in a prototype. 2. *Alternatives considered* — the load-bearing section authors skip and reviewers need most. For each option, force a why-not with the MADR grammar: "Good, because… / Bad, because… / Neutral, because…". Even one rejected option, with the reason, beats presenting one path as inevitable. 3. *Decisions* — capture each real choice as an org =TODO= task carrying an inline mini-ADR (Nygard's spine): - The heading is =** TODO <Decision name>=. It flips to =DONE= when the decision-maker agrees with the call; until then it stays =TODO=. diff --git a/.ai/workflows/spec-review.org b/.ai/workflows/spec-review.org index d4998eb..0da8e65 100644 --- a/.ai/workflows/spec-review.org +++ b/.ai/workflows/spec-review.org @@ -134,6 +134,7 @@ Work the spec against these. Each is a source of concrete findings, not a box to - *Security & privacy.* API keys safe? Debug logs leaking secrets or private issue text? Confirmations before mutating shared workspace objects? Personal vs shared distinguished? Local files holding sensitive descriptions/comments? Anything to redact from messages/logs? Any work-tracker integration may handle private company data. - *UX & accessibility.* Discoverable commands? Recoverable mistakes? Prompts ordered to the task? Safe, useful defaults? Informative-not-noisy status messages? Does the UI avoid implying unsupported actions are supported? Match the upstream product's permissions/concepts? Are customizations named in user language, with clear defaults and docstrings? For Emacs packages, command names, completion candidates, buffer layout, defcustom names, and message wording *are* the UX. - *Operational-panel UI traps.* Applies when the spec covers a user-facing panel, dialog, or control surface; skip otherwise. Lists that mix saved, current, and generated items must name each item's source. Refresh or scan actions must not gate data that could be shown immediately. Add-forms must not ask the user to retype values the system already discovered. Destructive confirmations read in future tense before the action and verified-result tense after it. Diagnostics, performance, logging, and repair affordances are reviewed as one coherent flow before extra pages or buttons are added. A popup launched from a bar, tray, or tool surface should visually belong to that launcher. (Promoted from archsetup's Waybar network-panel review, 2026-06-30.) +- *Prototype process for non-trivial UI.* Applies when the deliverable is a real UI (a panel, a multi-control surface, an interacting visual layout — not a single dialog or CLI flag); skip otherwise. Verify the =claude-rules/ui-prototyping.md= process ran: category research is cited in Goals/Design, the final prototype is linked in the design section, a =Prototype iterations= subsection under the status heading lists every pass, and each UI design decision is backed by a prototype it was seen working in rather than asserted on the page. A non-trivial-UI spec with decisions but no prototype evidence is a =:blocking:= finding. - *Test strategy and coverage.* Characterization tests before behavior changes? Pure functions to unit-test? API responses needing fixtures? Command flows needing stubs? Regression tests for prior bugs? Boundary/error cases? What's covered elsewhere and shouldn't be re-tested? Which existing tests must change? How is coverage generated, summarized, and used to find untested/refactor-worthy code? Prefer tests that lock contracts: representation shape, query compilation, sync no-op, conflict refusal, pagination, dirty-buffer protection, log redaction, and long-running/slow-operation behavior via fakes rather than flaky live dependencies. - *Observability & operations.* How does a user see what the package is doing? Progress messages for long ops? Useful, safe debug logging? Are logs structured enough to isolate issues from a bug report? Are commands provided to inspect/clear caches, test connectivity, diagnose backends/tools, copy redacted debug info, or reproduce command invocations? How are terminal states discovered: completion, failure, partial success, stalled/hung, cancelled, cleanup-unverified, and "needs user action"? Does the product notify only when useful, avoid noisy success spam, and keep non-success states visible until acknowledged? For generated org files, headers should often carry source, filter/view name, refresh time, count, truncation state. - *Comparable-product sentiment.* When there are obvious adjacent products, research what users love and hate about them from official docs plus current community reports. Do not cargo-cult their feature set; translate findings into the spec's scope. For each loved behavior, say whether the spec provides it, intentionally omits it, or defers it. For each hated behavior, say whether the spec avoids, resolves, inherits, or accepts it. diff --git a/.ai/workflows/startup.org b/.ai/workflows/startup.org index 4e3d417..929d482 100644 --- a/.ai/workflows/startup.org +++ b/.ai/workflows/startup.org @@ -1,5 +1,5 @@ #+TITLE: Startup Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-04-25 * Summary diff --git a/.ai/workflows/status-check.org b/.ai/workflows/status-check.org index efff16d..4a9972c 100644 --- a/.ai/workflows/status-check.org +++ b/.ai/workflows/status-check.org @@ -1,5 +1,5 @@ #+TITLE: Status Check Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-02 * Overview diff --git a/.ai/workflows/summarize-emails.org b/.ai/workflows/summarize-emails.org index 6ac5e6f..c9c7001 100644 --- a/.ai/workflows/summarize-emails.org +++ b/.ai/workflows/summarize-emails.org @@ -1,5 +1,5 @@ #+TITLE: Summarize Emails Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-14 * Overview diff --git a/.ai/workflows/suspend.org b/.ai/workflows/suspend.org index 1c16bb9..3691f60 100644 --- a/.ai/workflows/suspend.org +++ b/.ai/workflows/suspend.org @@ -1,5 +1,5 @@ #+TITLE: Session Suspend Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-28 * Overview diff --git a/.ai/workflows/sync-email.org b/.ai/workflows/sync-email.org index 52a7caf..863b400 100644 --- a/.ai/workflows/sync-email.org +++ b/.ai/workflows/sync-email.org @@ -1,5 +1,5 @@ #+TITLE: Sync Email Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-02-01 * Overview diff --git a/.ai/workflows/task-audit.org b/.ai/workflows/task-audit.org index 7d2b758..aa50176 100644 --- a/.ai/workflows/task-audit.org +++ b/.ai/workflows/task-audit.org @@ -1,5 +1,5 @@ #+TITLE: Task Audit Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-22 * Overview diff --git a/.ai/workflows/task-review.org b/.ai/workflows/task-review.org index ba1571a..7ea2e8e 100644 --- a/.ai/workflows/task-review.org +++ b/.ai/workflows/task-review.org @@ -1,5 +1,5 @@ #+TITLE: Task Review Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-20 * Overview @@ -92,7 +92,7 @@ Set =:LAST_REVIEWED:= to today's date (from above) in the task's =:PROPERTIES:= Body... #+end_example -The exact date string matters: =task-review-staleness.sh= and the wrap-up health check both parse =:LAST_REVIEWED: YYYY-MM-DD=. +Format: =:LAST_REVIEWED:= takes a bare ISO date (=2026-05-20=) or an org-native inactive timestamp (=[2026-05-20 Tue]=, matching the =CREATED:=/=CLOSED:= cookies beside it); =task-review-staleness.sh= and the wrap-up health check normalize both to the date. A value that is neither is a data error — the staleness script warns loudly to stderr (naming the file, line, and value) and leaves the task out of the stale count rather than silently reporting a freshly-reviewed task as never-reviewed. Stamp a clean date and the warning never fires. *** Killing a task diff --git a/.ai/workflows/triage-intake.cmail.org b/.ai/workflows/triage-intake.cmail.org index d818c72..8d8abfb 100644 --- a/.ai/workflows/triage-intake.cmail.org +++ b/.ai/workflows/triage-intake.cmail.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake — cmail (Proton) Source -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-26 # Source plugin for the triage-intake engine. See triage-intake.org for the diff --git a/.ai/workflows/triage-intake.github-prs.org b/.ai/workflows/triage-intake.github-prs.org index c1bc796..644421c 100644 --- a/.ai/workflows/triage-intake.github-prs.org +++ b/.ai/workflows/triage-intake.github-prs.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake — Personal GitHub PRs Source -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-26 # Source plugin for the triage-intake engine. See triage-intake.org for the diff --git a/.ai/workflows/triage-intake.org b/.ai/workflows/triage-intake.org index a5a3bda..fde67a7 100644 --- a/.ai/workflows/triage-intake.org +++ b/.ai/workflows/triage-intake.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake Workflow (Engine) -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-01 * Summary @@ -414,7 +414,7 @@ The sentinel is checked into git, but git tracks content, not mtime — so an mt Craig, via the work project's same-day handoff: "we only need to report if anything's changed when we do triage intake." Sweep summaries report deltas only — a new invite, a new/moved/cancelled event, a new message needing attention. Unchanged sources get no block (the "Calendar — quiet" roll-call is retired), and an all-quiet sweep renders as a single "HH:MM sweep: no changes" line. Failures keep their loud banner (never folded into the no-change line) and the suggested-actions line stays when actions are queued. Same ruling: the telegram plugin's dev-community group traffic is dropped from reports entirely unless Craig asks (see that plugin's 2026-06-11 note). **** 2026-06-10: Loud failure surfacing (Phase C item 0 + Common Mistake 9) -Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the account lock while the signel daemon owned it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the telegram plugin's failure path points at this rule. +Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the signal-cli account lock while another client held it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the telegram plugin's failure path points at this rule. **** 2026-05-26: Refactor into engine + source plugins Split the monolithic workflow into a source-agnostic engine (this file) and per-source plugins named =triage-intake.<source>.org=. The engine carries the anchor/sentinel logic, the four-bucket model, the Phase A-D orchestration, the todo.org persistence convention, and the exit criteria. Each source's scan/classify/render/action knowledge moved to its own plugin. General plugins (personal-gmail, personal-calendar, cmail, github-prs) live in =.ai/workflows/= and are template-synced; project-specific plugins (a work project's Linear, work Gmail, work Slack, enterprise PRs) live in the project's =.ai/project-workflows/= and are never synced. Phase 0 globs *both* directories — the loud requirement, because missing the project dir silently halves the sweep. Naming convention: first dot is the engine/plugin boundary, deeper dots reserved for sub-adapters. This removed all DeepSat/Linear specifics from the engine; they become work-project plugins. diff --git a/.ai/workflows/triage-intake.personal-calendar.org b/.ai/workflows/triage-intake.personal-calendar.org index bf7d543..b5ee67a 100644 --- a/.ai/workflows/triage-intake.personal-calendar.org +++ b/.ai/workflows/triage-intake.personal-calendar.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake — Personal Calendar Source -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-26 # Source plugin for the triage-intake engine. See triage-intake.org for the diff --git a/.ai/workflows/triage-intake.personal-gmail.org b/.ai/workflows/triage-intake.personal-gmail.org index aa0554d..7fb1231 100644 --- a/.ai/workflows/triage-intake.personal-gmail.org +++ b/.ai/workflows/triage-intake.personal-gmail.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake — Personal Gmail Source -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-05-26 # Source plugin for the triage-intake engine. See triage-intake.org for the @@ -21,10 +21,29 @@ Personal Gmail unread in the inbox since the anchor: mcp__google-docs-personal__listMessages q="is:unread in:inbox after:<anchor-epoch>" maxResults=100 #+end_src -⚠ *Express the cutoff as the literal UNIX epoch* — =after:1778856990=, not =after:YYYY/MM/DD=. Gmail's =after:YYYY/MM/DD= operator only supports day resolution; the =YYYY/MM/DD HH:MM:SS= form is NOT valid syntax — Gmail parses the space as a term separator, treats =HH:MM:SS= as a search term that never matches, and returns 0 results, silently masking unread mail. The engine supplies =<anchor-epoch>= because this source declares =ANCHOR: epoch=. +⚠ *Express every anchor cutoff as the literal UNIX epoch* — =after:1784177122= and =before:1784177122= for the same anchor, never the =YYYY/MM/DD= form. This governs *both* anchored queries: the scan above and the backlog-residue probe below. They must meet at the same instant or mail falls between them permanently. Gmail's day-resolution operators fail two different ways: =after:YYYY/MM/DD HH:MM:SS= is not valid syntax at all — Gmail parses the space as a term separator, treats =HH:MM:SS= as a search term that never matches, and returns 0 results, silently masking unread mail — while =before:YYYY/MM/DD= is valid but excludes the named day entirely, so pairing it with a second-resolution scan leaves the whole anchor day covered by neither query. The engine supplies =<anchor-epoch>= because this source declares =ANCHOR: epoch=. + +The rule binds the *anchor* windows only. The date-slice walk below deliberately uses =before:<oldest-full-day-seen>= at day resolution — safe there because consecutive slices overlap and get deduped by message id. ⚠ *Do NOT add =-category:promotions -category:social=.* That filter masked 67 promo+social messages across two runs (2026-05-04, 2026-05-06), both needing a follow-up sweep. Pull the full unfiltered set; the trash-leaning bias in Classify handles promotions and social directly. +⚠ *The MCP caps at =maxResults=100= and exposes NO =pageToken= parameter.* The response carries a =nextPageToken=, but the tool can't consume it, so a pile over 100 is silently truncated — the tail below the cap never gets classified, and every later anchored sweep skips it (it predates the new anchor). This is exactly how a 300+ backlog accumulated invisibly by 2026-07-08. Two consequences: + +- *Never treat a 100-row result as complete.* When a scan returns exactly 100, walk the tail in *date slices*: re-query with =before:<oldest-full-day-seen>= (day resolution), repeat until a page returns fewer than 100, dedupe by message id across slices (the day-resolution boundary overlaps). +- *Never report =resultSizeEstimate= as a count.* It's unreliable — observed stuck at "201" across three different queries whose real union exceeded 300. + +*** Backlog-residue check (every sweep — cheap, mandatory) + +The anchored scan is blind to anything unread from *before* the anchor. After it, run one probe for pre-anchor residue: + +#+begin_src text +mcp__google-docs-personal__listMessages q="is:unread in:inbox before:<anchor-epoch>" maxResults=5 +#+end_src + +The cutoff is the epoch, matching the scan's =after:<anchor-epoch>= — see the epoch rule above. + +If it returns any messages, surface one loud line in the sweep summary: "Backlog: unread predating the anchor exists (N+ shown; date-slice to inventory)" and offer a backlog sweep. Never fold the residue into a quiet sweep — an anchored "no changes" claim is only true for the window the scan saw. (Added 2026-07-08 after ~300 pre-anchor unread accumulated unseen; the probe returns actual messages, so it works where the estimate lies. Shipped with a day-resolution cutoff that hid the entire anchor day; fixed to epoch 2026-07-16 after a home sweep reported the backlog clear while two July-15 messages sat unread.) + ** Classify Bias: *trash-leaning* — personal Gmail is high noise volume. diff --git a/.ai/workflows/triage-intake.telegram.org b/.ai/workflows/triage-intake.telegram.org index 9caa4e1..5039a8b 100644 --- a/.ai/workflows/triage-intake.telegram.org +++ b/.ai/workflows/triage-intake.telegram.org @@ -1,5 +1,5 @@ #+TITLE: Triage Intake — Telegram Source -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-06-09 # Source plugin for the triage-intake engine. See triage-intake.org for the diff --git a/.ai/workflows/work-the-backlog.org b/.ai/workflows/work-the-backlog.org index b0666e7..090841d 100644 --- a/.ai/workflows/work-the-backlog.org +++ b/.ai/workflows/work-the-backlog.org @@ -1,5 +1,5 @@ #+TITLE: Work the Backlog -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-07-02 * Overview @@ -152,7 +152,7 @@ With paging on, fire one page when the set is done or the cap is hit — end-of- notify info "Page" "<project>: <N> done, <M> remaining — <one-line summary>" --persist #+end_src -=--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. +=--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. =notify= is the desktop paging surface; a run that expects Craig to be away also fires =agent-page= with the same message (the Signal phone channel — protocols.org "Paging Craig — the agent pager"). * Metrics diff --git a/.ai/workflows/wrap-it-up.org b/.ai/workflows/wrap-it-up.org index d0c4e75..5ce88a5 100644 --- a/.ai/workflows/wrap-it-up.org +++ b/.ai/workflows/wrap-it-up.org @@ -1,5 +1,5 @@ #+TITLE: Session Wrap-Up Workflow -#+AUTHOR: Craig Jennings & Claude +#+AUTHOR: Craig Jennings #+DATE: 2026-04-20 * Overview @@ -202,9 +202,14 @@ else followups=".ai/lint-followups.org" fi [ -f todo.org ] && emacs --batch -q -l .ai/scripts/lint-org.el \ - --followups-file="$followups" todo.org + --fix --followups-file="$followups" todo.org #+end_src +The =--fix= flag is required for the writes: lint-org's CLI default is +report-only (a linter reports, it doesn't write), and this wrap-up pass is +the deliberate exception that applies fixes — its diff rides the wrap-up +commit for review. + =lint-org= runs =org-lint= over =todo.org=, auto-applies four mechanical categories (=item-number= counters, bare =#+begin_src= → =#+begin_example=, multi-line planning-info merged onto one line, =**X.**= → =*X.*=), and |
