[BTAPI-76] Delegate the doorbell trigger endpoint to wealthsimple-api #93

Merged
chris merged 6 commits from feature/BTAPI-76 into main 2026-09-14 14:26:33 -06:00
Owner

Ticket

BTAPI-76 Delegate the doorbell trigger endpoint to wealthsimple-api

Summary

  • POST /sync/wealthsimple now also forwards to wealthsimple-api's own POST /sync/trigger, gated behind Config.wealthsimpleApiEnabled. When the flag is off (the default), behavior is unchanged — a single forward to the existing local sync:9998/trigger hop.
  • When the flag is on, the new forward is awaited first, then the existing unconditional local hop runs — sequencing the two so the local tick reads a freshly-written wealthsimple-api cache whenever that upstream trigger runs synchronously (best-effort, not absolute: wealthsimple-api's own debounce can make the await return before its real fetch lands, in which case the existing local retry ladder picks up the fresher data on the next pass).
  • Added requestActivityFeedSync() to lib/wealthsimple/activity-feed-client, timeout sized (4s) to fit under the Android doorbell client's own 10s read timeout alongside the local hop's 5s timeout.
  • Added a boot-time config assertion in api/app.ts so a flag-on deployment missing WS_API_KEY fails loud at startup instead of 401ing silently on every doorbell ring.
  • Updated server/lib/wealthsimple/CLAUDE.md and .env.example to document the new branch point.

A related architecture gap was found during pre-PR review: wealthsimple-api's /sync/trigger currently only debounces itself and has no rolling-hour rate ceiling or 401/429 cooldown of its own, unlike the direct-Wealthsimple engine this forward runs alongside. That's tracked separately as WSAPI-8 ("Add a rolling-hour rate ceiling + 401/429 cooldown to /sync/trigger") — out of scope for this PR.

🤖 Generated with Claude Code

## Ticket [BTAPI-76](http://192.168.2.100:7123/home/browse/BTAPI-76/) Delegate the doorbell trigger endpoint to wealthsimple-api ## Summary - `POST /sync/wealthsimple` now also forwards to `wealthsimple-api`'s own `POST /sync/trigger`, gated behind `Config.wealthsimpleApiEnabled`. When the flag is off (the default), behavior is unchanged — a single forward to the existing local `sync:9998/trigger` hop. - When the flag is on, the new forward is awaited first, then the existing unconditional local hop runs — sequencing the two so the local tick reads a freshly-written wealthsimple-api cache whenever that upstream trigger runs synchronously (best-effort, not absolute: wealthsimple-api's own debounce can make the await return before its real fetch lands, in which case the existing local retry ladder picks up the fresher data on the next pass). - Added `requestActivityFeedSync()` to `lib/wealthsimple/activity-feed-client`, timeout sized (4s) to fit under the Android doorbell client's own 10s read timeout alongside the local hop's 5s timeout. - Added a boot-time config assertion in `api/app.ts` so a flag-on deployment missing `WS_API_KEY` fails loud at startup instead of 401ing silently on every doorbell ring. - Updated `server/lib/wealthsimple/CLAUDE.md` and `.env.example` to document the new branch point. A related architecture gap was found during pre-PR review: `wealthsimple-api`'s `/sync/trigger` currently only debounces itself and has no rolling-hour rate ceiling or 401/429 cooldown of its own, unlike the direct-Wealthsimple engine this forward runs alongside. That's tracked separately as **WSAPI-8** ("Add a rolling-hour rate ceiling + 401/429 cooldown to /sync/trigger") — out of scope for this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Forwards the doorbell trigger to wealthsimple-api's POST /sync/trigger,
mirroring fetchActivityFeed's conventions with a longer timeout for the
non-debounced round trip.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POST /sync/wealthsimple now forwards to wealthsimple-api's own
/sync/trigger, awaited before the existing local sync:9998 hop, only
when Config.wealthsimpleApiEnabled is true. Off (the default), behavior
is unchanged. The forward's failure is logged and swallowed so it can
never block or flip the response to the app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a bullet to "The sync trigger — the doorbell" section and a new
"BTAPI-76: the doorbell forward" subsection under "Migration cutover
flag", matching BTAPI-75's established documentation pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Lower SYNC_TRIGGER_TIMEOUT_MS to 4s so the doorbell route's worst
  case (new forward + local hop) fits under the Android client's own
  10s read timeout.
- Assert wealthsimple-api client config at api/app.ts boot too, so a
  flag-on deployment missing WS_API_KEY fails loud instead of 401ing
  silently on every ring.
- Add an ordering test proving the wealthsimple-api forward is awaited
  before the local hop, not fire-and-forget.
- Soften doc/comment claims that the await means the local tick "never"
  reads a stale cache — untrue while wealthsimple-api's own trigger is
  debounced.
- Cancel the unread response body on requestActivityFeedSync's success
  path.
- Split the "distinct log messages" test into two arrays with negative
  assertions, so it actually proves the messages differ per failure mode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix 2 of the pre-PR review added the same boot-time assertion to
api/app.ts, alongside sync/app.ts. .env.example still described it as
sync-only in three spots; now names both processes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
[BTAPI-76] Compress duplicated cross-reference in wealthsimple CLAUDE.md.
All checks were successful
server / check (pull_request) Successful in 33s
plane-sync / sync (pull_request) Successful in 1s
4f938a18d7
Step 8d: reread the file this branch touched twice (implementation +
8c fix-up). The one bloat spot was the "sync trigger" section's
doorbell-forward bullet, which pointed to the new BTAPI-76 subsection
twice in one sentence and restated a nuance that subsection already
covers in full. Tightened to a single forward reference; no content
lost. Rest of the file's additions match BTAPI-75's established
density and didn't need trimming.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
chris merged commit de56cb80c2 into main 2026-09-14 14:26:33 -06:00
chris deleted branch feature/BTAPI-76 2026-09-14 14:26:33 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chris/budget-tracker!93
No description provided.