Skip to content

feat(bridge): Phase 3 proactive scheduler and async Expo Push completion#494

Merged
equationalapplications merged 7 commits into
stagingfrom
phase3
Jun 29, 2026
Merged

feat(bridge): Phase 3 proactive scheduler and async Expo Push completion#494
equationalapplications merged 7 commits into
stagingfrom
phase3

Conversation

@equationalapplications

Copy link
Copy Markdown
Owner

Description

Phase 3 of the MV3 browser extension bridge: Cloud Scheduler–triggered proactive browser tasks, Expo Push delivery when results arrive after the voice session closes, and supporting hardening (billing, auth, rules).

Spec: MV3 Browser Extension Bridge Design (Phase 3)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update
  • Build/CI update

Related Issue

Fixes #

Changes Made

  • Add sendProactive to fcmDispatcher for PROACTIVE_TASK Expo Push payloads
  • Extend pushToLive with sessionId and fall back to sendTaskComplete when the voice WS closes before a browser task result arrives
  • Add POST /agent/browser/scheduler-trigger with Bearer secret auth, rate limiting, billing, and 60s synchronous wait
  • Reject scheduled tasks that require approval (REQUIRES_AUTH 422) — read-only monitoring only for Phase 3 gate
  • Fix Firestore rules to allow approvalToken in auth doc client updates (Phase 2 alignment)
  • Require build-injected env in the extension (fail fast instead of REPLACE_* placeholders)
  • Add Phase 3 implementation plan under docs/superpowers/plans/

Testing

Platforms Tested

  • iOS
  • Android
  • Web

Test Steps

  1. cd cloud-agent && npm test — 191 pass, 0 fail
  2. cd cloud-agent && npm run typecheck — clean
  3. Manual (Task 7 / Phase 3 gate): deploy with SCHEDULER_SECRET, create Cloud Scheduler job, force-run, verify extension wake + PROACTIVE_TASK Expo Push + /talk deep link

Screenshots

Before

N/A — server-side feature

After

N/A — pending manual GCP E2E verification

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

  • Scheduler endpoint requires SCHEDULER_SECRET on Cloud Run; not configured locally (returns 503).
  • Phase 3 gate ("1 working scheduled monitoring task") depends on manual Cloud Scheduler setup documented in the implementation plan.
  • Proactive booking / awaiting_auth scheduled flows are intentionally out of scope; destructive scheduled actions return 422.

Made with Cursor

equationalapplications and others added 6 commits June 29, 2026 17:37
Co-authored-by: Cursor <cursoragent@cursor.com>
…ions

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fail fast when the extension is loaded without esbuild env injection
instead of falling back to REPLACE_* placeholders.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align security rules with the Phase 2 approval flow where mobile writes
approvalToken alongside status and approvedAt.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f237e692-14b0-42bd-be7d-b1cb970a26f6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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: 7

🧹 Nitpick comments (2)
cloud-agent/src/handlers/wsLiveAgentHandler.test.ts (1)

1017-1019: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the propagated sessionId and taskId in these fallback tests.

These tests exercise the new bridge signature, but they currently pass even if sessionId is dropped or swapped because they only check token/body. Pinning those fields here would protect the main contract change in this PR.

Also applies to: 1098-1099

🤖 Prompt for 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.

In `@cloud-agent/src/handlers/wsLiveAgentHandler.test.ts` around lines 1017 -
1019, The fallback tests around sendTaskComplete currently only assert token and
message text, so they can miss regressions where sessionId or taskId are dropped
or swapped. Update the relevant assertions in wsLiveAgentHandler.test.ts for the
sendTaskComplete bridge cases to explicitly check the propagated sessionId and
taskId on expoPushCalls, using the same call objects already referenced by these
tests, so the contract change is protected even if the payload body still
matches.
cloud-agent/src/handlers/schedulerTriggerHandler.test.ts (1)

121-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the failed spend attempt explicitly.

The override throws before incrementing creditCalls.spend, so Line 131 only proves no successful debit was recorded; it does not prove the handler attempted spendCredit. Track a separate attempt counter.

Test refactor
 test('returns 402 when user has insufficient credits', async () => {
+  let spendAttempts = 0
   const { app, creditCalls } = buildApp({
-    spendCredit: async () => { throw new Error('INSUFFICIENT_CREDITS') },
+    spendCredit: async () => {
+      spendAttempts++
+      throw new Error('INSUFFICIENT_CREDITS')
+    },
   })
@@
   assert.equal(res.status, 402)
   assert.match(res.body.error, /insufficient credits/i)
+  assert.equal(spendAttempts, 1)
   assert.equal(creditCalls.spend, 0)
 })
