aboutsummaryrefslogtreecommitdiff
path: root/inbox
diff options
context:
space:
mode:
Diffstat (limited to 'inbox')
-rw-r--r--inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry-pass-list-amendment-craig-s.org5
-rw-r--r--inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry.org217
-rw-r--r--inbox/PROCESSED-2026-07-20-2355-from-.dotfiles-kb-personal-roots-amendment-craig-s.org5
-rw-r--r--inbox/PROCESSED-2026-07-21-0006-from-org-drill-memory-sweep-handoff-2026-06-10-done-in.org5
-rw-r--r--inbox/PROCESSED-2026-07-23-0241-from-.dotfiles-ack-from-dotfiles-both-amendments.org5
-rw-r--r--inbox/PROCESSED-2026-07-23-2127-from-home-rulesets-voice-correspondence-patterns.org85
-rw-r--r--inbox/PROCESSED-2026-07-23-2132-from-home-rulesets-voice-correspondence-followup.org102
-rw-r--r--inbox/lint-followups.org17
8 files changed, 441 insertions, 0 deletions
diff --git a/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry-pass-list-amendment-craig-s.org b/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry-pass-list-amendment-craig-s.org
new file mode 100644
index 0000000..9f2fbf6
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry-pass-list-amendment-craig-s.org
@@ -0,0 +1,5 @@
+#+TITLE: Sentry pass-list amendment (Craig's order, 2026-07-21, from
+#+SOURCE: from .dotfiles
+#+DATE: 2026-07-20 23:53:35 -0500
+
+Sentry pass-list amendment (Craig's order, 2026-07-21, from the dotfiles session): sentry never checks email or messengers, and gains a bug-finding pass. Attached alongside this note is dotfiles' edited .ai/workflows/sentry.org — reconcile it into the canonical claude-templates copy. Three changes: (1) Overview pass list now reads 'triage (no mail or messengers)' and adds 'bug finding'. (2) Pass 3 loads only non-mail, non-messenger triage plugins (calendar, PR/ticketing); cmail, Gmail variants, Telegram, Signal, chat DMs are never loaded by a sentry fire — a manual 'triage intake' still scans everything. (3) New pass 11 'Bug finding': static analysis + config sanity + one rotating targeted code-read area per fire; verified findings filed as graded bug tasks per the severity-times-frequency matrix; find, never fix unattended. No companion files need reconciling — triage-intake.org already documents that sentry invokes it under the no-approvals contract; the plugin subset is sentry's own concern.
diff --git a/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry.org b/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry.org
new file mode 100644
index 0000000..8369c46
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-20-2353-from-.dotfiles-sentry.org
@@ -0,0 +1,217 @@
+#+TITLE: Sentry — Overnight Hygiene Supervisor
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-19
+
+* Overview
+
+Sentry is an interval loop that keeps a project's hygiene current while Craig is away. Each fire walks a fixed list of hygiene passes — roam pull, inbox zero, triage (no mail or messengers), todo cleanup, task audit, working-files hygiene, spec board, link integrity, git health, prep freshness, bug finding — and commits each pass's writing to a throwaway daily branch. Nothing pushes. In the morning Craig reviews the branch, squash-merges what he wants, and deletes it.
+
+The design goal is a project that greets the morning already tidy, with every judgment call and every destructive action parked in an approval queue rather than executed unattended. Sentry does the mechanical sweeping; Craig does the deciding.
+
+This file is the engine. It owns the entry gates, the branch mechanics, the lock model, the per-fire pass runner, the digest and approval queue, the skip semantics, and the stop-sentry shutdown. The =agent-lock= helper (=.ai/scripts/agent-lock=) provides the locks. The passes reuse existing workflows (=inbox.org=, =triage-intake.org=, =clean-todo.org=, =task-audit.org=) under sentry's unattended contract.
+
+* When to Use This Workflow
+
+Craig arms sentry at the end of a session, with the machine left running, to have overnight hygiene done by morning.
+
+Triggers:
+
+- "start sentry", "run sentry", "arm sentry", "sentry mode"
+- "let sentry watch this overnight", "keep this tidy overnight"
+- "start sentry hourly", "start sentry every <interval>" (sets the loop interval)
+
+Stop trigger (see Stop Sentry below):
+
+- "stop sentry", "stand down sentry", "sentry off"
+
+Sentry is deliberately *not* auto-armed. Running it in a project is a per-project grant (the =:COMMIT_AUTONOMY:= marker) plus a deliberate launch with Craig at the terminal for the entry gates.
+
+* Prerequisite — the autonomy ticket
+
+Sentry commits unattended. =commits.md= gates commits on Craig's approval, so sentry needs standing, per-project authorization to run at all. Before anything else, read the project's =.ai/notes.org= Workflow State block for:
+
+: :COMMIT_AUTONOMY: yes
+
+If the marker is absent or not =yes=, decline to start and name the marker:
+
+: Sentry needs ":COMMIT_AUTONOMY: yes" in .ai/notes.org Workflow State to run — it commits unattended. Add it to grant, or run the hygiene passes by hand.
+
+No half-running mode: a project without the grant doesn't run sentry's read-only passes either. The grant is one line away, so this is a deliberate opt-in, not a barrier.
+
+* Entry — interactive, with Craig present
+
+Craig types the sentry trigger, so the first moves run with him at the terminal. Do them in order; each gate that fails stops entry until Craig answers.
+
+1. *Autonomy ticket* — the prerequisite above. Absent → decline and stop.
+
+2. *Dirty-tree gate.* =git diff --quiet HEAD= (tracked modifications only; untracked and gitignored files never block — an inbox drop or scratch file is not in-progress work). If the tracked tree is dirty, describe what's dirty and offer, inline-numbered per =interaction.md=:
+
+ 1. Finish the job — commit the in-progress work first (recommended if it's a coherent unit)
+ 2. Stash it — =git stash= and start sentry on a clean tree
+ 3. Roll back named changes — discard specific files (names them)
+
+ Wait for an answer. Sentry can't start unattended from a dirty state; that's the point.
+
+3. *Green-suite gate.* Run the project's full suite (=make test=, or the project's equivalent — detect it). Read the output. If anything is red, describe the failures and offer to investigate before arming. The loop starts only on a green baseline, because every unattended fire measures itself against "did I break this?" and a pre-existing red poisons that check.
+
+4. *Prior sentry branch.* =git branch --list 'sentry/*'=. An unmerged =sentry/*= branch from a previous night means the morning review didn't happen. Surface it and offer to squash-merge or delete it now (Craig is present); don't stack a second sentry branch on the first.
+
+5. *Reconcile the project branch.* Fetch and fast-forward-only against upstream — the same reconcile =startup= runs:
+
+ : git fetch --all --prune
+ : git rev-list --left-right --count @{u}...HEAD
+
+ Zero-behind → continue. Behind-only and clean → =git merge --ff-only @{u}=. Diverged → surface to Craig (he's present); don't auto-resolve.
+
+6. *Create the daily branch.* From HEAD:
+
+ : git switch -c "sentry/$(date +%F)-$(uname -n)"
+
+ The host suffix (=uname -n=) stops a same-date collision between the two daily drivers. The working tree now sits on this branch overnight — the launch hands the repo to sentry until the morning merge. Reclaiming it mid-night means stopping sentry first (see Stop Sentry). Note the Emacs buffer-revert caveat to Craig if he has the repo open: files change on disk under him overnight, so buffers want reverting after the morning merge (see =emacs.md=).
+
+7. *Arm the loop.* Start =/loop= at the interval (default hourly; Craig's "every <interval>" phrase overrides) with the per-fire body being one sentry fire (the Pass Runner below). Confirm the arming in one line: interval, branch name, project.
+
+* The lock model
+
+Two locks, both served by =.ai/scripts/agent-lock= (names only; the helper owns the paths, which live on tmpfs under =$XDG_RUNTIME_DIR/agent-locks/=, host-local and cleared on reboot).
+
+*Single-runner lock* (=sentry-<project>=, where =<project>= is the repo-root basename: =basename "$(git rev-parse --show-toplevel)"= — the same derivation =wrap-it-up.org='s guard uses, so the two agree on the lock name). Each fire acquires it at fire start and releases it at fire end, and refreshes it between passes (the heartbeat, so a live fire's lock never ages past one pass). If =/loop= fires again while a previous fire still holds it, the new fire's acquire fails and the fire skips with one digest line — no two fires run at once. The bounded wait is short (a few seconds); a live fire means defer, not queue.
+
+*Roam-write lock* (=roam-write=). A pass that edits a file under =~/org/roam= acquires it, runs =capture-guard --wait= (the human-capture layer stays underneath), edits the working tree, triggers =systemctl --user start roam-sync.service=, and releases. The lock spans only edit-plus-trigger. Sentry never runs =git= against =~/org/roam= — roam-sync stays the repo's only committer (the 2026-06-24 one-git-owner rule). Pass 1's =pull --ff-only= is the sole, read-only exception.
+
+Every reclaim of a stale lock surfaces in the digest — the helper prints the reclaim note, and the fire records it. A reclaim during a genuinely slow pass is possible, so it's never silent.
+
+* The Pass Runner — one contract per pass
+
+Each fire, after acquiring the single-runner lock and verifying branch state (below), walks the pass list in order. Every pass follows the same four-step contract:
+
+1. *Probe* — a cheap existence check for the pass's target (named per pass below). Absent → the pass is one skip line in the digest and nothing more. This is what makes the pass list portable: passes self-activate where their target exists and stay silent elsewhere, with zero per-project configuration.
+
+2. *Work* — run the pass under the unattended contract. Quick, solo, already-agreed mechanical actions execute. Anything destructive or requiring judgment does *not* execute — it appends to the morning-approval queue (what, why, the exact command or edit that fires on approval). A pass runs fully or not at all; there is no reduced-form pass.
+
+3. *Session-context entry* — append the pass's digest line to the =session-context.org= Session Log (path resolved via =.ai/scripts/session-context-path=). This precedes the commit so a crash between them still leaves the trail.
+
+4. *Commit* — if the pass wrote to disk, commit it: =chore(sentry): <pass> — <what changed>=. One commit per writing pass. A probe-skip or a no-op pass writes nothing and commits nothing.
+
+Between passes, refresh the single-runner lock (=agent-lock refresh sentry-<project>=) — the heartbeat.
+
+** Branch-state verification (fire start, before the passes)
+
+After acquiring the lock, confirm the fire is safe to run:
+
+- *On the right branch* — HEAD is =sentry/<today>-<host>=. If the loop was armed on a prior day and crossed midnight, the branch keeps the arming date; that's fine, morning teardown handles it. If HEAD is somehow *not* a sentry branch (an interrupted stop, a manual checkout), skip the whole fire with a digest line rather than committing onto main.
+- *Clean of foreign changes* — =git diff --quiet HEAD= excluding the spine set (=session-context.org= / =session-context.d/=, resolved via =session-context-path=). Sentry's own spine writes must not trip this; a genuinely unexpected dirty tree (something outside the spine changed and wasn't committed by a prior pass) poisons the fire — skip it with a digest line, the next fire retries.
+
+* Unattended safety — skip, never degrade
+
+With no one at the terminal, any unsafe state makes the affected scope skip with one digest line, and the next fire retries. Unsafe states and their scope:
+
+- *Unexpected dirty tree* (non-spine) → skip the whole fire.
+- *Lost or un-acquirable single-runner lock* → skip the fire (another fire holds it, or the helper is missing).
+- *A pass's own precondition unmet* (its probe fails, or a dependency is dirty) → skip that pass only.
+- *Red suite at fire-end* (see below) → the commits stay on the branch, flagged in the digest for morning review; the fire doesn't roll back.
+
+Skips are never silent and never partial. A pass line in the digest means the pass fully ran; a skip line names why it didn't.
+
+** Multi-day stall notification
+
+An unmerged prior =sentry/*= branch at fire start (the morning review never happened) skips the fire. After the *second consecutive* fire skipped for this reason, send one persistent desktop notification naming the project and branch:
+
+: sentry stalled: <branch> unmerged — merge or delete to resume
+
+Then repeat at most daily. Persistent notify matches the paging convention — it stays on screen until dismissed. A multi-day stall never stays silent.
+
+* The pass list (v1)
+
+In order. Each names its detection probe. A pass whose probe fails is one skip line.
+
+1. *Roam pull* — =git -C ~/org/roam pull --ff-only=. Probe: =~/org/roam= is a git clone. Skipped when the roam tree is dirty (roam-sync owns that case) or the clone is absent. Read-only and ff-only — the one narrow exception to "don't touch roam git," so later passes read a fresh tree.
+
+2. *Inbox zero* — run =inbox.org= roam mode under the no-approvals contract: quick+solo+agreed items execute, shared-asset and convention proposals park (prepared diff, =VERIFY= task, sender reply) in the approval queue. Edits to =~/org/roam/inbox.org= take the roam-write lock + =capture-guard=. Probe: the roam clone or a project =inbox/= exists. Tidying the shared roam inbox is allowed from *any* project session, work included — it's housekeeping on a shared resource, not a durable KB-node write, so the work-denylist doesn't gate it (=knowledge-base.md=). Never park it as a cross-project boundary crossing.
+
+3. *Triage intake — mail and messenger sources excluded.* Run =triage-intake.org=, loading only the non-mail, non-messenger source plugins (calendar, PR/ticketing sources). The mail and messenger plugins — cmail, any Gmail variant, Telegram, Signal, chat DMs — are never loaded by a sentry fire: Craig ruled 2026-07-21 that sentry doesn't check email or messengers (a manual "triage intake" still scans everything). Probe: at least one non-mail, non-messenger plugin present for this project. Destructive actions (deleting, archiving, sending) queue; they never fire unattended.
+
+4. *Todo cleanup* — the =clean-todo.org= mechanics (hygiene pass + =--archive-done= + =--convert-subtasks=). Probe: a root =todo.org=.
+
+5. *Task audit* — =task-audit.org=. Probe: a root =todo.org=. Priority regrades and consolidations queue for review; factual staleness fixes that are unambiguous execute.
+
+6. *Working-files hygiene* — flag =working/<slug>/= directories whose backing task is closed (a filing candidate per =working-files.md=). Probe: a =working/= directory exists. The filing itself queues (it's a judgment move).
+
+7. *Spec status board* — the =docs-lifecycle= grep for spec keywords, surfacing any =DOING= spec whose bound build parent is closed. Probe: =docs/specs/= exists.
+
+8. *Link integrity* — broken =file:= links in the project's org files, via =lint-org.el=. Probe: =lint-org.el= present. Report-only into the digest; no unattended rewrites.
+
+9. *Git health* — uncommitted drift, unpushed commits on other branches, stale branches, main-behind-origin. Probe: =.git=. Report into the digest.
+
+10. *Prep + symlink freshness* — stale daily-prep docs, broken symlinks. Probe: the prep dir / symlinks exist (work and home only, in practice).
+
+11. *Bug finding* — hunt for real bugs in the project's codebase: static analysis (=shellcheck= for shell, the project's linters and test suite for its languages), config sanity checks, plus one targeted code-reading area per fire — rotate the area across fires and name it in the digest, so coverage accumulates over a night instead of re-reading the same corner. Probe: the project carries a codebase (source under version control beyond org/tooling files). File each verified finding as a graded bug task in =todo.org= per the severity × frequency matrix (=todo-format.md=), deduped against existing tasks; an unverifiable suspicion is a digest line, not a task. Find, never fix unattended — a proposed fix or any remediation beyond the task filing queues for morning approval with its exact edit. (Added at Craig's order 2026-07-21, first dogfooded in dotfiles.)
+
+(KB lesson promotion — the proposal's eleventh pass — is deferred to vNext. An unattended judgment pass writing to the shared knowledge base waits until sentry has quiet weeks behind it and a designed detection heuristic. See the filed lesson-detection-heuristic task.)
+
+* Fire-end — conditional suite, then the digest commit
+
+After the passes:
+
+1. *Conditional suite run.* If any pass this fire modified files *outside* the org/spine set (a code-touching pass, rare but possible via fixtures), run the full suite once. A green run confirms the fire's commits are safe; a red run flags the digest for morning review — the commits stay on the branch (nothing is pushed, so the morning gate catches it). No per-pass suite runs: the entry run is the green baseline, and hourly per-commit runs would turn a seconds-long fire into minutes all night. Fires that only touched org/spine files skip this.
+
+2. *Digest commit.* Commit any accumulated spine writes in one sweep — =chore(sentry): digest — <date> <time> fire= — so even a read-only fire (all passes probe-skipped or no-op) leaves a clean tree. This is what lets the next fire's branch-state check see a clean, spine-excluded tree.
+
+3. *Release the single-runner lock.*
+
+* The digest and the approval queue
+
+*Digest.* Each fire appends its lines to the =session-context.org= Session Log (the spine the fire already writes), so it survives a crash, rides the session archive, and is on screen in the running session. One block per fire: the timestamp, then one line per pass (ran + what, or skipped + why), plus any lock reclaim notes.
+
+*Approval queue.* Destructive and judgment actions accumulate under one heading in the same file — =* Sentry approval queue (<date>)= — newest last. Each item carries three things: *what* (the action), *why* (what triggered it), and the *exact command or edit* that fires on approval. The morning review is Craig reading this heading top to bottom and running or discarding each item.
+
+* Morning teardown — Craig's, documented not automated
+
+Sentry never merges its own branch. In the morning Craig:
+
+1. Reviews the digest and the approval queue in =session-context.org=.
+2. Runs or discards each approval-queue item.
+3. Reviews the branch: =git log main..sentry/<date>-<host>= and the diff.
+4. Squash-merges what he wants (=git switch main && git merge --squash sentry/<date>-<host>=, then one clean commit) or cherry-picks selectively.
+5. Deletes the branch: =git branch -D sentry/<date>-<host>=.
+6. Reverts any Emacs buffers still showing the pre-merge on-disk state (=emacs.md= buffer-revert caveat).
+
+A bad night is discarded by deleting one branch — nothing reached main, nothing was pushed.
+
+* Stop Sentry
+
+Trigger: "stop sentry" (and synonyms above). Sentry owns its own shutdown:
+
+1. *Cancel the loop* — stop the =/loop= (=ScheduleWakeup= stop / the loop's stop path). No further fires.
+2. *Release the single-runner lock* if this context holds it.
+3. *Branch disposition* — offer, inline-numbered:
+ 1. Squash-merge the day's branch into main now (walk the morning teardown steps 3-5 interactively)
+ 2. Leave it named for later review (=sentry/<date>-<host>= stays; review at leisure)
+4. *Approval queue* — offer to walk the queued items now, or carry them (they stay under the heading for whenever Craig reviews).
+
+Stopping sentry is the only way to reclaim the working tree mid-night. The entry gate fronts the handoff; stop-sentry ends it.
+
+* Wrap-up interaction
+
+=wrap-it-up.org= refuses while sentry is live: it detects the single-runner lock (=agent-lock status sentry-<project>= → held) and stops with "sentry is active — say 'stop sentry' first." The shutdown logic lives here, not in wrap-up; wrap-up carries only the one guard.
+
+* Common Mistakes
+
+1. *Running without the =:COMMIT_AUTONOMY:= grant* — sentry commits unattended; the marker is the entry ticket, and its absence is a hard stop, not a degrade.
+2. *Starting from a dirty or red tree* — the entry gates exist because an unattended fire can't tell Craig's in-progress work from a regression. Answer the gate; don't bypass it.
+3. *Committing onto main* — every writing pass commits to the daily =sentry/*= branch. A fire that finds HEAD off the sentry branch skips rather than commits.
+4. *Running a =git= write against =~/org/roam=* — roam-sync is the only committer. Sentry edits the tree under the roam-write lock and triggers the sync; it never commits or pushes roam.
+5. *A per-pass suite run* — the suite runs at entry (baseline) and conditionally at fire-end (only when a pass touched non-org files). Hourly per-commit runs all night is the anti-pattern the suite policy exists to prevent.
+6. *Executing a judgment or destructive action unattended* — those queue for the morning with their exact command. The pass did its detection; Craig makes the call.
+7. *A silent skip* — every skip writes a digest line naming why. A missing pass with no line reads as "ran clean" when it didn't.
+8. *Degrading a pass to a reduced form* — a pass runs fully or skips. No half-passes.
+9. *Letting an unmerged branch stall silently* — after two consecutive unmerged-branch skips, the persistent desktop notify fires. Don't suppress it.
+10. *Merging sentry's branch automatically* — the morning teardown is Craig's. Sentry creates and commits; it never merges or deletes its own branch.
+
+* Living Document
+
+Sentry ships with ten mechanical passes and a deferred KB pass. The pass list, the interval default, and the queue-vs-execute line for each pass are the knobs most likely to move with dogfooding. Fold in what the live trial surfaces — a pass that queues too eagerly, a probe that misfires, a digest line that wants more detail. Refine as the signal arrives.
+
+* History
+
+Built 2026-07-19 from the sentry spec (=docs/specs/2026-07-14-sentry-workflow-spec.org=, ID f6c51f27-d7a2-4b63-9ff9-5ba005a66dfb) — 10 decisions and 12 review findings resolved before the build. Phase 1 shipped the =agent-lock= helper (commit =a8b6cf4=); this file is Phase 2, the engine. Phase 3 reconciles the roam writers (=inbox.org=, =knowledge-base.md=) to acquire the roam-write lock and adds the =wrap-it-up.org= guard.
diff --git a/inbox/PROCESSED-2026-07-20-2355-from-.dotfiles-kb-personal-roots-amendment-craig-s.org b/inbox/PROCESSED-2026-07-20-2355-from-.dotfiles-kb-personal-roots-amendment-craig-s.org
new file mode 100644
index 0000000..d45abe6
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-20-2355-from-.dotfiles-kb-personal-roots-amendment-craig-s.org
@@ -0,0 +1,5 @@
+#+TITLE: KB personal-roots amendment (Craig's order, 2026-07-21, from
+#+SOURCE: from .dotfiles
+#+DATE: 2026-07-20 23:55:01 -0500
+
+KB personal-roots amendment (Craig's order, 2026-07-21, from the dotfiles session): add ~/.dotfiles to the personal-project roots in claude-rules/knowledge-base.md. Today the Writing section classifies personal as 'project root sits under ~/code/, ~/projects/, or ~/.emacs.d and is not denylisted', which makes ~/.dotfiles classify Unknown and blocks KB writes — tonight it couldn't promote the sentry pass-list rule, and the 2026-07-04 session hit the same wall with the xdg-desktop-portal Requisite=graphical-session.target gotcha. Dotfiles is a personal single-user repo (private cjennings.net remote), so Craig wants it KB-write-eligible. Suggested edit: extend the personal classification to '~/code/, ~/projects/, ~/.emacs.d, or ~/.dotfiles'. The startup KB probe and knowledge-base.md are the only places that enumerate the roots as far as we know — reconcile any other copy the rulesets session finds.
diff --git a/inbox/PROCESSED-2026-07-21-0006-from-org-drill-memory-sweep-handoff-2026-06-10-done-in.org b/inbox/PROCESSED-2026-07-21-0006-from-org-drill-memory-sweep-handoff-2026-06-10-done-in.org
new file mode 100644
index 0000000..340262a
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-21-0006-from-org-drill-memory-sweep-handoff-2026-06-10-done-in.org
@@ -0,0 +1,5 @@
+#+TITLE: Memory-sweep handoff (2026-06-10) done in org-drill. Counts:
+#+SOURCE: from org-drill
+#+DATE: 2026-07-21 00:06:44 -0500
+
+Memory-sweep handoff (2026-06-10) done in org-drill. Counts: 0 promoted to KB / 1 kept local / 0 deleted. The single memory (no-markup-chat-output — never use code fences, bold, or backtick spans in chat) is cross-project, but its durable home is interaction.md rather than a KB node; a separate proposal to fold it into that rule follows in your inbox. Also processed your 2026-06-12 priority-scheme handoff: scheme section added to org-drill's todo.org (derived from existing usage, pending Craig's ratification). One deviation: no commit — org-drill gitignores todo.org per its code-project convention.
diff --git a/inbox/PROCESSED-2026-07-23-0241-from-.dotfiles-ack-from-dotfiles-both-amendments.org b/inbox/PROCESSED-2026-07-23-0241-from-.dotfiles-ack-from-dotfiles-both-amendments.org
new file mode 100644
index 0000000..ffd2335
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-23-0241-from-.dotfiles-ack-from-dotfiles-both-amendments.org
@@ -0,0 +1,5 @@
+#+TITLE: Ack from dotfiles: both amendments verified in the rulesets
+#+SOURCE: from .dotfiles
+#+DATE: 2026-07-23 02:41:58 -0500
+
+Ack from dotfiles: both amendments verified in the rulesets canonical. knowledge-base.md line 25 now lists ~/.dotfiles as a personal root, and claude-templates/.ai/workflows/sentry.org carries the amended overview line, the pass-3 mail/messenger exclusion, and pass 11 bug finding. Good call applying the sentry edits by hand instead of rsyncing my copy — I hadn't noticed my rewrite dropped the :TRIAGE_SOURCES: activation probe, and a straight copy would have taken the heartbeat/digest split out with it. I've updated the dotfiles memory to record that the canonical owns this now, so no local re-apply after a sync. Nothing needed back.
diff --git a/inbox/PROCESSED-2026-07-23-2127-from-home-rulesets-voice-correspondence-patterns.org b/inbox/PROCESSED-2026-07-23-2127-from-home-rulesets-voice-correspondence-patterns.org
new file mode 100644
index 0000000..72beba6
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-23-2127-from-home-rulesets-voice-correspondence-patterns.org
@@ -0,0 +1,85 @@
+#+TITLE: Proposal — two correspondence patterns for the voice skill (prose mode)
+
+* What this is
+
+Craig taught two structural rules while I drafted a Signal reply for him on
+2026-07-23. They aren't in the voice skill yet. He asked whether they belong in
+=/voice personal=; I said no, because personal mode is publish artifacts only
+(commits, PR titles and bodies, PR review comments) and email and Signal are
+prose mode. Sending the proposal here since rulesets owns =voice/=.
+
+I did *not* edit =~/code/rulesets/voice/SKILL.md= from the home session. The
+=~/.claude/skills/voice= symlink points straight at the canonical, so a local
+"stopgap" edit would have been an unflagged write into rulesets' scope. Home
+carries a memory as its stopgap instead.
+
+* The two rules, as Craig stated them
+
+1. *Order by what matters most to the recipient*, not by what's easiest to
+ answer or the order they wrote it. Their news outranks your logistics. A
+ direct question they asked can sort *below* personal news they shared.
+2. *Group each topic into one paragraph.* Everything on a subject goes
+ together rather than fragmenting across several paragraphs.
+
+* The worked case
+
+His sister sent a long catch-up: 30+ lbs lost, a deep from-scratch cooking run
+(sourdough, English muffins, tortillas, home-roasted deli meat), a mixer she's
+considering, and one direct question about whether he subscribes to MasterClass.
+
+My first draft opened with the MasterClass answer, because it was the only
+explicit question, and split the cooking material across three paragraphs.
+
+Craig reordered it: weight first, then one paragraph carrying the entire cooking
+thread, then MasterClass, then the personal close. His framing was "start with
+what would be the most important things to her."
+
+The lesson is that leading with the easy answer reads as transactional, and a
+topic split across paragraphs reads as a checklist rather than a person talking.
+
+* The conflict that needs resolving in the same edit
+
+Pattern #43 currently reads, in part:
+
+#+begin_quote
+Never merge short paragraphs into multi-sentence ones in a "clean prose" pass
+(corpus: 41-74% of Craig's paragraphs are exactly one sentence, depending on
+register).
+#+end_quote
+
+Rule 2 above is a merge instruction. Taken naively the two patterns contradict,
+and a future run gets whipsawed.
+
+They aren't actually in conflict, but the boundary has to be stated or the
+skill can't act on both:
+
+- *#43 governs an angle shift.* A one-sentence paragraph stands when the next
+ thought moves to a different subject. Craig's openers and closers stay
+ one-sentence, and they did in the final draft.
+- *The new rule governs one topic that got fragmented.* Three paragraphs all
+ about cooking become one.
+
+Proposed formulation: same topic → one paragraph. New angle → new paragraph.
+Whichever way this is written, #43's "never merge" line needs a qualifier
+pointing at the new pattern, or it will keep reading as absolute.
+
+* Scope
+
+These govern *correspondence* (email, Signal, letters), which is narrower than
+prose mode's current surface. Prose mode also covers journals, working notes,
+and documents with no recipient, where "order by what matters to the recipient"
+has no referent. Worth deciding whether that's a tag on the two patterns, a
+sub-mode, or just a sentence in each rule.
+
+* What I'd suggest, but rulesets owns the call
+
+Two new prose-mode patterns, correspondence-scoped, plus a one-line qualifier on
+#43 naming the boundary. Numbering and the profile entries are yours. The
+=voice-profile.org= side wants the worked before/after above, since the
+reordering is the whole lesson and it doesn't survive as a rule line alone.
+
+* Provenance
+
+home session 2026-07-23, drafting a Signal reply to Craig's sister. Home holds a
+=correspondence-structure-rules= memory as its interim, marked as pending this
+proposal.
diff --git a/inbox/PROCESSED-2026-07-23-2132-from-home-rulesets-voice-correspondence-followup.org b/inbox/PROCESSED-2026-07-23-2132-from-home-rulesets-voice-correspondence-followup.org
new file mode 100644
index 0000000..1dfc788
--- /dev/null
+++ b/inbox/PROCESSED-2026-07-23-2132-from-home-rulesets-voice-correspondence-followup.org
@@ -0,0 +1,102 @@
+#+TITLE: Follow-up — better design for the correspondence patterns (supersedes the earlier proposal)
+
+* What this supersedes
+
+The 2026-07-23 21:27 handoff from home, =rulesets-voice-correspondence-patterns.org=.
+That one proposed *two* new prose-mode patterns and flagged a conflict with #43
+to be resolved. It asked the right question and proposed a worse answer. Craig
+and I worked it further the same evening. Take this design instead.
+
+* The reconciliation: there was no conflict
+
+I claimed rule 2 ("group each topic into one paragraph") contradicted #43
+("never merge short paragraphs"). It doesn't.
+
+#43 already carries the answer in the phrase *"when the next thought shifts
+angle."* The failure was reading "angle" at sentence granularity. Moving from
+her baking to the mixer to tortillas felt like three angles when I drafted it.
+It isn't. That's one topic with three facets. Moving from cooking to MasterClass
+is a real shift.
+
+So the fix is calibration, not a new competing rule: *angle means topic, not
+sentence.*
+
+Read that way the two rules are one rule seen from both sides. #43 protects the
+break *between* topics, which is where Craig's cadence actually lives. The
+grouping behavior fills in what happens *within* a topic, which #43 never
+specified and I filled in wrong.
+
+* The evidence is in the final draft
+
+Every one-sentence paragraph survived Craig's edit: the weight-loss opener, the
+MasterClass answer, the "I miss you and I love you" line, the closing ask for
+call times. Each is its own topic.
+
+The only merge was three paragraphs that were all cooking.
+
+So his edit didn't violate #43. It corrected my misreading of it. That's the
+strongest argument for folding this into #43 rather than adding a rule beside
+it: a pattern that has to be reconciled against its neighbor is a pattern that
+gets misapplied, and this one already was.
+
+* Proposed change (smaller than the first proposal)
+
+*One new pattern — recipient-priority ordering.* Correspondence-scoped, prose
+mode. Nothing in the skill covers it today.
+
+#+begin_quote
+In a reply, lead with what matters most to the recipient, not with what's
+easiest to answer or the order they wrote it. Their news outranks your
+logistics. A direct question they asked can sort below personal news they
+shared, because the news is what they care about.
+#+end_quote
+
+Scope note: this needs a recipient, so it applies to email, Signal, and letters.
+It has no referent in a journal, a working note, or a document addressed to
+nobody, all of which prose mode also covers. Worth a tag or a scope sentence.
+
+*One clarification to #43 — not a new pattern.* Three edits to the existing rule:
+
+1. State that "shifts angle" means shifts *topic*.
+2. Add that a single topic consolidates into one paragraph even when each
+ sentence in it is a complete thought.
+3. Keep the never-merge line, re-framed as a protection *across topic
+ boundaries*, which is what it was always defending.
+
+* The guard (Craig's number)
+
+Within-topic consolidation must not produce a wall. Craig set the ceiling at
+*about 5 to 6 sentences*, after which find a natural break and split there.
+
+Honest note on the worked example below: the cooking paragraph as sent runs
+seven sentences, slightly over its own guard. Craig called it an exception
+rather than re-cutting a message that had already gone out. The guard is the
+rule, that paragraph is a hair over it, and both facts belong in the record.
+
+* Worked before/after (for voice-profile.org)
+
+Her message: 30+ lbs lost, a deep from-scratch cooking run (sourdough, English
+muffins, tortillas, home-roasted deli meat), a mixer she's considering, and one
+direct question about whether Craig subscribes to MasterClass.
+
+*My first draft* opened with the MasterClass answer, because it was the only
+explicit question, and split the cooking material across three paragraphs.
+
+*Craig's order:* weight, then one paragraph carrying the entire cooking thread,
+then MasterClass, then the personal close. His framing: "start with what would
+be the most important things to her."
+
+The lesson: leading with the easy answer reads as transactional, and one topic
+split across paragraphs reads as a checklist rather than a person talking.
+
+* Mode confirmation
+
+Prose, confirmed with Craig. Personal mode stays publish artifacts only
+(commits, PR titles and bodies, PR review comments). Email and Signal are prose.
+
+* Provenance
+
+home session 2026-07-23, drafting and sending a Signal reply to Craig's sister.
+Home holds a =correspondence-structure-rules= memory as its interim; it will
+want updating to match this design once rulesets lands it, since it currently
+describes the earlier two-pattern framing.
diff --git a/inbox/lint-followups.org b/inbox/lint-followups.org
new file mode 100644
index 0000000..f63470c
--- /dev/null
+++ b/inbox/lint-followups.org
@@ -0,0 +1,17 @@
+* 2026-07-20 Mon — Task-review health: 1 top-level [#A]/[#B]/[#C] tasks unreviewed for >30 days (daily review may have slipped)
+
+* lint-org follow-ups — todo.org (2026-07-25)
+** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/validate-el.diff" (line 2066)
+** TODO misplaced-planning-info — Misplaced planning info line (line 2055)
+** TODO link-to-local-file — Link to non-existent local file "working/hook-fail-open/pre-commit.diff" (line 2050)
+** TODO misplaced-planning-info — Misplaced planning info line (line 2035)
+** TODO org-table-standard — table violates the org-table standard: no closing rule; missing rule between rows — wrap-org-table.el reflows it (line 141)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 299)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 302)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 309)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 317)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 320)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 329)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 427)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 586)
+** TODO task-missing-last-reviewed — task has no :LAST_REVIEWED: — stamp it at creation with today's date (a task you just wrote and graded is reviewed); otherwise it enters the next staleness batch as never-reviewed (line 595)