Skip to content

feat(agents): wake remote agents from scheduled workflows - #119

Merged
tee8z merged 1 commit into
mainfrom
feat/remote-workflow-wake
Nov 4, 2026
Merged

tee8z merged 1 commit into
mainfrom
feat/remote-workflow-wake

Conversation

@tee8z

@tee8z tee8z commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Scheduled workflow messages currently reach the relay but fail remote-agent admission because the relay signs them. This change authorizes those messages through their verified workflow owner, so schedules can wake registered remote agents while desktop is closed.

The existing desktop Workflows → Schedule → Send message flow supplies the task. Save an explicit @AgentName mention in the step text. Schedules use UTC.

flowchart LR
    S[Relay schedule] --> P[Signed workflow prompt]
    P --> M[Manager checks owner and current permissions]
    M --> A[Registered remote session]
    A --> R[Reply in the scheduled thread]
Loading

An owner's workflow can use their private agent. Teammate workflows require the target agent's current sharing grant. Admission still checks channel membership, workspace membership, ownership, archive status, and task budgets. Only the active relay's signed proof for an explicit saved mention delegates authority. Event signatures and budget receipts retain the actual signer, including during handoffs and restart.

The manager and harness share the existing workflow-proof parser. The signed admission response supplies the workflow owner for root-task attribution; handoffs retain their sending agent. Budget storage and control-message formats remain unchanged.

Validation:

  • Formatting, strict workspace Clippy across all targets, and repository file-size checks passed.
  • SDK: 331 tests passed. Manager: 141 tests passed.
  • ACP: 1,100 unit tests passed initially. One existing 150 ms observer-gate test passed on isolated retry; all 27 integration tests passed.
  • Real local relay: an actual cron fire woke one private agent while its owner was disconnected. The session matched the scheduled channel and thread, and the shipped harness replied once.
  • Real local relay: cross-owner startup, handoff, same-thread replies, and refusal after sharing revocation passed.
  • just ci passed Rust formatting, workspace Clippy, desktop checks, and Tauri formatting. It then stopped in unchanged legacy Tauri code because this host lacks glib-2.0.pc. Remaining full-repository gates require the CI environment.
  • CodeRabbit completed review of 0e26e06 with no actionable findings. Review result.

Roll out matching manager and agent images with ordinary mention startup and the existing shared_tasks: true configuration. No mobile changes or capacity changes are included.

Human acceptance is pending. Register a remote agent and create a short scheduled channel task with an explicit mention. Close desktop and confirm a remote reply beneath the scheduled message. Disable the workflow afterward.

Local integration uses a deterministic ACP adapter and simulated Kubernetes. Real Pod startup and external model execution require staging testing.

Signed-off-by: Tee8z <tee8z@protonmail.com>
@tee8z tee8z self-assigned this Nov 4, 2026
@tee8z
tee8z requested a review from thebrandonlucas November 4, 2026 17:53
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The change adds workflow-owner verification and applies the resolved identity to shared-task authorization and budget provenance. ACP admission now returns the authorized author or a refusal. New policy tests and an ignored live integration test cover scheduled workflow tasks.

Changes: Scheduled workflow task admission

Layer / File(s) Summary
Workflow owner and task policy
crates/buzz-sdk/src/workflow.rs, crates/buzz-sdk/src/lib.rs, crates/buzz-sdk/src/sharing/policy.rs, crates/buzz-sdk/src/sharing/policy/workflow_tests.rs, crates/buzz-sdk/src/sharing/policy/tests.rs, crates/buzz-sdk/src/sharing/task.rs, crates/buzz-acp/src/lib.rs
The SDK verifies workflow ownership and resolves the requester and human identities for task authorization. The Task records the original signer. Policy tests cover workflow access, provenance, handoffs, grants, and membership or workspace changes.
Budget requester provenance
crates/buzz-sdk/src/sharing/budget.rs, crates/buzz-sdk/src/sharing/budget/tests.rs, crates/buzz-sandbox-reaper/src/shared_budget/tests.rs
Initial budget receipts and validation now use Task.origin_requester. Test fixtures initialize that field.
ACP admission and admitted author
crates/buzz-acp/src/sharing.rs, crates/buzz-acp/src/lib.rs, crates/buzz-acp/src/pool.rs, crates/buzz-acp/src/sharing/tests.rs
Authorization returns Admission::Allowed(PublicKey) or Admission::Refused(String). Successful root-task replies use a valid human key when present; handoffs use the trigger author. ACP callers apply the returned author to authorized events.
Workflow integration coverage and guidance
crates/buzz-sandbox-reaper/src/mentions/tests/shared_live.rs, crates/buzz-sandbox-reaper/src/mentions/tests/workflow_live.rs, docs/shared-channel-agents.md
An ignored live test exercises a scheduled workflow with a private agent and verifies sandbox annotations and reply ancestry. The guide describes workflow setup, admission requirements, and the local test command.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RelayScheduler
  participant Manager
  participant Sandbox
  participant Harness
  participant Agent
  RelayScheduler->>Manager: deliver scheduled workflow prompt
  Manager->>Sandbox: create sandbox with channel and thread annotations
  Harness->>Agent: run the task
  Agent->>RelayScheduler: publish reply with prompt ancestry
Loading

Possibly related PRs

  • voltagecloud/buzz#10: Restricts ACP intake to the configured owner and covers relay-signed workflow messages.
  • voltagecloud/buzz#110: Adds shared-task authorization, provenance, handoff tracking, and grant checks that this change extends to workflow events.
  • voltagecloud/buzz#108: Adds the server-side agent wake path that this change exercises with scheduled workflow messages.