🤖 Prompt for 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.

In `@cloud-agent/src/handlers/schedulerTriggerHandler.test.ts` around lines 121 -
131, The test in schedulerTriggerHandler.test.ts only checks that no successful
debit was recorded, but it does not explicitly prove the handler attempted
spendCredit when credits are insufficient. Update the buildApp test harness and
the insufficient-credits case to track a separate spend attempt counter inside
the spendCredit override, then assert that the attempt happened even though the
call threw and creditCalls.spend stayed unchanged.
🤖 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 `@cloud-agent/src/handlers/schedulerTriggerHandler.ts`:
- Around line 18-20: The scheduler trigger payload currently treats action as an
arbitrary record, which lets invalid actions pass into downstream checks and
persistence. Update schedulerTriggerHandler to validate action with the shared
DSL schemas, using singleActionSchema and taskIntentSchema instead of a broad
z.record and unsafe SingleAction | SequenceAction casting. Keep the parsing
localized in the schedulerBodySchema flow so the handler only accepts actions
that match the shared contract.

In `@cloud-agent/src/handlers/wsLiveAgentHandler.test.ts`:
- Around line 963-1030: The websocket integration test leaves the server running
if the async flow fails before the final cleanup, which can leak handles and
cause later tests to flake. Wrap the test body that uses createLiveTestServer
and listen in a try/finally so close() is always awaited even when the Promise
rejects. Apply the same cleanup pattern to the other affected websocket test
block as well, and keep the assertions inside the try so failures still
propagate correctly.

In `@cloud-agent/src/handlers/wsLiveAgentHandler.ts`:
- Around line 410-413: The Expo fallback promise chain in getToken.then
currently discards the promise returned by fwd.sendTaskComplete, so delivery
failures bypass the existing catch and logging. Update the getToken(fbUid) flow
in wsLiveAgentHandler so the sendTaskComplete call is returned or awaited within
the then callback, allowing any rejection to propagate into the existing
.catch((err) => console.error('[pushToLive Expo fallback]', err)) handler.

In `@cloud-agent/src/index.test.ts`:
- Around line 443-457: The scheduler-trigger test is restoring SCHEDULER_SECRET
incorrectly, which can leave a stringified undefined value behind and leak env
state on failures. In cloud-agent/src/index.test.ts, update the restore logic
around the createApp/request flow so the original value is put back with delete
when SCHEDULER_SECRET was unset, and ensure the second restoration is also
protected by its own finally block. Use the existing savedSecret variable and
the scheduler-trigger test block to locate the change.

In `@docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md`:
- Line 7: Sync the plan to the shipped scheduler contract: update the
`phase3-proactive-scheduler` document to describe the shared `SCHEDULER_SECRET`
flow used by `cloud-agent/src/index.ts` and
`cloud-agent/src/handlers/schedulerTriggerHandler.ts`, including the current
`createSchedulerTriggerHandler` signature with `creditService` and
`resolveUserId`, and the fact that completion returns `200`. Remove the outdated
OIDC bearer-secret wording, the old 4-argument handler description, and the
`202` success path so the plan matches the implemented `schedulerTriggerHandler`
and `index` wiring.
- Around line 938-940: The fenced infra snippets in this document are missing
language labels, which triggers markdownlint MD040. Update the unlabeled fenced
blocks around the SCHEDULER_SECRET example and the other infra snippet section
so each fence includes an appropriate language tag such as bash or text. Use the
surrounding snippet content to choose the best label and keep the existing text
otherwise unchanged.

In `@firestore.rules`:
- Around line 17-19: The approval flow currently allows approved updates without
validating approvalToken, so tighten both the Firestore rule and the observer
logic. In firestore.rules, keep approvalToken restricted to a non-empty string
for allowed updates, and in cloud-agent/src/handlers/authApprovalObserver.ts
update the approved-handling path so it only resumes the extension after
verifying the token matches a valid expected value before calling the resume
logic.

---

Nitpick comments:
In `@cloud-agent/src/handlers/schedulerTriggerHandler.test.ts`:
- Around line 121-131: The test in schedulerTriggerHandler.test.ts only checks
that no successful debit was recorded, but it does not explicitly prove the
handler attempted spendCredit when credits are insufficient. Update the buildApp
test harness and the insufficient-credits case to track a separate spend attempt
counter inside the spendCredit override, then assert that the attempt happened
even though the call threw and creditCalls.spend stayed unchanged.

