Skip to content

(MOT-4401) feat(harness,console): push trigger-binding change events, drop the console trigger poll - #775

Merged
andersonleal merged 2 commits into
mainfrom
feat/triggers-changed-push
Aug 12, 2026
Merged

(MOT-4401) feat(harness,console): push trigger-binding change events, drop the console trigger poll#775
andersonleal merged 2 commits into
mainfrom
feat/triggers-changed-push

Conversation

@andersonleal

@andersonleal andersonleal commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Why

Live-engine traces showed every open console chat tab polling harness::triggers::list on a 5s interval (setInterval in ChatView, added in #452, pointed at the harness in #687) — 3,704 stored spans on the dev stack, almost all returning {"subscriptions":[]}. Each poll costs two invocations: the handler does a full-scope harness::state::list on the state worker and filters client-side.

Fires/expiry were already push-visible (durable trigger_fired transcript entries), but binding-set mutations had no event — and several happen out-of-turn (expiry sweep, GC, unregister from another tab, harness::function::trigger, deferred approval release), so the poll couldn't be replaced by inference from existing events.

What

Same pattern #452 used to kill the status poll with harness::message-queued:

harness

  • New harness::triggers-changed event trigger type (5th), registered alongside the existing four.
  • Doorbell payload {session_id, timestamp} — consumers refetch harness::triggers::list.
  • Emitted from the four BindingStore CAS primitives (reserve, attach_trigger_id, claim_fire, delete_if_unchanged), so every mutation path — registration, agent/console unregister, fires, once/max_fires retirement, expiry sweep, GC, teardown, rollbacks — rings from one layer. delete loops through delete_if_unchanged, so it's covered.

console/web

  • onTriggersChanged backend hook (same shape as onQueuedMessage), startTriggersChangedSubscription reusing the generic bind() in turn-events-live.
  • ChatView: the 5s interval is gone — initial fetch + doorbell subscription + visibilitychange catch-up (missed doorbells while hidden/throttled). Always-on for the selected conversation, unlike the streaming-gated queued subscription, because bindings mutate outside turns.
  • Ghost/fired-row behavior (seenTriggersRef, mergeFiredTriggers) unchanged; post-unregister refetches kept.

Version skew

Verification

  • cargo fmt --check, clippy --all-features -D warnings, cargo test --all-features (harness) ✓
  • pnpm test (1149 incl. 3 new doorbell subscription tests), pnpm typecheck, biome scoped to touched files ✓ (ChatView has two pre-existing biome errors on main, untouched)
  • Live on the dev stack: swapped the harness binary in, bound a harness::triggers-changed subscription for a test session, then registered a binding through the harness interceptor and unregistered it via harness::triggers::unregister. Traces show exactly three doorbell deliveries (reserve, attach, delete) with the session filter applied, and no periodic triggers::list traffic from the subscriber. Test session, subscription, and engine trigger cleaned up afterwards.

Summary by CodeRabbit

  • New Features

    • Trigger updates now refresh automatically when changes occur or when the page becomes visible again.
    • Added session-specific notifications for trigger changes.
  • Bug Fixes

    • Reduced delays and unnecessary background activity caused by periodic trigger polling.
    • Ensured trigger updates from unrelated sessions are ignored.
  • Tests

    • Added coverage for trigger-change notifications, session filtering, and subscription cleanup.

… drop the console trigger poll

Every open console chat tab polled harness::triggers::list on a 5s
interval (each call a full-scope harness::state::list on the state
worker). Replace the poll with a harness::triggers-changed doorbell —
the same pattern #452 used to kill the status poll:

- harness: 5th event trigger type; {session_id, timestamp} emitted from
  the four BindingStore CAS primitives (reserve, attach_trigger_id,
  claim_fire, delete_if_unchanged), so every mutation path incl. the
  out-of-turn ones (expiry sweep, GC, other-tab unregister, deferred
  release) rings from one layer.
- console: onTriggersChanged backend hook (shape of onQueuedMessage);
  ChatView swaps the interval for initial fetch + doorbell subscription
  + visibilitychange catch-up. Always-on for the selected conversation,
  unlike the streaming-gated queued subscription.

Version skew is graceful both ways: an old harness parks the unknown
trigger-type registration (console degrades to mount/visibility/post-
unregister refetches); an old console keeps polling a new harness.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview Aug 11, 2026 8:54pm
workers-tech-spec Ready Ready Preview Aug 11, 2026 8:54pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 57 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@andersonleal, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 89938a78-90c0-475d-94a3-6385593d0441

📥 Commits

Reviewing files that changed from the base of the PR and between 860e53c and a55d784.

📒 Files selected for processing (4)
  • console/web/src/components/chat/ChatView.tsx
  • console/web/src/lib/backend/real.ts
  • console/web/src/lib/backend/serial-refresh.test.ts
  • console/web/src/lib/backend/serial-refresh.ts
📝 Walkthrough

Walkthrough

The harness now emits session-scoped trigger-change events. The web backend subscribes to these events. ChatView replaces five-second polling with event-driven refreshes and visibility-change catch-up handling.

Changes

Trigger refresh flow

Layer / File(s) Summary
Trigger-change event emission
harness/src/events.rs, harness/src/bindings/store.rs, harness/src/deps.rs
TurnEvents registers and emits harness::triggers-changed. BindingStore emits notifications after successful binding changes and fire claims.
Web trigger-change subscription
console/web/src/types/iii-agent-event.ts, console/web/src/lib/backend/types.ts, console/web/src/lib/backend/turn-events-live.ts, console/web/src/lib/backend/real.ts, console/web/src/lib/backend/turn-events-live.test.ts
The web backend defines, registers, filters, exposes, and tests session-scoped trigger-change subscriptions.
Event-driven chat refresh
console/web/src/components/chat/ChatView.tsx, console/web/src/lib/backend/triggers.ts, harness/src/subscriptions/fired.rs
ChatView refreshes triggers after notifications and visibility changes. Trigger comments and merge terminology now describe listing and refetching.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BindingStore
  participant TurnEvents
  participant realBackend
  participant ChatView
  BindingStore->>TurnEvents: emit triggers-changed for session
  TurnEvents->>realBackend: deliver session event
  realBackend->>ChatView: invoke onTriggersChanged callback
  ChatView->>ChatView: refetch trigger list
Loading

Possibly related PRs

  • iii-hq/workers#468: Related trigger polling and fired-trigger history behavior in ChatView.tsx and triggers.ts.

Suggested reviewers: sergiofilhowz

Poem

I’m a rabbit watching events fly,
No five-second clock hops through the sky.
Bindings change, the doorbell rings,
ChatView fetches fresh trigger things.
Cleanup keeps the burrow bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: push trigger-binding change events and removal of console trigger polling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/triggers-changed-push

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@console/web/src/components/chat/ChatView.tsx`:
- Around line 342-356: Update refreshTriggers in the trigger-list subscription
flow to prevent overlapping listTriggers responses from applying stale data.
Track a monotonic request revision (or serialize refreshes) and only update
seenTriggersRef and sessionTriggers when the completed request is still the
newest; preserve the existing doorbell and visibility-change refresh behavior.

In `@console/web/src/lib/backend/types.ts`:
- Around line 317-323: Expose an explicit capability or registration-result
contract for onTriggersChanged in console/web/src/lib/backend/types.ts:317-323,
propagate unsupported or failed subscription setup from the real backend
implementation at console/web/src/lib/backend/real.ts:525-541, and update
ChatView at console/web/src/components/chat/ChatView.tsx:337-357 to retain
bounded refresh polling until subscription support is confirmed. Add a
mixed-version integration test covering a current console connected to a harness
without harness::triggers-changed registration.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 23635333-50b5-4baf-8442-be457c31203a

📥 Commits

Reviewing files that changed from the base of the PR and between 50b37eb and 860e53c.

📒 Files selected for processing (11)
  • console/web/src/components/chat/ChatView.tsx
  • console/web/src/lib/backend/real.ts
  • console/web/src/lib/backend/triggers.ts
  • console/web/src/lib/backend/turn-events-live.test.ts
  • console/web/src/lib/backend/turn-events-live.ts
  • console/web/src/lib/backend/types.ts
  • console/web/src/types/iii-agent-event.ts
  • harness/src/bindings/store.rs
  • harness/src/deps.rs
  • harness/src/events.rs
  • harness/src/subscriptions/fired.rs

Comment thread console/web/src/components/chat/ChatView.tsx Outdated
Comment thread console/web/src/lib/backend/types.ts
…on gaps

Review fixes on #775:

- serialRefresh: doorbells arrive at-least-once and burst on rapid fires;
  refetches now serialize behind one in-flight list read with a single
  trailing rerun, so an older snapshot can never resolve after and
  overwrite a newer one. reset() discards in-flight responses on
  conversation switch/unmount (also fixes the pre-existing late-write
  into a switched conversation).
- ChatView subscribes to the doorbell BEFORE the initial snapshot, so a
  mutation in the setup gap rings instead of being missed.
- realOnTriggersChanged reseeds on every 'connected' transition: the SDK
  replays trigger registrations on reconnect but outage-window doorbells
  are gone (same repair TracesV2 uses). The reseed is ordered after the
  SDK's synchronous registration replay, so it also covers the first
  connect.
@andersonleal
andersonleal merged commit 4286bec into main Aug 12, 2026
32 of 33 checks passed
@andersonleal
andersonleal deleted the feat/triggers-changed-push branch August 12, 2026 17:20
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