Suggested labels: human review wanted

Suggested reviewers: wesbillman, thebrandonlucas

Merge Risk: Minimal · up to 0e26e

No actionable merge-blocking issue was found. Deploy matching manager and agent images as planned.

Pre-merge checks: Passed 4 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Docstring Coverage Inconclusive Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "feat(agents): wake remote agents from scheduled workflows" matches the main change. The PR objectives state the aim is to let scheduled workflow messages wake registered remote agents while…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "feat(agents): wake remote agents from scheduled workflows" matches the main change. The PR objectives state the aim is to let scheduled workflow messages wake registered remote agents while the desktop is closed. The changeset supports this: it adds workflow owner verification (workflow::owner), admission handling in buzz-acp sharing, workflow policy tests, a live workflow integration test that runs the agent while the owner is offline, and docs for scheduling remote-agent work through workflows. The title is concise, specific, and follows a conventional commit style. No unrelated changes are described.

Full details: Docstring Coverage

Explanation

Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 16 functions across 14 files. (2 skipped: 1 unsupported, 1 too large.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between f629605 and 0e26e06.

Files selected for processing: 16
  • crates/buzz-acp/src/lib.rs
  • crates/buzz-acp/src/pool.rs
  • crates/buzz-acp/src/sharing.rs
  • crates/buzz-acp/src/sharing/tests.rs
  • crates/buzz-sandbox-reaper/src/mentions/tests/shared_live.rs
  • crates/buzz-sandbox-reaper/src/mentions/tests/workflow_live.rs
  • crates/buzz-sandbox-reaper/src/shared_budget/tests.rs
  • crates/buzz-sdk/src/lib.rs
  • crates/buzz-sdk/src/sharing/budget.rs
  • crates/buzz-sdk/src/sharing/budget/tests.rs
  • crates/buzz-sdk/src/sharing/policy.rs
  • crates/buzz-sdk/src/sharing/policy/tests.rs
  • crates/buzz-sdk/src/sharing/policy/workflow_tests.rs
  • crates/buzz-sdk/src/sharing/task.rs
  • crates/buzz-sdk/src/workflow.rs
  • docs/shared-channel-agents.md

Included review availability: This review used your included allowance. 1 included review remains after this review. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.

@tee8z

tee8z commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator Author

Agent review of 0e26e06bcd0a18109af3b26992c66e7fe8d742e8: no blocking correctness, security, or product-contract findings. Minimalism 9/10; elegance 9/10; correctness 9/10.

Reviewed the change against the remote-agent vision and review-proven rules. Workflow delegation requires the configured relay's valid signature and canonical, explicit saved-mention proof. Current ownership, membership, sharing grants, and dispatch-time admission remain enforced. Budget receipts retain the signed requester without changing the journal format.

The real local relay exercised scheduled wake with the owner disconnected, correct Sandbox channel/thread binding, and one harness reply. Existing cross-owner handoff and revocation also passed. Kubernetes is simulated and the ACP adapter is deterministic. Human staging acceptance remains pending.

@tee8z
tee8z merged commit 00d7d77 into main Nov 4, 2026
63 checks passed
@tee8z
tee8z deleted the feat/remote-workflow-wake branch November 4, 2026 18:10
@mfethe1

mfethe1 commented Nov 4, 2026

Copy link
Copy Markdown

Sharing cross-tree measurement that lands on the schedule path (we authored upstream block#6090 and block#6578; the signer-idempotency matrix is on #110, the wake-listener seam on #114).

#119 authorizes relay-signed workflow prompts through their verified workflow owner, and your budget layer (crates/buzz-sdk/src/sharing/budget.rs at 8d7dee4) bounds admission with LIFETIME_SECONDS=7d, the 60s future-dated check, MAX_AGENT_TASKS=2 and MAX_HANDOFFS=4, with retries of the same (task, agent) consuming no further budget — receipts still retain the actual signer. That is the same execution-side seam we measured on yjc801/buzz's independent waker, and their staleness numbers make a useful contrast:

Measured on yjc801/buzz (first-hand probe at c7b14c9, re-verified at a6568d6 on 2026-10-30; thread in their block#173 + block#175): a wake whose trigger mention is older than WAKE_DELIVERABLE_AGE_SECS still deploys the agent — the claim path maps FreshButUndeliverable => start-anyway (wake_loop.rs:389-395) and only logs afterwards. Their deliverable age had to widen 900→1500 (ae20fc0), the reached-but-undeliverable band now spans 1501..=2400s (RECONNECT_OVERLAP_SECS = 2*skew+600), and a9e1728 had to re-pin START_REQUEST_MAX_AGE_SECS=900 with a value test so the widened deliverable age would not silently widen the start window too. Two retunings to keep queued/scheduled wakes deliverable, and the overlap band still wakes-then-warns.

Your shape inverts that trade: instead of stale-but-wakes, the schedule path is bounded by the budget — saturation-shaped refusals rather than trigger-age staleness, as far as we can see in the #119 diff (no time constant on trigger age in scope()/authorize(); the 50s manager-exchange timeout and TURN_TIMEOUT=180s bound the wake itself).

Open question: for a saved workflow step that re-fires on a schedule against the same agent — does each fire present a fresh request event (fresh budget key, since Budget::key is (task.origin.request, task.origin.agent)), or does the same saved @AgentName mention journal under one key, where MAX_AGENT_TASKS=2 caps the schedule at two admitted fires before RepeatedAgent refuses until a human re-mentions? If it is the latter, an hourly schedule dies at minute ~30, silently. Even a one-line note in the Workflows docs saying which it is would save the next fork from rediscovering it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants