Skip to content

feat: add core durable signals and waits (Phase 1) - #93

Open
hacetin wants to merge 3 commits into
affandar:mainfrom
hacetin:alperen/durable-signals-and-webhooks-f67
Open

hacetin wants to merge 3 commits into
affandar:mainfrom
hacetin:alperen/durable-signals-and-webhooks-f67

Conversation

@hacetin

@hacetin hacetin commented Sep 17, 2026

Copy link
Copy Markdown

Summary

Implements Phase 1 only of #79: authenticated, typed durable signals and signal waits. This partially addresses the issue; it does not close the full webhook/connector roadmap.

  • Add validated version-1 signal envelopes, bounded durable FIFO buffering, deduplication, and attributed untrusted-data delivery.
  • Add wait_for_signal with indefinite or timed waits, interruption/re-arm preserving the original deadline, and wait-ID-scoped Stop/cancellation.
  • Expose start-aware raiseSignal and redacted state inspection through SDK/direct/web clients, authenticated HTTP/RPC, MCP, and tuner tools. Existing event APIs become typed-signal compatibility wrappers rather than raw queue writes.
  • Keep the TUI and portal aligned on pending signal names, deadlines, interruption, and lifecycle Activity/sequence entries; update the proposal, canonical docs, builder guidance, and sample documentation.

Replay and rollout safety

Merged main at 7cac3109, retained its non-signal orchestration 1.0.79 as a frozen handler, and introduced signals under 1.0.80 with pilotswarm.signals.v1 capability-routed turn activities. Signal writes and inspection reject 1.0.79 and older decoders. Existing main executions retain their original handler until a continue-as-new upgrade boundary; old activity descriptors and tool declarations remain unchanged.

The frozen 1.0.79 files match main with only their current-version constant pinned. The existing 1.0.78 frozen copy is unchanged and no longer appears as an addition relative to current main.

Draft-lab compatibility: the earlier unmerged draft used 1.0.79 for signals before main independently assigned that number to different behavior. Those draft-test histories are not main's 1.0.79 histories. Use a fresh isolated test database when moving those experiments to this corrected build; ordinary main histories remain supported.

Review corrections

  • Resolve the orchestration-version collision and enforce the new 1.0.80 signal-support floor.
  • Examine queued input before committing an overdue signal timeout, including after a delayed activity; enforce a named agent's required first tool on a signal-first turn.
  • Preserve saved user input and budget-retry timers during signal bursts, and let a matching wake satisfy the saved waiter without violating oldest-matching FIFO order.
  • Resume configured interval and wall-clock cron schedules after stopping a signal wait, without replacing another active budget-retry timer.
  • Expose the existing Stop control for valid parked signal waits in the portal/TUI, and preserve authoritative wait metadata across CMS-only list refreshes while still clearing ended waits on current rich snapshots.
  • Bound generated reasons so eight maximum-length valid signal names remain inspectable.

Validation

  • Full workspace build passed after merging main.
  • 1,051 SDK unit tests and 42 API tests passed.
  • All app TUI, portal, shared UI, and MCP regression suites passed.
  • 209 focused/native/adjacent orchestration tests passed across 10 files, including seven native Duroxide cases.
  • Native regressions cover replay of main's 1.0.79, rejection of signals on its old decoder, continue-as-new to 1.0.80, pre-deadline signal acceptance across a delayed waiting activity, matching budget-interrupted wakes, Stop/cron continuation, and the existing worker-replacement/buffering coverage. Native fixtures use real Duroxide/SQLite with stub model activities; they are not credentialed PostgreSQL/Copilot tests.

Pending before readiness: the full credentialed ./scripts/run-tests.sh gate is blocked because the worktree has no .env with DATABASE_URL and GITHUB_TOKEN. Secret configuration was not created or modified; native fixtures do not replace live PostgreSQL/Copilot coverage.

The manual section below is preserved verbatim from the user's tests of the earlier PR head fe675a87; it does not claim that the corrected 1.0.80 build has been manually re-tested.

Manual Local Testing

HAPPY PATH:

  1. Start a session send the prompt to both sessions (main and this branch)
  2. Wait a few seconds and send the same payload to both
PAYLOAD='{
  "signalId":"manual-build-1",
  "data":{"buildId":"B-42","status":"succeeded"},
  "wake":false
}'

# Main: expect HTTP 404 because this route does not exist.
curl -sS -o /dev/null -w 'main HTTP %{http_code}\n' \
  -X POST "$MAIN/api/v1/sessions/$MAIN_ID/signals/build-finished" \
  -H 'Content-Type: application/json' -d "$PAYLOAD"
main HTTP 404

# PR: expect a queued receipt.
curl -fsS \
  -X POST "$PR/api/v1/sessions/$PR_ID/signals/build-finished" \
  -H 'Content-Type: application/json' -d "$PAYLOAD" | jq
{
  "ok": true,
  "result": {
    "signalId": "manual-build-1",
    "name": "build-finished",
    "raisedAt": "2026-09-18T14:21:12.863Z",
    "status": "queued"
  }
}

Before:
image

After:
image

Also tested:

  • Indefinite wait/resume and duplicate suppression passed.
  • Pre-arrival buffering passed; FIFO consumption order remains untested.
  • User interruption preserved the deadline; timeout fired once.
  • wake:true woke an idle session; Stop cancelled waiting while preserving buffered signals.
  • A parked wait survived an actual process restart.
  • The 32-signal cap, audited oldest-first overflow, invalid-name rejection, and oversized-payload rejection passed.

Not included

wait_for_any, public webhook endpoints, GitHub/Azure DevOps connectors, trusted event bindings/session templates, coalescing, and webhook receipt/dead-letter operations remain later phases of #79.

Add typed signal envelopes, bounded durable buffering and deduplication, interruptible signal waits, and SDK/API/MCP/inspection/UI surfaces. Freeze orchestration 1.0.78 and route signal-aware turns through 1.0.79.

Refs affandar#79. Webhook ingress, provider connectors, and explicit races remain out of scope.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@hacetin
hacetin marked this pull request as ready for review September 18, 2026 14:28
hacetin and others added 2 commits September 18, 2026 18:56
Preserve main's non-signal orchestration 1.0.79 and introduce signals as 1.0.80. Correct expired timeout ordering, budget-interrupted wakes and retries, startup tool enforcement, Stop/cron continuation, inspection bounds, and shared UI state handling.

Add deterministic and native-runtime regressions for the review scenarios.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep main's deployed 1.0.79 implementation frozen and retain signal-aware 1.0.80 as the new execution target. Resolve the orchestration registry and version-test conflicts without rewriting published history.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

1 participant