aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-20 12:30:51 -0500
committerCraig Jennings <c@cjennings.net>2026-07-20 12:30:51 -0500
commit0767af804b980c23fdd759f65c0f50b499fb1bbe (patch)
tree1e521bbe6b9db8046c7137c09aa9588b0c1ccf67
parent986d6caeeaba6e4ab2f1e6f5e760ce841c922259 (diff)
downloadrulesets-0767af804b980c23fdd759f65c0f50b499fb1bbe.tar.gz
rulesets-0767af804b980c23fdd759f65c0f50b499fb1bbe.zip
feat(sentry): a quiet fire collapses to a one-line heartbeat
A sentry fire whose passes all probe-skip or no-op now writes a single "sentry at HH:MM: nothing" line instead of a full per-pass digest block. A fire that runs or queues anything writes the full digest as before. This is Phase 1 of the silent-until-signal policy. The live trial's fires 3-8 were walls of no-op digests, the noise this removes. The "no silent skip" discipline is reconciled, not broken: inside a working fire every pass still writes its ran/skipped line, and a quiet fire's heartbeat is the explicit "nothing to do" record rather than a hidden skip.
-rw-r--r--.ai/workflows/sentry.org10
-rw-r--r--claude-templates/.ai/workflows/sentry.org10
-rw-r--r--docs/specs/2026-07-20-silent-until-signal-monitors-spec.org5
-rw-r--r--todo.org2
4 files changed, 15 insertions, 12 deletions
diff --git a/.ai/workflows/sentry.org b/.ai/workflows/sentry.org
index 56f0c96..bfeeffc 100644
--- a/.ai/workflows/sentry.org
+++ b/.ai/workflows/sentry.org
@@ -89,7 +89,7 @@ Each fire, after acquiring the single-runner lock and verifying branch state (be
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.
+3. *Session-context entry* — a pass that does or queues work appends its digest line to the =session-context.org= Session Log (path resolved via =.ai/scripts/session-context-path=) before its commit, so a crash between them still leaves the trail. Per-pass lines for an all-quiet fire (every pass probe-skipped or no-op) are not written one by one — the fire collapses to a single heartbeat at fire-end (below), so an idle fire doesn't spray one skip line per pass.
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.
@@ -111,7 +111,7 @@ With no one at the terminal, any unsafe state makes the affected scope skip with
- *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.
+Skips are never silent and never partial. Inside a *working* fire, a pass line means the pass fully ran and a skip line names why it didn't. An *all-quiet* fire is not a silent skip either: its single =sentry at HH:MM: nothing= heartbeat is the explicit record that every pass found nothing, standing in for a wall of identical skip lines. The anti-silence rule targets a pass that hides work it should have surfaced; a quiet fire has surfaced that there was none.
** Multi-day stall notification
@@ -153,13 +153,13 @@ 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.
+2. *Heartbeat or digest, then commit.* Decide quiet vs working. A *quiet* fire — every pass probe-skipped or no-op, nothing added to the approval queue — writes a single heartbeat line to the Session Log, =sentry at HH:MM: nothing= (HH:MM local, from =date=), and no per-pass digest block. A *working* fire — any pass ran, wrote, or queued — writes its full per-pass digest block. Then commit any accumulated spine writes in one sweep: =chore(sentry): digest — <date> <time> fire= for a working fire, =chore(sentry): heartbeat — <date> <time>= for a quiet one, so even a quiet fire leaves a clean tree for the next branch-state check (where the spine is untracked, the mirror-only case, there is nothing to commit and the heartbeat line stays in the working-tree anchor). This is the silent-until-signal policy (see =docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=): an all-quiet night collapses from a wall of no-op digests to a list of one-line heartbeats, while a fire that actually did or queued something still writes the full record.
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.
+*Digest.* A *working* fire appends its block 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 working fire: the timestamp, then one line per pass (ran + what, or skipped + why), plus any lock reclaim notes. A *quiet* fire (nothing done or queued) writes no block — just the one heartbeat line =sentry at HH:MM: nothing= (the silent-until-signal policy). The per-pass block is a working-fire artifact; it still carries one line per pass so a real skip inside a working fire is never hidden.
*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.
@@ -201,7 +201,7 @@ Stopping sentry is the only way to reclaim the working tree mid-night. The entry
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.
+7. *A silent skip* — inside a working fire, every skip writes a digest line naming why; a missing pass with no line reads as "ran clean" when it didn't. The one exception is not a violation: an all-quiet fire collapses to a single =sentry at HH:MM: nothing= heartbeat instead of one skip line per pass — the heartbeat is the explicit "nothing to do" record, per the silent-until-signal policy.
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.
diff --git a/claude-templates/.ai/workflows/sentry.org b/claude-templates/.ai/workflows/sentry.org
index 56f0c96..bfeeffc 100644
--- a/claude-templates/.ai/workflows/sentry.org
+++ b/claude-templates/.ai/workflows/sentry.org
@@ -89,7 +89,7 @@ Each fire, after acquiring the single-runner lock and verifying branch state (be
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.
+3. *Session-context entry* — a pass that does or queues work appends its digest line to the =session-context.org= Session Log (path resolved via =.ai/scripts/session-context-path=) before its commit, so a crash between them still leaves the trail. Per-pass lines for an all-quiet fire (every pass probe-skipped or no-op) are not written one by one — the fire collapses to a single heartbeat at fire-end (below), so an idle fire doesn't spray one skip line per pass.
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.
@@ -111,7 +111,7 @@ With no one at the terminal, any unsafe state makes the affected scope skip with
- *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.
+Skips are never silent and never partial. Inside a *working* fire, a pass line means the pass fully ran and a skip line names why it didn't. An *all-quiet* fire is not a silent skip either: its single =sentry at HH:MM: nothing= heartbeat is the explicit record that every pass found nothing, standing in for a wall of identical skip lines. The anti-silence rule targets a pass that hides work it should have surfaced; a quiet fire has surfaced that there was none.
** Multi-day stall notification
@@ -153,13 +153,13 @@ 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.
+2. *Heartbeat or digest, then commit.* Decide quiet vs working. A *quiet* fire — every pass probe-skipped or no-op, nothing added to the approval queue — writes a single heartbeat line to the Session Log, =sentry at HH:MM: nothing= (HH:MM local, from =date=), and no per-pass digest block. A *working* fire — any pass ran, wrote, or queued — writes its full per-pass digest block. Then commit any accumulated spine writes in one sweep: =chore(sentry): digest — <date> <time> fire= for a working fire, =chore(sentry): heartbeat — <date> <time>= for a quiet one, so even a quiet fire leaves a clean tree for the next branch-state check (where the spine is untracked, the mirror-only case, there is nothing to commit and the heartbeat line stays in the working-tree anchor). This is the silent-until-signal policy (see =docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=): an all-quiet night collapses from a wall of no-op digests to a list of one-line heartbeats, while a fire that actually did or queued something still writes the full record.
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.
+*Digest.* A *working* fire appends its block 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 working fire: the timestamp, then one line per pass (ran + what, or skipped + why), plus any lock reclaim notes. A *quiet* fire (nothing done or queued) writes no block — just the one heartbeat line =sentry at HH:MM: nothing= (the silent-until-signal policy). The per-pass block is a working-fire artifact; it still carries one line per pass so a real skip inside a working fire is never hidden.
*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.
@@ -201,7 +201,7 @@ Stopping sentry is the only way to reclaim the working tree mid-night. The entry
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.
+7. *A silent skip* — inside a working fire, every skip writes a digest line naming why; a missing pass with no line reads as "ran clean" when it didn't. The one exception is not a violation: an all-quiet fire collapses to a single =sentry at HH:MM: nothing= heartbeat instead of one skip line per pass — the heartbeat is the explicit "nothing to do" record, per the silent-until-signal policy.
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.
diff --git a/docs/specs/2026-07-20-silent-until-signal-monitors-spec.org b/docs/specs/2026-07-20-silent-until-signal-monitors-spec.org
index 275bcfa..210c1e9 100644
--- a/docs/specs/2026-07-20-silent-until-signal-monitors-spec.org
+++ b/docs/specs/2026-07-20-silent-until-signal-monitors-spec.org
@@ -8,6 +8,7 @@
:PROPERTIES:
:ID: af592bd6-d3e6-47e2-8804-2a287b4d9303
:END:
+- 2026-07-20 Mon @ 12:30:00 -0500 — Phase 1 (sentry quiet-fire heartbeat) implemented ahead of the full READY gate, at Craig's direction — a quiet sentry fire now collapses to =sentry at HH:MM: nothing=, and the "no silent skip" discipline is reconciled (the heartbeat is the explicit "nothing" record). Phases 2-5 (triage, inbox, shared-policy home, verification) still pending Craig's deep read. Spec stays DRAFT.
- 2026-07-20 Mon @ 12:14:20 -0500 — drafted during the morning session. Design decisions resolved live with Craig off the .emacs.d proposal and the sentry live-trial evidence. DRAFT pending his deep read.
* Metadata
@@ -102,9 +103,11 @@ No new watcher-owned seen-list; the sentinel / branch-digest / disposition each
* Implementation Phases
-** Phase 1 — sentry quiet-fire heartbeat
+** Phase 1 — sentry quiet-fire heartbeat — DONE 2026-07-20
Edit =.ai/workflows/sentry.org= (canonical + mirror): a fire whose passes all probe-skip or no-op writes =sentry at HH:MM: nothing= instead of a full per-pass digest; a fire that runs or queues anything writes the full digest unchanged. Update the digest section and the Common Mistakes "silent skip" note to draw the quiet-fire-vs-working-fire line. Run sync-check. (Separable and the highest-value piece — do first.)
+Shipped 2026-07-20: five edits to sentry.org — Pass Runner step 3 (per-pass lines collapse on a quiet fire), Fire-end step 2 (the heartbeat-vs-digest decision + heartbeat commit variant), the digest section (working-block vs quiet-heartbeat), Unattended-safety (quiet fire is not a silent skip), Common Mistakes #7 (the carve-out). Canonical + mirror synced, lint clean.
+
** Phase 2 — auto triage-intake heartbeat
Edit =.ai/workflows/triage-intake.org= Auto mode: an empty sweep collapses to =triage intake at HH:MM: nothing=. Preserve in-session detection and the accumulate-don't-mutate contract. Run sync-check.
diff --git a/todo.org b/todo.org
index 03d209d..ea69ba4 100644
--- a/todo.org
+++ b/todo.org
@@ -186,7 +186,7 @@ Awaiting Craig's deep read of the spec (DRAFT → READY gate). Two decisions sti
** TODO [#B] Silent-until-signal for in-session monitor loops :feature:spec:
Spec: [[file:docs/specs/2026-07-20-silent-until-signal-monitors-spec.org][docs/specs/2026-07-20-silent-until-signal-monitors-spec.org]] (DRAFT, ID af592bd6-d3e6-47e2-8804-2a287b4d9303). Decisions resolved live with Craig 2026-07-20; awaiting his deep read for DRAFT → READY.
-Craig-approved proposal from .emacs.d (2026-07-20), demonstrated by the sentry live trial (fires 3-8 were walls of no-op lines). Reframed live from a watcher *mechanism* to a *policy*: an in-session monitor fire detects first, and on an empty check collapses to one labelled heartbeat line (=<workflow> at HH:MM: nothing=) instead of a full turn; only a real item earns the full surface-and-judge turn. Keeping detection in-session dissolves the MCP-auth split (triage's Gmail/Slack/Linear need session auth), so it applies uniformly to sentry, auto triage-intake, and auto inbox-zero. No external watcher, no new seen-list. Five build phases in the spec (sentry heartbeat first — separable and highest-value). Overlaps the sentry cluster ([[file:todo.org::*Sentry vNext passes][Sentry vNext passes]], [[file:todo.org::*Triage source activation][Triage source activation]]).
+Craig-approved proposal from .emacs.d (2026-07-20), demonstrated by the sentry live trial (fires 3-8 were walls of no-op lines). Reframed live from a watcher *mechanism* to a *policy*: an in-session monitor fire detects first, and on an empty check collapses to one labelled heartbeat line (=<workflow> at HH:MM: nothing=) instead of a full turn; only a real item earns the full surface-and-judge turn. Keeping detection in-session dissolves the MCP-auth split (triage's Gmail/Slack/Linear need session auth), so it applies uniformly to sentry, auto triage-intake, and auto inbox-zero. No external watcher, no new seen-list. Five build phases in the spec. Phase 1 (sentry quiet-fire heartbeat) shipped 2026-07-20 ahead of the full READY gate at Craig's direction — a quiet fire now collapses to =sentry at HH:MM: nothing=. Phases 2-4 (auto triage-intake, auto inbox-zero, shared-policy home) + Phase 5 verification pending Craig's spec read. Overlaps the sentry cluster ([[file:todo.org::*Sentry vNext passes][Sentry vNext passes]], [[file:todo.org::*Triage source activation][Triage source activation]]).
** TODO [#D] Research: MCP for device locations shared with you :feature:
From .emacs.d (2026-07-20, rulesets-owned research). Is there a Google Maps MCP (or similar) that reports the locations of devices sharing their location with you? If none exists, research how hard it would be to build one. (Google's location-sharing has no official public API; likely needs investigation of unofficial routes or a different provider.)