Repository navigation
Add agent status webhook sinks to the layout plugin - #3
Merged
Merged
Conversation
notify.sh is a second pane.agent_status_changed hook. It fans out to ~/.config/bercail/notify.d/<name>.toml sinks, one per bot. It keeps each pane's last live status (herdr pane get) under a lock, so it sends only finished (working/blocked -> done/idle) and blocked (once). It skips title-only changes, duplicate hooks, done->idle seen-flips, and shell or review panes. Payloads are bercail.agent_status/v1 with workspace, branch, beads issue, stage, harness, and the headless handoff result. Keys go through a 0600 curl header file, never argv. A 401 or 403 raises one Herdr notification per outage. handoff-spawn now records stage and harness in handoffs.jsonl.
…ess. The notify-hook lock was held for the whole POST (up to ~17s per sink), so other panes' hooks queued behind it and read a later live status; a short working -> done -> working turn was lost. Only the live read, the previous-status swap, and the classification stay under the lock. event_id gains the pid so two transitions in one second stay apart. pane.exited and workspace.closed now drop saved statuses, so a reused pane id (herdr numbers panes per workspace) cannot inherit a stale working and send a false finished. A corrupt sink state file is replaced instead of failing every record and leaving a temp file behind. README: headless runs have no Herdr pane and send no webhook; Shep reads their result when handoff-spawn returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simoncrypta
marked this pull request as ready for review
October 23, 2026 00:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The layout plugin gets a second
pane.agent_status_changedhook,notify.sh, that sends agent status to webhook sinks: one~/.config/bercail/notify.d/<name>.tomlper bot. It sends only meaningful transitions, and it does nothing until a sink file exists.How it works
Herdr's event carries no previous status. It also fires on title-only changes, and on
done→idlewhen a pane is viewed. Sonotify.sh:herdr pane get, under a lock, and keeps each pane's last status in<state>/notify/panes.finishedfor working or blocked → done or idle, andblockedonce per block. Duplicates, seen-flips, stale events, and first sightings send nothing.panes = agent, the default).workspaces), worktree repo (repos), and kind (notify). It also supportsenabledandtimeout.auth = both|bearer|x-automation-key|none), never through argv. It refuses a group- or world-readable key file.notify.sh status(never prints a URL or key) andnotify.sh test <sink>.handoff-spawnnow recordsstageandharnessinhandoffs.jsonlso events can say which desk they came from. Old records are unchanged.Payload (
bercail.agent_status/v1)event_idis the same for every sink and every retry.verified=falsemeans the pane could not be read live, so the event's own status was used.Files
plugins/agentic-layout/notify.sh(new): the hook, plusstatusandtest.plugins/agentic-layout/herdr-plugin.toml: the second[[events]]hook.plugins/agentic-layout/notify.sink.example(new): an annotated sink file.plugins/agentic-layout/tests/notify_test.sh(new): uses fake herdr and curl. It covers classification, filters and routing, enrichment, the live-status check, the unverified fallback, header styles, the key never reaching argv, body, or logs, 401 alerts and recovery, the 5xx retry, refusing an insecure key file, andstatus/test.skills/handoff/scripts/handoff-spawn,tests/handoff_spawn_test.sh: the optionalstageandharnessfields..github/workflows/ci.yml: runsnotify_test.sh.README.md,plugins/agentic-layout/ARCHITECTURE.md: docs.Checks
notify_test), and all 15 roottests/*.shpass.cargo fmt --check,clippy -D warnings,cargo test, and the release build all pass.notify.shandnotify_test.sh.handoff-spawnhas the same 5 findings it had before this change.Rollout
herdr plugin install simoncrypta/bercail/plugins/agentic-layout --ref <tag>orbercail update).~/.config/bercail/notify.d/<bot>.tomlwith mode 600 (url,keyorkey_file, filters). For example, the work bot getsrepos = leftlane_app,panes = agent, andnotify = finished, blocked.bash "$plugin/notify.sh" test <bot>and expect HTTP 200.Open questions
exclude_reposfilter, or a catch-all sink?startedkind (idle → working)?bercail doctorreport sink health, like the last 401?Follow-up (f32274b)
notify-hooklock was held for the whole POST, up to about 17s per sink, so other panes' hooks queued behind it. When a queued hook ran, it read a later live status, and a short working → done → working turn was lost. Now only the live read, the previous-status swap, and the classification run under the lock. A new test holds one sink for 3s and checks that another pane's finish still posts at once. That test fails against the previous commit.event_idnow includes the pid, so two real transitions of the same pane in the same second get different ids.pane.exitedandworkspace.closedhooks delete a pane's saved status. Herdr numbers panes per workspace (w2R:p9), so a reused id could inherit a staleworkingand send a falsefinished.notify_recordfail and left a temp file behind.handoff-spawnreturns.curl -m 8inside the hook without problems, and since delivery no longer holds the lock, a slow sink only delays its own hook.🤖 Generated with Claude Code