#+TITLE: Sentry Workflow — Proposal for rulesets #+AUTHOR: Craig Jennings #+DATE: 2026-07-13 * Status Proposal from the work project to the rulesets session, for implementation as a general (shared-layer) workflow: =.ai/workflows/sentry.org= plus a small supporting script or two under =.ai/scripts/=. * Problem An agent session left running overnight can keep a project clean and healthy on a cadence, but the current pieces don't coordinate. Two gaps: 1. *No agent-vs-agent collision guard on shared public files.* =capture-guard= only detects Emacs org-capture buffers (the human case) on one target file, and it's advisory, not a lock. There is no flock anywhere in =.ai/scripts/=. Multiple sessions (several projects, sometimes both daily drivers) can read-modify-write =~/org/roam/inbox.org= and the =agents/= KB nodes at once. The only coordination is the git pull/commit/push discipline in knowledge-base.md, which is optimistic — a pull/push race forks a =*sync-conflict*= file, and the KB search already globs those out, so we know the forks happen and get dropped. 2. *No single serialized runner.* Firing inbox-zero and triage-intake as separate hourly crons means they can overlap each other and other agents. Sentry is one supervisor loop that runs a fixed sequence of hygiene passes serially, holds locks so it never collides with itself or other agents, and checkpoints aggressively for crash recovery. * What sentry is A single workflow, driven by one interactive =/loop= (one cron), that on each fire walks an ordered list of passes inside a single turn. It replaces the per-task crons. Design invariants: - *Serial.* Passes run one at a time, in order. They never overlap each other. - *Single-runner lock.* Sentry takes a project-local flock (=.ai/.sentry.lock=) at entry. If a prior fire is still running, this fire skips instead of doubling up. - *Shared-file lock.* Any pass that writes a public roam file first acquires a roam-write lock (flock on =~/org/roam/.roam-write.lock=) held across the whole read-modify-write-commit-push cycle, so it serializes against other agents on the same host. =capture-guard= still runs underneath for the human case. (The roam-write lock is new; it's the general guard the dropped KB-guard note would have proposed, folded in here.) - *Non-destructive by default.* Read-only and idempotent-filing passes run fully. Anything destructive or judgment-heavy (triage trash/mark-read, task regrades, spec flips) queues its proposal into the digest for morning approval, never fires unattended. - *Idempotent + isolated.* Every pass is safe to repeat. A pass that errors doesn't abort the rest; its failure is a loud banner in the digest. * Crash-recovery spine (applies to every pass) Crash recovery has been a live consideration. Two enforced steps wrap each pass: 1. *Session-context between passes.* Before the next pass starts, the current pass appends a dated entry to =.ai/session-context.org= recording what it did. A crash mid-sequence leaves a readable "got this far" trail, so the next session resumes without re-deriving state. 2. *Local commit, no push, on any disk change.* Every pass that writes to disk gets its own =chore(sentry): = commit on the current branch, unpushed. A crash loses at most one pass. Craig reviews the stack in the morning and pushes deliberately. Conventional messages, no AI attribution. (Open decision: commit on the current branch vs a dedicated =sentry/= branch — see Open decisions.) The unit per pass is therefore: do the work → log to session-context → commit. That triplet is the recovery boundary. * Passes (v1 — all ten, plus the first-run KB promotion) Ordered so read-only/pull passes precede writes, and the heaviest external sweep sits mid-run: 1. *Roam pull* — =git -C ~/org/roam pull --ff-only= so later reads are fresh. Read-only. 2. *Inbox zero* — inbox.org roam mode. Route roam-inbox items this project owns. Acquires the roam-write lock for the inbox edit. 3. *Triage intake* — triage-intake.org. External-accounts sweep, classify, file Action tasks. Destructive actions (trash/mark-read) queue for morning; =mbsync -a= and the sentinel advance run. 4. *Todo cleanup* — clean-todo.org + todo-cleanup.el + lint-org.el. Convert level-3 done subtasks to dated logs, archive done subtrees, flag lint. 5. *Task audit* — task-audit.org. Re-check =:solo:=/=:quick:= tags, priority-scheme conformance, bug severity×frequency grades, stale =DOING= specs with closed parents, blocked/blocker reciprocity. Regrades queue for morning. 6. *Working-files hygiene* — flag =working//= dirs whose task is DONE but not filed to =assets/=. Report only; filing is a morning decision. 7. *Spec status board* — the docs-lifecycle grep. Flag =DOING= specs with closed parents, or specs stuck in =DRAFT=. 8. *Link integrity* — lint-org broken =file:= links across todo/notes/prep. 9. *Git health* — uncommitted drift, unpushed commits, stale local branches, whether main is behind origin. 10. *Prep + symlink freshness* — verify tomorrow's prep and both symlinks resolve. 11. *KB lesson promotion (first run of the session, and thereafter as new lessons accrue)* — promote recent durable lessons out of the fast capture layer. CLASSIFICATION-GATED: - Personal project → write to the roam KB as =agents/= nodes (pull, lock, write, commit, push), per knowledge-base.md. - Work / denylisted project → NEVER touch roam. Promote to the project's own durable store (e.g. =deepsat/knowledge.org=) instead, and emit the one-line refusal note from the knowledge-base.md refusal contract so nothing is lost silently. * Digest output One consolidated, timestamped summary per fire: run time first, then per-pass one-liners (what it found / did), the morning-approval queue (proposed trash/mark-read, regrades, files-to-move), and any failures as loud banners at the top. Delta-only where a pass supports it (triage): a pass with nothing to report is one line. * Cadence One hourly sentry to start (passes short-circuit fast when there's nothing to do). A slower evening interval (every 2–3 hours) is a config knob if hourly proves noisy. * Open decisions for rulesets 1. *Commit target* — per-pass commits on the current branch (simplest, matches "commit but don't push") vs a dedicated =sentry/= branch (keeps the working branch clean, costs a branch dance). Proposal leans current branch. 2. *Roam-write lock scope* — host-local flock only (solves same-host multi-agent, the common case) vs also hardening roam-sync to surface cross-host conflicts loudly. Proposal: ship the flock now, note the cross-host limit, treat roam-sync conflict-surfacing as a separate follow-up. 3. *Interval default* — hourly vs evening-only. Proposal: hourly, config knob. 4. *Which passes gate on a green tree* — e.g. skip todo-cleanup commits if the project suite is red, matching inbox monitor-mode's clean-tree gate. * Cover note from sender (work, 2026-07-14 00:02) Intro for the sentry-proposal.org file just sent. This is a NEW general workflow proposal (.ai/workflows/sentry.org + supporting scripts under .ai/scripts/), not an edit to an existing synced file. What it is: one serialized supervisor loop that runs a project's evening hygiene passes on a cadence, with locks so it never collides with itself or with other agents. It replaces firing inbox-zero + triage-intake as separate crons. Why now: an agent left running overnight can keep a project clean, but the current pieces don't coordinate. capture-guard only handles the Emacs-capture case on one file and holds no lock; there is no flock anywhere in .ai/scripts/; and separate crons let inbox-zero and triage overlap each other and other agents on shared roam files, which already fork *sync-conflict* files that the KB search globs out. Three design points worth your judgment: 1. Crash-recovery spine on every pass (crash recovery has been a live concern): enforce a session-context.org update BETWEEN passes, and a local commit (no push) after any disk change, so a crash loses at most one pass. 2. A new roam-write flock (~/org/roam/.roam-write.lock) as the shared-file guard. This absorbs a separate 'guard all public KB files' note we considered and dropped. The lock is the better home for that concern than a bespoke per-file guard. 3. The KB lesson-promotion pass is classification-gated: personal projects promote to roam agents/ nodes; a denylisted work project promotes to its own local store (deepsat/knowledge.org) and never touches roam, per knowledge-base.md's refusal contract. Companion files to reconcile: knowledge-base.md (the gated promotion + refusal contract), inbox.org (roam mode + the new lock), triage-intake.org (runs as a sentry pass; keep its own triggers), capture-guard (becomes one layer under the roam-write lock, not the whole guard), and clean-todo.org / task-audit.org / lint-org.el (invoked as passes). Open decisions for you are listed at the bottom of the proposal file: commit target, lock scope, interval default, and which passes gate on a green tree.