chore(webhook): pin local-webhook 0.10.1 so green CI stops spawning sessions - #196
Conversation
|
CI here is red for a reason that predates this branch: The leg this PR actually moves is green: Not merging this myself — it changes what the next 🤖 Generated with Claude Code |
|
Heads-up: #199 bumps the pin straight past 0.10.1 to local-channels 74a99dd (0.11.0, which contains the 0.10.1 brake unchanged for rule-less watches) and adds the policy half (#197). If #199 lands first, the pin change in the PR attached here becomes redundant — its green-run VM test is still worth landing as-is, since the rule-less watch it exercises keeps the failures-only brake. |
…essions The standing watch on this box put four hook-* sessions on defangdevs/agent-box this afternoon for a repo with nothing wrong: merging #189 emitted a green check_run.completed per job, a green workflow_run, a Pages deployment, and a run the concurrency group cancelled. Each session read CI, concluded "nothing to do", and held one of the four hook-session slots that a real failure needs. 0.10.0 was supposed to have stopped that — its own tool description, INSTRUCTIONS and the agent-box-webhook --deliver-to help all say a CI event spawns only on a FAILURE. The implementation only used the outcome to decide whether a CI event could override ignoreSenders, so with the sender not on the ignore list nothing was gated. An ignore list names the box's own identity (`defangdevs`), while CI is triggered by whoever pushed (`lionello`), so on this box the brake was never reached. local-channels 0.10.1 moves the verdict into dispatch_event: a CI outcome that is not a terminal failure never spawns, whoever triggered it. A failure still overrides an ignored sender, and session delivery is untouched. - `webhook.rev` / `webhook.sha256` → local-channels aec22b5 (0.10.1), and `modules/agent-box.nix` regenerated from the `.in` source. - `tests/webhook.nix` gains a leg for it, placed where neither other brake can explain the result: the owning session has already handed the topic back and the sender is on no ignore list, so only the outcome can hold the spawn. It asserts the suppression log line and that no hook-* session appears — the leg fails against a pre-0.10.1 pin, which is what makes it a pin test. Not covered here: the session plugin cache still carries its own copy of webhook.py (#193), so a session's MCP peer keeps running 0.10.0 until that is bumped too. Only the dispatcher's copy decides spawns, so the box gets the fix from this pin alone. ## Verification - `nix run .#assemble` regenerated the module; `nix build` of the five aarch64-evaluable checks (module-generated-up-to-date, module-single-file, webhook-route, download-route, multi-user) passes natively. - `nix eval .#checks.x86_64-linux.webhook.drvPath` resolves, so the new test script is valid Nix, and `check-testscript.sh tests/webhook.nix` passes both the driver's ty and ruff gates. - Upstream fix landed as defangdevs/local-channels#12 with 60 unit + e2e tests green on python 3.9 and 3.x, mutation-checked. Claude-Session: https://claude.ai/code/session_0195u4LJGkuYGkxCQQYEXEK1 Co-authored-by: defangdevs <defangdevs@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
771c962 to
2e45c20
Compare
|
CI was red on The fix already exists as #198 (green). Rather than duplicate it, this branch is now rebased on
|
|
Recurrence, 2026-08-10 08:03 UTC. The box is still on 0.10.0, so green CI still spawns. Three green runs on
That batch started two hook sessions, Two details this run adds to the PR body:
Slot cost is now 3 of 4. The third slot holds This pin is blocked behind #198, which is green and still open. #199 supersedes the pin, and its CI is red for the same 🤖 Generated with Claude Code |
…#197) A standing watch spawned a session for every non-CI event from an unmuted sender, and ignore_senders was the only lever — sender-granular, so stopping a human's close/merge echoes meant muting the human, which also swallowed the new issues the watch exists to catch. This box mutes its owner today for exactly that reason, and echo spawns eat hook- session slots that drop at the cap (#170). local-webhook 0.11.0 (local-channels#15, spun out of local-channels#14 after #13 settled that the upstream repo is mechanism only) adds per- subscription when/drop payload predicates: an entry carrying them owns its whole spawn policy — the failures-only CI brake steps aside, sender muting moves inside the rules where it can be scoped to echo-shaped events, and every declined event is logged. This PR is the policy half: - webhook.watchPolicy option: declarative rules per topic, enforced onto the matching filter.dispatch.json entry by an ExecStartPre on each user's receiver daemon. Upgrade-only: the module governs watches that sessions created, it never creates one, so the default is inert on a box without such a watch. The declaration replaces the managed fields (when/drop/ignoreSenders/note) wholesale; runtime fields (ttl, timestamps) stay the entry's own. - Default policy for the box's own-repos watch (github:defangdevs/*): spawn for issues/PRs opened or reopened by anyone but the box's own login, and for terminal CI failures whoever triggered the run; closes, merges, pushes, green and in-flight CI spawn nothing. The ignoreSenders on the governed entry is CLEARED — deploying this un-mutes the human owner, whose new issues then get triaged again. - Pin bump to local-channels 74a99dd (0.11.0), superseding the pin half of #196 (its green-run VM test remains valid: rule-less watches keep the 0.10.1 failures-only brake). - CLI + built-in AGENTS.md documentation for --when/--drop and the governed-entry rule (edit the NixOS config, not the entry). - VM test: a governed watch gets its rules and loses its ad-hoc sender mute on daemon restart while a rule-less sibling entry is untouched; a close echo is declined (and the decline logged); an outsider's opened issue still spawns. Verification: - nix run .#assemble; checks.aarch64-linux module-generated-up-to-date, multi-user, module-single-file, webhook-route, download-route all pass natively. - tests/webhook.nix passes nix-instantiate --parse and the ty/ruff testscript gates (the VM test itself is x86_64-only; not run here). - The reconcile jq was exercised against a fixture mirroring this box's live filter.dispatch.json (entry with ignoreSenders ["defangdevs","lionello"]): rules applied, mute cleared, timestamps/ ttl preserved, sibling entries untouched. The governed entry was then routed through the pinned webhook.py 0.11.0 directly: human's opened issue spawns, box echo / PR close / merge push / green run do not, box's own failing run does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SDHb5tyTqZwzKM6Qdr8cxv
…#197) A standing watch spawned a session for every non-CI event from an unmuted sender, and ignore_senders was the only lever — sender-granular, so stopping a human's close/merge echoes meant muting the human, which also swallowed the new issues the watch exists to catch. This box mutes its owner today for exactly that reason, and echo spawns eat hook- session slots that drop at the cap (#170). local-webhook 0.11.0 (local-channels#15, spun out of local-channels#14 after #13 settled that the upstream repo is mechanism only) adds per- subscription when/drop payload predicates: an entry carrying them owns its whole spawn policy — the failures-only CI brake steps aside, sender muting moves inside the rules where it can be scoped to echo-shaped events, and every declined event is logged. This PR is the policy half: - webhook.watchPolicy option: declarative rules per topic, enforced onto the matching filter.dispatch.json entry by an ExecStartPre on each user's receiver daemon. Upgrade-only: the module governs watches that sessions created, it never creates one, so the default is inert on a box without such a watch. The declaration replaces the managed fields (when/drop/ignoreSenders/note) wholesale; runtime fields (ttl, timestamps) stay the entry's own. - Default policy for the box's own-repos watch (github:defangdevs/*): spawn for issues/PRs opened or reopened by anyone but the box's own login, and for terminal CI failures whoever triggered the run; closes, merges, pushes, green and in-flight CI spawn nothing. The ignoreSenders on the governed entry is CLEARED — deploying this un-mutes the human owner, whose new issues then get triaged again. - Pin bump to local-channels 74a99dd (0.11.0), superseding the pin half of #196 (its green-run VM test remains valid: rule-less watches keep the 0.10.1 failures-only brake). - CLI + built-in AGENTS.md documentation for --when/--drop and the governed-entry rule (edit the NixOS config, not the entry). - VM test: a governed watch gets its rules and loses its ad-hoc sender mute on daemon restart while a rule-less sibling entry is untouched; a close echo is declined (and the decline logged); an outsider's opened issue still spawns. Verification: - nix run .#assemble; checks.aarch64-linux module-generated-up-to-date, multi-user, module-single-file, webhook-route, download-route all pass natively. - tests/webhook.nix passes nix-instantiate --parse and the ty/ruff testscript gates (the VM test itself is x86_64-only; not run here). - The reconcile jq was exercised against a fixture mirroring this box's live filter.dispatch.json (entry with ignoreSenders ["defangdevs","lionello"]): rules applied, mute cleared, timestamps/ ttl preserved, sibling entries untouched. The governed entry was then routed through the pinned webhook.py 0.11.0 directly: human's opened issue spawns, box echo / PR close / merge push / green run do not, box's own failing run does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SDHb5tyTqZwzKM6Qdr8cxv
The standing watch on this box put four hook-* sessions on defangdevs/agent-box
this afternoon for a repo with nothing wrong: merging #189 emitted a green
check_run.completed per job, a green workflow_run, a Pages deployment, and a run
the concurrency group cancelled. Each session read CI, concluded "nothing to do",
and held one of the four hook-session slots that a real failure needs.
0.10.0 was supposed to have stopped that — its own tool description, INSTRUCTIONS
and the agent-box-webhook --deliver-to help all say a CI event spawns only on a
FAILURE. The implementation only used the outcome to decide whether a CI event
could override ignoreSenders, so with the sender not on the ignore list nothing
was gated. An ignore list names the box's own identity (
defangdevs), while CIis triggered by whoever pushed (
lionello), so on this box the brake was neverreached. local-channels 0.10.1 moves the verdict into dispatch_event: a CI
outcome that is not a terminal failure never spawns, whoever triggered it. A
failure still overrides an ignored sender, and session delivery is untouched.
webhook.rev/webhook.sha256→ local-channels aec22b5 (0.10.1), andmodules/agent-box.nixregenerated from the.insource.tests/webhook.nixgains a leg for it, placed where neither other brake canexplain the result: the owning session has already handed the topic back and
the sender is on no ignore list, so only the outcome can hold the spawn. It
asserts the suppression log line and that no hook-* session appears — the leg
fails against a pre-0.10.1 pin, which is what makes it a pin test.
Not covered here: the session plugin cache still carries its own copy of
webhook.py (#193), so a session's MCP peer keeps running 0.10.0 until that is
bumped too. Only the dispatcher's copy decides spawns, so the box gets the fix
from this pin alone.
Verification
nix run .#assembleregenerated the module;nix buildof the fiveaarch64-evaluable checks (module-generated-up-to-date, module-single-file,
webhook-route, download-route, multi-user) passes natively.
nix eval .#checks.x86_64-linux.webhook.drvPathresolves, so the new testscript is valid Nix, and
check-testscript.sh tests/webhook.nixpasses boththe driver's ty and ruff gates.
green on python 3.9 and 3.x, mutation-checked.
🤖 Generated with Claude Code
https://claude.ai/code/session_0195u4LJGkuYGkxCQQYEXEK1