<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dingger.git/crates/dinggerd/src/notify.rs, branch master</title>
<subtitle>Watches the AI coding agents in your tmux panes on macOS and tells you which need you, from the menu bar, a floating panel, the status line and notifications; jumps to them, and restores tmux sessions and agents after a crash
</subtitle>
<id>https://git.soarez.net/sz/dingger.git/atom?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/dingger.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/'/>
<updated>2026-10-26T14:32:18+00:00</updated>
<entry>
<title>importance: mark an agent high, normal, or low</title>
<updated>2026-10-26T14:32:18+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-26T14:32:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=185af91b4d525db6c192a050b53615772fb5fe11'/>
<id>urn:sha1:185af91b4d525db6c192a050b53615772fb5fe11</id>
<content type='text'>
within a status, more important agents are listed first, and so jumped to
first; status stays first. a high agent shows a bold ! before its name, a
low agent's name is lightly dimmed with "low" on its second line, and it
makes no sound (it still notifies and waits in line).

in pi, /importance high|normal|low (completes the levels; none once one is
typed out, so Enter runs it). the extension saves it in the session as a
custom entry (dingger-importance), so /reload, restarts, /resume and
restore bring it back; /new starts at normal. it shows a level other than
normal in pi's footer, emits dingger:importance { level } for other
extensions, and sends it with every report. for other agents, `dingger
importance [level] [--pane]`; it refuses a pi that reports itself, whose
saved level would win.

the daemon stays the one writer of pane state: it writes
@dingger_importance (unset for normal), which goes with the agent and
survives a daemon restart. `dingger status` marks high and low.

tests: e2e step 21 (order, the option, no sound for low, the menu bar,
the command, the extension through a harness that now matches pi's
extension API), a DINGGER_SOUND seam so sounds are observable, and a
status-line check that read the counts once (flaky) now polls.
pi-live: /importance in real pi 1.4, its footer and session entry, and the
level after a restore; every pi there now uses the test's agent directory
(a restored pi read the user's). negative controls fail each part.
</content>
</entry>
<entry>
<title>restore, step 3: offer it in the panel, menu, and status line</title>
<updated>2026-10-23T10:59:59+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-23T10:59:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=9c9c9dad93b16781ec2fc40bd7582db62240171c'/>
<id>urn:sha1:9c9c9dad93b16781ec2fc40bd7582db62240171c</id>
<content type='text'>
when tmux stops (and its sessions are missing from the running server, or
no server runs), the daemon publishes the restore offer: offer.json for the
menu bar app and @dingger_restore for the status line, withdrawn once
restored or dismissed.

- panel: a first row "↺ Restore 9 sessions · 22 agents" with when it was
  saved and how many agents were mid-turn; clicking restores, "Not now"
  dismisses. an offer shows the panel like a waiting agent; off stays off.
- menu: the offer on top; restore all, one session, or not now.
- status line: a clickable blue ↺N before the clock.
- restore runs through the user's login shell, as a terminal starts tmux,
  so restored panes get a real environment, not launchd's; --report writes
  restore.log and notifies a summary. restore --dismiss.
- commands log only warnings by default; SIGPIPE ends them quietly.

e2e: the offer through the real menu bar binary, the summary notification,
the offer clearing, a restore with no tmux running, and not now staying
dismissed when a terminal starts tmux. the second test daemon is started
directly (it leaked through a function's $! before, and dozens of leaked
daemons fought over the test socket and loaded the machine); each run uses
its own socket and checks its daemons stop.
</content>
</entry>
<entry>
<title>rebuild the status model as herdr's state machine, event-driven and tested</title>
<updated>2026-10-20T09:43:07+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-20T09:43:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=4af6a4f625b1965b783c9efb857f8ac665adb8eb'/>
<id>urn:sha1:4af6a4f625b1965b783c9efb857f8ac665adb8eb</id>
<content type='text'>
the seen logic was inverted: first observations were marked done and real
completions idle, so the menu showed teal for agents that did nothing and
green for agents that finished, and attending changed nothing visible.

- watcher::advance is herdr's apply_pane_state_change + mark_active_tab_seen
  as a pure function, unit-tested against herdr's own cases; attendance is
  window-level like herdr's active tab
- hook reports are events applied in order, so a turn shorter than a poll is
  still a completion
- event-driven: hook reports, dingger focus, and tmux focus hooks wake the
  watcher; notifications are delivered off-thread (the macos call blocked
  ticks for 2+ seconds) after the pane options are written
- all per-pane state persists in tmux pane options, so restarts and deploys
  lose nothing, including the authoritative hook state
- every click goes through dingger focus: the menu bar shells out to it and
  the status-line binding runs it; it picks the right client, skips the
  window already shown, and activates the terminal app hosting the client
- the ready queue is ordered like the menu (blocked first, then oldest done)
- menu bar and tmux segment render herdr's symbols in herdr's palette
- tests/e2e.sh: 60 assertions against an isolated tmux server with a real
  client, a 30s poll interval, and a slow fake notifier
</content>
</entry>
<entry>
<title>initial dingger: tmux agent watcher with herdr's detection engine</title>
<updated>2026-10-19T13:02:57+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-19T13:02:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=ab33633ceb9e4bea3e292b2371241797d1587d02'/>
<id>urn:sha1:ab33633ceb9e4bea3e292b2371241797d1587d02</id>
<content type='text'>
extract detect-core from herdr's src/detect (engine + 22 manifests,
tests unchanged) and add the dingger binary: tmux capture-pane watcher,
process-tree agent identification, herdr's notification policy, embedded
notification sounds, @agent_state write-back, and catalog-based manifest
refresh. viewer screens are non-observations per the engine contract;
reviewed by a three-model panel (opus 5.5, gpt 6.1 sol, kimi k3) with
all confirmed findings applied as deletions.
</content>
</entry>
</feed>