In `@cloud-agent/src/handlers/wsLiveAgentHandler.test.ts`:
- Around line 1017-1019: The fallback tests around sendTaskComplete currently
only assert token and message text, so they can miss regressions where sessionId
or taskId are dropped or swapped. Update the relevant assertions in
wsLiveAgentHandler.test.ts for the sendTaskComplete bridge cases to explicitly
check the propagated sessionId and taskId on expoPushCalls, using the same call
objects already referenced by these tests, so the contract change is protected
even if the payload body still matches.
🪄 Autofix (Beta)

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

Run ID: 5c931bd5-a518-4d49-82eb-4d0ef605838e

📥 Commits

Reviewing files that changed from the base of the PR and between 65a9b5d and 165bdc2.

📒 Files selected for processing (14)
  • cloud-agent/src/handlers/schedulerTriggerHandler.test.ts
  • cloud-agent/src/handlers/schedulerTriggerHandler.ts
  • cloud-agent/src/handlers/wsLiveAgentHandler.test.ts
  • cloud-agent/src/handlers/wsLiveAgentHandler.ts
  • cloud-agent/src/index.test.ts
  • cloud-agent/src/index.ts
  • cloud-agent/src/services/fcmDispatcher.test.ts
  • cloud-agent/src/services/fcmDispatcher.ts
  • cloud-agent/src/services/liveToolAdapter.ts
  • cloud-agent/src/tools/browserAction.test.ts
  • cloud-agent/src/tools/browserAction.ts
  • docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md
  • extension/src/env.ts
  • firestore.rules

Comment thread cloud-agent/src/handlers/schedulerTriggerHandler.ts Outdated
Comment thread cloud-agent/src/handlers/wsLiveAgentHandler.test.ts
Comment thread cloud-agent/src/handlers/wsLiveAgentHandler.ts Outdated
Comment thread cloud-agent/src/index.test.ts Outdated
Comment thread docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md Outdated
Comment thread docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md Outdated
Comment thread firestore.rules Outdated
Validate scheduler actions with shared DSL schemas, verify approval
tokens before resuming, and harden test cleanup and error propagation.

Co-authored-by: Cursor <cursoragent@cursor.com>
@equationalapplications

Copy link
Copy Markdown
Owner Author

/fix-pr follow-up

Commit: 5f72fdd7

Review resolution

  • schedulerTriggerHandler DSL validationFixed: parse action with singleActionSchema union instead of z.record (files: cloud-agent/src/handlers/schedulerTriggerHandler.ts)
  • wsLiveAgentHandler.test server cleanupFixed: wrap both Expo Push fallback tests in try/finally so close() always runs (files: cloud-agent/src/handlers/wsLiveAgentHandler.test.ts)
  • pushToLive sendTaskComplete error propagationFixed: await sendTaskComplete inside the .then callback so rejections reach the existing .catch (files: cloud-agent/src/handlers/wsLiveAgentHandler.ts)
  • index.test SCHEDULER_SECRET restoreFixed: use delete when originally unset; wrap 503 test in try/finally (files: cloud-agent/src/index.test.ts)
  • Phase 3 plan contract syncFixed: updated architecture, handler signature, wiring, and type-consistency sections to match shipped SCHEDULER_SECRET / billing / 200 behavior (files: docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md)
  • Plan fenced code language tagsFixed: added bash and text labels to infra snippets (files: docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md)
  • approvalToken securityFixed: Firestore rules require non-empty approvalToken on approved updates; authApprovalObserver verifies token via verifyToken before FCM resume (files: firestore.rules, cloud-agent/src/handlers/authApprovalObserver.ts, cloud-agent/src/handlers/wsBrowserAgentHandler.ts, tests)

Verification

  • typecheck — pass (cd cloud-agent && npm run typecheck)
  • lint — N/A (no lint script in cloud-agent/package.json; root lint targets Expo app)
  • tests — pass (cd cloud-agent && npm test — 192 pass, 0 fail, 1 skipped)

@equationalapplications
equationalapplications merged commit a56fef7 into staging Jun 29, 2026
3 checks passed
@equationalapplications
equationalapplications deleted the phase3 branch June 29, 2026 22:23
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