You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-ups left after #122 closed. Written to be self-sufficient: a session
pointed here should not need the thread that produced it.
Orientation
The re-awakening bridge (RFC #122) wakes an idle Claude Code session by
typing a fixed prompt into its terminal pane, so a DM reaches a session whose
human is away instead of waiting for the next prompt.
Operator docs: docs/BRIDGE.md — read this first; it is the contract.
Design record: docs/superpowers/specs/2026-08-10-mux-wake-backend-design.md
— why things are the way they are, including decisions that were reversed.
Code: src/agent_event_bus/bridge.py (daemon), wake.py (the shared
wake-dir contract — read and written by both the bridge and the CLI, on
purpose), cli.py (panes, wake-state).
State of the bus host (mac-mini.local, as of 2026-08-16)
Multiplexer in use is zellij 0.44.3, not tmux (tmux 3.7b is installed and
the backend supports it, but no session runs under it).
The bridge LaunchAgent is installed with AGENT_EVENT_BUS_BRIDGE_BACKEND=mux
(injecting). /health reports registered: true.
wake/panes.json is {}. Sessions map themselves at SessionStart, so
any session started before dotfiles#338 merged is unmapped and unwakeable
until it restarts. An empty file is normal, not a fault.
Hooks are live immediately on merge: ~/.claude/settings.json is a
symlink into the dotfiles repo. The bridge plist is not — it is a copy in ~/Library/LaunchAgents, so plist changes need make install-bridge.
1. Reboot ← the one that needs a human
The actual supervision requirement (RunAtLoad plus login), never verified
because it needs the host restarted.
After the next reboot:
launchctl list | grep agent-event-bus # both jobs present
curl -s http://127.0.0.1:8082/health # registered: true
grep "Wake injection available via"~/.claude/contrib/agent-event-bus/agent-event-bus-bridge.err | tail -1
The third line is the one that matters: it must name the multiplexers found on launchd's PATH, which is narrower than a shell's.
2. ZELLIJ_PANE_ID uniqueness across a full pane set
The writer trusts $ZELLIJ_PANE_ID to identify a pane within a zellij session.
Verified directly: a single-pane session, and cross-tab addressing (pane 0 in
tab 1 accepted a write while tab 2 was focused). Not verified: that the id
stays unique and stable across a session's full pane set in daily use —
splits, closes, reopens.
To check: in one zellij session, open several panes across tabs, and in each
run echo $ZELLIJ_PANE_ID. Confirm no two share an id, then confirm each is
addressable:
Failure shape: a wake typed into the wrong pane of the right session, which
looks exactly like the stale-mapping symptom and would be easy to misdiagnose.
3. No established minimum zellij version
Only 0.44.3 has been exercised, so docs/BRIDGE.md names no floor. The
injection surface is zellij --session <n> action write-chars -p <pane> plus action write -p <pane> 13; -p is the part most likely to differ across
versions.
A spool-mux-failed now carries zellij's own stderr, so a CLI-surface mismatch
is self-diagnosing — but the floor is still unknown. Worth pinning if this ever
runs on a second machine.
4. Optional: per-tool-call marker refresh
--busy-ttl defaults to 1 hour because the only refresh in the default wiring
is UserPromptSubmit. A turn that outruns it reads as idle for the remainder,
so a late DM can inject mid-turn.
Any hook firing during a turn can run agent-event-bus-cli wake-state busy to
make the marker a heartbeat, allowing a much lower TTL. Not wired by default:
it costs a subprocess per tool call. Do this only if long turns turn out to be
common here.
5. The verification-boundaries rule only covers half its problem
CLAUDE.md (#148) says host-specific claims must be marked unverified rather
than asserted. That catches a claim made without evidence. It does not
catch the inverse, which #153 then had to fix twice:
docs/BRIDGE.md carried "unverified for Claude Code's TUI" after it had been
measured.
Both are caveats that outlived their uncertainty, with nothing forcing them
to track reality. Same root cause as the rule's original target: prose asserts
in one voice whether or not anything was run, and nothing re-checks it when the
world changes.
Worth extending the rule to say that a resolved caveat is a defect like any
stale claim, and that whoever resolves one is responsible for grepping for its
copies. Not urgent; it is a documentation-discipline change, not a code one.
Out of scope here
docs/BRIDGE.md names several older follow-ups that predate this work and are not part of this issue: fsync-on-append for spool writes, pruning spools
for dead sessions, list_sessions-based machine scoping (v2), periodic webhook
re-assertion, and a systemd unit for Linux. Don't fold them in.
Follow-ups left after #122 closed. Written to be self-sufficient: a session
pointed here should not need the thread that produced it.
Orientation
The re-awakening bridge (RFC #122) wakes an idle Claude Code session by
typing a fixed prompt into its terminal pane, so a DM reaches a session whose
human is away instead of waiting for the next prompt.
docs/BRIDGE.md— read this first; it is the contract.docs/superpowers/specs/2026-08-10-mux-wake-backend-design.md— why things are the way they are, including decisions that were reversed.
src/agent_event_bus/bridge.py(daemon),wake.py(the sharedwake-dir contract — read and written by both the bridge and the CLI, on
purpose),
cli.py(panes,wake-state).evansenter/dotfiles,home/.claude/hooks/(
session-start.sh,session-end.sh,wake-state.sh).State of the bus host (
mac-mini.local, as of 2026-08-16)the backend supports it, but no session runs under it).
AGENT_EVENT_BUS_BRIDGE_BACKEND=mux(injecting).
/healthreportsregistered: true.wake/panes.jsonis{}. Sessions map themselves at SessionStart, soany session started before dotfiles#338 merged is unmapped and unwakeable
until it restarts. An empty file is normal, not a fault.
~/.claude/settings.jsonis asymlink into the dotfiles repo. The bridge plist is not — it is a copy in
~/Library/LaunchAgents, so plist changes needmake install-bridge.1. Reboot ← the one that needs a human
The actual supervision requirement (
RunAtLoadplus login), never verifiedbecause it needs the host restarted.
After the next reboot:
The third line is the one that matters: it must name the multiplexers found on
launchd's PATH, which is narrower than a shell's.
2.
ZELLIJ_PANE_IDuniqueness across a full pane setThe writer trusts
$ZELLIJ_PANE_IDto identify a pane within a zellij session.Verified directly: a single-pane session, and cross-tab addressing (pane 0 in
tab 1 accepted a write while tab 2 was focused). Not verified: that the id
stays unique and stable across a session's full pane set in daily use —
splits, closes, reopens.
To check: in one zellij session, open several panes across tabs, and in each
run
echo $ZELLIJ_PANE_ID. Confirm no two share an id, then confirm each isaddressable:
Failure shape: a wake typed into the wrong pane of the right session, which
looks exactly like the stale-mapping symptom and would be easy to misdiagnose.
3. No established minimum zellij version
Only 0.44.3 has been exercised, so
docs/BRIDGE.mdnames no floor. Theinjection surface is
zellij --session <n> action write-chars -p <pane>plusaction write -p <pane> 13;-pis the part most likely to differ acrossversions.
A
spool-mux-failednow carries zellij's own stderr, so a CLI-surface mismatchis self-diagnosing — but the floor is still unknown. Worth pinning if this ever
runs on a second machine.
4. Optional: per-tool-call marker refresh
--busy-ttldefaults to 1 hour because the only refresh in the default wiringis
UserPromptSubmit. A turn that outruns it reads as idle for the remainder,so a late DM can inject mid-turn.
Any hook firing during a turn can run
agent-event-bus-cli wake-state busytomake the marker a heartbeat, allowing a much lower TTL. Not wired by default:
it costs a subprocess per tool call. Do this only if long turns turn out to be
common here.
5. The verification-boundaries rule only covers half its problem
CLAUDE.md(#148) says host-specific claims must be marked unverified ratherthan asserted. That catches a claim made without evidence. It does not
catch the inverse, which #153 then had to fix twice:
install-bridge-launchagent.shprinted "no session is woken until a drainhook exists" — true before feat(bridge): wake idle sessions via tmux or zellij (#122) #149, and still printing during the install that
enabled injection.
docs/BRIDGE.mdcarried "unverified for Claude Code's TUI" after it had beenmeasured.
Both are caveats that outlived their uncertainty, with nothing forcing them
to track reality. Same root cause as the rule's original target: prose asserts
in one voice whether or not anything was run, and nothing re-checks it when the
world changes.
Worth extending the rule to say that a resolved caveat is a defect like any
stale claim, and that whoever resolves one is responsible for grepping for its
copies. Not urgent; it is a documentation-discipline change, not a code one.
Out of scope here
docs/BRIDGE.mdnames several older follow-ups that predate this work and arenot part of this issue: fsync-on-append for spool writes, pruning spools
for dead sessions,
list_sessions-based machine scoping (v2), periodic webhookre-assertion, and a systemd unit for Linux. Don't fold them in.